mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 11:59:12 +03:00
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:
commit
4621488f21
24 changed files with 572 additions and 65 deletions
1
changelog.d/7457.bugfix
Normal file
1
changelog.d/7457.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[Session manager] Hide push notification toggle when there is no server support
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
|
@ -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,
|
|
@ -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) {
|
||||||
|
|
|
@ -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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(),
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue