mirror of
https://github.com/bitwarden/android.git
synced 2025-12-13 04:09:01 -06:00
PM-26312: Add browser integration help link (#5963)
This commit is contained in:
parent
6ca8a39355
commit
116bfd6351
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@ -14,6 +15,7 @@ import androidx.compose.material3.TopAppBarDefaults
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.pluralStringResource
|
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.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.bitwarden.ui.platform.base.util.EventsEffect
|
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.button.BitwardenOutlinedButton
|
||||||
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||||
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
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.components.util.rememberVectorPainter
|
||||||
import com.bitwarden.ui.platform.composition.LocalIntentManager
|
import com.bitwarden.ui.platform.composition.LocalIntentManager
|
||||||
import com.bitwarden.ui.platform.manager.IntentManager
|
import com.bitwarden.ui.platform.manager.IntentManager
|
||||||
@ -64,6 +68,12 @@ fun SetupBrowserAutofillScreen(
|
|||||||
browserPackage = event.browserPackage,
|
browserPackage = event.browserPackage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetupBrowserAutofillEvent.NavigateToBrowserIntegrationsInfo -> {
|
||||||
|
intentManager.launchUri(
|
||||||
|
"https://bitwarden.com/help/auto-fill-android/#browser-integrations/".toUri(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetupBrowserAutofillDialogs(
|
SetupBrowserAutofillDialogs(
|
||||||
@ -106,6 +116,9 @@ fun SetupBrowserAutofillScreen(
|
|||||||
) {
|
) {
|
||||||
SetupBrowserAutofillContent(
|
SetupBrowserAutofillContent(
|
||||||
state = state,
|
state = state,
|
||||||
|
onWhyIsThisStepRequiredClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(SetupBrowserAutofillAction.WhyIsThisStepRequiredClick) }
|
||||||
|
},
|
||||||
onBrowserClick = remember(viewModel) {
|
onBrowserClick = remember(viewModel) {
|
||||||
{ viewModel.trySendAction(SetupBrowserAutofillAction.BrowserIntegrationClick(it)) }
|
{ viewModel.trySendAction(SetupBrowserAutofillAction.BrowserIntegrationClick(it)) }
|
||||||
},
|
},
|
||||||
@ -120,9 +133,11 @@ fun SetupBrowserAutofillScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
private fun SetupBrowserAutofillContent(
|
private fun SetupBrowserAutofillContent(
|
||||||
state: SetupBrowserAutofillState,
|
state: SetupBrowserAutofillState,
|
||||||
|
onWhyIsThisStepRequiredClick: () -> Unit,
|
||||||
onBrowserClick: (BrowserPackage) -> Unit,
|
onBrowserClick: (BrowserPackage) -> Unit,
|
||||||
onContinueClick: () -> Unit,
|
onContinueClick: () -> Unit,
|
||||||
onTurnOnLaterClick: () -> Unit,
|
onTurnOnLaterClick: () -> Unit,
|
||||||
@ -154,7 +169,16 @@ private fun SetupBrowserAutofillContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.standardHorizontalMargin(),
|
.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(
|
BrowserAutofillSettingsCard(
|
||||||
options = state.browserAutofillSettingsOptions,
|
options = state.browserAutofillSettingsOptions,
|
||||||
onOptionClicked = onBrowserClick,
|
onOptionClicked = onBrowserClick,
|
||||||
@ -221,6 +245,7 @@ private fun SetupBrowserAutofillContent_preview() {
|
|||||||
BrowserAutofillSettingsOption.ChromeBeta(enabled = true),
|
BrowserAutofillSettingsOption.ChromeBeta(enabled = true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onWhyIsThisStepRequiredClick = { },
|
||||||
onBrowserClick = { },
|
onBrowserClick = { },
|
||||||
onContinueClick = { },
|
onContinueClick = { },
|
||||||
onTurnOnLaterClick = { },
|
onTurnOnLaterClick = { },
|
||||||
|
|||||||
@ -56,6 +56,10 @@ class SetupBrowserAutofillViewModel @Inject constructor(
|
|||||||
handleBrowserIntegrationClick(action)
|
handleBrowserIntegrationClick(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetupBrowserAutofillAction.WhyIsThisStepRequiredClick -> {
|
||||||
|
handleWhyIsThisStepRequiredClick()
|
||||||
|
}
|
||||||
|
|
||||||
SetupBrowserAutofillAction.CloseClick -> handleCloseClick()
|
SetupBrowserAutofillAction.CloseClick -> handleCloseClick()
|
||||||
SetupBrowserAutofillAction.DismissDialog -> handleDismissDialog()
|
SetupBrowserAutofillAction.DismissDialog -> handleDismissDialog()
|
||||||
SetupBrowserAutofillAction.ContinueClick -> handleContinueClick()
|
SetupBrowserAutofillAction.ContinueClick -> handleContinueClick()
|
||||||
@ -81,6 +85,10 @@ class SetupBrowserAutofillViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleWhyIsThisStepRequiredClick() {
|
||||||
|
sendEvent(SetupBrowserAutofillEvent.NavigateToBrowserIntegrationsInfo)
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleCloseClick() {
|
private fun handleCloseClick() {
|
||||||
sendEvent(SetupBrowserAutofillEvent.NavigateBack)
|
sendEvent(SetupBrowserAutofillEvent.NavigateBack)
|
||||||
}
|
}
|
||||||
@ -167,6 +175,11 @@ sealed class SetupBrowserAutofillEvent {
|
|||||||
data class NavigateToBrowserAutofillSettings(
|
data class NavigateToBrowserAutofillSettings(
|
||||||
val browserPackage: BrowserPackage,
|
val browserPackage: BrowserPackage,
|
||||||
) : SetupBrowserAutofillEvent()
|
) : SetupBrowserAutofillEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to the browser integrations info page.
|
||||||
|
*/
|
||||||
|
data object NavigateToBrowserIntegrationsInfo : SetupBrowserAutofillEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,6 +218,11 @@ sealed class SetupBrowserAutofillAction {
|
|||||||
*/
|
*/
|
||||||
data object TurnOnLaterConfirmClick : 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.
|
* Models actions the [SetupBrowserAutofillViewModel] itself may send.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
|
|||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import androidx.compose.ui.test.performScrollTo
|
import androidx.compose.ui.test.performScrollTo
|
||||||
|
import androidx.core.net.toUri
|
||||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||||
import com.bitwarden.ui.platform.manager.IntentManager
|
import com.bitwarden.ui.platform.manager.IntentManager
|
||||||
import com.bitwarden.ui.util.assertNoDialogExists
|
import com.bitwarden.ui.util.assertNoDialogExists
|
||||||
@ -34,7 +35,9 @@ import org.junit.Test
|
|||||||
|
|
||||||
class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
|
class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
|
||||||
private var onNavigateBackCalled = false
|
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 mutableEventFlow = bufferedMutableSharedFlow<SetupBrowserAutofillEvent>()
|
||||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||||
@ -68,6 +71,16 @@ class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
|
|||||||
assertTrue(onNavigateBackCalled)
|
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
|
@Test
|
||||||
fun `NavigateToBrowserAutofillSettings should start system autofill settings activity`() {
|
fun `NavigateToBrowserAutofillSettings should start system autofill settings activity`() {
|
||||||
val browserPackage = BrowserPackage.CHROME_STABLE
|
val browserPackage = BrowserPackage.CHROME_STABLE
|
||||||
@ -112,6 +125,18 @@ class SetupBrowserAutofillScreenTest : BitwardenComposeTest() {
|
|||||||
composeTestRule.onNodeWithContentDescription(label = "Close").assertExists()
|
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
|
@Test
|
||||||
fun `close button click should emit CloseClick`() {
|
fun `close button click should emit CloseClick`() {
|
||||||
mutableStateFlow.update { it.copy(isInitialSetup = false) }
|
mutableStateFlow.update { it.copy(isInitialSetup = false) }
|
||||||
|
|||||||
@ -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
|
@Test
|
||||||
fun `BrowserIntegrationClick should send NavigateToBrowserAutofillSettings event`() = runTest {
|
fun `BrowserIntegrationClick should send NavigateToBrowserAutofillSettings event`() = runTest {
|
||||||
val browserPackage = BrowserPackage.BRAVE_RELEASE
|
val browserPackage = BrowserPackage.BRAVE_RELEASE
|
||||||
|
|||||||
@ -1130,4 +1130,5 @@ Do you want to switch to this account?</string>
|
|||||||
<string name="passwords">Passwords</string>
|
<string name="passwords">Passwords</string>
|
||||||
<string name="passkeys">Passkeys</string>
|
<string name="passkeys">Passkeys</string>
|
||||||
<string name="import_verb">Import</string>
|
<string name="import_verb">Import</string>
|
||||||
|
<string name="why_is_this_step_required">Why is this step required?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user