diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt
index 858fa8906..bdfc46af1 100644
--- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt
@@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
+import android.os.Build
import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
@@ -19,6 +20,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult
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.util.baseWebVaultUrlOrDefault
+import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
@@ -71,9 +73,9 @@ class AccountSecurityViewModel @Inject constructor(
?.activeAccount
?.hasMasterPassword != false,
isUnlockWithPinEnabled = settingsRepository.isUnlockWithPinEnabled,
- shouldShowEnableAuthenticatorSync = featureFlagManager.getFeatureFlag(
- key = FlagKey.AuthenticatorSync,
- ),
+ shouldShowEnableAuthenticatorSync =
+ featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) &&
+ !isBuildVersionBelow(Build.VERSION_CODES.S),
userId = userId,
vaultTimeout = settingsRepository.vaultTimeout,
vaultTimeoutAction = settingsRepository.vaultTimeoutAction,
@@ -372,9 +374,11 @@ class AccountSecurityViewModel @Inject constructor(
private fun handleAuthenticatorSyncFeatureFlagUpdate(
action: AccountSecurityAction.Internal.AuthenticatorSyncFeatureFlagUpdate,
) {
+ val shouldShowAuthenticatorSync =
+ action.isEnabled && !isBuildVersionBelow(Build.VERSION_CODES.S)
mutableStateFlow.update {
it.copy(
- shouldShowEnableAuthenticatorSync = action.isEnabled,
+ shouldShowEnableAuthenticatorSync = shouldShowAuthenticatorSync,
)
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 50e9c553b..ce0057b3f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1001,7 +1001,7 @@ Do you want to switch to this account?
Please restart registration or try logging in. You may already have an account.
Restart registration
Authenticator Sync
- Allow Bitwarden Authenticator Syncing
+ Allow authenticator syncing
There was an issue validating the registration token.
Turn on autofill
Use autofill to log into your accounts with a single tap.
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt
index b1692e0da..09b2ba040 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt
@@ -1465,7 +1465,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
@Test
fun `sync with Bitwarden authenticator UI should be displayed according to state`() {
- val toggleText = "Allow Bitwarden Authenticator Syncing"
+ val toggleText = "Allow authenticator syncing"
composeTestRule.onNodeWithText(toggleText).assertDoesNotExist()
mutableStateFlow.update { DEFAULT_STATE.copy(shouldShowEnableAuthenticatorSync = true) }
@@ -1486,7 +1486,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
fun `sync with Bitwarden authenticator click should send AuthenticatorSyncToggle action`() {
mutableStateFlow.update { DEFAULT_STATE.copy(shouldShowEnableAuthenticatorSync = true) }
composeTestRule
- .onNodeWithText("Allow Bitwarden Authenticator Syncing")
+ .onNodeWithText("Allow authenticator syncing")
.performScrollTo()
.performClick()
verify { viewModel.trySendAction(AccountSecurityAction.AuthenticatorSyncToggle(true)) }
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt
index 49ee1973c..3128445d5 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt
@@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
+import android.os.Build
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.R
@@ -22,6 +23,7 @@ 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.util.FakeEnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
+import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy
@@ -35,7 +37,9 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
+import io.mockk.mockkStatic
import io.mockk.runs
+import io.mockk.unmockkStatic
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
@@ -44,7 +48,9 @@ import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
+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
import javax.crypto.Cipher
@@ -91,6 +97,16 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
every { getFeatureFlag(FlagKey.AuthenticatorSync) } returns false
}
+ @BeforeEach
+ fun setup() {
+ mockkStatic(::isBuildVersionBelow)
+ }
+
+ @AfterEach
+ fun teardown() {
+ unmockkStatic(::isBuildVersionBelow)
+ }
+
@Test
fun `initial state should be correct when saved state is set`() {
val viewModel = createViewModel(initialState = DEFAULT_STATE)
@@ -679,7 +695,8 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
@Suppress("MaxLineLength")
@Test
- fun `when featureFlagManger returns true for AuthenticatorSync, should show authenticator sync UI`() {
+ fun `when featureFlagManger returns true for AuthenticatorSync, and version is at least 31 should show authenticator sync UI`() {
+ every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns false
val vm = createViewModel(
initialState = null,
featureFlagManager = mockk {
@@ -693,8 +710,22 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
)
}
+ @Suppress("MaxLineLength")
+ @Test
+ fun `when featureFlagManger returns true for AuthenticatorSync, and version is under 31 should not show authenticator sync UI`() {
+ every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns true
+ every { featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) } returns true
+ every { featureFlagManager.getFeatureFlagFlow(FlagKey.AuthenticatorSync) } returns emptyFlow()
+ val vm = createViewModel(initialState = null)
+ assertEquals(
+ DEFAULT_STATE,
+ vm.stateFlow.value,
+ )
+ }
+
@Test
fun `when featureFlagManger updates value AuthenticatorSync, should update UI`() = runTest {
+ every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns false
val featureFlagFlow = MutableStateFlow(false)
val vm = createViewModel(
initialState = null,
@@ -712,6 +743,25 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
}
}
+ @Test
+ @Suppress("MaxLineLength")
+ fun `when featureFlagManger updates value AuthenticatorSync, authenticator sync row should never show if below API 31`() =
+ runTest {
+ every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns true
+ val featureFlagFlow = MutableStateFlow(false)
+ every { featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) } returns false
+ every {
+ featureFlagManager.getFeatureFlagFlow(FlagKey.AuthenticatorSync)
+ } returns featureFlagFlow
+ val vm = createViewModel(initialState = null)
+ vm.stateFlow.test {
+ assertEquals(DEFAULT_STATE, awaitItem())
+ featureFlagFlow.value = true
+ featureFlagFlow.value = false
+ expectNoEvents()
+ }
+ }
+
@Test
fun `when showUnlockBadgeFlow updates value, should update state`() = runTest {
val viewModel = createViewModel()
diff --git a/authenticatorbridge/src/main/java/com/bitwarden/authenticatorbridge/util/AndroidBuildUtils.kt b/authenticatorbridge/src/main/java/com/bitwarden/authenticatorbridge/util/AndroidBuildUtils.kt
index 3372d99c1..08f7b812b 100644
--- a/authenticatorbridge/src/main/java/com/bitwarden/authenticatorbridge/util/AndroidBuildUtils.kt
+++ b/authenticatorbridge/src/main/java/com/bitwarden/authenticatorbridge/util/AndroidBuildUtils.kt
@@ -7,4 +7,4 @@ import android.os.Build
*
* @see Build.VERSION_CODES
*/
-fun isBuildVersionBelow(version: Int): Boolean = version > Build.VERSION.SDK_INT
+internal fun isBuildVersionBelow(version: Int): Boolean = version > Build.VERSION.SDK_INT