From 3012465ecc6f7cb88c82ee763be3a89b3d3d402f Mon Sep 17 00:00:00 2001 From: Helen Qin Date: Fri, 2 Dec 2022 22:28:15 +0000 Subject: [PATCH] Update the color tokens to match the UX design. Bug: 261219158 Test: Local Deployment Change-Id: I104838fbe36c9cc717be8890280971750a92514f --- .../CredentialManagerRepo.kt | 2 +- .../common/material/ModalBottomSheet.kt | 5 +- .../common/ui/CancelButton.kt | 9 +- .../credentialmanager/common/ui/Cards.kt | 50 + .../common/ui/ConfirmButton.kt | 10 +- .../credentialmanager/common/ui/Entry.kt | 6 +- .../credentialmanager/common/ui/Texts.kt | 84 + .../createflow/CreateCredentialComponents.kt | 1432 +++++++++-------- .../getflow/GetCredentialComponents.kt | 726 +++++---- 9 files changed, 1276 insertions(+), 1048 deletions(-) create mode 100644 packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt create mode 100644 packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index e3ed3d9255669..1db1d1c1e375c 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -128,7 +128,7 @@ class CredentialManagerRepo( // TODO: handle runtime cast error providerEnabledList as List, 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, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt index 61e11feff3c07..f1f453da4f38d 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt @@ -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) } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt index 177d0e0288c7d..80764b5ec5bd4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt @@ -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) } } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt new file mode 100644 index 0000000000000..aaabce36c0036 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt @@ -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, + ) +} \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt index b2b0bdcdf3ce6..d8ee750a88d6c 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt @@ -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) } } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt index 51a1cbbbf9425..aefd534da63cd 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt @@ -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, ), ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt new file mode 100644 index 0000000000000..3a66dda738329 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt @@ -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) +} \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 5552d05093556..d8dd1a722b1eb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -14,7 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.Card +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -46,6 +46,10 @@ import com.android.credentialmanager.common.material.rememberModalBottomSheetSta import com.android.credentialmanager.common.ui.CancelButton import com.android.credentialmanager.common.ui.ConfirmButton 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.ui.theme.EntryShape import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL @@ -53,715 +57,767 @@ import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Compa @OptIn(ExperimentalMaterial3Api::class) @Composable fun CreateCredentialScreen( - viewModel: CreateCredentialViewModel, - providerActivityLauncher: ManagedActivityResultLauncher + viewModel: CreateCredentialViewModel, + providerActivityLauncher: ManagedActivityResultLauncher ) { - val selectEntryCallback: (EntryInfo) -> Unit = { - viewModel.onEntrySelected(it, providerActivityLauncher) - } - val confirmEntryCallback: () -> Unit = { - viewModel.onConfirmEntrySelected(providerActivityLauncher) - } - val state = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Expanded, - skipHalfExpanded = true - ) - ModalBottomSheetLayout( - sheetState = state, - sheetContent = { - val uiState = viewModel.uiState - when (uiState.currentScreenState) { - CreateScreenState.PASSKEY_INTRO -> ConfirmationCard( - onConfirm = viewModel::onConfirmIntro, - onCancel = viewModel::onCancel, - ) - CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard( - requestDisplayInfo = uiState.requestDisplayInfo, - enabledProviderList = uiState.enabledProviders, - disabledProviderList = uiState.disabledProviders, - onCancel = viewModel::onCancel, - onOptionSelected = viewModel::onEntrySelectedFromFirstUseScreen, - onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected, - onRemoteEntrySelected = selectEntryCallback, - ) - CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard( - requestDisplayInfo = uiState.requestDisplayInfo, - enabledProviderList = uiState.enabledProviders, - providerInfo = uiState.activeEntry?.activeProvider!!, - createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo, - showActiveEntryOnly = uiState.showActiveEntryOnly, - onOptionSelected = selectEntryCallback, - onConfirm = confirmEntryCallback, - onCancel = viewModel::onCancel, - onMoreOptionsSelected = viewModel::onMoreOptionsSelected, - ) - CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard( - requestDisplayInfo = uiState.requestDisplayInfo, - enabledProviderList = uiState.enabledProviders, - disabledProviderList = uiState.disabledProviders, - onBackButtonSelected = viewModel::onBackButtonSelected, - onOptionSelected = viewModel::onEntrySelectedFromMoreOptionScreen, - onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected, - onRemoteEntrySelected = selectEntryCallback, - ) - CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard( - providerInfo = uiState.activeEntry?.activeProvider!!, - onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected - ) - CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard( - requestDisplayInfo = uiState.requestDisplayInfo, - activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!, - onOptionSelected = selectEntryCallback, - onConfirm = confirmEntryCallback, - onCancel = viewModel::onCancel, - ) - } - }, - scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f), - sheetShape = EntryShape.TopRoundedCorner, - ) {} - LaunchedEffect(state.currentValue) { - if (state.currentValue == ModalBottomSheetValue.Hidden) { - viewModel.onCancel() + val selectEntryCallback: (EntryInfo) -> Unit = { + viewModel.onEntrySelected(it, providerActivityLauncher) + } + val confirmEntryCallback: () -> Unit = { + viewModel.onConfirmEntrySelected(providerActivityLauncher) + } + val state = rememberModalBottomSheetState( + initialValue = ModalBottomSheetValue.Expanded, + skipHalfExpanded = true + ) + ModalBottomSheetLayout( + sheetBackgroundColor = MaterialTheme.colorScheme.surface, + sheetState = state, + sheetContent = { + val uiState = viewModel.uiState + when (uiState.currentScreenState) { + CreateScreenState.PASSKEY_INTRO -> ConfirmationCard( + onConfirm = viewModel::onConfirmIntro, + onCancel = viewModel::onCancel, + ) + CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard( + requestDisplayInfo = uiState.requestDisplayInfo, + enabledProviderList = uiState.enabledProviders, + disabledProviderList = uiState.disabledProviders, + onCancel = viewModel::onCancel, + onOptionSelected = viewModel::onEntrySelectedFromFirstUseScreen, + onDisabledPasswordManagerSelected = + viewModel::onDisabledPasswordManagerSelected, + onRemoteEntrySelected = selectEntryCallback, + ) + CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard( + requestDisplayInfo = uiState.requestDisplayInfo, + enabledProviderList = uiState.enabledProviders, + providerInfo = uiState.activeEntry?.activeProvider!!, + createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo, + showActiveEntryOnly = uiState.showActiveEntryOnly, + onOptionSelected = selectEntryCallback, + onConfirm = confirmEntryCallback, + onCancel = viewModel::onCancel, + onMoreOptionsSelected = viewModel::onMoreOptionsSelected, + ) + CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard( + requestDisplayInfo = uiState.requestDisplayInfo, + enabledProviderList = uiState.enabledProviders, + disabledProviderList = uiState.disabledProviders, + onBackButtonSelected = viewModel::onBackButtonSelected, + onOptionSelected = viewModel::onEntrySelectedFromMoreOptionScreen, + onDisabledPasswordManagerSelected = + viewModel::onDisabledPasswordManagerSelected, + onRemoteEntrySelected = selectEntryCallback, + ) + CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard( + providerInfo = uiState.activeEntry?.activeProvider!!, + onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected + ) + CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard( + requestDisplayInfo = uiState.requestDisplayInfo, + activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!, + onOptionSelected = selectEntryCallback, + onConfirm = confirmEntryCallback, + onCancel = viewModel::onCancel, + ) + } + }, + scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f), + sheetShape = EntryShape.TopRoundedCorner, + ) {} + LaunchedEffect(state.currentValue) { + if (state.currentValue == ModalBottomSheetValue.Hidden) { + viewModel.onCancel() + } } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun ConfirmationCard( - onConfirm: () -> Unit, - onCancel: () -> Unit, + onConfirm: () -> Unit, + onCancel: () -> Unit, ) { - Card() { - Column() { - Icon( - painter = painterResource(R.drawable.ic_passkey), - contentDescription = null, - tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, - modifier = Modifier.align(alignment = Alignment.CenterHorizontally) - .padding(top = 24.dp, bottom = 12.dp) - ) - Text( - text = stringResource(R.string.passkey_creation_intro_title), - style = MaterialTheme.typography.titleMedium, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - textAlign = TextAlign.Center - ) - Divider( - thickness = 16.dp, - color = Color.Transparent - ) - Text( - text = stringResource(R.string.passkey_creation_intro_body), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(horizontal = 28.dp) - ) - Divider( - thickness = 32.dp, - color = Color.Transparent - ) - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - CancelButton( - stringResource(R.string.string_cancel), - onClick = onCancel - ) - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - } - Divider( - thickness = 18.dp, - color = Color.Transparent, - modifier = Modifier.padding(bottom = 18.dp) - ) + ContainerCard() { + Column() { + Icon( + painter = painterResource(R.drawable.ic_passkey), + contentDescription = null, + tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, + modifier = Modifier.align(alignment = Alignment.CenterHorizontally) + .padding(top = 24.dp, bottom = 12.dp) + ) + TextOnSurface( + text = stringResource(R.string.passkey_creation_intro_title), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + ) + Divider( + thickness = 16.dp, + color = Color.Transparent + ) + TextSecondary( + text = stringResource(R.string.passkey_creation_intro_body), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(horizontal = 28.dp), + ) + Divider( + thickness = 32.dp, + color = Color.Transparent + ) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ) { + CancelButton( + stringResource(R.string.string_cancel), + onClick = onCancel + ) + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 18.dp) + ) + } } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun ProviderSelectionCard( - requestDisplayInfo: RequestDisplayInfo, - enabledProviderList: List, - disabledProviderList: List?, - onOptionSelected: (ActiveEntry) -> Unit, - onDisabledPasswordManagerSelected: () -> Unit, - onCancel: () -> Unit, - onRemoteEntrySelected: (EntryInfo) -> Unit, + requestDisplayInfo: RequestDisplayInfo, + enabledProviderList: List, + disabledProviderList: List?, + onOptionSelected: (ActiveEntry) -> Unit, + onDisabledPasswordManagerSelected: () -> Unit, + onCancel: () -> Unit, + onRemoteEntrySelected: (EntryInfo) -> Unit, ) { - Card() { - Column() { - Icon( - bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap(), - contentDescription = null, - tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, - modifier = Modifier.align(alignment = Alignment.CenterHorizontally) - .padding(top = 24.dp, bottom = 16.dp).size(32.dp) - ) - Text( - text = stringResource( - R.string.choose_provider_title, - when (requestDisplayInfo.type) { - TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.create_your_passkeys) - TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.save_your_password) - else -> stringResource(R.string.save_your_sign_in_info) - }, - ), - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - textAlign = TextAlign.Center - ) - Divider( - thickness = 16.dp, - color = Color.Transparent - ) - Text( - text = stringResource(R.string.choose_provider_body), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(horizontal = 28.dp) - ) - Divider( - thickness = 18.dp, - color = Color.Transparent - ) - Card( - shape = MaterialTheme.shapes.medium, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - ) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - enabledProviderList.forEach { enabledProviderInfo -> - enabledProviderInfo.createOptions.forEach { createOptionInfo -> - item { - MoreOptionsInfoRow( - providerInfo = enabledProviderInfo, - createOptionInfo = createOptionInfo, - onOptionSelected = { - onOptionSelected(ActiveEntry(enabledProviderInfo, createOptionInfo)) - }) - } - } - } - if (disabledProviderList != null) { - item { - MoreOptionsDisabledProvidersRow( - disabledProviders = disabledProviderList, - onDisabledPasswordManagerSelected = onDisabledPasswordManagerSelected, - ) - } - } - } - } - // TODO: handle the error situation that if multiple remoteInfos exists - enabledProviderList.forEach { enabledProvider -> - if (enabledProvider.remoteEntry != null) { - TextButton( - onClick = { - onRemoteEntrySelected(enabledProvider.remoteEntry!!) }, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally) - ) { - Text( - text = stringResource(R.string.string_save_to_another_device), - textAlign = TextAlign.Center, + ContainerCard() { + Column() { + Icon( + bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap(), + contentDescription = null, + tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, + modifier = Modifier.align(alignment = Alignment.CenterHorizontally) + .padding(top = 24.dp, bottom = 16.dp).size(32.dp) + ) + TextOnSurface( + text = stringResource( + R.string.choose_provider_title, + when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.create_your_passkeys) + TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.save_your_password) + else -> stringResource(R.string.save_your_sign_in_info) + }, + ), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + ) + Divider( + thickness = 16.dp, + color = Color.Transparent + ) + TextSecondary( + text = stringResource(R.string.choose_provider_body), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(horizontal = 28.dp), + ) + Divider( + thickness = 18.dp, + color = Color.Transparent + ) + ContainerCard( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + ) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + enabledProviderList.forEach { enabledProviderInfo -> + enabledProviderInfo.createOptions.forEach { createOptionInfo -> + item { + MoreOptionsInfoRow( + providerInfo = enabledProviderInfo, + createOptionInfo = createOptionInfo, + onOptionSelected = { + onOptionSelected( + ActiveEntry( + enabledProviderInfo, + createOptionInfo + ) + ) + }) + } + } + } + if (disabledProviderList != null) { + item { + MoreOptionsDisabledProvidersRow( + disabledProviders = disabledProviderList, + onDisabledPasswordManagerSelected = + onDisabledPasswordManagerSelected, + ) + } + } + } + } + // TODO: handle the error situation that if multiple remoteInfos exists + enabledProviderList.forEach { enabledProvider -> + if (enabledProvider.remoteEntry != null) { + TextButton( + onClick = { + onRemoteEntrySelected(enabledProvider.remoteEntry!!) + }, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.primary, + ) + ) { + Text( + text = stringResource(R.string.string_save_to_another_device), + textAlign = TextAlign.Center, + ) + } + } + } + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + Row( + horizontalArrangement = Arrangement.Start, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ) { + CancelButton(stringResource(R.string.string_cancel), onCancel) + } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 16.dp) ) - } } - } - Divider( - thickness = 24.dp, - color = Color.Transparent - ) - Row( - horizontalArrangement = Arrangement.Start, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - CancelButton(stringResource(R.string.string_cancel), onCancel) - } - Divider( - thickness = 18.dp, - color = Color.Transparent, - modifier = Modifier.padding(bottom = 16.dp) - ) } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun MoreOptionsSelectionCard( - requestDisplayInfo: RequestDisplayInfo, - enabledProviderList: List, - disabledProviderList: List?, - onBackButtonSelected: () -> Unit, - onOptionSelected: (ActiveEntry) -> Unit, - onDisabledPasswordManagerSelected: () -> Unit, - onRemoteEntrySelected: (EntryInfo) -> Unit, + requestDisplayInfo: RequestDisplayInfo, + enabledProviderList: List, + disabledProviderList: List?, + onBackButtonSelected: () -> Unit, + onOptionSelected: (ActiveEntry) -> Unit, + onDisabledPasswordManagerSelected: () -> Unit, + onRemoteEntrySelected: (EntryInfo) -> Unit, ) { - Card() { - Column() { - TopAppBar( - title = { - Text( - text = when (requestDisplayInfo.type) { - TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.create_passkey_in_title) - TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.save_password_to_title) - else -> stringResource(R.string.save_sign_in_to_title) - }, - style = MaterialTheme.typography.titleMedium - ) - }, - navigationIcon = { - IconButton(onClick = onBackButtonSelected) { - Icon( - Icons.Filled.ArrowBack, - stringResource(R.string.accessibility_back_arrow_button)) - } - }, - colors = TopAppBarDefaults.smallTopAppBarColors - (containerColor = Color.Transparent), - ) - Divider( - thickness = 8.dp, - color = Color.Transparent - ) - Card( - shape = MaterialTheme.shapes.medium, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally) - ) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - enabledProviderList.forEach { enabledProviderInfo -> - enabledProviderInfo.createOptions.forEach { createOptionInfo -> - item { - MoreOptionsInfoRow( - providerInfo = enabledProviderInfo, - createOptionInfo = createOptionInfo, - onOptionSelected = { - onOptionSelected(ActiveEntry(enabledProviderInfo, createOptionInfo)) - }) - } + ContainerCard() { + Column() { + TopAppBar( + title = { + TextOnSurface( + text = when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> + stringResource(R.string.create_passkey_in_title) + TYPE_PASSWORD_CREDENTIAL -> + stringResource(R.string.save_password_to_title) + else -> stringResource(R.string.save_sign_in_to_title) + }, + style = MaterialTheme.typography.titleMedium, + ) + }, + navigationIcon = { + IconButton(onClick = onBackButtonSelected) { + Icon( + Icons.Filled.ArrowBack, + stringResource(R.string.accessibility_back_arrow_button) + ) + } + }, + colors = TopAppBarDefaults.smallTopAppBarColors + (containerColor = Color.Transparent), + ) + Divider( + thickness = 8.dp, + color = Color.Transparent + ) + ContainerCard( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally) + ) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + enabledProviderList.forEach { enabledProviderInfo -> + enabledProviderInfo.createOptions.forEach { createOptionInfo -> + item { + MoreOptionsInfoRow( + providerInfo = enabledProviderInfo, + createOptionInfo = createOptionInfo, + onOptionSelected = { + onOptionSelected( + ActiveEntry( + enabledProviderInfo, + createOptionInfo + ) + ) + }) + } + } + } + if (disabledProviderList != null) { + item { + MoreOptionsDisabledProvidersRow( + disabledProviders = disabledProviderList, + onDisabledPasswordManagerSelected = + onDisabledPasswordManagerSelected, + ) + } + } + // TODO: handle the error situation that if multiple remoteInfos exists + enabledProviderList.forEach { + if (it.remoteEntry != null) { + item { + RemoteEntryRow( + remoteInfo = it.remoteEntry!!, + onRemoteEntrySelected = onRemoteEntrySelected, + ) + } + } + } + } } - } - if (disabledProviderList != null) { - item { - MoreOptionsDisabledProvidersRow( - disabledProviders = disabledProviderList, - onDisabledPasswordManagerSelected = onDisabledPasswordManagerSelected, - ) - } - } - // TODO: handle the error situation that if multiple remoteInfos exists - enabledProviderList.forEach { - if (it.remoteEntry != null) { - item { - RemoteEntryRow( - remoteInfo = it.remoteEntry!!, - onRemoteEntrySelected = onRemoteEntrySelected, - ) - } - } - } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 40.dp) + ) } - } - Divider( - thickness = 18.dp, - color = Color.Transparent, - modifier = Modifier.padding(bottom = 40.dp) - ) } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun MoreOptionsRowIntroCard( - providerInfo: EnabledProviderInfo, - onDefaultOrNotSelected: () -> Unit, + providerInfo: EnabledProviderInfo, + onDefaultOrNotSelected: () -> Unit, ) { - Card() { - Column() { - Icon( - Icons.Outlined.NewReleases, - contentDescription = null, - modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(all = 24.dp) - ) - Text( - text = stringResource(R.string.use_provider_for_all_title, providerInfo.displayName), - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - textAlign = TextAlign.Center, - ) - Text( - text = stringResource(R.string.use_provider_for_all_description), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally) - ) - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - CancelButton( - stringResource(R.string.use_once), - onClick = onDefaultOrNotSelected - ) - ConfirmButton( - stringResource(R.string.set_as_default), - onClick = onDefaultOrNotSelected - ) - } - Divider( - thickness = 18.dp, - color = Color.Transparent, - modifier = Modifier.padding(bottom = 40.dp) - ) + ContainerCard() { + Column() { + Icon( + Icons.Outlined.NewReleases, + contentDescription = null, + modifier = Modifier.align(alignment = Alignment.CenterHorizontally) + .padding(all = 24.dp) + ) + TextOnSurface( + text = stringResource( + R.string.use_provider_for_all_title, + providerInfo.displayName + ), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + ) + TextSecondary( + text = stringResource(R.string.use_provider_for_all_description), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(all = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + ) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ) { + CancelButton( + stringResource(R.string.use_once), + onClick = onDefaultOrNotSelected + ) + ConfirmButton( + stringResource(R.string.set_as_default), + onClick = onDefaultOrNotSelected + ) + } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 40.dp) + ) + } } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun CreationSelectionCard( - requestDisplayInfo: RequestDisplayInfo, - enabledProviderList: List, - providerInfo: EnabledProviderInfo, - createOptionInfo: CreateOptionInfo, - showActiveEntryOnly: Boolean, - onOptionSelected: (EntryInfo) -> Unit, - onConfirm: () -> Unit, - onCancel: () -> Unit, - onMoreOptionsSelected: () -> Unit, + requestDisplayInfo: RequestDisplayInfo, + enabledProviderList: List, + providerInfo: EnabledProviderInfo, + createOptionInfo: CreateOptionInfo, + showActiveEntryOnly: Boolean, + onOptionSelected: (EntryInfo) -> Unit, + onConfirm: () -> Unit, + onCancel: () -> Unit, + onMoreOptionsSelected: () -> Unit, ) { - Card() { - Column() { - Icon( - bitmap = providerInfo.icon.toBitmap().asImageBitmap(), - contentDescription = null, - tint = Color.Unspecified, - modifier = Modifier.align(alignment = Alignment.CenterHorizontally) - .padding(all = 24.dp).size(32.dp) - ) - Text( - text = when (requestDisplayInfo.type) { - TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.choose_create_option_passkey_title, - providerInfo.displayName) - TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.choose_create_option_password_title, - providerInfo.displayName) - else -> stringResource(R.string.choose_create_option_sign_in_title, - providerInfo.displayName) - }, - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - textAlign = TextAlign.Center, - ) - if (createOptionInfo.userProviderDisplayName != null) { - Text( - text = stringResource( - R.string.choose_create_option_description, - requestDisplayInfo.appDomainName, - when (requestDisplayInfo.type) { - TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.passkey) - TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.password) - else -> stringResource(R.string.sign_ins) - }, - providerInfo.displayName, - createOptionInfo.userProviderDisplayName - ), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally) - ) - } - Card( - shape = MaterialTheme.shapes.medium, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - ) { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - entryInfo = createOptionInfo, - onOptionSelected = onOptionSelected - ) - } - if (!showActiveEntryOnly) { - var createOptionsSize = 0 - enabledProviderList.forEach{ - enabledProvider -> createOptionsSize += enabledProvider.createOptions.size} - if (createOptionsSize > 1) { - TextButton( - onClick = onMoreOptionsSelected, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally)){ - Text( - text = - when (requestDisplayInfo.type) { - TYPE_PUBLIC_KEY_CREDENTIAL -> - stringResource(R.string.string_create_in_another_place) - else -> stringResource(R.string.string_save_to_another_place)}, - textAlign = TextAlign.Center, + ContainerCard() { + Column() { + Icon( + bitmap = providerInfo.icon.toBitmap().asImageBitmap(), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.align(alignment = Alignment.CenterHorizontally) + .padding(all = 24.dp).size(32.dp) ) - } - } else if ( - requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL - ) { - // TODO: handle the error situation that if multiple remoteInfos exists - enabledProviderList.forEach { enabledProvider -> - if (enabledProvider.remoteEntry != null) { - TextButton( - onClick = { - onOptionSelected(enabledProvider.remoteEntry!!) }, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally) - ) { - Text( - text = stringResource(R.string.string_use_another_device), - textAlign = TextAlign.Center, + TextOnSurface( + text = when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource( + R.string.choose_create_option_passkey_title, + providerInfo.displayName + ) + TYPE_PASSWORD_CREDENTIAL -> stringResource( + R.string.choose_create_option_password_title, + providerInfo.displayName + ) + else -> stringResource( + R.string.choose_create_option_sign_in_title, + providerInfo.displayName + ) + }, + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + ) + if (createOptionInfo.userProviderDisplayName != null) { + TextSecondary( + text = stringResource( + R.string.choose_create_option_description, + requestDisplayInfo.appDomainName, + when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(R.string.passkey) + TYPE_PASSWORD_CREDENTIAL -> stringResource(R.string.password) + else -> stringResource(R.string.sign_ins) + }, + providerInfo.displayName, + createOptionInfo.userProviderDisplayName + ), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(all = 24.dp) + .align(alignment = Alignment.CenterHorizontally), ) - } } - } + ContainerCard( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + ) { + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + entryInfo = createOptionInfo, + onOptionSelected = onOptionSelected + ) + } + if (!showActiveEntryOnly) { + var createOptionsSize = 0 + enabledProviderList.forEach { enabledProvider -> + createOptionsSize += enabledProvider.createOptions.size + } + if (createOptionsSize > 1) { + TextButton( + onClick = onMoreOptionsSelected, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.primary, + ), + ) { + Text( + text = + when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> + stringResource(R.string.string_create_in_another_place) + else -> stringResource(R.string.string_save_to_another_place) + }, + textAlign = TextAlign.Center, + ) + } + } else if ( + requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL + ) { + // TODO: handle the error situation that if multiple remoteInfos exists + enabledProviderList.forEach { enabledProvider -> + if (enabledProvider.remoteEntry != null) { + TextButton( + onClick = { + onOptionSelected(enabledProvider.remoteEntry!!) + }, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.primary, + ), + ) { + Text( + text = stringResource(R.string.string_use_another_device), + textAlign = TextAlign.Center, + ) + } + } + } + } + } + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ) { + CancelButton( + stringResource(R.string.string_cancel), + onClick = onCancel + ) + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 16.dp) + ) } - } - Divider( - thickness = 24.dp, - color = Color.Transparent - ) - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - CancelButton( - stringResource(R.string.string_cancel), - onClick = onCancel - ) - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - } - Divider( - thickness = 18.dp, - color = Color.Transparent, - modifier = Modifier.padding(bottom = 16.dp) - ) } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun ExternalOnlySelectionCard( - requestDisplayInfo: RequestDisplayInfo, - activeRemoteEntry: EntryInfo, - onOptionSelected: (EntryInfo) -> Unit, - onConfirm: () -> Unit, - onCancel: () -> Unit, + requestDisplayInfo: RequestDisplayInfo, + activeRemoteEntry: EntryInfo, + onOptionSelected: (EntryInfo) -> Unit, + onConfirm: () -> Unit, + onCancel: () -> Unit, ) { - Card() { - Column() { - Icon( - painter = painterResource(R.drawable.ic_other_devices), - contentDescription = null, - tint = Color.Unspecified, - modifier = Modifier.align(alignment = Alignment.CenterHorizontally) - .padding(all = 24.dp).size(32.dp) - ) - Text( - text = stringResource(R.string.create_passkey_in_other_device_title), - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - textAlign = TextAlign.Center, - ) - Divider( - thickness = 24.dp, - color = Color.Transparent - ) - Card( - shape = MaterialTheme.shapes.medium, - modifier = Modifier - .padding(horizontal = 24.dp) - .align(alignment = Alignment.CenterHorizontally), - ) { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - entryInfo = activeRemoteEntry, - onOptionSelected = onOptionSelected - ) - } - Divider( - thickness = 24.dp, - color = Color.Transparent - ) - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - CancelButton( - stringResource(R.string.string_cancel), - onClick = onCancel - ) - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - } - Divider( - thickness = 18.dp, - color = Color.Transparent, - modifier = Modifier.padding(bottom = 16.dp) - ) + ContainerCard() { + Column() { + Icon( + painter = painterResource(R.drawable.ic_other_devices), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.align(alignment = Alignment.CenterHorizontally) + .padding(all = 24.dp).size(32.dp) + ) + TextOnSurface( + text = stringResource(R.string.create_passkey_in_other_device_title), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + ) + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + ContainerCard( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally), + ) { + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + entryInfo = activeRemoteEntry, + onOptionSelected = onOptionSelected + ) + } + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ) { + CancelButton( + stringResource(R.string.string_cancel), + onClick = onCancel + ) + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 16.dp) + ) + } } - } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun PrimaryCreateOptionRow( - requestDisplayInfo: RequestDisplayInfo, - entryInfo: EntryInfo, - onOptionSelected: (EntryInfo) -> Unit + requestDisplayInfo: RequestDisplayInfo, + entryInfo: EntryInfo, + onOptionSelected: (EntryInfo) -> Unit ) { - Entry( - onClick = {onOptionSelected(entryInfo)}, - icon = { - Icon( - bitmap = if (entryInfo is CreateOptionInfo) { - entryInfo.profileIcon.toBitmap().asImageBitmap() - } else {requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()}, - contentDescription = null, - tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, - modifier = Modifier.padding(start = 18.dp).size(32.dp) - ) - }, - label = { - Column() { - // TODO: Add the function to hide/view password when the type is create password - when (requestDisplayInfo.type) { - TYPE_PUBLIC_KEY_CREDENTIAL -> { - Text( - text = requestDisplayInfo.title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp) - ) - Text( - text = if (requestDisplayInfo.subtitle != null) { - stringResource( - R.string.passkey_before_subtitle) + " - " + requestDisplayInfo.subtitle - } else {stringResource(R.string.passkey_before_subtitle)}, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp) - ) - } - TYPE_PASSWORD_CREDENTIAL -> { - Text( - text = requestDisplayInfo.title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp) - ) - Text( - // This subtitle would never be null for create password - text = requestDisplayInfo.subtitle ?: "", - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp) - ) - } - else -> { - Text( - text = requestDisplayInfo.title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp, bottom = 16.dp) - ) + Entry( + onClick = { onOptionSelected(entryInfo) }, + icon = { + Icon( + bitmap = if (entryInfo is CreateOptionInfo) { + entryInfo.profileIcon.toBitmap().asImageBitmap() + } else { + requestDisplayInfo.typeIcon.toBitmap().asImageBitmap() + }, + contentDescription = null, + tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, + modifier = Modifier.padding(start = 18.dp).size(32.dp) + ) + }, + label = { + Column() { + // TODO: Add the function to hide/view password when the type is create password + when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> { + TextOnSurfaceVariant( + text = requestDisplayInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp), + ) + TextSecondary( + text = if (requestDisplayInfo.subtitle != null) { + stringResource( + R.string.passkey_before_subtitle + ) + " - " + requestDisplayInfo.subtitle + } else { + stringResource(R.string.passkey_before_subtitle) + }, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp), + ) + } + TYPE_PASSWORD_CREDENTIAL -> { + TextOnSurfaceVariant( + text = requestDisplayInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp), + ) + TextSecondary( + // This subtitle would never be null for create password + text = requestDisplayInfo.subtitle ?: "", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp), + ) + } + else -> { + TextOnSurfaceVariant( + text = requestDisplayInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp, bottom = 16.dp), + ) + } + } } } - } - } - ) + ) } @OptIn(ExperimentalMaterial3Api::class) @Composable fun MoreOptionsInfoRow( - providerInfo: EnabledProviderInfo, - createOptionInfo: CreateOptionInfo, - onOptionSelected: () -> Unit + providerInfo: EnabledProviderInfo, + createOptionInfo: CreateOptionInfo, + onOptionSelected: () -> Unit ) { - Entry( + Entry( onClick = onOptionSelected, icon = { - Image(modifier = Modifier.size(32.dp).padding(start = 16.dp), + Image( + modifier = Modifier.size(32.dp).padding(start = 16.dp), bitmap = providerInfo.icon.toBitmap().asImageBitmap(), - contentDescription = null) + contentDescription = null + ) }, label = { - Column() { - Text( - text = providerInfo.displayName, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp, start = 16.dp) - ) - if (createOptionInfo.userProviderDisplayName != null) { - Text( - text = createOptionInfo.userProviderDisplayName, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(start = 16.dp) - ) + Column() { + TextOnSurfaceVariant( + text = providerInfo.displayName, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp, start = 16.dp), + ) + if (createOptionInfo.userProviderDisplayName != null) { + TextSecondary( + text = createOptionInfo.userProviderDisplayName, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(start = 16.dp), + ) + } + if (createOptionInfo.passwordCount != null && + createOptionInfo.passkeyCount != null) { + TextSecondary( + text = + stringResource( + R.string.more_options_usage_passwords_passkeys, + createOptionInfo.passwordCount, + createOptionInfo.passkeyCount + ), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp, start = 16.dp), + ) + } else if (createOptionInfo.passwordCount != null) { + TextSecondary( + text = + stringResource( + R.string.more_options_usage_passwords, + createOptionInfo.passwordCount + ), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp, start = 16.dp), + ) + } else if (createOptionInfo.passkeyCount != null) { + TextSecondary( + text = + stringResource( + R.string.more_options_usage_passkeys, + createOptionInfo.passkeyCount + ), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp, start = 16.dp), + ) + } else if (createOptionInfo.totalCredentialCount != null) { + // TODO: Handle the case when there is total count + // but no passwords and passkeys after design is set + } } - if (createOptionInfo.passwordCount != null && createOptionInfo.passkeyCount != null) { - Text( - text = - stringResource( - R.string.more_options_usage_passwords_passkeys, - createOptionInfo.passwordCount, - createOptionInfo.passkeyCount - ), - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp, start = 16.dp) - ) - } else if (createOptionInfo.passwordCount != null) { - Text( - text = - stringResource( - R.string.more_options_usage_passwords, - createOptionInfo.passwordCount - ), - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp, start = 16.dp) - ) - } else if (createOptionInfo.passkeyCount != null) { - Text( - text = - stringResource( - R.string.more_options_usage_passkeys, - createOptionInfo.passkeyCount - ), - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp, start = 16.dp) - ) - } else if (createOptionInfo.totalCredentialCount != null) { - // TODO: Handle the case when there is total count - // but no passwords and passkeys after design is set - } - } } ) } @@ -769,61 +825,61 @@ fun MoreOptionsInfoRow( @OptIn(ExperimentalMaterial3Api::class) @Composable fun MoreOptionsDisabledProvidersRow( - disabledProviders: List, - onDisabledPasswordManagerSelected: () -> Unit, + disabledProviders: List, + onDisabledPasswordManagerSelected: () -> Unit, ) { - Entry( - onClick = onDisabledPasswordManagerSelected, - icon = { - Icon( - Icons.Filled.Add, - contentDescription = null, - modifier = Modifier.padding(start = 16.dp) - ) - }, - label = { - Column() { - Text( - text = stringResource(R.string.other_password_manager), - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp, start = 16.dp) - ) - // TODO: Update the subtitle once design is confirmed - Text( - text = disabledProviders.joinToString(separator = ", "){ it.displayName }, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp, start = 16.dp) - ) - } - } - ) + Entry( + onClick = onDisabledPasswordManagerSelected, + icon = { + Icon( + Icons.Filled.Add, + contentDescription = null, + modifier = Modifier.padding(start = 16.dp) + ) + }, + label = { + Column() { + TextOnSurfaceVariant( + text = stringResource(R.string.other_password_manager), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp, start = 16.dp), + ) + // TODO: Update the subtitle once design is confirmed + TextSecondary( + text = disabledProviders.joinToString(separator = ", ") { it.displayName }, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp, start = 16.dp), + ) + } + } + ) } @OptIn(ExperimentalMaterial3Api::class) @Composable fun RemoteEntryRow( - remoteInfo: RemoteInfo, - onRemoteEntrySelected: (RemoteInfo) -> Unit, + remoteInfo: RemoteInfo, + onRemoteEntrySelected: (RemoteInfo) -> Unit, ) { - Entry( - onClick = {onRemoteEntrySelected(remoteInfo)}, - icon = { - Icon( - painter = painterResource(R.drawable.ic_other_devices), - contentDescription = null, - tint = Color.Unspecified, - modifier = Modifier.padding(start = 18.dp) - ) - }, - label = { - Column() { - Text( - text = stringResource(R.string.another_device), - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp) - .align(alignment = Alignment.CenterHorizontally) - ) - } - } - ) + Entry( + onClick = { onRemoteEntrySelected(remoteInfo) }, + icon = { + Icon( + painter = painterResource(R.drawable.ic_other_devices), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.padding(start = 18.dp) + ) + }, + label = { + Column() { + TextOnSurfaceVariant( + text = stringResource(R.string.another_device), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp) + .align(alignment = Alignment.CenterHorizontally), + ) + } + } + ) } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 720f231609c3e..31dc0695e8edb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -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 + viewModel: GetCredentialViewModel, + providerActivityLauncher: ManagedActivityResultLauncher ) { - 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, - providerDisplayInfo: ProviderDisplayInfo, - onEntrySelected: (EntryInfo) -> Unit, - onBackButtonClicked: () -> Unit, + providerInfoList: List, + 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, - onEntrySelected: (EntryInfo) -> Unit, + providerInfoList: List, + 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, - onEntrySelected: (EntryInfo) -> Unit, + authenticationEntryList: List, + 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) + ) + } + ) }