Merge branch 'develop' into feature/bca/rust_flavor
This commit is contained in:
commit
ea37029631
|
@ -0,0 +1 @@
|
|||
Remind unverified sessions with a banner once a week
|
|
@ -2653,8 +2653,12 @@
|
|||
<string name="encrypted_unverified">Encrypted by an unverified device</string>
|
||||
<string name="encrypted_by_deleted">Encrypted by a deleted device</string>
|
||||
<string name="key_authenticity_not_guaranteed">The authenticity of this encrypted message can\'t be guaranteed on this device.</string>
|
||||
<string name="review_logins">Review where you’re logged in</string>
|
||||
<string name="verify_other_sessions">Verify all your sessions to ensure your account & messages are safe</string>
|
||||
<!-- TODO TO BE REMOVED -->
|
||||
<string name="review_logins" tools:ignore="UnusedResources">Review where you’re logged in</string>
|
||||
<!-- TODO TO BE REMOVED -->
|
||||
<string name="verify_other_sessions" tools:ignore="UnusedResources">Verify all your sessions to ensure your account & messages are safe</string>
|
||||
<string name="review_unverified_sessions_title">You have unverified sessions</string>
|
||||
<string name="review_unverified_sessions_description">Review to ensure your account is safe</string>
|
||||
<!-- Argument will be replaced by the other session name (e.g, Desktop, mobile) -->
|
||||
<string name="verify_this_session">Verify the new login accessing your account: %1$s</string>
|
||||
|
||||
|
|
|
@ -88,6 +88,9 @@ class DebugVectorFeatures(
|
|||
override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
|
||||
?: vectorFeatures.isVoiceBroadcastEnabled()
|
||||
|
||||
override fun isUnverifiedSessionsAlertEnabled(): Boolean = read(DebugFeatureKeys.unverifiedSessionsAlertEnabled)
|
||||
?: vectorFeatures.isUnverifiedSessionsAlertEnabled()
|
||||
|
||||
fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
|
||||
if (value == null) {
|
||||
it.remove(key)
|
||||
|
@ -151,4 +154,5 @@ object DebugFeatureKeys {
|
|||
val qrCodeLoginForAllServers = booleanPreferencesKey("qr-code-login-for-all-servers")
|
||||
val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login")
|
||||
val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
|
||||
val unverifiedSessionsAlertEnabled = booleanPreferencesKey("unverified-sessions-alert-enabled")
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package im.vector.app.config
|
||||
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
/**
|
||||
* Set of flags to configure the application.
|
||||
*/
|
||||
|
@ -95,4 +97,6 @@ object Config {
|
|||
val NIGHTLY_ANALYTICS_CONFIG = RELEASE_ANALYTICS_CONFIG.copy(sentryEnvironment = "NIGHTLY")
|
||||
val ER_NIGHTLY_ANALYTICS_CONFIG = RELEASE_ANALYTICS_CONFIG.copy(sentryEnvironment = "element-r")
|
||||
val ER_DEBUG_ANALYTICS_CONFIG = DEBUG_ANALYTICS_CONFIG.copy(sentryEnvironment = "element-r")
|
||||
|
||||
val SHOW_UNVERIFIED_SESSIONS_ALERT_AFTER_MILLIS = 7.days.inWholeMilliseconds // 1 Week
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ interface VectorFeatures {
|
|||
fun isQrCodeLoginForAllServers(): Boolean
|
||||
fun isReciprocateQrCodeLogin(): Boolean
|
||||
fun isVoiceBroadcastEnabled(): Boolean
|
||||
fun isUnverifiedSessionsAlertEnabled(): Boolean
|
||||
}
|
||||
|
||||
class DefaultVectorFeatures : VectorFeatures {
|
||||
|
@ -63,4 +64,5 @@ class DefaultVectorFeatures : VectorFeatures {
|
|||
override fun isQrCodeLoginForAllServers(): Boolean = false
|
||||
override fun isReciprocateQrCodeLogin(): Boolean = false
|
||||
override fun isVoiceBroadcastEnabled(): Boolean = true
|
||||
override fun isUnverifiedSessionsAlertEnabled(): Boolean = true
|
||||
}
|
||||
|
|
|
@ -239,12 +239,12 @@ class HomeDetailFragment :
|
|||
.requestSessionVerification(vectorBaseActivity, newest.deviceId ?: "")
|
||||
}
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreNewLogin(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
)
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreNewLogin(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -256,8 +256,8 @@ class HomeDetailFragment :
|
|||
alertManager.postVectorAlert(
|
||||
VerificationVectorAlert(
|
||||
uid = uid,
|
||||
title = getString(R.string.review_logins),
|
||||
description = getString(R.string.verify_other_sessions),
|
||||
title = getString(R.string.review_unverified_sessions_title),
|
||||
description = getString(R.string.review_unverified_sessions_description),
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
|
||||
|
|
|
@ -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.features.home
|
||||
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
class IsNewLoginAlertShownUseCase @Inject constructor(
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
) {
|
||||
|
||||
fun execute(deviceId: String?): Boolean {
|
||||
deviceId ?: return false
|
||||
|
||||
return vectorPreferences.isNewLoginAlertShownForDevice(deviceId)
|
||||
}
|
||||
}
|
|
@ -253,12 +253,12 @@ class NewHomeDetailFragment :
|
|||
.requestSessionVerification(vectorBaseActivity, newest.deviceId ?: "")
|
||||
}
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreNewLogin(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
)
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreNewLogin(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -270,8 +270,8 @@ class NewHomeDetailFragment :
|
|||
alertManager.postVectorAlert(
|
||||
VerificationVectorAlert(
|
||||
uid = uid,
|
||||
title = getString(R.string.review_logins),
|
||||
description = getString(R.string.verify_other_sessions),
|
||||
title = getString(R.string.review_unverified_sessions_title),
|
||||
description = getString(R.string.review_unverified_sessions_description),
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
|
||||
|
|
|
@ -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.features.home
|
||||
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetNewLoginAlertShownUseCase @Inject constructor(
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
) {
|
||||
|
||||
fun execute(deviceIds: List<String>) {
|
||||
deviceIds.forEach {
|
||||
vectorPreferences.setNewLoginAlertShownForDevice(it)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.home
|
||||
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetUnverifiedSessionsAlertShownUseCase @Inject constructor(
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
fun execute(deviceIds: List<String>) {
|
||||
val epochMillis = clock.epochMillis()
|
||||
deviceIds.forEach {
|
||||
vectorPreferences.setUnverifiedSessionsAlertLastShownMillis(it, epochMillis)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.features.home
|
||||
|
||||
import im.vector.app.config.Config
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.VectorFeatures
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
class ShouldShowUnverifiedSessionsAlertUseCase @Inject constructor(
|
||||
private val vectorFeatures: VectorFeatures,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
fun execute(deviceId: String?): Boolean {
|
||||
deviceId ?: return false
|
||||
|
||||
val isUnverifiedSessionsAlertEnabled = vectorFeatures.isUnverifiedSessionsAlertEnabled()
|
||||
val unverifiedSessionsAlertLastShownMillis = vectorPreferences.getUnverifiedSessionsAlertLastShownMillis(deviceId)
|
||||
return isUnverifiedSessionsAlertEnabled &&
|
||||
clock.epochMillis() - unverifiedSessionsAlertLastShownMillis >= Config.SHOW_UNVERIFIED_SESSIONS_ALERT_AFTER_MILLIS
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ package im.vector.app.features.home
|
|||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
|
@ -33,7 +32,6 @@ import im.vector.app.core.platform.EmptyViewEvents
|
|||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
@ -61,14 +59,19 @@ data class DeviceDetectionInfo(
|
|||
val currentSessionTrust: Boolean
|
||||
)
|
||||
|
||||
class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState,
|
||||
session: Session,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val clock: Clock,) :
|
||||
VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
|
||||
class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: UnknownDevicesState,
|
||||
session: Session,
|
||||
clock: Clock,
|
||||
private val shouldShowUnverifiedSessionsAlertUseCase: ShouldShowUnverifiedSessionsAlertUseCase,
|
||||
private val setUnverifiedSessionsAlertShownUseCase: SetUnverifiedSessionsAlertShownUseCase,
|
||||
private val isNewLoginAlertShownUseCase: IsNewLoginAlertShownUseCase,
|
||||
private val setNewLoginAlertShownUseCase: SetNewLoginAlertShownUseCase,
|
||||
) : VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
|
||||
|
||||
sealed class Action : VectorViewModelAction {
|
||||
data class IgnoreDevice(val deviceIds: List<String>) : Action()
|
||||
data class IgnoreNewLogin(val deviceIds: List<String>) : Action()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -86,37 +89,35 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
}
|
||||
|
||||
private val ignoredDeviceList = ArrayList<String>()
|
||||
|
||||
init {
|
||||
ignoredDeviceList.addAll(
|
||||
vectorPreferences.getUnknownDeviceDismissedList().also {
|
||||
Timber.v("## Detector - Remembered ignored list $it")
|
||||
}
|
||||
)
|
||||
val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId)
|
||||
.firstOrNull { it.deviceId == session.sessionParams.deviceId }
|
||||
?.firstTimeSeenLocalTs
|
||||
?: clock.epochMillis()
|
||||
Timber.v("## Detector - Current Session first time seen $currentSessionTs")
|
||||
|
||||
combine(
|
||||
session.flow().liveUserCryptoDevices(session.myUserId),
|
||||
session.flow().liveMyDevicesInfo(),
|
||||
session.flow().liveCrossSigningPrivateKeys(),
|
||||
session.firstTimeDeviceSeen(),
|
||||
) { cryptoList, infoList, pInfo, firstTimeDeviceSeen ->
|
||||
) { cryptoList, infoList, pInfo ->
|
||||
// Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}")
|
||||
// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}")
|
||||
Timber.v("## Detector - Current Session first time seen $firstTimeDeviceSeen")
|
||||
infoList
|
||||
.filter { info ->
|
||||
// filter verified session, by checking the crypto device info
|
||||
cryptoList.firstOrNull { info.deviceId == it.deviceId }?.isVerified?.not().orFalse()
|
||||
}
|
||||
// filter out ignored devices
|
||||
.filter { !ignoredDeviceList.contains(it.deviceId) }
|
||||
.filter { shouldShowUnverifiedSessionsAlertUseCase.execute(it.deviceId) }
|
||||
.sortedByDescending { it.lastSeenTs }
|
||||
.map { deviceInfo ->
|
||||
val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firstTimeSeenLocalTs ?: 0
|
||||
val isNew = isNewLoginAlertShownUseCase.execute(deviceInfo.deviceId).not() && deviceKnownSince > currentSessionTs
|
||||
|
||||
DeviceDetectionInfo(
|
||||
deviceInfo,
|
||||
deviceKnownSince > firstTimeDeviceSeen + 60_000, // short window to avoid false positive,
|
||||
isNew,
|
||||
pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change
|
||||
)
|
||||
}
|
||||
|
@ -148,30 +149,11 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
|
|||
override fun handle(action: Action) {
|
||||
when (action) {
|
||||
is Action.IgnoreDevice -> {
|
||||
ignoredDeviceList.addAll(action.deviceIds)
|
||||
// local echo
|
||||
withState { state ->
|
||||
state.unknownSessions.invoke()?.let { detectedSessions ->
|
||||
val updated = detectedSessions.filter { !action.deviceIds.contains(it.deviceInfo.deviceId) }
|
||||
setState {
|
||||
copy(unknownSessions = Success(updated))
|
||||
}
|
||||
}
|
||||
}
|
||||
setUnverifiedSessionsAlertShownUseCase.execute(action.deviceIds)
|
||||
}
|
||||
is Action.IgnoreNewLogin -> {
|
||||
setNewLoginAlertShownUseCase.execute(action.deviceIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
vectorPreferences.storeUnknownDeviceDismissedList(ignoredDeviceList)
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
private fun Session.firstTimeDeviceSeen() = flow {
|
||||
val value = cryptoService().getCryptoDeviceInfoList(myUserId)
|
||||
.firstOrNull { it.deviceId == sessionParams.deviceId }
|
||||
?.firstTimeSeenLocalTs
|
||||
?: clock.epochMillis()
|
||||
emit(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,8 +228,6 @@ class VectorPreferences @Inject constructor(
|
|||
private const val MEDIA_SAVING_1_MONTH = 2
|
||||
private const val MEDIA_SAVING_FOREVER = 3
|
||||
|
||||
private const val SETTINGS_UNKNOWN_DEVICE_DISMISSED_LIST = "SETTINGS_UNKNWON_DEVICE_DISMISSED_LIST"
|
||||
|
||||
private const val TAKE_PHOTO_VIDEO_MODE = "TAKE_PHOTO_VIDEO_MODE"
|
||||
|
||||
private const val SETTINGS_LABS_ENABLE_LIVE_LOCATION = "SETTINGS_LABS_ENABLE_LIVE_LOCATION"
|
||||
|
@ -246,6 +244,9 @@ class VectorPreferences @Inject constructor(
|
|||
// This key will be used to enable user for displaying live user info or not.
|
||||
const val SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO = "SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO"
|
||||
|
||||
const val SETTINGS_UNVERIFIED_SESSIONS_ALERT_LAST_SHOWN_MILLIS = "SETTINGS_UNVERIFIED_SESSIONS_ALERT_LAST_SHOWN_MILLIS_"
|
||||
const val SETTINGS_NEW_LOGIN_ALERT_SHOWN_FOR_DEVICE = "SETTINGS_NEW_LOGIN_ALERT_SHOWN_FOR_DEVICE_"
|
||||
|
||||
// Possible values for TAKE_PHOTO_VIDEO_MODE
|
||||
const val TAKE_PHOTO_VIDEO_MODE_ALWAYS_ASK = 0
|
||||
const val TAKE_PHOTO_VIDEO_MODE_PHOTO = 1
|
||||
|
@ -521,18 +522,6 @@ class VectorPreferences @Inject constructor(
|
|||
return defaultPrefs.getBoolean(SETTINGS_PLAY_SHUTTER_SOUND_KEY, true)
|
||||
}
|
||||
|
||||
fun storeUnknownDeviceDismissedList(deviceIds: List<String>) {
|
||||
defaultPrefs.edit(true) {
|
||||
putStringSet(SETTINGS_UNKNOWN_DEVICE_DISMISSED_LIST, deviceIds.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
fun getUnknownDeviceDismissedList(): List<String> {
|
||||
return tryOrNull {
|
||||
defaultPrefs.getStringSet(SETTINGS_UNKNOWN_DEVICE_DISMISSED_LIST, null)?.toList()
|
||||
}.orEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the notification ringtone.
|
||||
*
|
||||
|
@ -1249,7 +1238,27 @@ class VectorPreferences @Inject constructor(
|
|||
|
||||
fun setIpAddressVisibilityInDeviceManagerScreens(isVisible: Boolean) {
|
||||
defaultPrefs.edit {
|
||||
putBoolean(VectorPreferences.SETTINGS_SESSION_MANAGER_SHOW_IP_ADDRESS, isVisible)
|
||||
putBoolean(SETTINGS_SESSION_MANAGER_SHOW_IP_ADDRESS, isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
fun getUnverifiedSessionsAlertLastShownMillis(deviceId: String): Long {
|
||||
return defaultPrefs.getLong(SETTINGS_UNVERIFIED_SESSIONS_ALERT_LAST_SHOWN_MILLIS + deviceId, 0)
|
||||
}
|
||||
|
||||
fun setUnverifiedSessionsAlertLastShownMillis(deviceId: String, lastShownMillis: Long) {
|
||||
defaultPrefs.edit {
|
||||
putLong(SETTINGS_UNVERIFIED_SESSIONS_ALERT_LAST_SHOWN_MILLIS + deviceId, lastShownMillis)
|
||||
}
|
||||
}
|
||||
|
||||
fun isNewLoginAlertShownForDevice(deviceId: String): Boolean {
|
||||
return defaultPrefs.getBoolean(SETTINGS_NEW_LOGIN_ALERT_SHOWN_FOR_DEVICE + deviceId, false)
|
||||
}
|
||||
|
||||
fun setNewLoginAlertShownForDevice(deviceId: String) {
|
||||
defaultPrefs.edit {
|
||||
putBoolean(SETTINGS_NEW_LOGIN_ALERT_SHOWN_FOR_DEVICE + deviceId, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.home
|
||||
|
||||
import im.vector.app.config.Config
|
||||
import im.vector.app.test.fakes.FakeClock
|
||||
import im.vector.app.test.fakes.FakeVectorFeatures
|
||||
import im.vector.app.test.fakes.FakeVectorPreferences
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Test
|
||||
|
||||
private val AN_EPOCH = Config.SHOW_UNVERIFIED_SESSIONS_ALERT_AFTER_MILLIS.toLong()
|
||||
private const val A_DEVICE_ID = "A_DEVICE_ID"
|
||||
|
||||
class ShouldShowUnverifiedSessionsAlertUseCaseTest {
|
||||
|
||||
private val fakeVectorFeatures = FakeVectorFeatures()
|
||||
private val fakeVectorPreferences = FakeVectorPreferences()
|
||||
private val fakeClock = FakeClock()
|
||||
|
||||
private val shouldShowUnverifiedSessionsAlertUseCase = ShouldShowUnverifiedSessionsAlertUseCase(
|
||||
vectorFeatures = fakeVectorFeatures,
|
||||
vectorPreferences = fakeVectorPreferences.instance,
|
||||
clock = fakeClock,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given the feature is disabled then the use case returns false`() {
|
||||
fakeVectorFeatures.givenUnverifiedSessionsAlertEnabled(false)
|
||||
fakeVectorPreferences.givenUnverifiedSessionsAlertLastShownMillis(0L)
|
||||
|
||||
shouldShowUnverifiedSessionsAlertUseCase.execute(A_DEVICE_ID) shouldBe false
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given the feature in enabled and there is not a saved preference then the use case returns true`() {
|
||||
fakeVectorFeatures.givenUnverifiedSessionsAlertEnabled(true)
|
||||
fakeVectorPreferences.givenUnverifiedSessionsAlertLastShownMillis(0L)
|
||||
fakeClock.givenEpoch(AN_EPOCH + 1)
|
||||
|
||||
shouldShowUnverifiedSessionsAlertUseCase.execute(A_DEVICE_ID) shouldBe true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given the feature in enabled and last shown is a long time ago then the use case returns true`() {
|
||||
fakeVectorFeatures.givenUnverifiedSessionsAlertEnabled(true)
|
||||
fakeVectorPreferences.givenUnverifiedSessionsAlertLastShownMillis(AN_EPOCH)
|
||||
fakeClock.givenEpoch(AN_EPOCH * 2 + 1)
|
||||
|
||||
shouldShowUnverifiedSessionsAlertUseCase.execute(A_DEVICE_ID) shouldBe true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given the feature in enabled and last shown is not a long time ago then the use case returns false`() {
|
||||
fakeVectorFeatures.givenUnverifiedSessionsAlertEnabled(true)
|
||||
fakeVectorPreferences.givenUnverifiedSessionsAlertLastShownMillis(AN_EPOCH)
|
||||
fakeClock.givenEpoch(AN_EPOCH + 1)
|
||||
|
||||
shouldShowUnverifiedSessionsAlertUseCase.execute(A_DEVICE_ID) shouldBe false
|
||||
}
|
||||
}
|
|
@ -50,4 +50,8 @@ class FakeVectorFeatures : VectorFeatures by spyk<DefaultVectorFeatures>() {
|
|||
fun givenVoiceBroadcast(isEnabled: Boolean) {
|
||||
every { isVoiceBroadcastEnabled() } returns isEnabled
|
||||
}
|
||||
|
||||
fun givenUnverifiedSessionsAlertEnabled(isEnabled: Boolean) {
|
||||
every { isUnverifiedSessionsAlertEnabled() } returns isEnabled
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,4 +56,8 @@ class FakeVectorPreferences {
|
|||
fun givenSessionManagerShowIpAddress(showIpAddress: Boolean) {
|
||||
every { instance.showIpAddressInSessionManagerScreens() } returns showIpAddress
|
||||
}
|
||||
|
||||
fun givenUnverifiedSessionsAlertLastShownMillis(lastShownMillis: Long) {
|
||||
every { instance.getUnverifiedSessionsAlertLastShownMillis(any()) } returns lastShownMillis
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue