Merge "Update the color tokens to match the UX design."
This commit is contained in:
committed by
Android (Google) Code Review
commit
9b8706de8c
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user