Merge "Update the color tokens to match the UX design."

This commit is contained in:
TreeHugger Robot
2022-12-03 00:56:37 +00:00
committed by Android (Google) Code Review
9 changed files with 1276 additions and 1048 deletions

View File

@@ -128,7 +128,7 @@ class CredentialManagerRepo(
// TODO: handle runtime cast error
providerEnabledList as List<GetCredentialProviderData>, context)
// TODO: covert from real requestInfo
val requestDisplayInfo = com.android.credentialmanager.getflow.RequestDisplayInfo("tribank")
val requestDisplayInfo = com.android.credentialmanager.getflow.RequestDisplayInfo("the app")
return GetCredentialUiState(
providerEnabledList,
GetScreenState.PRIMARY_SELECTION,

View File

@@ -62,7 +62,6 @@ import com.android.credentialmanager.R
import com.android.credentialmanager.common.material.ModalBottomSheetValue.Expanded
import com.android.credentialmanager.common.material.ModalBottomSheetValue.HalfExpanded
import com.android.credentialmanager.common.material.ModalBottomSheetValue.Hidden
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
import kotlin.math.max
@@ -319,7 +318,7 @@ fun ModalBottomSheetLayout(
rememberModalBottomSheetState(Hidden),
sheetShape: Shape = MaterialTheme.shapes.large,
sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
sheetBackgroundColor: Color = ModalBottomSheetDefaults.scrimColor,
sheetBackgroundColor: Color = MaterialTheme.colorScheme.surface,
sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
content: @Composable () -> Unit
@@ -477,5 +476,5 @@ object ModalBottomSheetDefaults {
*/
val scrimColor: Color
@Composable
get() = LocalAndroidColorScheme.current.colorSurfaceHighlight
get() = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.32f)
}

View File

@@ -16,13 +16,20 @@
package com.android.credentialmanager.common.ui
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@Composable
fun CancelButton(text: String, onClick: () -> Unit) {
TextButton(onClick = onClick) {
TextButton(
onClick = onClick,
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.primary,
)
) {
Text(text = text)
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.credentialmanager.common.ui
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
/**
* By default the card is filled with surfaceVariant color. This container card instead fills the
* background color with surface corlor.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ContainerCard(
modifier: Modifier = Modifier,
shape: Shape = CardDefaults.shape,
border: BorderStroke? = null,
content: @Composable ColumnScope.() -> Unit,
) {
Card(
modifier = modifier,
shape = shape,
border = border,
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface,
),
content = content,
)
}

View File

@@ -16,13 +16,21 @@
package com.android.credentialmanager.common.ui
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun ConfirmButton(text: String, onClick: () -> Unit) {
FilledTonalButton(onClick = onClick) {
FilledTonalButton(
onClick = onClick,
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
)
) {
Text(text = text)
}
}

View File

@@ -18,13 +18,13 @@ package com.android.credentialmanager.common.ui
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.SuggestionChipDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.android.credentialmanager.ui.theme.EntryShape
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -42,7 +42,9 @@ fun Entry(
icon = icon,
border = null,
colors = SuggestionChipDefaults.suggestionChipColors(
containerColor = LocalAndroidColorScheme.current.colorSurface,
containerColor = MaterialTheme.colorScheme.surfaceVariant,
labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
iconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.credentialmanager.common.ui
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
@Composable
fun TextOnSurface(
text: String,
modifier: Modifier = Modifier,
textAlign: TextAlign? = null,
style: TextStyle,
) {
TextInternal(
text = text,
color = MaterialTheme.colorScheme.onSurface,
modifier = modifier,
textAlign = textAlign,
style = style,
)
}
@Composable
fun TextSecondary(
text: String,
modifier: Modifier = Modifier,
textAlign: TextAlign? = null,
style: TextStyle,
) {
TextInternal(
text = text,
color = MaterialTheme.colorScheme.secondary,
modifier = modifier,
textAlign = textAlign,
style = style,
)
}
@Composable
fun TextOnSurfaceVariant(
text: String,
modifier: Modifier = Modifier,
textAlign: TextAlign? = null,
style: TextStyle,
) {
TextInternal(
text = text,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = modifier,
textAlign = textAlign,
style = style,
)
}
@Composable
private fun TextInternal(
text: String,
color: Color,
modifier: Modifier,
textAlign: TextAlign?,
style: TextStyle,
) {
Text(text = text, color = color, modifier = modifier, textAlign = textAlign, style = style)
}

View File

@@ -22,6 +22,7 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -31,13 +32,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material.icons.Icons
@@ -59,202 +58,216 @@ import com.android.credentialmanager.common.material.ModalBottomSheetValue
import com.android.credentialmanager.common.material.rememberModalBottomSheetState
import com.android.credentialmanager.common.ui.CancelButton
import com.android.credentialmanager.common.ui.Entry
import com.android.credentialmanager.common.ui.TextOnSurface
import com.android.credentialmanager.common.ui.TextSecondary
import com.android.credentialmanager.common.ui.TextOnSurfaceVariant
import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.common.ui.TransparentBackgroundEntry
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
import com.android.credentialmanager.ui.theme.EntryShape
@Composable
fun GetCredentialScreen(
viewModel: GetCredentialViewModel,
providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
viewModel: GetCredentialViewModel,
providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val entrySelectionCallback: (EntryInfo) -> Unit = {
viewModel.onEntrySelected(it, providerActivityLauncher)
}
val state = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Expanded,
skipHalfExpanded = true
)
ModalBottomSheetLayout(
sheetState = state,
sheetContent = {
val uiState = viewModel.uiState
when (uiState.currentScreenState) {
GetScreenState.PRIMARY_SELECTION -> PrimarySelectionCard(
requestDisplayInfo = uiState.requestDisplayInfo,
providerDisplayInfo = uiState.providerDisplayInfo,
onEntrySelected = entrySelectionCallback,
onCancel = viewModel::onCancel,
onMoreOptionSelected = viewModel::onMoreOptionSelected,
)
GetScreenState.ALL_SIGN_IN_OPTIONS -> AllSignInOptionCard(
providerInfoList = uiState.providerInfoList,
providerDisplayInfo = uiState.providerDisplayInfo,
onEntrySelected = entrySelectionCallback,
onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
)
}
},
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f),
sheetShape = EntryShape.TopRoundedCorner,
) {}
LaunchedEffect(state.currentValue) {
if (state.currentValue == ModalBottomSheetValue.Hidden) {
viewModel.onCancel()
val entrySelectionCallback: (EntryInfo) -> Unit = {
viewModel.onEntrySelected(it, providerActivityLauncher)
}
val state = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Expanded,
skipHalfExpanded = true
)
ModalBottomSheetLayout(
sheetBackgroundColor = MaterialTheme.colorScheme.surface,
modifier = Modifier.background(Color.Transparent),
sheetState = state,
sheetContent = {
val uiState = viewModel.uiState
when (uiState.currentScreenState) {
GetScreenState.PRIMARY_SELECTION -> PrimarySelectionCard(
requestDisplayInfo = uiState.requestDisplayInfo,
providerDisplayInfo = uiState.providerDisplayInfo,
onEntrySelected = entrySelectionCallback,
onCancel = viewModel::onCancel,
onMoreOptionSelected = viewModel::onMoreOptionSelected,
)
GetScreenState.ALL_SIGN_IN_OPTIONS -> AllSignInOptionCard(
providerInfoList = uiState.providerInfoList,
providerDisplayInfo = uiState.providerDisplayInfo,
onEntrySelected = entrySelectionCallback,
onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
)
}
},
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f),
sheetShape = EntryShape.TopRoundedCorner,
) {}
LaunchedEffect(state.currentValue) {
if (state.currentValue == ModalBottomSheetValue.Hidden) {
viewModel.onCancel()
}
}
}
}
/** Draws the primary credential selection page. */
@Composable
fun PrimarySelectionCard(
requestDisplayInfo: RequestDisplayInfo,
providerDisplayInfo: ProviderDisplayInfo,
onEntrySelected: (EntryInfo) -> Unit,
onCancel: () -> Unit,
onMoreOptionSelected: () -> Unit,
requestDisplayInfo: RequestDisplayInfo,
providerDisplayInfo: ProviderDisplayInfo,
onEntrySelected: (EntryInfo) -> Unit,
onCancel: () -> Unit,
onMoreOptionSelected: () -> Unit,
) {
val sortedUserNameToCredentialEntryList = providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
Card() {
Column() {
Text(
modifier = Modifier.padding(all = 24.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineSmall,
text = stringResource(
if (sortedUserNameToCredentialEntryList.size == 1) {
if (sortedUserNameToCredentialEntryList.first().sortedCredentialEntryList
.first().credentialType == PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
ContainerCard() {
Column() {
TextOnSurface(
modifier = Modifier.padding(all = 24.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineSmall,
text = stringResource(
if (sortedUserNameToCredentialEntryList.size == 1) {
if (sortedUserNameToCredentialEntryList.first().sortedCredentialEntryList
.first().credentialType
== PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
)
R.string.get_dialog_title_use_passkey_for
else R.string.get_dialog_title_use_sign_in_for
} else R.string.get_dialog_title_choose_sign_in_for,
requestDisplayInfo.appDomainName
),
)
R.string.get_dialog_title_use_passkey_for
else R.string.get_dialog_title_use_sign_in_for
} else R.string.get_dialog_title_choose_sign_in_for,
requestDisplayInfo.appDomainName
),
)
Card(
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.padding(horizontal = 24.dp)
.align(alignment = Alignment.CenterHorizontally)
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
items(sortedUserNameToCredentialEntryList) {
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
ContainerCard(
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.padding(horizontal = 24.dp)
.align(alignment = Alignment.CenterHorizontally)
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
items(sortedUserNameToCredentialEntryList) {
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
)
}
items(authenticationEntryList) {
AuthenticationEntryRow(
authenticationEntryInfo = it,
onEntrySelected = onEntrySelected,
)
}
item {
SignInAnotherWayRow(onSelect = onMoreOptionSelected)
}
}
}
Divider(
thickness = 24.dp,
color = Color.Transparent
)
}
items(authenticationEntryList) {
AuthenticationEntryRow(
authenticationEntryInfo = it,
onEntrySelected = onEntrySelected,
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) {
CancelButton(stringResource(R.string.get_dialog_button_label_no_thanks), onCancel)
}
Divider(
thickness = 18.dp,
color = Color.Transparent,
modifier = Modifier.padding(bottom = 16.dp)
)
}
item {
SignInAnotherWayRow(onSelect = onMoreOptionSelected)
}
}
}
Divider(
thickness = 24.dp,
color = Color.Transparent
)
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) {
CancelButton(stringResource(R.string.get_dialog_button_label_no_thanks), onCancel)
}
Divider(
thickness = 18.dp,
color = Color.Transparent,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
}
/** Draws the secondary credential selection page, where all sign-in options are listed. */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AllSignInOptionCard(
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
onEntrySelected: (EntryInfo) -> Unit,
onBackButtonClicked: () -> Unit,
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
onEntrySelected: (EntryInfo) -> Unit,
onBackButtonClicked: () -> Unit,
) {
val sortedUserNameToCredentialEntryList = providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
Card() {
Column() {
TopAppBar(
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Transparent,
),
title = {
Text(
text = stringResource(R.string.get_dialog_title_sign_in_options),
style = MaterialTheme.typography.titleMedium
)
},
navigationIcon = {
IconButton(onClick = onBackButtonClicked) {
Icon(
Icons.Filled.ArrowBack,
contentDescription = stringResource(R.string.accessibility_back_arrow_button))
}
},
modifier = Modifier.padding(top = 12.dp)
)
Card(
shape = MaterialTheme.shapes.large,
modifier = Modifier
.padding(start = 24.dp, end = 24.dp, bottom = 24.dp)
.align(alignment = Alignment.CenterHorizontally)
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// For username
items(sortedUserNameToCredentialEntryList) { item ->
PerUserNameCredentials(
perUserNameCredentialEntryList = item,
onEntrySelected = onEntrySelected,
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
ContainerCard() {
Column() {
TopAppBar(
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Transparent,
),
title = {
TextOnSurface(
text = stringResource(R.string.get_dialog_title_sign_in_options),
style = MaterialTheme.typography.titleMedium
)
},
navigationIcon = {
IconButton(onClick = onBackButtonClicked) {
Icon(
Icons.Filled.ArrowBack,
contentDescription = stringResource(
R.string.accessibility_back_arrow_button)
)
}
},
modifier = Modifier.padding(top = 12.dp)
)
}
// Locked password manager
if (!authenticationEntryList.isEmpty()) {
item {
LockedCredentials(
authenticationEntryList = authenticationEntryList,
onEntrySelected = onEntrySelected,
)
ContainerCard(
shape = MaterialTheme.shapes.large,
modifier = Modifier
.padding(start = 24.dp, end = 24.dp, bottom = 24.dp)
.align(alignment = Alignment.CenterHorizontally),
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// For username
items(sortedUserNameToCredentialEntryList) { item ->
PerUserNameCredentials(
perUserNameCredentialEntryList = item,
onEntrySelected = onEntrySelected,
)
}
// Locked password manager
if (!authenticationEntryList.isEmpty()) {
item {
LockedCredentials(
authenticationEntryList = authenticationEntryList,
onEntrySelected = onEntrySelected,
)
}
}
// From another device
val remoteEntry = providerDisplayInfo.remoteEntry
if (remoteEntry != null) {
item {
RemoteEntryCard(
remoteEntry = remoteEntry,
onEntrySelected = onEntrySelected,
)
}
}
// Manage sign-ins (action chips)
item {
ActionChips(
providerInfoList = providerInfoList,
onEntrySelected = onEntrySelected
)
}
}
}
}
// From another device
val remoteEntry = providerDisplayInfo.remoteEntry
if (remoteEntry != null) {
item {
RemoteEntryCard(
remoteEntry = remoteEntry,
onEntrySelected = onEntrySelected,
)
}
}
// Manage sign-ins (action chips)
item {
ActionChips(providerInfoList = providerInfoList, onEntrySelected = onEntrySelected)
}
}
}
}
}
}
// TODO: create separate rows for primary and secondary pages.
@@ -262,236 +275,245 @@ fun AllSignInOptionCard(
@Composable
fun ActionChips(
providerInfoList: List<ProviderInfo>,
onEntrySelected: (EntryInfo) -> Unit,
providerInfoList: List<ProviderInfo>,
onEntrySelected: (EntryInfo) -> Unit,
) {
val actionChips = providerInfoList.flatMap { it.actionEntryList }
if (actionChips.isEmpty()) {
return
}
Text(
text = stringResource(R.string.get_dialog_heading_manage_sign_ins),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
// TODO: tweak padding.
Card(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
actionChips.forEach {
ActionEntryRow(it, onEntrySelected)
}
val actionChips = providerInfoList.flatMap { it.actionEntryList }
if (actionChips.isEmpty()) {
return
}
TextSecondary(
text = stringResource(R.string.get_dialog_heading_manage_sign_ins),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
// TODO: tweak padding.
ContainerCard(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
actionChips.forEach {
ActionEntryRow(it, onEntrySelected)
}
}
}
}
}
@Composable
fun RemoteEntryCard(
remoteEntry: RemoteEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
remoteEntry: RemoteEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
) {
Text(
text = stringResource(R.string.get_dialog_heading_from_another_device),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
Card(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
TextSecondary(
text = stringResource(R.string.get_dialog_heading_from_another_device),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
ContainerCard(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
Entry(
onClick = {onEntrySelected(remoteEntry)},
icon = {
Icon(
painter = painterResource(R.drawable.ic_other_devices),
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.padding(start = 18.dp)
)
},
label = {
Text(
text = stringResource(R.string.get_dialog_option_headline_use_a_different_device),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
.align(alignment = Alignment.CenterHorizontally)
)
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
Entry(
onClick = { onEntrySelected(remoteEntry) },
icon = {
Icon(
painter = painterResource(R.drawable.ic_other_devices),
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.padding(start = 18.dp)
)
},
label = {
TextOnSurfaceVariant(
text = stringResource(
R.string.get_dialog_option_headline_use_a_different_device),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
.align(alignment = Alignment.CenterHorizontally)
)
}
)
}
)
}
}
}
@Composable
fun LockedCredentials(
authenticationEntryList: List<AuthenticationEntryInfo>,
onEntrySelected: (EntryInfo) -> Unit,
authenticationEntryList: List<AuthenticationEntryInfo>,
onEntrySelected: (EntryInfo) -> Unit,
) {
Text(
text = stringResource(R.string.get_dialog_heading_locked_password_managers),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
Card(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
TextSecondary(
text = stringResource(R.string.get_dialog_heading_locked_password_managers),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
ContainerCard(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
authenticationEntryList.forEach {
AuthenticationEntryRow(it, onEntrySelected)
}
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
authenticationEntryList.forEach {
AuthenticationEntryRow(it, onEntrySelected)
}
}
}
}
}
@Composable
fun PerUserNameCredentials(
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
onEntrySelected: (EntryInfo) -> Unit,
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
onEntrySelected: (EntryInfo) -> Unit,
) {
Text(
text = stringResource(
R.string.get_dialog_heading_for_username, perUserNameCredentialEntryList.userName),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
Card(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
TextSecondary(
text = stringResource(
R.string.get_dialog_heading_for_username, perUserNameCredentialEntryList.userName
),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
ContainerCard(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
) {
perUserNameCredentialEntryList.sortedCredentialEntryList.forEach {
CredentialEntryRow(it, onEntrySelected)
}
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
perUserNameCredentialEntryList.sortedCredentialEntryList.forEach {
CredentialEntryRow(it, onEntrySelected)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CredentialEntryRow(
credentialEntryInfo: CredentialEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
credentialEntryInfo: CredentialEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
) {
Entry(
onClick = {onEntrySelected(credentialEntryInfo)},
icon = {
Image(modifier = Modifier.padding(start = 10.dp).size(32.dp),
bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
// TODO: add description.
contentDescription = "")
},
label = {
Column() {
// TODO: fix the text values.
Text(
text = credentialEntryInfo.userName,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp)
)
Text(
text =
if (TextUtils.isEmpty(credentialEntryInfo.displayName))
credentialEntryInfo.credentialTypeDisplayName
else
credentialEntryInfo.credentialTypeDisplayName +
stringResource(R.string.get_dialog_sign_in_type_username_separator) +
credentialEntryInfo.displayName,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
)
Entry(
onClick = { onEntrySelected(credentialEntryInfo) },
icon = {
Image(
modifier = Modifier.padding(start = 10.dp).size(32.dp),
bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
// TODO: add description.
contentDescription = ""
)
},
label = {
Column() {
// TODO: fix the text values.
TextOnSurfaceVariant(
text = credentialEntryInfo.userName,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp)
)
TextSecondary(
text =
if (TextUtils.isEmpty(credentialEntryInfo.displayName))
credentialEntryInfo.credentialTypeDisplayName
else
credentialEntryInfo.credentialTypeDisplayName +
stringResource(
R.string.get_dialog_sign_in_type_username_separator) +
credentialEntryInfo.displayName,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AuthenticationEntryRow(
authenticationEntryInfo: AuthenticationEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
authenticationEntryInfo: AuthenticationEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
) {
Entry(
onClick = {onEntrySelected(authenticationEntryInfo)},
icon = {
Image(modifier = Modifier.padding(start = 10.dp).size(32.dp),
bitmap = authenticationEntryInfo.icon.toBitmap().asImageBitmap(),
// TODO: add description.
contentDescription = "")
},
label = {
Column() {
// TODO: fix the text values.
Text(
text = authenticationEntryInfo.title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp)
)
Text(
text = stringResource(R.string.locked_credential_entry_label_subtext),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
)
Entry(
onClick = { onEntrySelected(authenticationEntryInfo) },
icon = {
Image(
modifier = Modifier.padding(start = 10.dp).size(32.dp),
bitmap = authenticationEntryInfo.icon.toBitmap().asImageBitmap(),
// TODO: add description.
contentDescription = ""
)
},
label = {
Column() {
// TODO: fix the text values.
TextOnSurfaceVariant(
text = authenticationEntryInfo.title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp)
)
TextSecondary(
text = stringResource(R.string.locked_credential_entry_label_subtext),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ActionEntryRow(
actionEntryInfo: ActionEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
actionEntryInfo: ActionEntryInfo,
onEntrySelected: (EntryInfo) -> Unit,
) {
TransparentBackgroundEntry(
icon = {
Image(modifier = Modifier.padding(start = 10.dp).size(32.dp),
bitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
// TODO: add description.
contentDescription = "")
},
label = {
Column() {
Text(
text = actionEntryInfo.title,
style = MaterialTheme.typography.titleLarge,
)
if (actionEntryInfo.subTitle != null) {
Text(
text = actionEntryInfo.subTitle,
style = MaterialTheme.typography.bodyMedium,
)
}
}
},
onClick = { onEntrySelected(actionEntryInfo) },
)
TransparentBackgroundEntry(
icon = {
Image(
modifier = Modifier.padding(start = 10.dp).size(32.dp),
bitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
// TODO: add description.
contentDescription = ""
)
},
label = {
Column() {
TextOnSurfaceVariant(
text = actionEntryInfo.title,
style = MaterialTheme.typography.titleLarge,
)
if (actionEntryInfo.subTitle != null) {
TextSecondary(
text = actionEntryInfo.subTitle,
style = MaterialTheme.typography.bodyMedium,
)
}
}
},
onClick = { onEntrySelected(actionEntryInfo) },
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SignInAnotherWayRow(onSelect: () -> Unit) {
Entry(
onClick = onSelect,
label = {
Text(
text = stringResource(R.string.get_dialog_use_saved_passkey_for),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(vertical = 16.dp)
)
}
)
Entry(
onClick = onSelect,
label = {
TextOnSurfaceVariant(
text = stringResource(R.string.get_dialog_use_saved_passkey_for),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(vertical = 16.dp)
)
}
)
}