Merge pull request #7523 from vector-im/feature/mna/push-toggle-current-session

Push notifications toggle: align implementation for current session (PSG-971)
This commit is contained in:
Maxime NATUREL 2022-11-10 13:44:49 +01:00 committed by GitHub
commit 744b03a806
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 922 additions and 183 deletions

1
changelog.d/7512.feature Normal file
View file

@ -0,0 +1 @@
Push notifications toggle: align implementation for current session

View file

@ -1679,7 +1679,8 @@
<string name="create_new_room">Create New Room</string>
<string name="create_new_space">Create New Space</string>
<string name="error_no_network">No network. Please check your Internet connection.</string>
<string name="error_check_network">Something went wrong. Please check your network connection and try again.</string>
<!-- TODO delete -->
<string name="error_check_network" tools:ignore="UnusedResources">Something went wrong. Please check your network connection and try again.</string>
<string name="change_room_directory_network">"Change network"</string>
<string name="please_wait">"Please wait…"</string>
<string name="updating_your_data">Updating your data…</string>

View file

@ -16,6 +16,9 @@
package org.matrix.android.sdk.api.session.homeserver
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.util.Optional
/**
* This interface defines a method to retrieve the homeserver capabilities.
*/
@ -30,4 +33,9 @@ interface HomeServerCapabilitiesService {
* Get the HomeServer capabilities.
*/
fun getHomeServerCapabilities(): HomeServerCapabilities
/**
* Get a LiveData on the HomeServer capabilities.
*/
fun getHomeServerCapabilitiesLive(): LiveData<Optional<HomeServerCapabilities>>
}

View file

@ -16,8 +16,10 @@
package org.matrix.android.sdk.internal.session.homeserver
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.util.Optional
import javax.inject.Inject
internal class DefaultHomeServerCapabilitiesService @Inject constructor(
@ -33,4 +35,8 @@ internal class DefaultHomeServerCapabilitiesService @Inject constructor(
return homeServerCapabilitiesDataSource.getHomeServerCapabilities()
?: HomeServerCapabilities()
}
override fun getHomeServerCapabilitiesLive(): LiveData<Optional<HomeServerCapabilities>> {
return homeServerCapabilitiesDataSource.getHomeServerCapabilitiesLive()
}
}

View file

@ -16,9 +16,14 @@
package org.matrix.android.sdk.internal.session.homeserver
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.kotlin.where
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.database.mapper.HomeServerCapabilitiesMapper
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
import org.matrix.android.sdk.internal.database.query.get
@ -26,7 +31,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
import javax.inject.Inject
internal class HomeServerCapabilitiesDataSource @Inject constructor(
@SessionDatabase private val monarchy: Monarchy
@SessionDatabase private val monarchy: Monarchy,
) {
fun getHomeServerCapabilities(): HomeServerCapabilities? {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
@ -35,4 +40,14 @@ internal class HomeServerCapabilitiesDataSource @Inject constructor(
}
}
}
fun getHomeServerCapabilitiesLive(): LiveData<Optional<HomeServerCapabilities>> {
val liveData = monarchy.findAllMappedWithChanges(
{ realm: Realm -> realm.where<HomeServerCapabilitiesEntity>() },
{ HomeServerCapabilitiesMapper.map(it) }
)
return Transformations.map(liveData) {
it.firstOrNull().toOptional()
}
}
}

View file

@ -0,0 +1,39 @@
/*
* 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.core.notification
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class EnableNotificationsSettingUpdater @Inject constructor(
private val updateEnableNotificationsSettingOnChangeUseCase: UpdateEnableNotificationsSettingOnChangeUseCase,
) {
private var job: Job? = null
fun onSessionsStarted(session: Session) {
job?.cancel()
job = session.coroutineScope.launch {
updateEnableNotificationsSettingOnChangeUseCase.execute(session)
}
}
}

View file

@ -0,0 +1,50 @@
/*
* 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.core.notification
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
/**
* Listen for changes in either Pusher or Account data to update the local enable notifications
* setting for the current device.
*/
class UpdateEnableNotificationsSettingOnChangeUseCase @Inject constructor(
private val vectorPreferences: VectorPreferences,
private val getNotificationsStatusUseCase: GetNotificationsStatusUseCase,
) {
suspend fun execute(session: Session) {
val deviceId = session.sessionParams.deviceId ?: return
getNotificationsStatusUseCase.execute(session, deviceId)
.onEach(::updatePreference)
.collect()
}
private fun updatePreference(notificationStatus: NotificationsStatus) {
when (notificationStatus) {
NotificationsStatus.ENABLED -> vectorPreferences.setNotificationEnabledForDevice(true)
NotificationsStatus.DISABLED -> vectorPreferences.setNotificationEnabledForDevice(false)
else -> Unit
}
}
}

View file

@ -97,12 +97,6 @@ class PushersManager @Inject constructor(
return session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
}
suspend fun togglePusherForCurrentSession(enable: Boolean) {
val session = activeSessionHolder.getSafeActiveSession() ?: return
val pusher = getPusherForCurrentSession() ?: return
session.pushersService().togglePusher(pusher, enable)
}
suspend fun unregisterEmailPusher(email: String) {
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.pushersService().removeEmailPusher(email)

View file

@ -19,6 +19,7 @@ package im.vector.app.core.session
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import im.vector.app.core.extensions.startSyncing
import im.vector.app.core.notification.EnableNotificationsSettingUpdater
import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.settings.VectorPreferences
@ -32,6 +33,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
private val webRtcCallManager: WebRtcCallManager,
private val updateMatrixClientInfoUseCase: UpdateMatrixClientInfoUseCase,
private val vectorPreferences: VectorPreferences,
private val enableNotificationsSettingUpdater: EnableNotificationsSettingUpdater,
) {
suspend fun execute(session: Session, startSyncing: Boolean = true) {
@ -46,5 +48,6 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
if (vectorPreferences.isClientInfoRecordingEnabled()) {
updateMatrixClientInfoUseCase.execute(session)
}
enableNotificationsSettingUpdater.onSessionsStarted(session)
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.lifecycle.asFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.flow.unwrap
import javax.inject.Inject
class CanTogglePushNotificationsViaPusherUseCase @Inject constructor() {
fun execute(session: Session): Flow<Boolean> {
return session
.homeServerCapabilitiesService()
.getHomeServerCapabilitiesLive()
.asFlow()
.unwrap()
.map { it.canRemotelyTogglePushNotificationsOfDevices }
.distinctUntilChanged()
}
}

View file

@ -16,18 +16,15 @@
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import javax.inject.Inject
class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor() {
fun execute(deviceId: String): Boolean {
return activeSessionHolder
.getSafeActiveSession()
?.accountDataService()
?.getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) != null
fun execute(session: Session, deviceId: String): Boolean {
return session
.accountDataService()
.getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) != null
}
}

View file

@ -16,20 +16,15 @@
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 org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
class CheckIfCanTogglePushNotificationsViaPusherUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
class CheckIfCanTogglePushNotificationsViaPusherUseCase @Inject constructor() {
fun execute(): Boolean {
return activeSessionHolder
.getSafeActiveSession()
?.homeServerCapabilitiesService()
?.getHomeServerCapabilities()
?.canRemotelyTogglePushNotificationsOfDevices
.orFalse()
fun execute(session: Session): Boolean {
return session
.homeServerCapabilitiesService()
.getHomeServerCapabilities()
.canRemotelyTogglePushNotificationsOfDevices
}
}

View file

@ -16,12 +16,13 @@
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.flatMapLatest
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.Session
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
@ -29,16 +30,13 @@ 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 canTogglePushNotificationsViaPusherUseCase: CanTogglePushNotificationsViaPusherUseCase,
private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase,
) {
fun execute(deviceId: String): Flow<NotificationsStatus> {
val session = activeSessionHolder.getSafeActiveSession()
fun execute(session: Session, deviceId: String): Flow<NotificationsStatus> {
return when {
session == null -> flowOf(NotificationsStatus.NOT_SUPPORTED)
checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId) -> {
checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(session, deviceId) -> {
session.flow()
.liveUserAccountData(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId)
.unwrap()
@ -46,15 +44,19 @@ class GetNotificationsStatusUseCase @Inject constructor(
.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)
else -> canTogglePushNotificationsViaPusherUseCase.execute(session)
.flatMapLatest { canToggle ->
if (canToggle) {
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

@ -31,14 +31,14 @@ class TogglePushNotificationUseCase @Inject constructor(
suspend fun execute(deviceId: String, enabled: Boolean) {
val session = activeSessionHolder.getSafeActiveSession() ?: return
if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute()) {
if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) {
val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
devicePusher?.let { pusher ->
session.pushersService().togglePusher(pusher, enabled)
}
}
if (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId)) {
if (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(session, deviceId)) {
val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled)
session.accountDataService().updateUserAccountData(
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId,

View file

@ -96,9 +96,11 @@ class SessionOverviewViewModel @AssistedInject constructor(
}
private fun observeNotificationsStatus(deviceId: String) {
getNotificationsStatusUseCase.execute(deviceId)
.onEach { setState { copy(notificationsStatus = it) } }
.launchIn(viewModelScope)
activeSessionHolder.getSafeActiveSession()?.let { session ->
getNotificationsStatusUseCase.execute(session, deviceId)
.onEach { setState { copy(notificationsStatus = it) } }
.launchIn(viewModelScope)
}
}
override fun handle(action: SessionOverviewAction) {

View file

@ -0,0 +1,43 @@
/*
* 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.notifications
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
import javax.inject.Inject
class DisableNotificationsForCurrentSessionUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
private val unifiedPushHelper: UnifiedPushHelper,
private val pushersManager: PushersManager,
private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase,
private val togglePushNotificationUseCase: TogglePushNotificationUseCase,
) {
suspend fun execute() {
val session = activeSessionHolder.getSafeActiveSession() ?: return
val deviceId = session.sessionParams.deviceId ?: return
if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) {
togglePushNotificationUseCase.execute(deviceId, enabled = false)
} else {
unifiedPushHelper.unregister(pushersManager)
}
}
}

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.notifications
import androidx.fragment.app.FragmentActivity
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.UnifiedPushHelper
import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
class EnableNotificationsForCurrentSessionUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
private val unifiedPushHelper: UnifiedPushHelper,
private val pushersManager: PushersManager,
private val fcmHelper: FcmHelper,
private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase,
private val togglePushNotificationUseCase: TogglePushNotificationUseCase,
) {
suspend fun execute(fragmentActivity: FragmentActivity) {
val pusherForCurrentSession = pushersManager.getPusherForCurrentSession()
if (pusherForCurrentSession == null) {
registerPusher(fragmentActivity)
}
val session = activeSessionHolder.getSafeActiveSession() ?: return
if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) {
val deviceId = session.sessionParams.deviceId ?: return
togglePushNotificationUseCase.execute(deviceId, enabled = true)
}
}
private suspend fun registerPusher(fragmentActivity: FragmentActivity) {
suspendCoroutine { continuation ->
try {
unifiedPushHelper.register(fragmentActivity) {
if (unifiedPushHelper.isEmbeddedDistributor()) {
fcmHelper.ensureFcmTokenIsRetrieved(
fragmentActivity,
pushersManager,
registerPusher = true
)
}
continuation.resume(Unit)
}
} catch (error: Exception) {
continuation.resumeWithException(error)
}
}
}
}

View file

@ -57,7 +57,6 @@ import im.vector.app.features.settings.VectorSettingsBaseFragment
import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session
@ -81,6 +80,8 @@ class VectorSettingsNotificationPreferenceFragment :
@Inject lateinit var guardServiceStarter: GuardServiceStarter
@Inject lateinit var vectorFeatures: VectorFeatures
@Inject lateinit var notificationPermissionManager: NotificationPermissionManager
@Inject lateinit var disableNotificationsForCurrentSessionUseCase: DisableNotificationsForCurrentSessionUseCase
@Inject lateinit var enableNotificationsForCurrentSessionUseCase: EnableNotificationsForCurrentSessionUseCase
override var titleRes: Int = R.string.settings_notifications
override val preferenceXmlRes = R.xml.vector_settings_notifications
@ -119,48 +120,25 @@ class VectorSettingsNotificationPreferenceFragment :
(pref as SwitchPreference).isChecked = areNotifEnabledAtAccountLevel
}
findPreference<SwitchPreference>(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)?.let {
pushersManager.getPusherForCurrentSession()?.let { pusher ->
it.isChecked = pusher.enabled
}
findPreference<SwitchPreference>(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)
?.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked ->
if (isChecked) {
enableNotificationsForCurrentSessionUseCase.execute(requireActivity())
it.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked ->
if (isChecked) {
unifiedPushHelper.register(requireActivity()) {
// Update the summary
if (unifiedPushHelper.isEmbeddedDistributor()) {
fcmHelper.ensureFcmTokenIsRetrieved(
requireActivity(),
pushersManager,
vectorPreferences.areNotificationEnabledForDevice()
)
}
findPreference<VectorPreference>(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY)
?.summary = unifiedPushHelper.getCurrentDistributorName()
lifecycleScope.launch {
val result = runCatching {
pushersManager.togglePusherForCurrentSession(true)
}
result.exceptionOrNull()?.let { _ ->
Toast.makeText(context, R.string.error_check_network, Toast.LENGTH_SHORT).show()
it.isChecked = false
}
}
notificationPermissionManager.eventuallyRequestPermission(
requireActivity(),
postPermissionLauncher,
showRationale = false,
ignorePreference = true
)
} else {
disableNotificationsForCurrentSessionUseCase.execute()
notificationPermissionManager.eventuallyRevokePermission(requireActivity())
}
notificationPermissionManager.eventuallyRequestPermission(
requireActivity(),
postPermissionLauncher,
showRationale = false,
ignorePreference = true
)
} else {
unifiedPushHelper.unregister(pushersManager)
session.pushersService().refreshPushers()
notificationPermissionManager.eventuallyRevokePermission(requireActivity())
}
}
}
findPreference<VectorPreference>(VectorPreferences.SETTINGS_FDROID_BACKGROUND_SYNC_MODE)?.let {
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {

View file

@ -0,0 +1,90 @@
/*
* 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.core.notification
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
import im.vector.app.test.fakes.FakeGetNotificationsStatusUseCase
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fakes.FakeVectorPreferences
import kotlinx.coroutines.test.runTest
import org.junit.Test
private const val A_SESSION_ID = "session-id"
class UpdateEnableNotificationsSettingOnChangeUseCaseTest {
private val fakeSession = FakeSession().also { it.givenSessionId(A_SESSION_ID) }
private val fakeVectorPreferences = FakeVectorPreferences()
private val fakeGetNotificationsStatusUseCase = FakeGetNotificationsStatusUseCase()
private val updateEnableNotificationsSettingOnChangeUseCase = UpdateEnableNotificationsSettingOnChangeUseCase(
vectorPreferences = fakeVectorPreferences.instance,
getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase.instance,
)
@Test
fun `given notifications are enabled when execute then setting is updated to true`() = runTest {
// Given
fakeGetNotificationsStatusUseCase.givenExecuteReturns(
fakeSession,
A_SESSION_ID,
NotificationsStatus.ENABLED,
)
fakeVectorPreferences.givenSetNotificationEnabledForDevice()
// When
updateEnableNotificationsSettingOnChangeUseCase.execute(fakeSession)
// Then
fakeVectorPreferences.verifySetNotificationEnabledForDevice(true)
}
@Test
fun `given notifications are disabled when execute then setting is updated to false`() = runTest {
// Given
fakeGetNotificationsStatusUseCase.givenExecuteReturns(
fakeSession,
A_SESSION_ID,
NotificationsStatus.DISABLED,
)
fakeVectorPreferences.givenSetNotificationEnabledForDevice()
// When
updateEnableNotificationsSettingOnChangeUseCase.execute(fakeSession)
// Then
fakeVectorPreferences.verifySetNotificationEnabledForDevice(false)
}
@Test
fun `given notifications toggle is not supported when execute then nothing is done`() = runTest {
// Given
fakeGetNotificationsStatusUseCase.givenExecuteReturns(
fakeSession,
A_SESSION_ID,
NotificationsStatus.NOT_SUPPORTED,
)
fakeVectorPreferences.givenSetNotificationEnabledForDevice()
// When
updateEnableNotificationsSettingOnChangeUseCase.execute(fakeSession)
// Then
fakeVectorPreferences.verifySetNotificationEnabledForDevice(true, inverse = true)
fakeVectorPreferences.verifySetNotificationEnabledForDevice(false, inverse = true)
}
}

View file

@ -29,7 +29,6 @@ import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
import im.vector.app.test.fixtures.PusherFixture
import im.vector.app.test.fixtures.SessionParamsFixture
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
@ -101,19 +100,4 @@ class PushersManagerTest {
pusher shouldBeEqualTo expectedPusher
}
@Test
fun `when togglePusherForCurrentSession, then do service toggle pusher`() = runTest {
val deviceId = "device_id"
val sessionParams = SessionParamsFixture.aSessionParams(
credentials = CredentialsFixture.aCredentials(deviceId = deviceId)
)
session.givenSessionParams(sessionParams)
val pusher = PusherFixture.aPusher(deviceId = deviceId)
pushersService.givenGetPushers(listOf(pusher))
pushersManager.togglePusherForCurrentSession(true)
pushersService.verifyTogglePusherCalled(pusher, true)
}
}

View file

@ -19,6 +19,7 @@ package im.vector.app.core.session
import im.vector.app.core.extensions.startSyncing
import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
import im.vector.app.test.fakes.FakeContext
import im.vector.app.test.fakes.FakeEnableNotificationsSettingUpdater
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fakes.FakeVectorPreferences
import im.vector.app.test.fakes.FakeWebRtcCallManager
@ -43,12 +44,14 @@ class ConfigureAndStartSessionUseCaseTest {
private val fakeWebRtcCallManager = FakeWebRtcCallManager()
private val fakeUpdateMatrixClientInfoUseCase = mockk<UpdateMatrixClientInfoUseCase>()
private val fakeVectorPreferences = FakeVectorPreferences()
private val fakeEnableNotificationsSettingUpdater = FakeEnableNotificationsSettingUpdater()
private val configureAndStartSessionUseCase = ConfigureAndStartSessionUseCase(
context = fakeContext.instance,
webRtcCallManager = fakeWebRtcCallManager.instance,
updateMatrixClientInfoUseCase = fakeUpdateMatrixClientInfoUseCase,
vectorPreferences = fakeVectorPreferences.instance,
enableNotificationsSettingUpdater = fakeEnableNotificationsSettingUpdater.instance,
)
@Before
@ -68,6 +71,7 @@ class ConfigureAndStartSessionUseCaseTest {
fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true)
fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession)
// When
configureAndStartSessionUseCase.execute(fakeSession, startSyncing = true)
@ -87,6 +91,7 @@ class ConfigureAndStartSessionUseCaseTest {
fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = false)
fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession)
// When
configureAndStartSessionUseCase.execute(fakeSession, startSyncing = true)
@ -106,6 +111,7 @@ class ConfigureAndStartSessionUseCaseTest {
fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true)
fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession)
// When
configureAndStartSessionUseCase.execute(fakeSession, startSyncing = false)

View file

@ -0,0 +1,65 @@
/*
* 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.FakeFlowLiveDataConversions
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fakes.givenAsFlow
import im.vector.app.test.fixtures.aHomeServerCapabilities
import io.mockk.unmockkAll
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
import org.junit.After
import org.junit.Before
import org.junit.Test
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canRemotelyTogglePushNotificationsOfDevices = true)
class CanTogglePushNotificationsViaPusherUseCaseTest {
private val fakeSession = FakeSession()
private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions()
private val canTogglePushNotificationsViaPusherUseCase =
CanTogglePushNotificationsViaPusherUseCase()
@Before
fun setUp() {
fakeFlowLiveDataConversions.setup()
}
@After
fun tearDown() {
unmockkAll()
}
@Test
fun `given current session when execute then flow of the toggle capability is returned`() = runTest {
// Given
fakeSession
.fakeHomeServerCapabilitiesService
.givenCapabilitiesLiveReturns(A_HOMESERVER_CAPABILITIES)
.givenAsFlow()
// When
val result = canTogglePushNotificationsViaPusherUseCase.execute(fakeSession).firstOrNull()
// Then
result shouldBeEqualTo A_HOMESERVER_CAPABILITIES.canRemotelyTogglePushNotificationsOfDevices
}
}

View file

@ -16,7 +16,7 @@
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeSession
import io.mockk.mockk
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
@ -26,18 +26,15 @@ private const val A_DEVICE_ID = "device-id"
class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val fakeSession = FakeSession()
private val checkIfCanTogglePushNotificationsViaAccountDataUseCase =
CheckIfCanTogglePushNotificationsViaAccountDataUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
)
CheckIfCanTogglePushNotificationsViaAccountDataUseCase()
@Test
fun `given current session and an account data for the device id when execute then result is true`() {
// Given
fakeActiveSessionHolder
.fakeSession
fakeSession
.accountDataService()
.givenGetUserAccountDataEventReturns(
type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID,
@ -45,7 +42,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest {
)
// When
val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID)
val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID)
// Then
result shouldBeEqualTo true
@ -54,8 +51,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest {
@Test
fun `given current session and NO account data for the device id when execute then result is false`() {
// Given
fakeActiveSessionHolder
.fakeSession
fakeSession
.accountDataService()
.givenGetUserAccountDataEventReturns(
type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID,
@ -63,7 +59,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest {
)
// When
val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID)
val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID)
// Then
result shouldBeEqualTo false

View file

@ -16,7 +16,7 @@
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fixtures.aHomeServerCapabilities
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
@ -25,37 +25,22 @@ private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canRemotelyToggl
class CheckIfCanTogglePushNotificationsViaPusherUseCaseTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val fakeSession = FakeSession()
private val checkIfCanTogglePushNotificationsViaPusherUseCase =
CheckIfCanTogglePushNotificationsViaPusherUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
)
CheckIfCanTogglePushNotificationsViaPusherUseCase()
@Test
fun `given current session when execute then toggle capability is returned`() {
// Given
fakeActiveSessionHolder
.fakeSession
fakeSession
.fakeHomeServerCapabilitiesService
.givenCapabilities(A_HOMESERVER_CAPABILITIES)
// When
val result = checkIfCanTogglePushNotificationsViaPusherUseCase.execute()
val result = checkIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession)
// 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

@ -17,7 +17,7 @@
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.fakes.FakeSession
import im.vector.app.test.fixtures.PusherFixture
import im.vector.app.test.testDispatcher
import io.mockk.every
@ -25,6 +25,7 @@ import io.mockk.mockk
import io.mockk.verifyOrder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
@ -44,17 +45,16 @@ class GetNotificationsStatusUseCaseTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase =
mockk<CheckIfCanTogglePushNotificationsViaPusherUseCase>()
private val fakeSession = FakeSession()
private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase =
mockk<CheckIfCanTogglePushNotificationsViaAccountDataUseCase>()
private val fakeCanTogglePushNotificationsViaPusherUseCase =
mockk<CanTogglePushNotificationsViaPusherUseCase>()
private val getNotificationsStatusUseCase =
GetNotificationsStatusUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase,
checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase,
canTogglePushNotificationsViaPusherUseCase = fakeCanTogglePushNotificationsViaPusherUseCase,
)
@Before
@ -67,33 +67,21 @@ class GetNotificationsStatusUseCaseTest {
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
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false
every { fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false)
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.NOT_SUPPORTED
verifyOrder {
// we should first check account data
fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID)
fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute()
fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID)
fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession)
}
}
@ -106,12 +94,12 @@ class GetNotificationsStatusUseCaseTest {
enabled = true,
)
)
fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) } returns false
fakeSession.pushersService().givenPushersLive(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false
every { fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(true)
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.ENABLED
@ -120,8 +108,7 @@ class GetNotificationsStatusUseCaseTest {
@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
fakeSession
.accountDataService()
.givenGetUserAccountDataEventReturns(
type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID,
@ -129,11 +116,11 @@ class GetNotificationsStatusUseCaseTest {
isSilenced = false
).toContent(),
)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns true
every { fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false)
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.ENABLED

View file

@ -49,10 +49,11 @@ class TogglePushNotificationUseCaseTest {
PusherFixture.aPusher(deviceId = sessionId, enabled = false),
PusherFixture.aPusher(deviceId = "another id", enabled = false)
)
activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns false
val fakeSession = activeSessionHolder.fakeSession
fakeSession.pushersService().givenPushersLive(pushers)
fakeSession.pushersService().givenGetPushers(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns false
// When
togglePushNotificationUseCase.execute(sessionId, true)
@ -69,13 +70,14 @@ class TogglePushNotificationUseCaseTest {
PusherFixture.aPusher(deviceId = sessionId, enabled = false),
PusherFixture.aPusher(deviceId = "another id", enabled = false)
)
activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
activeSessionHolder.fakeSession.accountDataService().givenGetUserAccountDataEventReturns(
val fakeSession = activeSessionHolder.fakeSession
fakeSession.pushersService().givenPushersLive(pushers)
fakeSession.accountDataService().givenGetUserAccountDataEventReturns(
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
LocalNotificationSettingsContent(isSilenced = true).toContent()
)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns true
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns true
// When
togglePushNotificationUseCase.execute(sessionId, true)

View file

@ -22,11 +22,11 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.test.MavericksTestRule
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.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.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeGetNotificationsStatusUseCase
import im.vector.app.test.fakes.FakePendingAuthHandler
import im.vector.app.test.fakes.FakeSignoutSessionsUseCase
import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase
@ -75,7 +75,7 @@ class SessionOverviewViewModelTest {
private val fakePendingAuthHandler = FakePendingAuthHandler()
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>(relaxed = true)
private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase()
private val fakeGetNotificationsStatusUseCase = mockk<GetNotificationsStatusUseCase>()
private val fakeGetNotificationsStatusUseCase = FakeGetNotificationsStatusUseCase()
private val notificationsStatus = NotificationsStatus.ENABLED
private fun createViewModel() = SessionOverviewViewModel(
@ -88,7 +88,7 @@ class SessionOverviewViewModelTest {
activeSessionHolder = fakeActiveSessionHolder.instance,
refreshDevicesUseCase = refreshDevicesUseCase,
togglePushNotificationUseCase = togglePushNotificationUseCase.instance,
getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase,
getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase.instance,
)
@Before
@ -98,7 +98,11 @@ class SessionOverviewViewModelTest {
every { SystemClock.elapsedRealtime() } returns 1234
givenVerificationService()
every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationsStatus)
fakeGetNotificationsStatusUseCase.givenExecuteReturns(
fakeActiveSessionHolder.fakeSession,
A_SESSION_ID_1,
notificationsStatus
)
}
private fun givenVerificationService(): FakeVerificationService {
@ -412,13 +416,10 @@ class SessionOverviewViewModelTest {
@Test
fun `when viewModel init, then observe pushers and emit to state`() {
val notificationStatus = NotificationsStatus.ENABLED
every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationStatus)
val viewModel = createViewModel()
viewModel.test()
.assertLatestState { state -> state.notificationsStatus == notificationStatus }
.assertLatestState { state -> state.notificationsStatus == notificationsStatus }
.finish()
}

View file

@ -0,0 +1,78 @@
/*
* 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.notifications
import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakePushersManager
import im.vector.app.test.fakes.FakeUnifiedPushHelper
import io.mockk.coJustRun
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Test
private const val A_SESSION_ID = "session-id"
class DisableNotificationsForCurrentSessionUseCaseTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val fakeUnifiedPushHelper = FakeUnifiedPushHelper()
private val fakePushersManager = FakePushersManager()
private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = mockk<CheckIfCanTogglePushNotificationsViaPusherUseCase>()
private val fakeTogglePushNotificationUseCase = mockk<TogglePushNotificationUseCase>()
private val disableNotificationsForCurrentSessionUseCase = DisableNotificationsForCurrentSessionUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
unifiedPushHelper = fakeUnifiedPushHelper.instance,
pushersManager = fakePushersManager.instance,
checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase,
togglePushNotificationUseCase = fakeTogglePushNotificationUseCase,
)
@Test
fun `given toggle via pusher is possible when execute then disable notification via toggle of existing pusher`() = runTest {
// Given
val fakeSession = fakeActiveSessionHolder.fakeSession
fakeSession.givenSessionId(A_SESSION_ID)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns true
coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) }
// When
disableNotificationsForCurrentSessionUseCase.execute()
// Then
coVerify { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, false) }
}
@Test
fun `given toggle via pusher is NOT possible when execute then disable notification by unregistering the pusher`() = runTest {
// Given
val fakeSession = fakeActiveSessionHolder.fakeSession
fakeSession.givenSessionId(A_SESSION_ID)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false
fakeUnifiedPushHelper.givenUnregister(fakePushersManager.instance)
// When
disableNotificationsForCurrentSessionUseCase.execute()
// Then
fakeUnifiedPushHelper.verifyUnregister(fakePushersManager.instance)
}
}

View file

@ -0,0 +1,87 @@
/*
* 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.notifications
import androidx.fragment.app.FragmentActivity
import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeFcmHelper
import im.vector.app.test.fakes.FakePushersManager
import im.vector.app.test.fakes.FakeUnifiedPushHelper
import io.mockk.coJustRun
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Test
private const val A_SESSION_ID = "session-id"
class EnableNotificationsForCurrentSessionUseCaseTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val fakeUnifiedPushHelper = FakeUnifiedPushHelper()
private val fakePushersManager = FakePushersManager()
private val fakeFcmHelper = FakeFcmHelper()
private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = mockk<CheckIfCanTogglePushNotificationsViaPusherUseCase>()
private val fakeTogglePushNotificationUseCase = mockk<TogglePushNotificationUseCase>()
private val enableNotificationsForCurrentSessionUseCase = EnableNotificationsForCurrentSessionUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
unifiedPushHelper = fakeUnifiedPushHelper.instance,
pushersManager = fakePushersManager.instance,
fcmHelper = fakeFcmHelper.instance,
checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase,
togglePushNotificationUseCase = fakeTogglePushNotificationUseCase,
)
@Test
fun `given no existing pusher for current session when execute then a new pusher is registered`() = runTest {
// Given
val fragmentActivity = mockk<FragmentActivity>()
fakePushersManager.givenGetPusherForCurrentSessionReturns(null)
fakeUnifiedPushHelper.givenRegister(fragmentActivity)
fakeUnifiedPushHelper.givenIsEmbeddedDistributorReturns(true)
fakeFcmHelper.givenEnsureFcmTokenIsRetrieved(fragmentActivity, fakePushersManager.instance)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeActiveSessionHolder.fakeSession) } returns false
// When
enableNotificationsForCurrentSessionUseCase.execute(fragmentActivity)
// Then
fakeUnifiedPushHelper.verifyRegister(fragmentActivity)
fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(fragmentActivity, fakePushersManager.instance, registerPusher = true)
}
@Test
fun `given toggle via Pusher is possible when execute then current pusher is toggled to true`() = runTest {
// Given
val fragmentActivity = mockk<FragmentActivity>()
fakePushersManager.givenGetPusherForCurrentSessionReturns(mockk())
val fakeSession = fakeActiveSessionHolder.fakeSession
fakeSession.givenSessionId(A_SESSION_ID)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeActiveSessionHolder.fakeSession) } returns true
coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) }
// When
enableNotificationsForCurrentSessionUseCase.execute(fragmentActivity)
// Then
coVerify { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, true) }
}
}

View file

@ -0,0 +1,31 @@
/*
* 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.core.notification.EnableNotificationsSettingUpdater
import io.mockk.justRun
import io.mockk.mockk
import org.matrix.android.sdk.api.session.Session
class FakeEnableNotificationsSettingUpdater {
val instance = mockk<EnableNotificationsSettingUpdater>()
fun givenOnSessionsStarted(session: Session) {
justRun { instance.onSessionsStarted(session) }
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 androidx.fragment.app.FragmentActivity
import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.pushers.PushersManager
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.verify
class FakeFcmHelper {
val instance = mockk<FcmHelper>()
fun givenEnsureFcmTokenIsRetrieved(
fragmentActivity: FragmentActivity,
pushersManager: PushersManager,
) {
justRun { instance.ensureFcmTokenIsRetrieved(fragmentActivity, pushersManager, any()) }
}
fun verifyEnsureFcmTokenIsRetrieved(
fragmentActivity: FragmentActivity,
pushersManager: PushersManager,
registerPusher: Boolean,
) {
verify { instance.ensureFcmTokenIsRetrieved(fragmentActivity, pushersManager, registerPusher) }
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.notification.GetNotificationsStatusUseCase
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import org.matrix.android.sdk.api.session.Session
class FakeGetNotificationsStatusUseCase {
val instance = mockk<GetNotificationsStatusUseCase>()
fun givenExecuteReturns(
session: Session,
sessionId: String,
notificationsStatus: NotificationsStatus
) {
every { instance.execute(session, sessionId) } returns flowOf(notificationsStatus)
}
}

View file

@ -16,14 +16,24 @@
package im.vector.app.test.fakes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import io.mockk.every
import io.mockk.mockk
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
class FakeHomeServerCapabilitiesService : HomeServerCapabilitiesService by mockk() {
fun givenCapabilities(homeServerCapabilities: HomeServerCapabilities) {
every { getHomeServerCapabilities() } returns homeServerCapabilities
}
fun givenCapabilitiesLiveReturns(homeServerCapabilities: HomeServerCapabilities): LiveData<Optional<HomeServerCapabilities>> {
return MutableLiveData(homeServerCapabilities.toOptional()).also {
every { getHomeServerCapabilitiesLive() } returns it
}
}
}

View file

@ -0,0 +1,31 @@
/*
* 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.core.pushers.PushersManager
import io.mockk.every
import io.mockk.mockk
import org.matrix.android.sdk.api.session.pushers.Pusher
class FakePushersManager {
val instance = mockk<PushersManager>()
fun givenGetPusherForCurrentSessionReturns(pusher: Pusher?) {
every { instance.getPusherForCurrentSession() } returns pusher
}
}

View file

@ -0,0 +1,53 @@
/*
* 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 androidx.fragment.app.FragmentActivity
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.UnifiedPushHelper
import io.mockk.coJustRun
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
class FakeUnifiedPushHelper {
val instance = mockk<UnifiedPushHelper>()
fun givenRegister(fragmentActivity: FragmentActivity) {
every { instance.register(fragmentActivity, any()) } answers {
secondArg<Runnable>().run()
}
}
fun verifyRegister(fragmentActivity: FragmentActivity) {
verify { instance.register(fragmentActivity, any()) }
}
fun givenUnregister(pushersManager: PushersManager) {
coJustRun { instance.unregister(pushersManager) }
}
fun verifyUnregister(pushersManager: PushersManager) {
coVerify { instance.unregister(pushersManager) }
}
fun givenIsEmbeddedDistributorReturns(isEmbedded: Boolean) {
every { instance.isEmbeddedDistributor() } returns isEmbedded
}
}

View file

@ -18,6 +18,7 @@ package im.vector.app.test.fakes
import im.vector.app.features.settings.VectorPreferences
import io.mockk.every
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.verify
@ -42,5 +43,13 @@ class FakeVectorPreferences {
}
fun givenTextFormatting(isEnabled: Boolean) =
every { instance.isTextFormattingEnabled() } returns isEnabled
every { instance.isTextFormattingEnabled() } returns isEnabled
fun givenSetNotificationEnabledForDevice() {
justRun { instance.setNotificationEnabledForDevice(any()) }
}
fun verifySetNotificationEnabledForDevice(enabled: Boolean, inverse: Boolean = false) {
verify(inverse = inverse) { instance.setNotificationEnabledForDevice(enabled) }
}
}