mirror of
https://github.com/bitwarden/android.git
synced 2024-11-21 08:55:48 +03:00
PM-14410: App restart timeout action (#4237)
This commit is contained in:
parent
88a741c93a
commit
29384596d4
21 changed files with 380 additions and 174 deletions
|
@ -5,7 +5,7 @@ import androidx.lifecycle.LifecycleCoroutineScope
|
|||
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityActivityManager
|
||||
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityActivityManagerImpl
|
||||
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -24,13 +24,13 @@ object ActivityAccessibilityModule {
|
|||
fun providesAccessibilityActivityManager(
|
||||
@ApplicationContext context: Context,
|
||||
accessibilityEnabledManager: AccessibilityEnabledManager,
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
): AccessibilityActivityManager =
|
||||
AccessibilityActivityManagerImpl(
|
||||
context = context,
|
||||
accessibilityEnabledManager = accessibilityEnabledManager,
|
||||
appForegroundManager = appForegroundManager,
|
||||
appStateManager = appStateManager,
|
||||
lifecycleScope = lifecycleScope,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.x8bit.bitwarden.data.autofill.accessibility.manager
|
|||
import android.content.Context
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
|
@ -13,11 +13,11 @@ import kotlinx.coroutines.flow.onEach
|
|||
class AccessibilityActivityManagerImpl(
|
||||
private val context: Context,
|
||||
private val accessibilityEnabledManager: AccessibilityEnabledManager,
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) : AccessibilityActivityManager {
|
||||
init {
|
||||
appForegroundManager
|
||||
appStateManager
|
||||
.appForegroundStateFlow
|
||||
.onEach {
|
||||
accessibilityEnabledManager.isAccessibilityEnabled =
|
||||
|
|
|
@ -8,7 +8,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import com.x8bit.bitwarden.data.autofill.manager.AutofillActivityManager
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillActivityManagerImpl
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -27,13 +27,13 @@ object ActivityAutofillModule {
|
|||
@Provides
|
||||
fun provideAutofillActivityManager(
|
||||
@ActivityScopedManager autofillManager: AutofillManager,
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
autofillEnabledManager: AutofillEnabledManager,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
): AutofillActivityManager =
|
||||
AutofillActivityManagerImpl(
|
||||
autofillManager = autofillManager,
|
||||
appForegroundManager = appForegroundManager,
|
||||
appStateManager = appStateManager,
|
||||
autofillEnabledManager = autofillEnabledManager,
|
||||
lifecycleScope = lifecycleScope,
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.autofill.manager
|
|||
|
||||
import android.view.autofill.AutofillManager
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
|
@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.onEach
|
|||
class AutofillActivityManagerImpl(
|
||||
private val autofillManager: AutofillManager,
|
||||
private val autofillEnabledManager: AutofillEnabledManager,
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) : AutofillActivityManager {
|
||||
private val isAutofillEnabledAndSupported: Boolean
|
||||
|
@ -21,7 +21,7 @@ class AutofillActivityManagerImpl(
|
|||
autofillManager.isAutofillSupported
|
||||
|
||||
init {
|
||||
appForegroundManager
|
||||
appStateManager
|
||||
.appForegroundStateFlow
|
||||
.onEach { autofillEnabledManager.isAutofillEnabled = isAutofillEnabledAndSupported }
|
||||
.launchIn(lifecycleScope)
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* A manager for tracking app foreground state changes.
|
||||
*/
|
||||
interface AppForegroundManager {
|
||||
|
||||
/**
|
||||
* Emits whenever there are changes to the app foreground state.
|
||||
*/
|
||||
val appForegroundStateFlow: StateFlow<AppForegroundState>
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* Primary implementation of [AppForegroundManager].
|
||||
*/
|
||||
class AppForegroundManagerImpl(
|
||||
processLifecycleOwner: LifecycleOwner = ProcessLifecycleOwner.get(),
|
||||
) : AppForegroundManager {
|
||||
private val mutableAppForegroundStateFlow =
|
||||
MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||
|
||||
override val appForegroundStateFlow: StateFlow<AppForegroundState>
|
||||
get() = mutableAppForegroundStateFlow.asStateFlow()
|
||||
|
||||
init {
|
||||
processLifecycleOwner.lifecycle.addObserver(
|
||||
object : DefaultLifecycleObserver {
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.FOREGROUNDED
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.BACKGROUNDED
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.accessibility.BitwardenAccessibilityService
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* A manager for tracking app foreground state changes.
|
||||
*/
|
||||
interface AppStateManager {
|
||||
/**
|
||||
* Emits whenever there are changes to the app creation state.
|
||||
*
|
||||
* This is required because the [BitwardenAccessibilityService] will keep the app process alive
|
||||
* when the app would otherwise be destroyed.
|
||||
*/
|
||||
val appCreatedStateFlow: StateFlow<AppCreationState>
|
||||
|
||||
/**
|
||||
* Emits whenever there are changes to the app foreground state.
|
||||
*/
|
||||
val appForegroundStateFlow: StateFlow<AppForegroundState>
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* Primary implementation of [AppStateManager].
|
||||
*/
|
||||
class AppStateManagerImpl(
|
||||
application: Application,
|
||||
processLifecycleOwner: LifecycleOwner = ProcessLifecycleOwner.get(),
|
||||
) : AppStateManager {
|
||||
private val mutableAppCreationStateFlow = MutableStateFlow(AppCreationState.DESTROYED)
|
||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||
|
||||
override val appCreatedStateFlow: StateFlow<AppCreationState>
|
||||
get() = mutableAppCreationStateFlow.asStateFlow()
|
||||
|
||||
override val appForegroundStateFlow: StateFlow<AppForegroundState>
|
||||
get() = mutableAppForegroundStateFlow.asStateFlow()
|
||||
|
||||
init {
|
||||
application.registerActivityLifecycleCallbacks(AppCreationCallback())
|
||||
processLifecycleOwner.lifecycle.addObserver(AppForegroundObserver())
|
||||
}
|
||||
|
||||
private inner class AppForegroundObserver : DefaultLifecycleObserver {
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.FOREGROUNDED
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
mutableAppForegroundStateFlow.value = AppForegroundState.BACKGROUNDED
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AppCreationCallback : Application.ActivityLifecycleCallbacks {
|
||||
private var activityCount: Int = 0
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
activityCount++
|
||||
// Always be in a created state if we have an activity
|
||||
mutableAppCreationStateFlow.value = AppCreationState.CREATED
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
activityCount--
|
||||
if (activityCount == 0 && !activity.isChangingConfigurations) {
|
||||
mutableAppCreationStateFlow.value = AppCreationState.DESTROYED
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityResumed(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityPaused(activity: Activity) = Unit
|
||||
|
||||
override fun onActivityStopped(activity: Activity) = Unit
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
|
||||
}
|
||||
}
|
|
@ -14,8 +14,8 @@ import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.Refres
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.EventService
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.PushService
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.AssetManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AssetManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
|
||||
|
@ -80,8 +80,9 @@ object PlatformManagerModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppForegroundManager(): AppForegroundManager =
|
||||
AppForegroundManagerImpl()
|
||||
fun provideAppStateManager(
|
||||
application: Application,
|
||||
): AppStateManager = AppStateManagerImpl(application = application)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
@ -267,11 +268,11 @@ object PlatformManagerModule {
|
|||
@Singleton
|
||||
fun provideRestrictionManager(
|
||||
@ApplicationContext context: Context,
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
environmentRepository: EnvironmentRepository,
|
||||
): RestrictionManager = RestrictionManagerImpl(
|
||||
appForegroundManager = appForegroundManager,
|
||||
appStateManager = appStateManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
context = context,
|
||||
environmentRepository = environmentRepository,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
/**
|
||||
* Represents the creation state of the app.
|
||||
*/
|
||||
enum class AppCreationState {
|
||||
/**
|
||||
* Denotes that the app is currently created.
|
||||
*/
|
||||
CREATED,
|
||||
|
||||
/**
|
||||
* Denotes that the app is currently destroyed.
|
||||
*/
|
||||
DESTROYED,
|
||||
}
|
|
@ -6,7 +6,7 @@ import android.content.Intent
|
|||
import android.content.IntentFilter
|
||||
import android.content.RestrictionsManager
|
||||
import android.os.Bundle
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
|
@ -19,7 +19,7 @@ import kotlinx.coroutines.flow.onEach
|
|||
* The default implementation of the [RestrictionManager].
|
||||
*/
|
||||
class RestrictionManagerImpl(
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
private val context: Context,
|
||||
private val environmentRepository: EnvironmentRepository,
|
||||
|
@ -31,7 +31,7 @@ class RestrictionManagerImpl(
|
|||
private var isReceiverRegistered = false
|
||||
|
||||
init {
|
||||
appForegroundManager
|
||||
appStateManager
|
||||
.appForegroundStateFlow
|
||||
.onEach {
|
||||
when (it) {
|
||||
|
|
|
@ -12,8 +12,9 @@ import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
|
|||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokens
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.userSwitchingChangesFlow
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
|
||||
|
@ -65,7 +66,7 @@ class VaultLockManagerImpl(
|
|||
private val authSdkSource: AuthSdkSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val appForegroundManager: AppForegroundManager,
|
||||
private val appStateManager: AppStateManager,
|
||||
private val userLogoutManager: UserLogoutManager,
|
||||
private val trustedDeviceManager: TrustedDeviceManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
|
@ -90,6 +91,7 @@ class VaultLockManagerImpl(
|
|||
get() = mutableVaultStateEventSharedFlow.asSharedFlow()
|
||||
|
||||
init {
|
||||
observeAppCreationChanges()
|
||||
observeAppForegroundChanges()
|
||||
observeUserSwitchingChanges()
|
||||
observeVaultTimeoutChanges()
|
||||
|
@ -302,10 +304,31 @@ class VaultLockManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeAppCreationChanges() {
|
||||
appStateManager
|
||||
.appCreatedStateFlow
|
||||
.onEach { appCreationState ->
|
||||
when (appCreationState) {
|
||||
AppCreationState.CREATED -> Unit
|
||||
AppCreationState.DESTROYED -> handleOnDestroyed()
|
||||
}
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
}
|
||||
|
||||
private fun handleOnDestroyed() {
|
||||
activeUserId?.let { userId ->
|
||||
checkForVaultTimeout(
|
||||
userId = userId,
|
||||
checkTimeoutReason = CheckTimeoutReason.APP_RESTARTED,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeAppForegroundChanges() {
|
||||
var isFirstForeground = true
|
||||
|
||||
appForegroundManager
|
||||
appStateManager
|
||||
.appForegroundStateFlow
|
||||
.onEach { appForegroundState ->
|
||||
when (appForegroundState) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
|||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
|
||||
import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
|
||||
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
|
@ -72,7 +72,7 @@ object VaultManagerModule {
|
|||
authSdkSource: AuthSdkSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
settingsRepository: SettingsRepository,
|
||||
appForegroundManager: AppForegroundManager,
|
||||
appStateManager: AppStateManager,
|
||||
userLogoutManager: UserLogoutManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
trustedDeviceManager: TrustedDeviceManager,
|
||||
|
@ -82,7 +82,7 @@ object VaultManagerModule {
|
|||
authSdkSource = authSdkSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
settingsRepository = settingsRepository,
|
||||
appForegroundManager = appForegroundManager,
|
||||
appStateManager = appStateManager,
|
||||
userLogoutManager = userLogoutManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
trustedDeviceManager = trustedDeviceManager,
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
@ -26,7 +26,7 @@ class AccessibilityActivityManagerTest {
|
|||
private val accessibilityEnabledManager: AccessibilityEnabledManager =
|
||||
AccessibilityEnabledManagerImpl()
|
||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||
private val appForegroundManager: AppForegroundManager = mockk {
|
||||
private val appStateManager: AppStateManager = mockk {
|
||||
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
||||
}
|
||||
private val lifecycleScope = mockk<LifecycleCoroutineScope> {
|
||||
|
@ -43,7 +43,7 @@ class AccessibilityActivityManagerTest {
|
|||
autofillActivityManager = AccessibilityActivityManagerImpl(
|
||||
context = context,
|
||||
accessibilityEnabledManager = accessibilityEnabledManager,
|
||||
appForegroundManager = appForegroundManager,
|
||||
appStateManager = appStateManager,
|
||||
lifecycleScope = lifecycleScope,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.x8bit.bitwarden.data.autofill.manager
|
|||
import android.view.autofill.AutofillManager
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
|
@ -28,7 +28,7 @@ class AutofillActivityManagerTest {
|
|||
private val autofillEnabledManager: AutofillEnabledManager = AutofillEnabledManagerImpl()
|
||||
|
||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||
private val appForegroundManager: AppForegroundManager = mockk {
|
||||
private val appStateManager: AppStateManager = mockk {
|
||||
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
||||
}
|
||||
private val lifecycleScope = mockk<LifecycleCoroutineScope> {
|
||||
|
@ -39,7 +39,7 @@ class AutofillActivityManagerTest {
|
|||
@Suppress("unused")
|
||||
private val autofillActivityManager: AutofillActivityManager = AutofillActivityManagerImpl(
|
||||
autofillManager = autofillManager,
|
||||
appForegroundManager = appForegroundManager,
|
||||
appStateManager = appStateManager,
|
||||
autofillEnabledManager = autofillEnabledManager,
|
||||
lifecycleScope = lifecycleScope,
|
||||
)
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import com.x8bit.bitwarden.data.util.FakeLifecycleOwner
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AppForegroundManagerTest {
|
||||
|
||||
private val fakeLifecycleOwner = FakeLifecycleOwner()
|
||||
|
||||
private val appForegroundManager = AppForegroundManagerImpl(
|
||||
processLifecycleOwner = fakeLifecycleOwner,
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `appForegroundStateFlow should emit whenever the underlying ProcessLifecycleOwner receives start and stop events`() =
|
||||
runTest {
|
||||
appForegroundManager.appForegroundStateFlow.test {
|
||||
// Initial state is BACKGROUNDED
|
||||
assertEquals(
|
||||
AppForegroundState.BACKGROUNDED,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
fakeLifecycleOwner.lifecycle.dispatchOnStart()
|
||||
|
||||
assertEquals(
|
||||
AppForegroundState.FOREGROUNDED,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
fakeLifecycleOwner.lifecycle.dispatchOnStop()
|
||||
|
||||
assertEquals(
|
||||
AppForegroundState.BACKGROUNDED,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import com.x8bit.bitwarden.data.util.FakeLifecycleOwner
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AppStateManagerTest {
|
||||
|
||||
private val activityLifecycleCallbacks = slot<Application.ActivityLifecycleCallbacks>()
|
||||
private val application = mockk<Application> {
|
||||
every { registerActivityLifecycleCallbacks(capture(activityLifecycleCallbacks)) } just runs
|
||||
}
|
||||
private val fakeLifecycleOwner = FakeLifecycleOwner()
|
||||
|
||||
private val appStateManager = AppStateManagerImpl(
|
||||
application = application,
|
||||
processLifecycleOwner = fakeLifecycleOwner,
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `appForegroundStateFlow should emit whenever the underlying ProcessLifecycleOwner receives start and stop events`() =
|
||||
runTest {
|
||||
appStateManager.appForegroundStateFlow.test {
|
||||
// Initial state is BACKGROUNDED
|
||||
assertEquals(
|
||||
AppForegroundState.BACKGROUNDED,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
fakeLifecycleOwner.lifecycle.dispatchOnStart()
|
||||
|
||||
assertEquals(
|
||||
AppForegroundState.FOREGROUNDED,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
fakeLifecycleOwner.lifecycle.dispatchOnStop()
|
||||
|
||||
assertEquals(
|
||||
AppForegroundState.BACKGROUNDED,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `appCreatedStateFlow should emit whenever the underlying activities are all destroyed or a creation event occurs`() =
|
||||
runTest {
|
||||
val activity = mockk<Activity> {
|
||||
every { isChangingConfigurations } returns false
|
||||
}
|
||||
appStateManager.appCreatedStateFlow.test {
|
||||
// Initial state is DESTROYED
|
||||
assertEquals(AppCreationState.DESTROYED, awaitItem())
|
||||
|
||||
activityLifecycleCallbacks.captured.onActivityCreated(activity, null)
|
||||
assertEquals(AppCreationState.CREATED, awaitItem())
|
||||
|
||||
activityLifecycleCallbacks.captured.onActivityCreated(activity, null)
|
||||
expectNoEvents()
|
||||
|
||||
activityLifecycleCallbacks.captured.onActivityDestroyed(activity)
|
||||
expectNoEvents()
|
||||
|
||||
activityLifecycleCallbacks.captured.onActivityDestroyed(activity)
|
||||
assertEquals(AppCreationState.DESTROYED, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import android.os.Bundle
|
|||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.FakeAppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.FakeAppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||
import io.mockk.clearMocks
|
||||
|
@ -27,7 +27,7 @@ class RestrictionManagerTest {
|
|||
every { registerReceiver(any(), any()) } returns null
|
||||
every { unregisterReceiver(any()) } just runs
|
||||
}
|
||||
private val fakeAppForegroundManager = FakeAppForegroundManager()
|
||||
private val fakeAppStateManager = FakeAppStateManager()
|
||||
private val fakeDispatcherManager = FakeDispatcherManager().apply {
|
||||
setMain(unconfined)
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class RestrictionManagerTest {
|
|||
private val restrictionsManager = mockk<RestrictionsManager>()
|
||||
|
||||
private val restrictionManager: RestrictionManager = RestrictionManagerImpl(
|
||||
appForegroundManager = fakeAppForegroundManager,
|
||||
appStateManager = fakeAppStateManager,
|
||||
dispatcherManager = fakeDispatcherManager,
|
||||
context = context,
|
||||
environmentRepository = fakeEnvironmentRepository,
|
||||
|
@ -51,7 +51,7 @@ class RestrictionManagerTest {
|
|||
fun `on app foreground with a null bundle should register receiver and do nothing else`() {
|
||||
every { restrictionsManager.applicationRestrictions } returns null
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -63,7 +63,7 @@ class RestrictionManagerTest {
|
|||
fun `on app foreground with an empty bundle should register receiver and do nothing else`() {
|
||||
every { restrictionsManager.applicationRestrictions } returns mockBundle()
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -78,7 +78,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("key" to "unknown")
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -93,7 +93,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.com")
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -109,7 +109,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -130,7 +130,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.eu")
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -147,7 +147,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -172,7 +172,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -192,7 +192,7 @@ class RestrictionManagerTest {
|
|||
restrictionsManager.applicationRestrictions
|
||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.registerReceiver(any(), any())
|
||||
|
@ -207,7 +207,7 @@ class RestrictionManagerTest {
|
|||
|
||||
@Test
|
||||
fun `on app background when not foregrounded should do nothing`() {
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
|
||||
verify(exactly = 0) {
|
||||
context.unregisterReceiver(any())
|
||||
|
@ -218,10 +218,10 @@ class RestrictionManagerTest {
|
|||
@Test
|
||||
fun `on app background after foreground should unregister receiver`() {
|
||||
every { restrictionsManager.applicationRestrictions } returns null
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
clearMocks(context, restrictionsManager, answers = false)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
|
||||
verify(exactly = 1) {
|
||||
context.unregisterReceiver(any())
|
||||
|
|
|
@ -1,20 +1,34 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.util
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* A faked implementation of [AppForegroundManager]
|
||||
* A faked implementation of [AppStateManager]
|
||||
*/
|
||||
class FakeAppForegroundManager : AppForegroundManager {
|
||||
class FakeAppStateManager : AppStateManager {
|
||||
private val mutableAppCreationStateFlow = MutableStateFlow(AppCreationState.DESTROYED)
|
||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||
|
||||
override val appCreatedStateFlow: StateFlow<AppCreationState>
|
||||
get() = mutableAppCreationStateFlow.asStateFlow()
|
||||
|
||||
override val appForegroundStateFlow: StateFlow<AppForegroundState>
|
||||
get() = mutableAppForegroundStateFlow.asStateFlow()
|
||||
|
||||
/**
|
||||
* The current [AppCreationState] tracked by the [appCreatedStateFlow].
|
||||
*/
|
||||
var appCreationState: AppCreationState
|
||||
get() = mutableAppCreationStateFlow.value
|
||||
set(value) {
|
||||
mutableAppCreationStateFlow.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The current [AppForegroundState] tracked by the [appForegroundStateFlow].
|
||||
*/
|
|
@ -15,8 +15,9 @@ import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
|
|||
import com.x8bit.bitwarden.data.auth.manager.model.LogoutEvent
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.FakeAppForegroundManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.FakeAppStateManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
|
@ -53,7 +54,7 @@ import org.junit.jupiter.api.Test
|
|||
@Suppress("LargeClass")
|
||||
class VaultLockManagerTest {
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val fakeAppForegroundManager = FakeAppForegroundManager()
|
||||
private val fakeAppStateManager = FakeAppStateManager()
|
||||
private val authSdkSource: AuthSdkSource = mockk {
|
||||
coEvery {
|
||||
hashPassword(
|
||||
|
@ -90,7 +91,7 @@ class VaultLockManagerTest {
|
|||
authSdkSource = authSdkSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
settingsRepository = settingsRepository,
|
||||
appForegroundManager = fakeAppForegroundManager,
|
||||
appStateManager = fakeAppStateManager,
|
||||
userLogoutManager = userLogoutManager,
|
||||
trustedDeviceManager = trustedDeviceManager,
|
||||
dispatcherManager = fakeDispatcherManager,
|
||||
|
@ -147,18 +148,86 @@ class VaultLockManagerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `app coming into background subsequent times should perform timeout action if necessary`() {
|
||||
fun `app being destroyed should perform timeout action if necessary`() {
|
||||
setAccountTokens()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
// Start in a foregrounded state
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
// Will be used within each loop to reset the test to a suitable initial state.
|
||||
fun resetTest(vaultTimeout: VaultTimeout) {
|
||||
clearVerifications(userLogoutManager)
|
||||
mutableVaultTimeoutStateFlow.value = vaultTimeout
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appCreationState = AppCreationState.CREATED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
||||
// Test Lock action
|
||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||
resetTest(vaultTimeout = vaultTimeout)
|
||||
fakeAppStateManager.appCreationState = AppCreationState.DESTROYED
|
||||
|
||||
when (vaultTimeout) {
|
||||
VaultTimeout.FifteenMinutes,
|
||||
VaultTimeout.ThirtyMinutes,
|
||||
VaultTimeout.OneHour,
|
||||
VaultTimeout.FourHours,
|
||||
is VaultTimeout.Custom,
|
||||
VaultTimeout.Immediately,
|
||||
VaultTimeout.OneMinute,
|
||||
VaultTimeout.FiveMinutes,
|
||||
VaultTimeout.OnAppRestart,
|
||||
-> {
|
||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
||||
VaultTimeout.Never -> {
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
}
|
||||
verify(exactly = 0) { userLogoutManager.softLogout(any()) }
|
||||
}
|
||||
|
||||
// Test Logout action
|
||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||
resetTest(vaultTimeout = vaultTimeout)
|
||||
fakeAppStateManager.appCreationState = AppCreationState.DESTROYED
|
||||
|
||||
when (vaultTimeout) {
|
||||
VaultTimeout.OnAppRestart,
|
||||
VaultTimeout.FifteenMinutes,
|
||||
VaultTimeout.ThirtyMinutes,
|
||||
VaultTimeout.OneHour,
|
||||
VaultTimeout.FourHours,
|
||||
is VaultTimeout.Custom,
|
||||
VaultTimeout.Immediately,
|
||||
VaultTimeout.OneMinute,
|
||||
VaultTimeout.FiveMinutes,
|
||||
-> {
|
||||
verify(exactly = 1) { userLogoutManager.softLogout(any()) }
|
||||
}
|
||||
|
||||
VaultTimeout.Never -> {
|
||||
verify(exactly = 0) { userLogoutManager.softLogout(any()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `app coming into background subsequent times should perform timeout action if necessary`() {
|
||||
setAccountTokens()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
// Start in a foregrounded state
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
// Will be used within each loop to reset the test to a suitable initial state.
|
||||
fun resetTest(vaultTimeout: VaultTimeout) {
|
||||
clearVerifications(userLogoutManager)
|
||||
mutableVaultTimeoutStateFlow.value = vaultTimeout
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
@ -168,7 +237,7 @@ class VaultLockManagerTest {
|
|||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||
resetTest(vaultTimeout = vaultTimeout)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||
|
||||
|
@ -201,7 +270,7 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||
resetTest(vaultTimeout = vaultTimeout)
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||
|
||||
|
@ -236,11 +305,11 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.Never
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
@ -253,11 +322,11 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.OnAppRestart
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
@ -269,11 +338,11 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
@ -286,7 +355,7 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
}
|
||||
|
@ -298,11 +367,11 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 1) { settingsRepository.getVaultTimeoutActionStateFlow(USER_ID) }
|
||||
}
|
||||
|
@ -314,11 +383,11 @@ class VaultLockManagerTest {
|
|||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
verify(exactly = 0) { settingsRepository.getVaultTimeoutActionStateFlow(USER_ID) }
|
||||
}
|
||||
|
@ -329,14 +398,14 @@ class VaultLockManagerTest {
|
|||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
// Start in a foregrounded state
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
// We want to skip the first time since that is different from subsequent foregrounds
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
|
||||
// Will be used within each loop to reset the test to a suitable initial state.
|
||||
fun resetTest(vaultTimeout: VaultTimeout) {
|
||||
mutableVaultTimeoutStateFlow.value = vaultTimeout
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||
clearVerifications(userLogoutManager)
|
||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||
|
@ -347,7 +416,7 @@ class VaultLockManagerTest {
|
|||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||
resetTest(vaultTimeout = vaultTimeout)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||
|
||||
|
@ -361,7 +430,7 @@ class VaultLockManagerTest {
|
|||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||
resetTest(vaultTimeout = vaultTimeout)
|
||||
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||
|
||||
|
@ -376,7 +445,7 @@ class VaultLockManagerTest {
|
|||
fun `switching users should perform lock actions or start a timer for each user if necessary`() {
|
||||
val userId2 = "mockId-2"
|
||||
setAccountTokens(listOf(USER_ID, userId2))
|
||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||
fakeAuthDiskSource.userState = UserStateJson(
|
||||
activeUserId = USER_ID,
|
||||
accounts = mapOf(
|
||||
|
|
|
@ -46,7 +46,7 @@ Note that these data sources are constructed in a manner that adheres to a very
|
|||
|
||||
### Managers
|
||||
|
||||
Manager classes represent something of a middle level of the data layer. While some manager classes like [VaultLockManager](../app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt) depend on the the lower-level data sources, others are wrappers around OS-level classes (ex: [AppForegroundManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppForegroundManager.kt)) while others have no dependencies at all (ex: [SpecialCircumstanceManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/SpecialCircumstanceManager.kt)). The commonality amongst the manager classes is that they tend to have a single discrete responsibility. These classes may also exist solely in the data layer for use inside a repository or manager class, like [AppForegroundManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppForegroundManager.kt), or may be exposed directly to the UI layer, like [SpecialCircumstanceManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/SpecialCircumstanceManager.kt).
|
||||
Manager classes represent something of a middle level of the data layer. While some manager classes like [VaultLockManager](../app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt) depend on the the lower-level data sources, others are wrappers around OS-level classes (ex: [AppStateManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppStateManager.kt)) while others have no dependencies at all (ex: [SpecialCircumstanceManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/SpecialCircumstanceManager.kt)). The commonality amongst the manager classes is that they tend to have a single discrete responsibility. These classes may also exist solely in the data layer for use inside a repository or manager class, like [AppStateManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppStateManager.kt), or may be exposed directly to the UI layer, like [SpecialCircumstanceManager](../app/src/main/java/com/x8bit/bitwarden/data/platform/manager/SpecialCircumstanceManager.kt).
|
||||
|
||||
### Repositories
|
||||
|
||||
|
|
Loading…
Reference in a new issue