mirror of
https://github.com/bitwarden/android.git
synced 2026-05-08 21:10:40 -05:00
[deps]: Update androidxCredentialsProviderEvents to v1.0.0-alpha06 (#6734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Patrick Honkonen <phonkonen@bitwarden.com>
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
* F-Droid implementation of [GmsManager]. Always returns `false` since GMS is not available.
|
||||
*/
|
||||
@Suppress("UnusedParameter")
|
||||
class GmsManagerImpl(
|
||||
context: Context,
|
||||
) : GmsManager {
|
||||
|
||||
override fun isVersionAtLeast(version: Int): Boolean = false
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
/**
|
||||
* The minimum GMS Core version required for Credential Exchange Protocol (CXP) features.
|
||||
*/
|
||||
const val MINIMUM_CXP_GMS_VERSION: Int = 261031035
|
||||
|
||||
/**
|
||||
* Manages checks against the installed Google Mobile Services (GMS) Core version.
|
||||
*/
|
||||
interface GmsManager {
|
||||
|
||||
/**
|
||||
* Returns `true` if the installed GMS Core version is at least [version], or `false` if
|
||||
* GMS Core is not installed or does not meet the minimum version.
|
||||
*/
|
||||
fun isVersionAtLeast(version: Int): Boolean
|
||||
}
|
||||
@@ -47,8 +47,6 @@ import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.GmsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.GmsManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.LogsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.LogsManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
@@ -338,12 +336,6 @@ object PlatformManagerModule {
|
||||
thirdPartyAutofillEnabledManager = thirdPartyAutofillEnabledManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGmsManager(
|
||||
@ApplicationContext context: Context,
|
||||
): GmsManager = GmsManagerImpl(context = context)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDatabaseSchemeManager(
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.data.manager.BuildInfoManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.network.model.PolicyTypeJson
|
||||
import com.bitwarden.ui.platform.base.BackgroundEvent
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
|
||||
import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.GmsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.MINIMUM_CXP_GMS_VERSION
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
@@ -28,18 +27,19 @@ import javax.inject.Inject
|
||||
@HiltViewModel
|
||||
class VaultSettingsViewModel @Inject constructor(
|
||||
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
|
||||
private val buildInfoManager: BuildInfoManager,
|
||||
private val firstTimeActionManager: FirstTimeActionManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
private val gmsManager: GmsManager,
|
||||
private val policyManager: PolicyManager,
|
||||
) : BaseViewModel<VaultSettingsState, VaultSettingsEvent, VaultSettingsAction>(
|
||||
initialState = run {
|
||||
val firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState
|
||||
VaultSettingsState(
|
||||
showImportActionCard = firstTimeState.showImportLoginsCardInSettings,
|
||||
showImportItemsChevron = featureFlagManager.getFeatureFlag(
|
||||
key = FlagKey.CredentialExchangeProtocolImport,
|
||||
) && gmsManager.isVersionAtLeast(MINIMUM_CXP_GMS_VERSION),
|
||||
showImportItemsChevron = !buildInfoManager.isFdroid &&
|
||||
featureFlagManager.getFeatureFlag(
|
||||
key = FlagKey.CredentialExchangeProtocolImport,
|
||||
),
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -67,8 +67,8 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
) { isEnabled, policies ->
|
||||
VaultSettingsAction.Internal.CredentialExchangeAvailabilityChanged(
|
||||
isEnabled = isEnabled &&
|
||||
policies.isEmpty() &&
|
||||
gmsManager.isVersionAtLeast(MINIMUM_CXP_GMS_VERSION),
|
||||
!buildInfoManager.isFdroid &&
|
||||
policies.isEmpty(),
|
||||
)
|
||||
}
|
||||
.onEach(::sendAction)
|
||||
@@ -143,9 +143,9 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleImportItemsClicked() {
|
||||
if (featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport) &&
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP).isEmpty() &&
|
||||
gmsManager.isVersionAtLeast(MINIMUM_CXP_GMS_VERSION)
|
||||
if (!buildInfoManager.isFdroid &&
|
||||
featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport) &&
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP).isEmpty()
|
||||
) {
|
||||
sendEvent(VaultSettingsEvent.NavigateToImportItems)
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.os.Parcelable
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.data.manager.BuildInfoManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.util.persistentListOfNotNull
|
||||
@@ -37,8 +38,6 @@ import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.CredentialExchangeRegistryManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.GmsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.MINIMUM_CXP_GMS_VERSION
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
@@ -117,7 +116,7 @@ class VaultViewModel @Inject constructor(
|
||||
private val networkConnectionManager: NetworkConnectionManager,
|
||||
private val browserAutofillDialogManager: BrowserAutofillDialogManager,
|
||||
private val credentialExchangeRegistryManager: CredentialExchangeRegistryManager,
|
||||
private val gmsManager: GmsManager,
|
||||
private val buildInfoManager: BuildInfoManager,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
|
||||
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
||||
@@ -1028,7 +1027,7 @@ class VaultViewModel @Inject constructor(
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
if (action.isCredentialExchangeProtocolExportEnabled &&
|
||||
gmsManager.isVersionAtLeast(MINIMUM_CXP_GMS_VERSION)
|
||||
!buildInfoManager.isFdroid
|
||||
) {
|
||||
credentialExchangeRegistryManager.register()
|
||||
} else {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.gms.common.GoogleApiAvailabilityLight
|
||||
|
||||
/**
|
||||
* Primary implementation of [GmsManager].
|
||||
*/
|
||||
class GmsManagerImpl(
|
||||
private val context: Context,
|
||||
) : GmsManager {
|
||||
|
||||
override fun isVersionAtLeast(version: Int): Boolean =
|
||||
GoogleApiAvailabilityLight.getInstance().getApkVersion(context) >= version
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import androidx.credentials.providerevents.exception.ClearExportUnknownErrorException
|
||||
import androidx.credentials.providerevents.exception.RegisterExportUnknownErrorException
|
||||
import androidx.credentials.providerevents.transfer.ClearExportResponse
|
||||
import androidx.credentials.providerevents.transfer.CredentialTypes
|
||||
import androidx.credentials.providerevents.transfer.RegisterExportResponse
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
@@ -28,7 +30,7 @@ class CredentialExchangeRegistryManagerImplTest {
|
||||
|
||||
private val credentialExchangeRegistry: CredentialExchangeRegistry = mockk {
|
||||
coEvery { register(any()) } returns RegisterExportResponse().asSuccess()
|
||||
coEvery { unregister() } returns RegisterExportResponse().asSuccess()
|
||||
coEvery { unregister() } returns ClearExportResponse(isDeleted = true).asSuccess()
|
||||
}
|
||||
private val settingsDiskSource: SettingsDiskSource = mockk {
|
||||
every { getAppRegisteredForExport() } returns false
|
||||
@@ -109,7 +111,7 @@ class CredentialExchangeRegistryManagerImplTest {
|
||||
fun `unregister should return Failure when unregistration fails`() = runTest {
|
||||
coEvery {
|
||||
credentialExchangeRegistry.unregister()
|
||||
} returns RegisterExportUnknownErrorException().asFailure()
|
||||
} returns ClearExportUnknownErrorException().asFailure()
|
||||
|
||||
val result = registryManager.unregister()
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.gms.common.GoogleApiAvailabilityLight
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class GmsManagerTest {
|
||||
|
||||
private val context: Context = mockk()
|
||||
private val mockGoogleApiAvailabilityLight: GoogleApiAvailabilityLight = mockk()
|
||||
private val gmsManager = GmsManagerImpl(context = context)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockkStatic(GoogleApiAvailabilityLight::class)
|
||||
every {
|
||||
GoogleApiAvailabilityLight.getInstance()
|
||||
} returns mockGoogleApiAvailabilityLight
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic(GoogleApiAvailabilityLight::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isVersionAtLeast should return true when installed version equals required version`() {
|
||||
every { mockGoogleApiAvailabilityLight.getApkVersion(context) } returns 261031035
|
||||
assertTrue(gmsManager.isVersionAtLeast(261031035))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isVersionAtLeast should return true when installed version exceeds required version`() {
|
||||
every { mockGoogleApiAvailabilityLight.getApkVersion(context) } returns 261031036
|
||||
assertTrue(gmsManager.isVersionAtLeast(261031035))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isVersionAtLeast should return false when installed version is below required version`() {
|
||||
every { mockGoogleApiAvailabilityLight.getApkVersion(context) } returns 261031034
|
||||
assertFalse(gmsManager.isVersionAtLeast(261031035))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isVersionAtLeast should return false when GMS is not installed`() {
|
||||
every { mockGoogleApiAvailabilityLight.getApkVersion(context) } returns 0
|
||||
assertFalse(gmsManager.isVersionAtLeast(261031035))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.manager.BuildInfoManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.network.model.PolicyTypeJson
|
||||
@@ -11,7 +12,6 @@ import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.GmsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.ui.platform.model.SnackbarRelay
|
||||
@@ -29,6 +29,9 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
private val buildInfoManager = mockk<BuildInfoManager> {
|
||||
every { isFdroid } returns false
|
||||
}
|
||||
private val mutableFirstTimeStateFlow = MutableStateFlow(DEFAULT_FIRST_TIME_STATE)
|
||||
private val firstTimeActionManager = mockk<FirstTimeActionManager> {
|
||||
every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE
|
||||
@@ -42,9 +45,6 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolImport)
|
||||
} returns mutableFeatureFlagFlow
|
||||
}
|
||||
private val gmsManager = mockk<GmsManager> {
|
||||
every { isVersionAtLeast(any()) } returns true
|
||||
}
|
||||
private val mutablePoliciesFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Policy>>()
|
||||
private val policyManager = mockk<PolicyManager> {
|
||||
every { getActivePolicies(any()) } returns emptyList()
|
||||
@@ -224,10 +224,12 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `showImportItemsChevron should be false when GMS version is insufficient`() {
|
||||
every { gmsManager.isVersionAtLeast(any()) } returns false
|
||||
fun `showImportItemsChevron should be false when feature flag is enabled but policy exists`() {
|
||||
val viewModel = createViewModel()
|
||||
mutableFeatureFlagFlow.tryEmit(true)
|
||||
mutablePoliciesFlow.tryEmit(listOf(mockk()))
|
||||
assertEquals(
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
|
||||
viewModel.stateFlow.value,
|
||||
@@ -235,9 +237,22 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ImportItemsClick should emit NavigateToImportVault when GMS version is insufficient`() =
|
||||
fun `showImportItemsChevron should be false when isFdroid is true`() {
|
||||
every { buildInfoManager.isFdroid } returns true
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
VaultSettingsState(
|
||||
showImportActionCard = true,
|
||||
showImportItemsChevron = false,
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ImportItemsClick should emit NavigateToImportVault when isFdroid is true`() =
|
||||
runTest {
|
||||
every { gmsManager.isVersionAtLeast(any()) } returns false
|
||||
every { buildInfoManager.isFdroid } returns true
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||
@@ -248,23 +263,11 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `showImportItemsChevron should be false when feature flag and GMS sufficient but policy exists`() {
|
||||
val viewModel = createViewModel()
|
||||
mutableFeatureFlagFlow.tryEmit(true)
|
||||
mutablePoliciesFlow.tryEmit(listOf(mockk()))
|
||||
assertEquals(
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel(
|
||||
buildInfoManager = buildInfoManager,
|
||||
firstTimeActionManager = firstTimeActionManager,
|
||||
snackbarRelayManager = snackbarRelayManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
gmsManager = gmsManager,
|
||||
policyManager = policyManager,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.manager.BuildInfoManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
@@ -34,7 +35,6 @@ import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.CredentialExchangeRegistryManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.GmsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
@@ -213,13 +213,14 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
every { delayDialog() } just runs
|
||||
}
|
||||
|
||||
private val buildInfoManager = mockk<BuildInfoManager> {
|
||||
every { isFdroid } returns false
|
||||
}
|
||||
|
||||
private val credentialExchangeRegistryManager: CredentialExchangeRegistryManager = mockk {
|
||||
coEvery { register() } returns RegisterExportResult.Success
|
||||
coEvery { unregister() } returns UnregisterExportResult.Success
|
||||
}
|
||||
private val gmsManager: GmsManager = mockk {
|
||||
every { isVersionAtLeast(any()) } returns true
|
||||
}
|
||||
private val mutableCxpExportFeatureFlagFlow = MutableStateFlow(false)
|
||||
private val mutableArchiveItemsFlagFlow = MutableStateFlow(true)
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
@@ -3798,10 +3799,10 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `CredentialExchangeProtocolExportFlagUpdateReceive should unregister when flag is enabled but GMS version is insufficient`() =
|
||||
fun `CredentialExchangeProtocolExportFlagUpdateReceive should unregister when flag is enabled but isFdroid is true`() =
|
||||
runTest {
|
||||
every { buildInfoManager.isFdroid } returns true
|
||||
mutableCxpExportFeatureFlagFlow.value = false
|
||||
every { gmsManager.isVersionAtLeast(any()) } returns false
|
||||
coEvery { credentialExchangeRegistryManager.unregister() } just awaits
|
||||
|
||||
val viewModel = createViewModel()
|
||||
@@ -3815,26 +3816,8 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
coVerify {
|
||||
credentialExchangeRegistryManager.unregister()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `CredentialExchangeProtocolExportFlagUpdateReceive should unregister when flag is disabled and GMS version is sufficient`() =
|
||||
runTest {
|
||||
mutableCxpExportFeatureFlagFlow.value = true
|
||||
every { settingsRepository.isAppRegisteredForExport() } returns true
|
||||
coEvery { credentialExchangeRegistryManager.unregister() } just awaits
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.trySendAction(
|
||||
VaultAction.Internal.CredentialExchangeProtocolExportFlagUpdateReceive(
|
||||
isCredentialExchangeProtocolExportEnabled = false,
|
||||
),
|
||||
)
|
||||
|
||||
coVerify {
|
||||
credentialExchangeRegistryManager.unregister()
|
||||
coVerify(exactly = 0) {
|
||||
credentialExchangeRegistryManager.register()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3856,7 +3839,7 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
networkConnectionManager = networkConnectionManager,
|
||||
browserAutofillDialogManager = browserAutofillDialogManager,
|
||||
credentialExchangeRegistryManager = credentialExchangeRegistryManager,
|
||||
gmsManager = gmsManager,
|
||||
buildInfoManager = buildInfoManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bitwarden.cxf.registry
|
||||
|
||||
import androidx.credentials.providerevents.transfer.ClearExportResponse
|
||||
import androidx.credentials.providerevents.transfer.RegisterExportResponse
|
||||
import com.bitwarden.cxf.registry.model.RegistrationRequest
|
||||
|
||||
@@ -15,7 +16,7 @@ interface CredentialExchangeRegistry {
|
||||
*
|
||||
* @param registrationRequest The request to register as a credential provider.
|
||||
*
|
||||
* @return A [Result] indicating if the application was add to the registry. [Result.isSuccess]
|
||||
* @return A [Result] indicating if the application was added to the registry. [Result.isSuccess]
|
||||
* does not indicate if the application was added to the registry. Use the result value to check
|
||||
* if the application was added or not. [Result.isFailure] only indicates if an error occurred.
|
||||
*/
|
||||
@@ -27,7 +28,8 @@ interface CredentialExchangeRegistry {
|
||||
* By unregistering as a credential provider, the application will no longer be presented as an
|
||||
* option to users when they initiate the Import process from another credential manager.
|
||||
*
|
||||
* @return True if the unregistration was successful, false otherwise.
|
||||
* @return A [Result] containing a [ClearExportResponse] indicating if the export entries were
|
||||
* cleared. [Result.isFailure] only indicates if an error occurred during the clear operation.
|
||||
*/
|
||||
suspend fun unregister(): Result<RegisterExportResponse>
|
||||
suspend fun unregister(): Result<ClearExportResponse>
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ import android.app.Application
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toBitmapOrNull
|
||||
import androidx.credentials.providerevents.ProviderEventsManager
|
||||
import androidx.credentials.providerevents.exception.ClearExportException
|
||||
import androidx.credentials.providerevents.exception.RegisterExportException
|
||||
import androidx.credentials.providerevents.transfer.ClearExportRequest
|
||||
import androidx.credentials.providerevents.transfer.ClearExportResponse
|
||||
import androidx.credentials.providerevents.transfer.ExportEntry
|
||||
import androidx.credentials.providerevents.transfer.RegisterExportRequest
|
||||
import androidx.credentials.providerevents.transfer.RegisterExportResponse
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.cxf.R
|
||||
import com.bitwarden.cxf.registry.model.RegistrationRequest
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
@@ -26,20 +28,6 @@ internal class CredentialExchangeRegistryImpl(
|
||||
private val providerEventsManager: ProviderEventsManager =
|
||||
ProviderEventsManager.create(application)
|
||||
|
||||
/**
|
||||
* This is the default wasm binary provided by Google that runs the logic of deciding whether
|
||||
* the registered exporter can support the incoming import request.
|
||||
*
|
||||
* See https://github.com/danjkim/identity-samples/tree/main/CredentialProvider/credential_exchange_matcher
|
||||
* for source code and documentation.
|
||||
*/
|
||||
private val exportMatcher: ByteArray by lazy {
|
||||
application
|
||||
.resources
|
||||
.openRawResource(R.raw.export_matcher)
|
||||
.use { it.readBytes() }
|
||||
}
|
||||
|
||||
override suspend fun register(
|
||||
registrationRequest: RegistrationRequest,
|
||||
): Result<RegisterExportResponse> {
|
||||
@@ -52,17 +40,19 @@ internal class CredentialExchangeRegistryImpl(
|
||||
?: return IllegalArgumentException("Icon drawable must not be null.")
|
||||
.asFailure()
|
||||
|
||||
val request = RegisterExportRequest(
|
||||
val request = RegisterExportRequest.create(
|
||||
context = application,
|
||||
entries = listOf(
|
||||
ExportEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
accountDisplayName = null,
|
||||
userDisplayName = application.getString(registrationRequest.appNameRes),
|
||||
userDisplayName = application.getString(
|
||||
registrationRequest.appNameRes,
|
||||
),
|
||||
icon = icon,
|
||||
supportedCredentialTypes = registrationRequest.credentialTypes,
|
||||
),
|
||||
),
|
||||
exportMatcher = exportMatcher,
|
||||
)
|
||||
return try {
|
||||
providerEventsManager
|
||||
@@ -74,18 +64,12 @@ internal class CredentialExchangeRegistryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unregister(): Result<RegisterExportResponse> =
|
||||
override suspend fun unregister(): Result<ClearExportResponse> =
|
||||
try {
|
||||
providerEventsManager.registerExport(
|
||||
// This is a workaround for unregistering an account since an explicit "unregister"
|
||||
// API is not currently available.
|
||||
request = RegisterExportRequest(
|
||||
entries = emptyList(),
|
||||
exportMatcher = byteArrayOf(),
|
||||
),
|
||||
)
|
||||
providerEventsManager
|
||||
.clearExport(request = ClearExportRequest())
|
||||
.asSuccess()
|
||||
} catch (e: RegisterExportException) {
|
||||
} catch (e: ClearExportException) {
|
||||
Timber.e(e, "Failed to unregister application for export.")
|
||||
e.asFailure()
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -22,7 +22,7 @@ androidxCamera = "1.6.0"
|
||||
androidxComposeBom = "2026.03.01"
|
||||
androidxCore = "1.18.0"
|
||||
androidxCredentials = "1.6.0-rc02"
|
||||
androidxCredentialsProviderEvents = "1.0.0-alpha05"
|
||||
androidxCredentialsProviderEvents = "1.0.0-alpha06"
|
||||
androidxHiltNavigationCompose = "1.3.0"
|
||||
androidxLifecycle = "2.10.0"
|
||||
androidxNavigation = "2.9.7"
|
||||
|
||||
Reference in New Issue
Block a user