PM-26312: Add browser integration help link (#5963)

This commit is contained in:
David Perez 2025-09-30 12:47:43 -05:00 committed by GitHub
parent 6ca8a39355
commit 116bfd6351
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 84 additions and 2 deletions

View File

@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
@ -14,6 +15,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.pluralStringResource
@ -21,6 +23,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitwarden.ui.platform.base.util.EventsEffect
@ -31,6 +34,7 @@ import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.bitwarden.ui.platform.components.text.BitwardenClickableText
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.bitwarden.ui.platform.composition.LocalIntentManager
import com.bitwarden.ui.platform.manager.IntentManager
@ -64,6 +68,12 @@ fun SetupBrowserAutofillScreen(
browserPackage = event.browserPackage,
)
}
SetupBrowserAutofillEvent.NavigateToBrowserIntegrationsInfo -> {
intentManager.launchUri(
"https://bitwarden.com/help/auto-fill-android/#browser-integrations/".toUri(),
)
}
}
}
SetupBrowserAutofillDialogs(
@ -106,6 +116,9 @@ fun SetupBrowserAutofillScreen(
) {
SetupBrowserAutofillContent(
state = state,
onWhyIsThisStepRequiredClick = remember(viewModel) {
{ viewModel.trySendAction(SetupBrowserAutofillAction.WhyIsThisStepRequiredClick) }
},
onBrowserClick = remember(viewModel) {
{ viewModel.trySendAction(SetupBrowserAutofillAction.BrowserIntegrationClick(it)) }
},
@ -120,9 +133,11 @@ fun SetupBrowserAutofillScreen(
}
}
@Suppress("LongMethod")
@Composable
private fun SetupBrowserAutofillContent(
state: SetupBrowserAutofillState,
onWhyIsThisStepRequiredClick: () -> Unit,
onBrowserClick: (BrowserPackage) -> Unit,
onContinueClick: () -> Unit,
onTurnOnLaterClick: () -> Unit,
@ -154,7 +169,16 @@ private fun SetupBrowserAutofillContent(
.fillMaxWidth()
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(height = 24.dp))
BitwardenClickableText(
label = stringResource(id = BitwardenString.why_is_this_step_required),
style = BitwardenTheme.typography.labelMedium,
onClick = onWhyIsThisStepRequiredClick,
modifier = Modifier
.wrapContentWidth()
.align(alignment = Alignment.CenterHorizontally)
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(height = 8.dp))
BrowserAutofillSettingsCard(
options = state.browserAutofillSettingsOptions,
onOptionClicked = onBrowserClick,
@ -221,6 +245,7 @@ private fun SetupBrowserAutofillContent_preview() {
BrowserAutofillSettingsOption.ChromeBeta(enabled = true),
),
),
onWhyIsThisStepRequiredClick = { },
onBrowserClick = { },
onContinueClick = { },
onTurnOnLaterClick = { },

View File

@ -56,6 +56,10 @@ class SetupBrowserAutofillViewModel @Inject constructor(
handleBrowserIntegrationClick(action)
}
SetupBrowserAutofillAction.WhyIsThisStepRequiredClick -> {
handleWhyIsThisStepRequiredClick()
}
SetupBrowserAutofillAction.CloseClick -> handleCloseClick()
SetupBrowserAutofillAction.DismissDialog -> handleDismissDialog()
SetupBrowserAutofillAction.ContinueClick -> handleContinueClick()
@ -81,6 +85,10 @@ class SetupBrowserAutofillViewModel @Inject constructor(
)
}
private fun handleWhyIsThisStepRequiredClick() {
sendEvent(SetupBrowserAutofillEvent.NavigateToBrowserIntegrationsInfo)
}
private fun handleCloseClick() {
sendEvent(SetupBrowserAutofillEvent.NavigateBack)
}
@ -167,6 +175,11 @@ sealed class SetupBrowserAutofillEvent {
data class NavigateToBrowserAutofillSettings(
val browserPackage: BrowserPackage,
) : SetupBrowserAutofillEvent()
/**
* Navigates to the browser integrations info page.
*/
data object NavigateToBrowserIntegrationsInfo : SetupBrowserAutofillEvent()
}
/**
@ -205,6 +218,11 @@ sealed class SetupBrowserAutofillAction {
*/
data object TurnOnLaterConfirmClick : SetupBrowserAutofillAction()
/**
* Indicates that the "Why is this step required?" button was clicked.
*/
data object WhyIsThisStepRequiredClick : SetupBrowserAutofillAction()
/**
* Models actions the [SetupBrowserAutofillViewModel] itself may send.
*/

View File

@ -9,6 +9,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.core.net.toUri
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.util.assertNoDialogExists
@ -34,7 +35,9 @@ import org.junit.Test
class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
private var onNavigateBackCalled = false
private val intentManager = mockk<IntentManager>()
private val intentManager = mockk<IntentManager> {
every { launchUri(uri = any()) } just runs
}
private val mutableEventFlow = bufferedMutableSharedFlow<SetupBrowserAutofillEvent>()
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
@ -68,6 +71,16 @@ class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
assertTrue(onNavigateBackCalled)
}
@Test
fun `NavigateToBrowserIntegrationsInfo should call onNavigateBack`() {
mutableEventFlow.tryEmit(SetupBrowserAutofillEvent.NavigateToBrowserIntegrationsInfo)
verify(exactly = 1) {
intentManager.launchUri(
uri = "https://bitwarden.com/help/auto-fill-android/#browser-integrations/".toUri(),
)
}
}
@Test
fun `NavigateToBrowserAutofillSettings should start system autofill settings activity`() {
val browserPackage = BrowserPackage.CHROME_STABLE
@ -112,6 +125,18 @@ class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
composeTestRule.onNodeWithContentDescription(label = "Close").assertExists()
}
@Test
fun `why is this step required button click should emit WhyIsThisStepRequiredClick`() {
mutableStateFlow.update { it.copy(isInitialSetup = false) }
composeTestRule
.onNodeWithText(text = "Why is this step required?")
.performScrollTo()
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(SetupBrowserAutofillAction.WhyIsThisStepRequiredClick)
}
}
@Test
fun `close button click should emit CloseClick`() {
mutableStateFlow.update { it.copy(isInitialSetup = false) }

View File

@ -101,6 +101,19 @@ class SetupBrowserAutofillViewModelTest {
}
}
@Test
fun `WhyIsThisStepRequiredClick should send NavigateToBrowserIntegrationsInfo event`() =
runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
viewModel.trySendAction(SetupBrowserAutofillAction.WhyIsThisStepRequiredClick)
assertEquals(
SetupBrowserAutofillEvent.NavigateToBrowserIntegrationsInfo,
awaitItem(),
)
}
}
@Test
fun `BrowserIntegrationClick should send NavigateToBrowserAutofillSettings event`() = runTest {
val browserPackage = BrowserPackage.BRAVE_RELEASE

View File

@ -1130,4 +1130,5 @@ Do you want to switch to this account?</string>
<string name="passwords">Passwords</string>
<string name="passkeys">Passkeys</string>
<string name="import_verb">Import</string>
<string name="why_is_this_step_required">Why is this step required?</string>
</resources>