PM-35281: feat: Update the BlockAutofill screen UI (#6807)

This commit is contained in:
David Perez
2026-04-21 09:25:22 -05:00
committed by GitHub
parent 39240b3317
commit 88a80cd2be
6 changed files with 200 additions and 155 deletions

View File

@@ -42,7 +42,6 @@ fun AddEditBlockedUriDialog(
onUriChange: (String) -> Unit,
onCancelClick: () -> Unit,
onSaveClick: (String) -> Unit,
onDeleteClick: (() -> Unit)? = null,
onDismissRequest: () -> Unit,
) {
Dialog(
@@ -66,7 +65,13 @@ fun AddEditBlockedUriDialog(
modifier = Modifier
.padding(top = 24.dp, start = 24.dp, end = 24.dp)
.fillMaxWidth(),
text = stringResource(id = BitwardenString.new_uri),
text = stringResource(
id = if (isEdit) {
BitwardenString.edit_blocked_uri
} else {
BitwardenString.new_blocked_uri
},
),
color = BitwardenTheme.colorScheme.text.primary,
style = BitwardenTheme.typography.headlineSmall,
)
@@ -104,13 +109,6 @@ fun AddEditBlockedUriDialog(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 8.dp, top = 24.dp, bottom = 24.dp, end = 24.dp),
) {
if (isEdit && onDeleteClick != null) {
BitwardenTextButton(
label = stringResource(id = BitwardenString.remove),
onClick = onDeleteClick,
)
}
BitwardenTextButton(
label = stringResource(id = BitwardenString.cancel),
onClick = onCancelClick,

View File

@@ -4,8 +4,6 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -17,17 +15,17 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
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
@@ -38,18 +36,24 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitwarden.ui.platform.base.util.EventsEffect
import com.bitwarden.ui.platform.base.util.bottomDivider
import com.bitwarden.ui.platform.base.util.cardStyle
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
import com.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
import com.bitwarden.ui.platform.components.appbar.model.OverflowMenuItemData
import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
import com.bitwarden.ui.platform.components.fab.BitwardenFloatingActionButton
import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import kotlinx.collections.immutable.persistentListOf
/**
* Displays the block auto-fill screen.
* Displays the block autofill screen.
*/
@Suppress("LongMethod")
@OptIn(ExperimentalMaterial3Api::class)
@@ -74,7 +78,6 @@ fun BlockAutoFillScreen(
BlockAutoFillAction.SaveUri(newUri = newUri, originalUri = originalUri),
)
},
onRemoveClick = { viewModel.trySendAction(BlockAutoFillAction.RemoveUriClick(it)) },
onDismissRequest = { viewModel.trySendAction(BlockAutoFillAction.DismissDialog) },
)
@@ -107,84 +110,94 @@ fun BlockAutoFillScreen(
}
},
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
when (val viewState = state.viewState) {
is BlockAutoFillState.ViewState.Content -> {
item {
Row(
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 20.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Text(
text = stringResource(
id = BitwardenString
.auto_fill_will_not_be_offered_for_these_ur_is,
),
color = BitwardenTheme.colorScheme.text.primary,
style = BitwardenTheme.typography.bodyMedium,
modifier = Modifier.align(Alignment.CenterVertically),
)
}
}
items(viewState.blockedUris, key = { it }) { uri ->
BlockAutoFillListItem(
label = uri,
onClick = {
viewModel.trySendAction(BlockAutoFillAction.EditUriClick(uri))
},
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
)
}
}
is BlockAutoFillState.ViewState.Empty -> {
item {
BlockAutoFillNoItems(
addItemClickAction = {
viewModel.trySendAction(BlockAutoFillAction.AddUriClick)
},
modifier = Modifier.fillMaxSize(),
)
}
}
when (val viewState = state.viewState) {
is BlockAutoFillState.ViewState.Content -> {
BlockedAutofillContent(
viewState = viewState,
onEditUriClick = {
viewModel.trySendAction(BlockAutoFillAction.EditUriClick(it))
},
onRemoveClick = {
viewModel.trySendAction(BlockAutoFillAction.RemoveUriClick(it))
},
modifier = Modifier.fillMaxSize(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
BlockAutoFillState.ViewState.Empty -> {
BlockAutoFillNoItems(
addItemClickAction = {
viewModel.trySendAction(BlockAutoFillAction.AddUriClick)
},
modifier = Modifier.fillMaxSize(),
)
}
}
}
}
@Composable
private fun BlockedAutofillContent(
viewState: BlockAutoFillState.ViewState.Content,
onEditUriClick: (String) -> Unit,
onRemoveClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(modifier = modifier) {
item {
Spacer(modifier = Modifier.height(height = 24.dp))
Text(
text = stringResource(
id = BitwardenString.auto_fill_will_not_be_offered_for_these_ur_is,
),
textAlign = TextAlign.Center,
color = BitwardenTheme.colorScheme.text.secondary,
style = BitwardenTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.animateItem(),
)
Spacer(modifier = Modifier.height(height = 24.dp))
}
itemsIndexed(
items = viewState.blockedUris,
key = { _, uri -> uri },
) { index, uri ->
BlockAutoFillListItem(
label = uri,
onDeleteClick = { onRemoveClick(uri) },
onEditClick = { onEditUriClick(uri) },
cardStyle = viewState.blockedUris.toListItemCardStyle(index = index),
modifier = Modifier
.standardHorizontalMargin()
.fillMaxWidth()
.animateItem(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}
}
}
@Composable
private fun BlockAutoFillDialogs(
dialogState: BlockAutoFillState.DialogState? = null,
onUriTextChange: (String) -> Unit,
onSaveClick: (String, String?) -> Unit,
onRemoveClick: (String) -> Unit,
onDismissRequest: () -> Unit,
) {
when (dialogState) {
is BlockAutoFillState.DialogState.AddEdit -> {
AddEditBlockedUriDialog(
uri = dialogState.uri,
isEdit = dialogState.originalUri != null,
isEdit = dialogState.isEdit,
errorMessage = dialogState.errorMessage?.invoke(),
onUriChange = onUriTextChange,
onDismissRequest = onDismissRequest,
onDeleteClick = if (dialogState.isEdit) {
{ dialogState.originalUri?.let { onRemoveClick(it) } }
} else {
null
},
onCancelClick = onDismissRequest,
onSaveClick = { newUri -> onSaveClick(newUri, dialogState.originalUri) },
)
@@ -203,7 +216,7 @@ private fun BlockAutoFillNoItems(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
modifier = modifier.verticalScroll(state = rememberScrollState()),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -217,66 +230,87 @@ private fun BlockAutoFillNoItems(
.size(size = 124.dp)
.align(Alignment.CenterHorizontally),
)
Spacer(modifier = Modifier.height(32.dp))
Spacer(modifier = Modifier.height(height = 24.dp))
Text(
textAlign = TextAlign.Center,
text = stringResource(id = BitwardenString.no_uris_blocked),
style = BitwardenTheme.typography.titleMedium,
color = BitwardenTheme.colorScheme.text.primary,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
Text(
textAlign = TextAlign.Center,
text = stringResource(
id = BitwardenString.auto_fill_will_not_be_offered_for_these_ur_is,
),
style = BitwardenTheme.typography.bodyMedium,
color = BitwardenTheme.colorScheme.text.primary,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenOutlinedButton(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
BitwardenFilledButton(
label = stringResource(id = BitwardenString.new_blocked_uri),
onClick = addItemClickAction,
icon = rememberVectorPainter(id = BitwardenDrawable.ic_plus_small),
modifier = Modifier
.wrapContentWidth()
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(height = 24.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}
}
@Composable
private fun BlockAutoFillListItem(
label: String,
onClick: () -> Unit,
onEditClick: () -> Unit,
onDeleteClick: () -> Unit,
cardStyle: CardStyle,
modifier: Modifier = Modifier,
) {
Row(
modifier = Modifier
.defaultMinSize(minHeight = 60.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(
color = BitwardenTheme.colorScheme.background.pressed,
),
onClick = onClick,
)
.bottomDivider(paddingStart = 16.dp)
.padding(end = 8.dp, top = 16.dp, bottom = 16.dp)
.then(modifier),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.defaultMinSize(minHeight = 60.dp)
.cardStyle(
cardStyle = cardStyle,
paddingStart = 16.dp,
paddingEnd = 4.dp,
),
) {
Text(
modifier = Modifier
.padding(end = 16.dp)
.weight(1f),
text = label,
style = BitwardenTheme.typography.bodyLarge,
color = BitwardenTheme.colorScheme.text.primary,
modifier = Modifier
.padding(end = 16.dp)
.weight(weight = 1f),
)
Icon(
painter = rememberVectorPainter(id = BitwardenDrawable.ic_pencil_square),
contentDescription = null,
tint = BitwardenTheme.colorScheme.icon.primary,
modifier = Modifier.size(24.dp),
BitwardenOverflowActionItem(
menuItemDataList = persistentListOf(
OverflowMenuItemData(
text = stringResource(id = BitwardenString.edit),
onClick = onEditClick,
),
OverflowMenuItemData(
text = stringResource(id = BitwardenString.delete),
onClick = onDeleteClick,
),
),
vectorIconRes = BitwardenDrawable.ic_ellipsis_horizontal,
testTag = "Options",
)
}
}

View File

@@ -10,6 +10,9 @@ import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.blockautofill.util.isValidPattern
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.blockautofill.util.validateUri
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.update
import kotlinx.parcelize.Parcelize
import javax.inject.Inject
@@ -40,7 +43,9 @@ class BlockAutoFillViewModel @Inject constructor(
mutableStateFlow.update { currentState ->
if (uris.isNotEmpty()) {
currentState.copy(
viewState = BlockAutoFillState.ViewState.Content(uris.map { it }),
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = uris.distinct().toImmutableList(),
),
)
} else {
currentState.copy(
@@ -163,7 +168,7 @@ class BlockAutoFillViewModel @Inject constructor(
}
/**
* Represents the state for block auto fill.
* Represents the state for block autofill.
*
* @property viewState indicates what view state the screen is in.
*/
@@ -203,7 +208,7 @@ data class BlockAutoFillState(
*/
@Parcelize
data class Content(
val blockedUris: List<String> = emptyList(),
val blockedUris: ImmutableList<String> = persistentListOf(),
) : ViewState()
/**

View File

@@ -11,10 +11,12 @@ import androidx.compose.ui.test.performScrollTo
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.ui.util.asText
import com.bitwarden.ui.util.assertNoDialogExists
import com.bitwarden.ui.util.assertNoPopupExists
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -68,7 +70,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
@Test
fun `Screen should display content state view when in ViewState Content`() {
mutableStateFlow.value = BlockAutoFillState(
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1", "uri2")),
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("uri1", "uri2")),
)
listOf("uri1", "uri2").forEach { uri ->
@@ -109,17 +111,6 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
verify { viewModel.trySendAction(BlockAutoFillAction.AddUriClick) }
}
@Test
fun `on URI item click should send EditUriClick`() {
mutableStateFlow.value = BlockAutoFillState(
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
)
composeTestRule.onNodeWithText("uri1").performClick()
verify { viewModel.trySendAction(BlockAutoFillAction.EditUriClick(uri = "uri1")) }
}
@Test
fun `should show add URI dialog according to state`() {
composeTestRule.assertNoDialogExists()
@@ -130,33 +121,36 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
originalUri = null,
errorMessage = null,
),
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("uri1")),
)
composeTestRule
.onNodeWithText("New URI")
.onNodeWithText("New blocked URI")
.assert(hasAnyAncestor(isDialog()))
}
@Test
fun `clicking a uri from the list should send EditUriClick action`() {
val testUri = "http://test.com"
composeTestRule.assertNoDialogExists()
fun `clicking edit in popup menu should send EditUriClick action`() {
composeTestRule.assertNoPopupExists()
val uriToRemove = "http://uriToRemove.com"
mutableStateFlow.value = BlockAutoFillState(
dialog = BlockAutoFillState.DialogState.AddEdit(
uri = testUri,
originalUri = testUri,
errorMessage = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = persistentListOf(uriToRemove),
),
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
)
composeTestRule
.onNodeWithText("New URI")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
.onNodeWithContentDescription(label = "More options")
.performScrollTo()
.performClick()
composeTestRule
.onNodeWithText(text = "Edit")
.performScrollTo()
.performClick()
verify { viewModel.trySendAction(BlockAutoFillAction.EditUriClick(uri = uriToRemove)) }
}
@Test
@@ -171,7 +165,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
originalUri = null,
errorMessage = errorMessage.asText(),
),
viewState = BlockAutoFillState.ViewState.Content(listOf("uri1")),
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("uri1")),
)
composeTestRule
@@ -190,7 +184,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
originalUri = null,
errorMessage = null,
),
viewState = BlockAutoFillState.ViewState.Content(listOf("existingUri")),
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("existingUri")),
)
val newUri = "http://newuri.com"
@@ -209,7 +203,7 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
originalUri = null,
errorMessage = null,
),
viewState = BlockAutoFillState.ViewState.Content(emptyList()),
viewState = BlockAutoFillState.ViewState.Content(persistentListOf()),
)
composeTestRule.onNodeWithText("Cancel").performClick()
@@ -218,20 +212,25 @@ class BlockAutoFillScreenTest : BitwardenComposeTest() {
}
@Test
fun `clicking remove in dialog should send RemoveUriClick action`() {
composeTestRule.assertNoDialogExists()
fun `clicking delete in popup menu should send RemoveUriClick action`() {
composeTestRule.assertNoPopupExists()
val uriToRemove = "http://uriToRemove.com"
mutableStateFlow.value = BlockAutoFillState(
dialog = BlockAutoFillState.DialogState.AddEdit(
uri = uriToRemove,
originalUri = uriToRemove,
errorMessage = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = persistentListOf(uriToRemove),
),
viewState = BlockAutoFillState.ViewState.Content(listOf(uriToRemove, "otherUri")),
)
composeTestRule.onNodeWithText("Remove").performClick()
composeTestRule
.onNodeWithContentDescription(label = "More options")
.performScrollTo()
.performClick()
composeTestRule
.onNodeWithText(text = "Delete")
.performScrollTo()
.performClick()
verify { viewModel.trySendAction(BlockAutoFillAction.RemoveUriClick(uri = uriToRemove)) }
}

View File

@@ -8,6 +8,8 @@ import com.bitwarden.ui.util.asText
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import io.mockk.every
import io.mockk.mockk
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@@ -24,7 +26,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
val expectedState = BlockAutoFillState(
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = listOf("blockedUri"),
blockedUris = persistentListOf("blockedUri"),
),
)
@@ -64,7 +66,9 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
originalUri = null,
errorMessage = null,
),
viewState = BlockAutoFillState.ViewState.Content(blockedUris = listOf("blockedUri")),
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = persistentListOf("blockedUri"),
),
)
assertEquals(expectedState, viewModel.stateFlow.value)
@@ -82,7 +86,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
originalUri = testUri,
errorMessage = null,
),
viewState = BlockAutoFillState.ViewState.Content(listOf("blockedUri")),
viewState = BlockAutoFillState.ViewState.Content(persistentListOf("blockedUri")),
)
assertEquals(expectedState, viewModel.stateFlow.value)
@@ -104,7 +108,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
val expectedState = BlockAutoFillState(
dialog = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = listOf("http://b.com"),
blockedUris = persistentListOf("http://b.com"),
),
)
@@ -128,7 +132,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
val expectedState = BlockAutoFillState(
dialog = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = blockedUris,
blockedUris = blockedUris.toImmutableList(),
),
)
@@ -152,7 +156,11 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
val expectedState = BlockAutoFillState(
dialog = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = listOf("http://existing.com", "http://new.com", "http://another.com"),
blockedUris = persistentListOf(
"http://existing.com",
"http://new.com",
"http://another.com",
),
),
)
@@ -189,7 +197,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
val expectedState = BlockAutoFillState(
dialog = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = listOf("http://other.com", "http://new.com"),
blockedUris = persistentListOf("http://other.com", "http://new.com"),
),
)
@@ -217,7 +225,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
val expectedState = BlockAutoFillState(
dialog = null,
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = listOf("http://other.com", "http://same.com"),
blockedUris = persistentListOf("http://other.com", "http://same.com"),
),
)
@@ -249,7 +257,7 @@ class BlockAutoFillViewModelTest : BaseViewModelTest() {
errorMessage = BitwardenString.the_urix_is_already_blocked.asText("http://b.com"),
),
viewState = BlockAutoFillState.ViewState.Content(
blockedUris = listOf("http://a.com", "http://b.com"),
blockedUris = persistentListOf("http://a.com", "http://b.com"),
),
)

View File

@@ -295,7 +295,6 @@ Scanning will happen automatically.</string>
<string name="field_type_text">Text</string>
<string name="select_type_field">What type of custom field do you want to add?</string>
<string name="remove">Remove</string>
<string name="new_uri">New URI</string>
<string name="add_website">Add website</string>
<string name="base_domain">Base domain</string>
<string name="default_text">Default (%1$s)</string>
@@ -667,7 +666,9 @@ Do you want to switch to this account?</string>
<string name="you_will_be_notified_once_approved">You will be notified once approved.</string>
<string name="trouble_logging_in">Trouble logging in?</string>
<string name="block_auto_fill">Block autofill</string>
<string name="no_uris_blocked">No URIs blocked</string>
<string name="auto_fill_will_not_be_offered_for_these_ur_is">Autofill will not be offered for these URIs.</string>
<string name="edit_blocked_uri">Edit blocked URI</string>
<string name="new_blocked_uri">New blocked URI</string>
<string name="invalid_format_use_https_http_or_android_app">Invalid format. Use https://, http://, or androidapp://</string>
<string name="enter_uri">Enter URI</string>