Move the AppStateManager to the data module (#6593)

This commit is contained in:
David Perez authored and GitHub committed 2026-03-02 20:53:22 +00:00
1 parent 3538ca54ca
commit 8a2b46e81a
17 files changed
+112 -57

No files matched your search

@@ -5,13 +5,13 @@ import android.view.autofill.AutofillManager
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.bitwarden.data.manager.appstate.AppStateManager
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.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManager
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillManager
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillManagerImpl
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -2,10 +2,10 @@ package com.x8bit.bitwarden.data.autofill.manager
import android.view.autofill.AutofillManager
import androidx.lifecycle.LifecycleCoroutineScope
import com.bitwarden.data.manager.appstate.AppStateManager
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManager
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillManager
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserThirdPartyAutofillStatus
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -2,7 +2,6 @@
package com.x8bit.bitwarden.data.autofill.util
import android.app.Activity
import android.app.PendingIntent
import android.app.assist.AssistStructure
import android.content.Context
@@ -13,6 +12,10 @@ import android.view.autofill.AutofillManager
import androidx.core.os.bundleOf
import com.bitwarden.annotation.OmitFromCoverage
import com.bitwarden.core.util.toPendingIntentMutabilityFlag
import com.bitwarden.data.autofill.util.AUTOFILL_BUNDLE_KEY
import com.bitwarden.data.autofill.util.AUTOFILL_CALLBACK_DATA_KEY
import com.bitwarden.data.autofill.util.AUTOFILL_SAVE_ITEM_DATA_KEY
import com.bitwarden.data.autofill.util.AUTOFILL_SELECTION_DATA_KEY
import com.bitwarden.ui.platform.util.getSafeParcelableExtra
import com.x8bit.bitwarden.AutofillCallbackActivity
import com.x8bit.bitwarden.MainActivity
@@ -22,11 +25,6 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import kotlin.random.Random
private const val AUTOFILL_SAVE_ITEM_DATA_KEY = "autofill-save-item-data"
private const val AUTOFILL_SELECTION_DATA_KEY = "autofill-selection-data"
private const val AUTOFILL_CALLBACK_DATA_KEY = "autofill-callback-data"
private const val AUTOFILL_BUNDLE_KEY = "autofill-bundle-key"
/**
* Creates an [Intent] in order to send the user to a manual selection process for autofill.
*/
@@ -149,12 +147,3 @@ fun Intent.getAutofillSelectionDataOrNull(): AutofillSelectionData? =
fun Intent.getAutofillCallbackIntentOrNull(): AutofillCallbackData? =
getBundleExtra(AUTOFILL_BUNDLE_KEY)
?.getSafeParcelableExtra(AUTOFILL_CALLBACK_DATA_KEY)
/**
* Checks if the given [Activity] was created for Autofill. This is useful to avoid locking the
* vault if one of the Autofill services starts the only instance of the [MainActivity].
*/
val Activity.createdForAutofill: Boolean
get() = intent.getAutofillSelectionDataOrNull() != null ||
intent.getAutofillSaveItemOrNull() != null ||
intent.getAutofillAssistStructureOrNull() != null
@@ -30,8 +30,6 @@ import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.legacy.LegacyAppCenterMigrator
import com.x8bit.bitwarden.data.platform.manager.AppResumeManager
import com.x8bit.bitwarden.data.platform.manager.AppResumeManagerImpl
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
@@ -106,12 +104,6 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object PlatformManagerModule {
@Provides
@Singleton
fun provideAppStateManager(
application: Application,
): AppStateManager = AppStateManagerImpl(application = application)
@Provides
@Singleton
fun provideAuthenticatorBridgeProcessor(
@@ -17,6 +17,9 @@ import com.bitwarden.core.data.util.concurrentMapOf
import com.bitwarden.core.data.util.flatMap
import com.bitwarden.crypto.HashPurpose
import com.bitwarden.crypto.Kdf
import com.bitwarden.data.manager.appstate.AppStateManager
import com.bitwarden.data.manager.appstate.model.AppCreationState
import com.bitwarden.data.manager.appstate.model.AppForegroundState
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.KdfManager
@@ -29,9 +32,6 @@ 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.error.NoActiveUserException
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.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
@@ -4,6 +4,7 @@ import android.content.Context
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
import com.bitwarden.core.data.manager.realtime.RealtimeManager
import com.bitwarden.cxf.parser.CredentialExchangePayloadParser
import com.bitwarden.data.manager.appstate.AppStateManager
import com.bitwarden.data.manager.file.FileManager
import com.bitwarden.network.service.CiphersService
import com.bitwarden.network.service.FolderService
@@ -16,7 +17,6 @@ import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
import com.x8bit.bitwarden.data.auth.manager.UserStateManager
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
import com.x8bit.bitwarden.data.platform.manager.DatabaseSchemeManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
@@ -3,13 +3,13 @@ package com.x8bit.bitwarden.data.autofill.manager
import android.view.autofill.AutofillManager
import androidx.lifecycle.LifecycleCoroutineScope
import app.cash.turbine.test
import com.bitwarden.data.manager.appstate.AppStateManager
import com.bitwarden.data.manager.appstate.model.AppForegroundState
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManager
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManagerImpl
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillManager
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserThirdPartyAutoFillData
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserThirdPartyAutofillStatus
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
import io.mockk.mockk
@@ -14,6 +14,9 @@ import com.bitwarden.core.data.manager.realtime.RealtimeManager
import com.bitwarden.core.data.util.asFailure
import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.crypto.HashPurpose
import com.bitwarden.data.manager.appstate.FakeAppStateManager
import com.bitwarden.data.manager.appstate.model.AppCreationState
import com.bitwarden.data.manager.appstate.model.AppForegroundState
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
@@ -26,9 +29,6 @@ import com.x8bit.bitwarden.data.auth.manager.model.LogoutEvent
import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
import com.x8bit.bitwarden.data.auth.repository.model.UpdateKdfMinimumsResult
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
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.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
+1
View File
@@ -54,6 +54,7 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.security.crypto)
implementation(libs.androidx.lifecycle.process)
implementation(libs.google.hilt.android)
ksp(libs.google.hilt.compiler)
implementation(libs.kotlinx.serialization)
@@ -0,0 +1,56 @@
@file:OmitFromCoverage
package com.bitwarden.data.autofill.util
import android.app.Activity
import android.content.Intent
import com.bitwarden.annotation.OmitFromCoverage
/**
* A unique key used to set or get Autofill saved item data.
*/
const val AUTOFILL_SAVE_ITEM_DATA_KEY = "autofill-save-item-data"
/**
* A unique key used to set or get Autofill selection data.
*/
const val AUTOFILL_SELECTION_DATA_KEY = "autofill-selection-data"
/**
* A unique key used to set or get Autofill callback data.
*/
const val AUTOFILL_CALLBACK_DATA_KEY = "autofill-callback-data"
/**
* A unique key used to set and get the `Bundle` containing autofill data.
*/
const val AUTOFILL_BUNDLE_KEY = "autofill-bundle-key"
/**
* Checks if the given [Intent] contains an `AutofillSaveItem` related to an ongoing save item
* process.
*/
private val Intent.hasAutofillSaveItem: Boolean
get() = this.hasExtra(AUTOFILL_SAVE_ITEM_DATA_KEY)
/**
* Checks if the given [Intent] contains data about an ongoing manual autofill selection process.
*/
private val Intent.hasAutofillSelectionData: Boolean
get() = getBundleExtra(AUTOFILL_BUNDLE_KEY)
?.containsKey(AUTOFILL_SELECTION_DATA_KEY) == true
/**
* Checks if the given [Intent] contains Autofill callback data.
*/
private val Intent.hasAutofillCallbackIntent: Boolean
get() = getBundleExtra(AUTOFILL_BUNDLE_KEY)?.containsKey(AUTOFILL_CALLBACK_DATA_KEY) == true
/**
* Checks if the given [Activity] was created for Autofill. This is useful to avoid locking the
* vault if one of the Autofill services starts the only instance of the `Activity`.
*/
val Activity.createdForAutofill: Boolean
get() = intent.hasAutofillSelectionData ||
intent.hasAutofillSaveItem ||
intent.hasAutofillCallbackIntent
@@ -1,8 +1,7 @@
package com.x8bit.bitwarden.data.platform.manager
package com.bitwarden.data.manager.appstate
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 com.bitwarden.data.manager.appstate.model.AppCreationState
import com.bitwarden.data.manager.appstate.model.AppForegroundState
import kotlinx.coroutines.flow.StateFlow
/**
@@ -12,8 +11,8 @@ 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.
* This is required because the Accessibility Service will keep the app process alive when the
* app would otherwise be destroyed.
*/
val appCreatedStateFlow: StateFlow<AppCreationState>
@@ -1,4 +1,4 @@
package com.x8bit.bitwarden.data.platform.manager
package com.bitwarden.data.manager.appstate
import android.app.Activity
import android.app.Application
@@ -6,9 +6,9 @@ import android.os.Bundle
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import com.x8bit.bitwarden.data.autofill.util.createdForAutofill
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.bitwarden.data.autofill.util.createdForAutofill
import com.bitwarden.data.manager.appstate.model.AppCreationState
import com.bitwarden.data.manager.appstate.model.AppForegroundState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -17,7 +17,7 @@ import timber.log.Timber
/**
* Primary implementation of [AppStateManager].
*/
class AppStateManagerImpl(
internal class AppStateManagerImpl(
application: Application,
processLifecycleOwner: LifecycleOwner = ProcessLifecycleOwner.get(),
) : AppStateManager {
@@ -1,4 +1,4 @@
package com.x8bit.bitwarden.data.platform.manager.model
package com.bitwarden.data.manager.appstate.model
/**
* Represents the creation state of the app.
@@ -1,4 +1,4 @@
package com.x8bit.bitwarden.data.platform.manager.model
package com.bitwarden.data.manager.appstate.model
/**
* Represents the foreground state of the app.
@@ -1,5 +1,6 @@
package com.bitwarden.data.manager.di
import android.app.Application
import android.content.Context
import com.bitwarden.core.data.manager.BuildInfoManager
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
@@ -8,6 +9,8 @@ import com.bitwarden.data.manager.BitwardenPackageManager
import com.bitwarden.data.manager.BitwardenPackageManagerImpl
import com.bitwarden.data.manager.NativeLibraryManager
import com.bitwarden.data.manager.NativeLibraryManagerImpl
import com.bitwarden.data.manager.appstate.AppStateManager
import com.bitwarden.data.manager.appstate.AppStateManagerImpl
import com.bitwarden.data.manager.file.FileManager
import com.bitwarden.data.manager.file.FileManagerImpl
import com.bitwarden.data.manager.flightrecorder.FlightRecorderManager
@@ -30,6 +33,12 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object DataManagerModule {
@Provides
@Singleton
fun provideAppStateManager(
application: Application,
): AppStateManager = AppStateManagerImpl(application = application)
@Provides
@Singleton
fun provideBitwardenPackageManager(
@@ -1,12 +1,12 @@
package com.x8bit.bitwarden.data.platform.manager
package com.bitwarden.data.manager.appstate
import android.app.Activity
import android.app.Application
import app.cash.turbine.test
import com.bitwarden.core.data.util.FakeLifecycleOwner
import com.x8bit.bitwarden.data.autofill.util.createdForAutofill
import com.x8bit.bitwarden.data.platform.manager.model.AppCreationState
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.bitwarden.data.autofill.util.createdForAutofill
import com.bitwarden.data.manager.appstate.model.AppCreationState
import com.bitwarden.data.manager.appstate.model.AppForegroundState
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
@@ -15,7 +15,9 @@ import io.mockk.runs
import io.mockk.slot
import io.mockk.unmockkStatic
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class AppStateManagerTest {
@@ -31,6 +33,16 @@ class AppStateManagerTest {
processLifecycleOwner = fakeLifecycleOwner,
)
@BeforeEach
fun setup() {
mockkStatic(Activity::createdForAutofill)
}
@AfterEach
fun tearDown() {
unmockkStatic(Activity::createdForAutofill)
}
@Suppress("MaxLineLength")
@Test
fun `appForegroundStateFlow should emit whenever the underlying ProcessLifecycleOwner receives start and stop events`() =
@@ -62,7 +74,6 @@ class AppStateManagerTest {
@Test
fun `appCreatedStateFlow should emit whenever the underlying activities are all destroyed or a creation event occurs`() =
runTest {
mockkStatic(Activity::createdForAutofill)
val activity = mockk<Activity> {
every { isChangingConfigurations } returns false
every { createdForAutofill } returns false
@@ -83,6 +94,5 @@ class AppStateManagerTest {
activityLifecycleCallbacks.captured.onActivityDestroyed(activity)
assertEquals(AppCreationState.Destroyed, awaitItem())
}
unmockkStatic(Activity::createdForAutofill)
}
}
@@ -1,8 +1,7 @@
package com.x8bit.bitwarden.data.platform.manager.util
package com.bitwarden.data.manager.appstate
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.bitwarden.data.manager.appstate.model.AppCreationState
import com.bitwarden.data.manager.appstate.model.AppForegroundState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow