Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.x8bit.bitwarden.data.autofill.processor.AutofillProcessorImpl
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProviderImpl
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
Expand Down Expand Up @@ -70,11 +71,13 @@ object AutofillModule {
autofillEnabledManager: AutofillEnabledManager,
browserThirdPartyAutofillEnabledManager: BrowserThirdPartyAutofillEnabledManager,
clock: Clock,
firstTimeActionManager: FirstTimeActionManager,
settingsDiskSource: SettingsDiskSource,
): BrowserAutofillDialogManager = BrowserAutofillDialogManagerImpl(
autofillEnabledManager = autofillEnabledManager,
browserThirdPartyAutofillEnabledManager = browserThirdPartyAutofillEnabledManager,
clock = clock,
firstTimeActionManager = firstTimeActionManager,
settingsDiskSource = settingsDiskSource,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ package com.x8bit.bitwarden.data.autofill.manager.browser
* Manager to handle whether the Browser Autofill Dialog should be displayed.
*/
interface BrowserAutofillDialogManager {
/**
* Number of browsers installed that may need autofill enabled.
*/
val browserCount: Int

/**
* Indicates whether the dialog should be displayed to the user.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.autofill.manager.browser

import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import java.time.Clock

/**
Expand All @@ -16,13 +17,22 @@ internal class BrowserAutofillDialogManagerImpl(
private val autofillEnabledManager: AutofillEnabledManager,
private val browserThirdPartyAutofillEnabledManager: BrowserThirdPartyAutofillEnabledManager,
private val clock: Clock,
private val firstTimeActionManager: FirstTimeActionManager,
private val settingsDiskSource: SettingsDiskSource,
) : BrowserAutofillDialogManager {
override val browserCount: Int
get() = browserThirdPartyAutofillEnabledManager
.browserThirdPartyAutofillStatus
.availableCount

override val shouldShowDialog: Boolean
get() = autofillEnabledManager.isAutofillEnabled &&
browserThirdPartyAutofillEnabledManager
.browserThirdPartyAutofillStatus
.isAnyIsAvailableAndDisabled &&
!firstTimeActionManager
.currentOrDefaultUserFirstTimeState
.showSetupBrowserAutofillCard &&
settingsDiskSource.browserAutofillDialogReshowTime?.isBefore(clock.instant()) != false

override fun delayDialog() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ data class BrowserThirdPartyAutofillStatus(
val chromeStableStatusData: BrowserThirdPartyAutoFillData,
val chromeBetaChannelStatusData: BrowserThirdPartyAutoFillData,
) {
/**
* The total number of available browsers.
*/
val availableCount: Int
get() = (if (braveStableStatusData.isAvailable) 1 else 0) +
(if (chromeStableStatusData.isAvailable) 1 else 0) +
(if (chromeBetaChannelStatusData.isAvailable) 1 else 0)

/**
* Whether any of the available browsers have third party autofill disabled.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class FirstTimeActionManagerImpl @Inject constructor(
showImportLoginsCardInSettings = settingsDiskSource
.getShowImportLoginsSettingBadge(it),
showSetupBrowserAutofillCard = settingsDiskSource
.getShowUnlockSettingBadge(it),
.getShowBrowserAutofillSettingBadge(it),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug that was missed.

)
}
?: FirstTimeState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
Expand All @@ -34,6 +35,7 @@ import com.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.bitwarden.ui.platform.composition.LocalIntentManager
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenPlurals
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
Expand Down Expand Up @@ -141,8 +143,9 @@ private fun SetupBrowserAutofillContent(
)
Spacer(Modifier.height(height = 8.dp))
Text(
text = stringResource(
id = BitwardenString.youre_using_a_browser_that_requires_special_permissions,
text = pluralStringResource(
id = BitwardenPlurals.youre_using_a_browser_that_requires_special_permissions,
count = state.browserCount,
),
style = BitwardenTheme.typography.bodyMedium,
color = BitwardenTheme.colorScheme.text.primary,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,15 @@ data class SetupBrowserAutofillState(
val isInitialSetup: Boolean,
val browserAutofillSettingsOptions: ImmutableList<BrowserAutofillSettingsOption>,
) : Parcelable {
/**
* The number of browsers that can be configured.
*/
val browserCount: Int get() = browserAutofillSettingsOptions.size

/**
* Indicates if the Continue button should be enabled or not.
*/
val isContinueEnabled: Boolean get() = browserAutofillSettingsOptions.any { it.isEnabled }
val isContinueEnabled: Boolean get() = browserAutofillSettingsOptions.all { it.isEnabled }

/**
* Models dialogs that can be shown on the Setup Browser Autofill screen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
Expand Down Expand Up @@ -54,6 +55,7 @@ import com.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.bitwarden.ui.platform.composition.LocalIntentManager
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenPlurals
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
Expand Down Expand Up @@ -197,9 +199,10 @@ private fun AutoFillScreenContent(
cardTitle = stringResource(
id = BitwardenString.turn_on_browser_autofill_integration,
),
cardSubtitle = stringResource(
id = BitwardenString
cardSubtitle = pluralStringResource(
id = BitwardenPlurals
.youre_using_a_browser_that_requires_special_permissions_for_bitwarden,
count = state.browserCount,
),
actionText = stringResource(id = BitwardenString.get_started),
onActionClick = autoFillHandlers.onBrowserAutofillActionCardClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ data class AutoFillState(
*/
val showInlineAutofill: Boolean get() = isAutoFillServicesEnabled && showInlineAutofillOption

/**
* The number of browsers that can be configured.
*/
val browserCount: Int get() = browserAutofillSettingsOptions.size

/**
* Whether or not the toggles for enabling 3rd-party autofill support should be displayed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,13 +447,14 @@ private fun VaultDialogs(
)
}

VaultState.DialogState.ThirdPartyBrowserAutofill -> {
is VaultState.DialogState.ThirdPartyBrowserAutofill -> {
BitwardenTwoButtonDialog(
title = stringResource(
id = BitwardenString.enable_browser_autofill_to_keep_filling_passwords,
),
message = stringResource(
id = BitwardenString.your_browser_recently_updated_how_autofill_works,
message = pluralStringResource(
id = BitwardenPlurals.your_browser_recently_updated_how_autofill_works,
count = dialogState.browserCount,
),
confirmButtonText = stringResource(id = BitwardenString.go_to_settings),
dismissButtonText = stringResource(id = BitwardenString.not_now),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ class VaultViewModel @Inject constructor(
delay(timeMillis = BROWSER_AUTOFILL_DIALOG_DELAY)
mutableStateFlow.update { vaultState ->
vaultState.copy(
dialog = VaultState.DialogState.ThirdPartyBrowserAutofill
dialog = VaultState.DialogState
.ThirdPartyBrowserAutofill(browserAutofillDialogManager.browserCount)
.takeIf {
vaultState.dialog == null &&
browserAutofillDialogManager.shouldShowDialog
Expand Down Expand Up @@ -953,7 +954,7 @@ class VaultViewModel @Inject constructor(
cipherCount = vaultData.data.decryptCipherListResult.failures.size,
)
} else if (state.dialog is VaultState.DialogState.ThirdPartyBrowserAutofill) {
VaultState.DialogState.ThirdPartyBrowserAutofill
state.dialog
} else {
null
},
Expand Down Expand Up @@ -1495,7 +1496,9 @@ data class VaultState(
* Represents a dialog indicating that a 3rd party browser required Autofill configuration.
*/
@Parcelize
data object ThirdPartyBrowserAutofill : DialogState()
data class ThirdPartyBrowserAutofill(
val browserCount: Int,
) : DialogState()

/**
* Represents a dialog indicating that there was a decryption error loading ciphers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserThirdPartyAutoFillData
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserThirdPartyAutofillStatus
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
Expand All @@ -18,16 +21,32 @@ import java.time.temporal.ChronoUnit
class BrowserAutofillDialogManagerTest {

private val autofillEnabledManager: AutofillEnabledManager = mockk()
private val firstTimeActionManager: FirstTimeActionManager = mockk()
private val thirdPartyAutofillEnabledManager: BrowserThirdPartyAutofillEnabledManager = mockk()
private val fakeSettingsDiskSource: FakeSettingsDiskSource = FakeSettingsDiskSource()

private val manager: BrowserAutofillDialogManager = BrowserAutofillDialogManagerImpl(
autofillEnabledManager = autofillEnabledManager,
browserThirdPartyAutofillEnabledManager = thirdPartyAutofillEnabledManager,
clock = FIXED_CLOCK,
firstTimeActionManager = firstTimeActionManager,
settingsDiskSource = fakeSettingsDiskSource,
)

@Test
fun `browserCount should return the browser count`() {
val count = 0
every {
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
} returns mockk { every { availableCount } returns count }

assertEquals(count, manager.browserCount)

verify(exactly = 1) {
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
}
}

@Test
fun `shouldShowDialog should be false when autofill is disabled`() {
every { autofillEnabledManager.isAutofillEnabled } returns false
Expand Down Expand Up @@ -91,6 +110,41 @@ class BrowserAutofillDialogManagerTest {
every {
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
} returns browserThirdPartyAutofillStatus
every {
firstTimeActionManager.currentOrDefaultUserFirstTimeState
} returns FirstTimeState(showSetupBrowserAutofillCard = false)

assertFalse(manager.shouldShowDialog)

verify(exactly = 1) {
autofillEnabledManager.isAutofillEnabled
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
}
}

@Test
fun `shouldShowDialog should be false browser autofill badge is being displayed`() {
val browserThirdPartyAutofillStatus = BrowserThirdPartyAutofillStatus(
braveStableStatusData = BrowserThirdPartyAutoFillData(
isAvailable = true,
isThirdPartyEnabled = false,
),
chromeStableStatusData = BrowserThirdPartyAutoFillData(
isAvailable = true,
isThirdPartyEnabled = false,
),
chromeBetaChannelStatusData = BrowserThirdPartyAutoFillData(
isAvailable = true,
isThirdPartyEnabled = false,
),
)
every { autofillEnabledManager.isAutofillEnabled } returns true
every {
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
} returns browserThirdPartyAutofillStatus
every {
firstTimeActionManager.currentOrDefaultUserFirstTimeState
} returns FirstTimeState(showSetupBrowserAutofillCard = true)

assertFalse(manager.shouldShowDialog)

Expand Down Expand Up @@ -120,6 +174,9 @@ class BrowserAutofillDialogManagerTest {
every {
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
} returns browserThirdPartyAutofillStatus
every {
firstTimeActionManager.currentOrDefaultUserFirstTimeState
} returns FirstTimeState(showSetupBrowserAutofillCard = false)
fakeSettingsDiskSource.browserAutofillDialogReshowTime = FIXED_CLOCK.instant()

assertFalse(manager.shouldShowDialog)
Expand Down Expand Up @@ -150,6 +207,9 @@ class BrowserAutofillDialogManagerTest {
every {
thirdPartyAutofillEnabledManager.browserThirdPartyAutofillStatus
} returns browserThirdPartyAutofillStatus
every {
firstTimeActionManager.currentOrDefaultUserFirstTimeState
} returns FirstTimeState(showSetupBrowserAutofillCard = false)
fakeSettingsDiskSource.browserAutofillDialogReshowTime = null

assertTrue(manager.shouldShowDialog)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ class VaultScreenTest : BitwardenComposeTest() {
fun `ThirdPartyBrowserAutofill should be displayed according to state`() {
composeTestRule.assertNoDialogExists()
mutableStateFlow.update {
it.copy(dialog = VaultState.DialogState.ThirdPartyBrowserAutofill)
it.copy(dialog = VaultState.DialogState.ThirdPartyBrowserAutofill(browserCount = 1))
}

composeTestRule
Expand All @@ -601,7 +601,7 @@ class VaultScreenTest : BitwardenComposeTest() {
@Test
fun `ThirdPartyBrowserAutofill dialog Not now button should emit DismissThirdPartyAutofillDialogClick`() {
mutableStateFlow.update {
it.copy(dialog = VaultState.DialogState.ThirdPartyBrowserAutofill)
it.copy(dialog = VaultState.DialogState.ThirdPartyBrowserAutofill(browserCount = 2))
}

composeTestRule
Expand All @@ -619,7 +619,7 @@ class VaultScreenTest : BitwardenComposeTest() {
@Test
fun `ThirdPartyBrowserAutofill dialog Go to settings now button should emit EnableThirdPartyAutofillClick`() {
mutableStateFlow.update {
it.copy(dialog = VaultState.DialogState.ThirdPartyBrowserAutofill)
it.copy(dialog = VaultState.DialogState.ThirdPartyBrowserAutofill(browserCount = 3))
}

composeTestRule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ class VaultViewModelTest : BaseViewModelTest() {
}
private val browserAutofillDialogManager: BrowserAutofillDialogManager = mockk {
every { shouldShowDialog } returns false
every { browserCount } returns 1
every { delayDialog() } just runs
}

Expand Down
Loading