mirror of
https://github.com/bitwarden/android.git
synced 2024-12-03 19:54:20 +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.AccessibilityActivityManager
|
||||||
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityActivityManagerImpl
|
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityActivityManagerImpl
|
||||||
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManager
|
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.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
@ -24,13 +24,13 @@ object ActivityAccessibilityModule {
|
||||||
fun providesAccessibilityActivityManager(
|
fun providesAccessibilityActivityManager(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
accessibilityEnabledManager: AccessibilityEnabledManager,
|
accessibilityEnabledManager: AccessibilityEnabledManager,
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
): AccessibilityActivityManager =
|
): AccessibilityActivityManager =
|
||||||
AccessibilityActivityManagerImpl(
|
AccessibilityActivityManagerImpl(
|
||||||
context = context,
|
context = context,
|
||||||
accessibilityEnabledManager = accessibilityEnabledManager,
|
accessibilityEnabledManager = accessibilityEnabledManager,
|
||||||
appForegroundManager = appForegroundManager,
|
appStateManager = appStateManager,
|
||||||
lifecycleScope = lifecycleScope,
|
lifecycleScope = lifecycleScope,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.x8bit.bitwarden.data.autofill.accessibility.manager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled
|
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.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ import kotlinx.coroutines.flow.onEach
|
||||||
class AccessibilityActivityManagerImpl(
|
class AccessibilityActivityManagerImpl(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val accessibilityEnabledManager: AccessibilityEnabledManager,
|
private val accessibilityEnabledManager: AccessibilityEnabledManager,
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
) : AccessibilityActivityManager {
|
) : AccessibilityActivityManager {
|
||||||
init {
|
init {
|
||||||
appForegroundManager
|
appStateManager
|
||||||
.appForegroundStateFlow
|
.appForegroundStateFlow
|
||||||
.onEach {
|
.onEach {
|
||||||
accessibilityEnabledManager.isAccessibilityEnabled =
|
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.AutofillActivityManager
|
||||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillActivityManagerImpl
|
import com.x8bit.bitwarden.data.autofill.manager.AutofillActivityManagerImpl
|
||||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
|
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.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
@ -27,13 +27,13 @@ object ActivityAutofillModule {
|
||||||
@Provides
|
@Provides
|
||||||
fun provideAutofillActivityManager(
|
fun provideAutofillActivityManager(
|
||||||
@ActivityScopedManager autofillManager: AutofillManager,
|
@ActivityScopedManager autofillManager: AutofillManager,
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
autofillEnabledManager: AutofillEnabledManager,
|
autofillEnabledManager: AutofillEnabledManager,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
): AutofillActivityManager =
|
): AutofillActivityManager =
|
||||||
AutofillActivityManagerImpl(
|
AutofillActivityManagerImpl(
|
||||||
autofillManager = autofillManager,
|
autofillManager = autofillManager,
|
||||||
appForegroundManager = appForegroundManager,
|
appStateManager = appStateManager,
|
||||||
autofillEnabledManager = autofillEnabledManager,
|
autofillEnabledManager = autofillEnabledManager,
|
||||||
lifecycleScope = lifecycleScope,
|
lifecycleScope = lifecycleScope,
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.autofill.manager
|
||||||
|
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
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.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.onEach
|
||||||
class AutofillActivityManagerImpl(
|
class AutofillActivityManagerImpl(
|
||||||
private val autofillManager: AutofillManager,
|
private val autofillManager: AutofillManager,
|
||||||
private val autofillEnabledManager: AutofillEnabledManager,
|
private val autofillEnabledManager: AutofillEnabledManager,
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
) : AutofillActivityManager {
|
) : AutofillActivityManager {
|
||||||
private val isAutofillEnabledAndSupported: Boolean
|
private val isAutofillEnabledAndSupported: Boolean
|
||||||
|
@ -21,7 +21,7 @@ class AutofillActivityManagerImpl(
|
||||||
autofillManager.isAutofillSupported
|
autofillManager.isAutofillSupported
|
||||||
|
|
||||||
init {
|
init {
|
||||||
appForegroundManager
|
appStateManager
|
||||||
.appForegroundStateFlow
|
.appForegroundStateFlow
|
||||||
.onEach { autofillEnabledManager.isAutofillEnabled = isAutofillEnabledAndSupported }
|
.onEach { autofillEnabledManager.isAutofillEnabled = isAutofillEnabledAndSupported }
|
||||||
.launchIn(lifecycleScope)
|
.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.interceptor.BaseUrlInterceptors
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.EventService
|
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.datasource.network.service.PushService
|
||||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
|
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManagerImpl
|
import com.x8bit.bitwarden.data.platform.manager.AppStateManagerImpl
|
||||||
import com.x8bit.bitwarden.data.platform.manager.AssetManager
|
import com.x8bit.bitwarden.data.platform.manager.AssetManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.AssetManagerImpl
|
import com.x8bit.bitwarden.data.platform.manager.AssetManagerImpl
|
||||||
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
|
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
|
||||||
|
@ -80,8 +80,9 @@ object PlatformManagerModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideAppForegroundManager(): AppForegroundManager =
|
fun provideAppStateManager(
|
||||||
AppForegroundManagerImpl()
|
application: Application,
|
||||||
|
): AppStateManager = AppStateManagerImpl(application = application)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -267,11 +268,11 @@ object PlatformManagerModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideRestrictionManager(
|
fun provideRestrictionManager(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
dispatcherManager: DispatcherManager,
|
dispatcherManager: DispatcherManager,
|
||||||
environmentRepository: EnvironmentRepository,
|
environmentRepository: EnvironmentRepository,
|
||||||
): RestrictionManager = RestrictionManagerImpl(
|
): RestrictionManager = RestrictionManagerImpl(
|
||||||
appForegroundManager = appForegroundManager,
|
appStateManager = appStateManager,
|
||||||
dispatcherManager = dispatcherManager,
|
dispatcherManager = dispatcherManager,
|
||||||
context = context,
|
context = context,
|
||||||
environmentRepository = environmentRepository,
|
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.IntentFilter
|
||||||
import android.content.RestrictionsManager
|
import android.content.RestrictionsManager
|
||||||
import android.os.Bundle
|
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.dispatcher.DispatcherManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||||
|
@ -19,7 +19,7 @@ import kotlinx.coroutines.flow.onEach
|
||||||
* The default implementation of the [RestrictionManager].
|
* The default implementation of the [RestrictionManager].
|
||||||
*/
|
*/
|
||||||
class RestrictionManagerImpl(
|
class RestrictionManagerImpl(
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
dispatcherManager: DispatcherManager,
|
dispatcherManager: DispatcherManager,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val environmentRepository: EnvironmentRepository,
|
private val environmentRepository: EnvironmentRepository,
|
||||||
|
@ -31,7 +31,7 @@ class RestrictionManagerImpl(
|
||||||
private var isReceiverRegistered = false
|
private var isReceiverRegistered = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
appForegroundManager
|
appStateManager
|
||||||
.appForegroundStateFlow
|
.appForegroundStateFlow
|
||||||
.onEach {
|
.onEach {
|
||||||
when (it) {
|
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.toSdkParams
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokens
|
import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokens
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.userSwitchingChangesFlow
|
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.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.manager.model.AppForegroundState
|
||||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
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.VaultTimeout
|
||||||
|
@ -65,7 +66,7 @@ class VaultLockManagerImpl(
|
||||||
private val authSdkSource: AuthSdkSource,
|
private val authSdkSource: AuthSdkSource,
|
||||||
private val vaultSdkSource: VaultSdkSource,
|
private val vaultSdkSource: VaultSdkSource,
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val appForegroundManager: AppForegroundManager,
|
private val appStateManager: AppStateManager,
|
||||||
private val userLogoutManager: UserLogoutManager,
|
private val userLogoutManager: UserLogoutManager,
|
||||||
private val trustedDeviceManager: TrustedDeviceManager,
|
private val trustedDeviceManager: TrustedDeviceManager,
|
||||||
dispatcherManager: DispatcherManager,
|
dispatcherManager: DispatcherManager,
|
||||||
|
@ -90,6 +91,7 @@ class VaultLockManagerImpl(
|
||||||
get() = mutableVaultStateEventSharedFlow.asSharedFlow()
|
get() = mutableVaultStateEventSharedFlow.asSharedFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
observeAppCreationChanges()
|
||||||
observeAppForegroundChanges()
|
observeAppForegroundChanges()
|
||||||
observeUserSwitchingChanges()
|
observeUserSwitchingChanges()
|
||||||
observeVaultTimeoutChanges()
|
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() {
|
private fun observeAppForegroundChanges() {
|
||||||
var isFirstForeground = true
|
var isFirstForeground = true
|
||||||
|
|
||||||
appForegroundManager
|
appStateManager
|
||||||
.appForegroundStateFlow
|
.appForegroundStateFlow
|
||||||
.onEach { appForegroundState ->
|
.onEach { appForegroundState ->
|
||||||
when (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.datasource.sdk.AuthSdkSource
|
||||||
import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
|
import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
|
||||||
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
|
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.manager.dispatcher.DispatcherManager
|
||||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||||
|
@ -72,7 +72,7 @@ object VaultManagerModule {
|
||||||
authSdkSource: AuthSdkSource,
|
authSdkSource: AuthSdkSource,
|
||||||
vaultSdkSource: VaultSdkSource,
|
vaultSdkSource: VaultSdkSource,
|
||||||
settingsRepository: SettingsRepository,
|
settingsRepository: SettingsRepository,
|
||||||
appForegroundManager: AppForegroundManager,
|
appStateManager: AppStateManager,
|
||||||
userLogoutManager: UserLogoutManager,
|
userLogoutManager: UserLogoutManager,
|
||||||
dispatcherManager: DispatcherManager,
|
dispatcherManager: DispatcherManager,
|
||||||
trustedDeviceManager: TrustedDeviceManager,
|
trustedDeviceManager: TrustedDeviceManager,
|
||||||
|
@ -82,7 +82,7 @@ object VaultManagerModule {
|
||||||
authSdkSource = authSdkSource,
|
authSdkSource = authSdkSource,
|
||||||
vaultSdkSource = vaultSdkSource,
|
vaultSdkSource = vaultSdkSource,
|
||||||
settingsRepository = settingsRepository,
|
settingsRepository = settingsRepository,
|
||||||
appForegroundManager = appForegroundManager,
|
appStateManager = appStateManager,
|
||||||
userLogoutManager = userLogoutManager,
|
userLogoutManager = userLogoutManager,
|
||||||
dispatcherManager = dispatcherManager,
|
dispatcherManager = dispatcherManager,
|
||||||
trustedDeviceManager = trustedDeviceManager,
|
trustedDeviceManager = trustedDeviceManager,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.x8bit.bitwarden.data.autofill.accessibility.util.isAccessibilityServiceEnabled
|
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 com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -26,7 +26,7 @@ class AccessibilityActivityManagerTest {
|
||||||
private val accessibilityEnabledManager: AccessibilityEnabledManager =
|
private val accessibilityEnabledManager: AccessibilityEnabledManager =
|
||||||
AccessibilityEnabledManagerImpl()
|
AccessibilityEnabledManagerImpl()
|
||||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||||
private val appForegroundManager: AppForegroundManager = mockk {
|
private val appStateManager: AppStateManager = mockk {
|
||||||
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
||||||
}
|
}
|
||||||
private val lifecycleScope = mockk<LifecycleCoroutineScope> {
|
private val lifecycleScope = mockk<LifecycleCoroutineScope> {
|
||||||
|
@ -43,7 +43,7 @@ class AccessibilityActivityManagerTest {
|
||||||
autofillActivityManager = AccessibilityActivityManagerImpl(
|
autofillActivityManager = AccessibilityActivityManagerImpl(
|
||||||
context = context,
|
context = context,
|
||||||
accessibilityEnabledManager = accessibilityEnabledManager,
|
accessibilityEnabledManager = accessibilityEnabledManager,
|
||||||
appForegroundManager = appForegroundManager,
|
appStateManager = appStateManager,
|
||||||
lifecycleScope = lifecycleScope,
|
lifecycleScope = lifecycleScope,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.x8bit.bitwarden.data.autofill.manager
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import app.cash.turbine.test
|
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 com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
|
@ -28,7 +28,7 @@ class AutofillActivityManagerTest {
|
||||||
private val autofillEnabledManager: AutofillEnabledManager = AutofillEnabledManagerImpl()
|
private val autofillEnabledManager: AutofillEnabledManager = AutofillEnabledManagerImpl()
|
||||||
|
|
||||||
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||||
private val appForegroundManager: AppForegroundManager = mockk {
|
private val appStateManager: AppStateManager = mockk {
|
||||||
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
every { appForegroundStateFlow } returns mutableAppForegroundStateFlow
|
||||||
}
|
}
|
||||||
private val lifecycleScope = mockk<LifecycleCoroutineScope> {
|
private val lifecycleScope = mockk<LifecycleCoroutineScope> {
|
||||||
|
@ -39,7 +39,7 @@ class AutofillActivityManagerTest {
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
private val autofillActivityManager: AutofillActivityManager = AutofillActivityManagerImpl(
|
private val autofillActivityManager: AutofillActivityManager = AutofillActivityManagerImpl(
|
||||||
autofillManager = autofillManager,
|
autofillManager = autofillManager,
|
||||||
appForegroundManager = appForegroundManager,
|
appStateManager = appStateManager,
|
||||||
autofillEnabledManager = autofillEnabledManager,
|
autofillEnabledManager = autofillEnabledManager,
|
||||||
lifecycleScope = lifecycleScope,
|
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.auth.datasource.disk.model.EnvironmentUrlDataJson
|
||||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
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.model.Environment
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||||
import io.mockk.clearMocks
|
import io.mockk.clearMocks
|
||||||
|
@ -27,7 +27,7 @@ class RestrictionManagerTest {
|
||||||
every { registerReceiver(any(), any()) } returns null
|
every { registerReceiver(any(), any()) } returns null
|
||||||
every { unregisterReceiver(any()) } just runs
|
every { unregisterReceiver(any()) } just runs
|
||||||
}
|
}
|
||||||
private val fakeAppForegroundManager = FakeAppForegroundManager()
|
private val fakeAppStateManager = FakeAppStateManager()
|
||||||
private val fakeDispatcherManager = FakeDispatcherManager().apply {
|
private val fakeDispatcherManager = FakeDispatcherManager().apply {
|
||||||
setMain(unconfined)
|
setMain(unconfined)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class RestrictionManagerTest {
|
||||||
private val restrictionsManager = mockk<RestrictionsManager>()
|
private val restrictionsManager = mockk<RestrictionsManager>()
|
||||||
|
|
||||||
private val restrictionManager: RestrictionManager = RestrictionManagerImpl(
|
private val restrictionManager: RestrictionManager = RestrictionManagerImpl(
|
||||||
appForegroundManager = fakeAppForegroundManager,
|
appStateManager = fakeAppStateManager,
|
||||||
dispatcherManager = fakeDispatcherManager,
|
dispatcherManager = fakeDispatcherManager,
|
||||||
context = context,
|
context = context,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
@ -51,7 +51,7 @@ class RestrictionManagerTest {
|
||||||
fun `on app foreground with a null bundle should register receiver and do nothing else`() {
|
fun `on app foreground with a null bundle should register receiver and do nothing else`() {
|
||||||
every { restrictionsManager.applicationRestrictions } returns null
|
every { restrictionsManager.applicationRestrictions } returns null
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
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`() {
|
fun `on app foreground with an empty bundle should register receiver and do nothing else`() {
|
||||||
every { restrictionsManager.applicationRestrictions } returns mockBundle()
|
every { restrictionsManager.applicationRestrictions } returns mockBundle()
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -78,7 +78,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("key" to "unknown")
|
} returns mockBundle("key" to "unknown")
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -93,7 +93,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.com")
|
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.com")
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -109,7 +109,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -130,7 +130,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.eu")
|
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.eu")
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -147,7 +147,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -172,7 +172,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -192,7 +192,7 @@ class RestrictionManagerTest {
|
||||||
restrictionsManager.applicationRestrictions
|
restrictionsManager.applicationRestrictions
|
||||||
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
} returns mockBundle("baseEnvironmentUrl" to baseUrl)
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.registerReceiver(any(), any())
|
context.registerReceiver(any(), any())
|
||||||
|
@ -207,7 +207,7 @@ class RestrictionManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `on app background when not foregrounded should do nothing`() {
|
fun `on app background when not foregrounded should do nothing`() {
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
|
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
context.unregisterReceiver(any())
|
context.unregisterReceiver(any())
|
||||||
|
@ -218,10 +218,10 @@ class RestrictionManagerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `on app background after foreground should unregister receiver`() {
|
fun `on app background after foreground should unregister receiver`() {
|
||||||
every { restrictionsManager.applicationRestrictions } returns null
|
every { restrictionsManager.applicationRestrictions } returns null
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
clearMocks(context, restrictionsManager, answers = false)
|
clearMocks(context, restrictionsManager, answers = false)
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
context.unregisterReceiver(any())
|
context.unregisterReceiver(any())
|
||||||
|
|
|
@ -1,20 +1,34 @@
|
||||||
package com.x8bit.bitwarden.data.platform.manager.util
|
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 com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
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)
|
private val mutableAppForegroundStateFlow = MutableStateFlow(AppForegroundState.BACKGROUNDED)
|
||||||
|
|
||||||
|
override val appCreatedStateFlow: StateFlow<AppCreationState>
|
||||||
|
get() = mutableAppCreationStateFlow.asStateFlow()
|
||||||
|
|
||||||
override val appForegroundStateFlow: StateFlow<AppForegroundState>
|
override val appForegroundStateFlow: StateFlow<AppForegroundState>
|
||||||
get() = mutableAppForegroundStateFlow.asStateFlow()
|
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].
|
* 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.manager.model.LogoutEvent
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
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.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.SettingsRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
|
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||||
|
@ -53,7 +54,7 @@ import org.junit.jupiter.api.Test
|
||||||
@Suppress("LargeClass")
|
@Suppress("LargeClass")
|
||||||
class VaultLockManagerTest {
|
class VaultLockManagerTest {
|
||||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||||
private val fakeAppForegroundManager = FakeAppForegroundManager()
|
private val fakeAppStateManager = FakeAppStateManager()
|
||||||
private val authSdkSource: AuthSdkSource = mockk {
|
private val authSdkSource: AuthSdkSource = mockk {
|
||||||
coEvery {
|
coEvery {
|
||||||
hashPassword(
|
hashPassword(
|
||||||
|
@ -90,7 +91,7 @@ class VaultLockManagerTest {
|
||||||
authSdkSource = authSdkSource,
|
authSdkSource = authSdkSource,
|
||||||
vaultSdkSource = vaultSdkSource,
|
vaultSdkSource = vaultSdkSource,
|
||||||
settingsRepository = settingsRepository,
|
settingsRepository = settingsRepository,
|
||||||
appForegroundManager = fakeAppForegroundManager,
|
appStateManager = fakeAppStateManager,
|
||||||
userLogoutManager = userLogoutManager,
|
userLogoutManager = userLogoutManager,
|
||||||
trustedDeviceManager = trustedDeviceManager,
|
trustedDeviceManager = trustedDeviceManager,
|
||||||
dispatcherManager = fakeDispatcherManager,
|
dispatcherManager = fakeDispatcherManager,
|
||||||
|
@ -147,18 +148,86 @@ class VaultLockManagerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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()
|
setAccountTokens()
|
||||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
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.
|
// Will be used within each loop to reset the test to a suitable initial state.
|
||||||
fun resetTest(vaultTimeout: VaultTimeout) {
|
fun resetTest(vaultTimeout: VaultTimeout) {
|
||||||
clearVerifications(userLogoutManager)
|
clearVerifications(userLogoutManager)
|
||||||
mutableVaultTimeoutStateFlow.value = vaultTimeout
|
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)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
}
|
}
|
||||||
|
@ -168,7 +237,7 @@ class VaultLockManagerTest {
|
||||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||||
resetTest(vaultTimeout = 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.
|
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||||
|
|
||||||
|
@ -201,7 +270,7 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||||
resetTest(vaultTimeout = 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.
|
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||||
|
|
||||||
|
@ -236,11 +305,11 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.Never
|
mutableVaultTimeoutStateFlow.value = VaultTimeout.Never
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
}
|
}
|
||||||
|
@ -253,11 +322,11 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.OnAppRestart
|
mutableVaultTimeoutStateFlow.value = VaultTimeout.OnAppRestart
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
}
|
}
|
||||||
|
@ -269,11 +338,11 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
}
|
}
|
||||||
|
@ -286,7 +355,7 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK
|
||||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertFalse(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
}
|
}
|
||||||
|
@ -298,11 +367,11 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 1) { settingsRepository.getVaultTimeoutActionStateFlow(USER_ID) }
|
verify(exactly = 1) { settingsRepository.getVaultTimeoutActionStateFlow(USER_ID) }
|
||||||
}
|
}
|
||||||
|
@ -314,11 +383,11 @@ class VaultLockManagerTest {
|
||||||
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOGOUT
|
||||||
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
|
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
|
|
||||||
verify(exactly = 0) { settingsRepository.getVaultTimeoutActionStateFlow(USER_ID) }
|
verify(exactly = 0) { settingsRepository.getVaultTimeoutActionStateFlow(USER_ID) }
|
||||||
}
|
}
|
||||||
|
@ -329,14 +398,14 @@ class VaultLockManagerTest {
|
||||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||||
|
|
||||||
// Start in a foregrounded 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
|
// 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.
|
// Will be used within each loop to reset the test to a suitable initial state.
|
||||||
fun resetTest(vaultTimeout: VaultTimeout) {
|
fun resetTest(vaultTimeout: VaultTimeout) {
|
||||||
mutableVaultTimeoutStateFlow.value = vaultTimeout
|
mutableVaultTimeoutStateFlow.value = vaultTimeout
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
|
||||||
clearVerifications(userLogoutManager)
|
clearVerifications(userLogoutManager)
|
||||||
verifyUnlockedVaultBlocking(userId = USER_ID)
|
verifyUnlockedVaultBlocking(userId = USER_ID)
|
||||||
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
assertTrue(vaultLockManager.isVaultUnlocked(USER_ID))
|
||||||
|
@ -347,7 +416,7 @@ class VaultLockManagerTest {
|
||||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||||
resetTest(vaultTimeout = 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.
|
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
||||||
|
|
||||||
|
@ -361,7 +430,7 @@ class VaultLockManagerTest {
|
||||||
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
MOCK_TIMEOUTS.forEach { vaultTimeout ->
|
||||||
resetTest(vaultTimeout = 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.
|
// Advance by 6 minutes. Only actions with a timeout less than this will be triggered.
|
||||||
testDispatcher.scheduler.advanceTimeBy(delayTimeMillis = 6 * 60 * 1000L)
|
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`() {
|
fun `switching users should perform lock actions or start a timer for each user if necessary`() {
|
||||||
val userId2 = "mockId-2"
|
val userId2 = "mockId-2"
|
||||||
setAccountTokens(listOf(USER_ID, userId2))
|
setAccountTokens(listOf(USER_ID, userId2))
|
||||||
fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
|
||||||
fakeAuthDiskSource.userState = UserStateJson(
|
fakeAuthDiskSource.userState = UserStateJson(
|
||||||
activeUserId = USER_ID,
|
activeUserId = USER_ID,
|
||||||
accounts = mapOf(
|
accounts = mapOf(
|
||||||
|
|
|
@ -46,7 +46,7 @@ Note that these data sources are constructed in a manner that adheres to a very
|
||||||
|
|
||||||
### Managers
|
### 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
|
### Repositories
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue