Merge "[CredManUI] Make all bottom sheet pages scrollable." into udc-dev am: cc21455e00
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21655427 Change-Id: I2fdee8a7f029c3960e42ef2b24b0474cca4cafe1 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
package com.android.credentialmanager.common.ui
|
package com.android.credentialmanager.common.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -40,7 +42,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
fun SheetContainerCard(
|
fun SheetContainerCard(
|
||||||
topAppBar: (@Composable () -> Unit)? = null,
|
topAppBar: (@Composable () -> Unit)? = null,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
contentVerticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||||
|
content: LazyListScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier.fillMaxWidth().wrapContentHeight(),
|
modifier = modifier.fillMaxWidth().wrapContentHeight(),
|
||||||
@@ -54,7 +57,7 @@ fun SheetContainerCard(
|
|||||||
if (topAppBar != null) {
|
if (topAppBar != null) {
|
||||||
topAppBar()
|
topAppBar()
|
||||||
}
|
}
|
||||||
Column(
|
LazyColumn(
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
start = 24.dp,
|
start = 24.dp,
|
||||||
end = 24.dp,
|
end = 24.dp,
|
||||||
@@ -63,6 +66,7 @@ fun SheetContainerCard(
|
|||||||
).fillMaxWidth().wrapContentHeight(),
|
).fillMaxWidth().wrapContentHeight(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
content = content,
|
content = content,
|
||||||
|
verticalArrangement = contentVerticalArrangement,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun EntryListColumn(content: LazyListScope.() -> Unit) {
|
|
||||||
LazyColumn(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
|
||||||
content = content,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -8,11 +8,12 @@ import androidx.activity.result.IntentSenderRequest
|
|||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -23,7 +24,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
@@ -43,7 +43,6 @@ import com.android.credentialmanager.common.ui.ConfirmButton
|
|||||||
import com.android.credentialmanager.common.ui.CredentialContainerCard
|
import com.android.credentialmanager.common.ui.CredentialContainerCard
|
||||||
import com.android.credentialmanager.common.ui.CtaButtonRow
|
import com.android.credentialmanager.common.ui.CtaButtonRow
|
||||||
import com.android.credentialmanager.common.ui.Entry
|
import com.android.credentialmanager.common.ui.Entry
|
||||||
import com.android.credentialmanager.common.ui.EntryListColumn
|
|
||||||
import com.android.credentialmanager.common.ui.HeadlineIcon
|
import com.android.credentialmanager.common.ui.HeadlineIcon
|
||||||
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
|
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
|
||||||
import com.android.credentialmanager.common.ui.ModalBottomSheet
|
import com.android.credentialmanager.common.ui.ModalBottomSheet
|
||||||
@@ -157,53 +156,67 @@ fun PasskeyIntroCard(
|
|||||||
onLearnMore: () -> Unit,
|
onLearnMore: () -> Unit,
|
||||||
) {
|
) {
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
val onboardingImageResource = remember {
|
item {
|
||||||
mutableStateOf(R.drawable.ic_passkeys_onboarding)
|
val onboardingImageResource = remember {
|
||||||
|
mutableStateOf(R.drawable.ic_passkeys_onboarding)
|
||||||
|
}
|
||||||
|
if (isSystemInDarkTheme()) {
|
||||||
|
onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark
|
||||||
|
} else {
|
||||||
|
onboardingImageResource.value = R.drawable.ic_passkeys_onboarding
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.wrapContentHeight().fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(onboardingImageResource.value),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(316.dp, 168.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isSystemInDarkTheme()) {
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark
|
item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) }
|
||||||
} else {
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
onboardingImageResource.value = R.drawable.ic_passkeys_onboarding
|
item {
|
||||||
|
PasskeyBenefitRow(
|
||||||
|
leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password),
|
||||||
|
text = stringResource(R.string.passkey_creation_intro_body_password),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Image(
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
painter = painterResource(onboardingImageResource.value),
|
item {
|
||||||
contentDescription = null,
|
PasskeyBenefitRow(
|
||||||
modifier = Modifier
|
leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint),
|
||||||
.align(alignment = Alignment.CenterHorizontally).size(316.dp, 168.dp)
|
text = stringResource(R.string.passkey_creation_intro_body_fingerprint),
|
||||||
)
|
)
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
}
|
||||||
HeadlineText(text = stringResource(R.string.passkey_creation_intro_title))
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
item {
|
||||||
PasskeyBenefitRow(
|
PasskeyBenefitRow(
|
||||||
leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password),
|
leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device),
|
||||||
text = stringResource(R.string.passkey_creation_intro_body_password),
|
text = stringResource(R.string.passkey_creation_intro_body_device),
|
||||||
)
|
)
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
}
|
||||||
PasskeyBenefitRow(
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint),
|
|
||||||
text = stringResource(R.string.passkey_creation_intro_body_fingerprint),
|
|
||||||
)
|
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
|
||||||
PasskeyBenefitRow(
|
|
||||||
leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device),
|
|
||||||
text = stringResource(R.string.passkey_creation_intro_body_device),
|
|
||||||
)
|
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
|
||||||
|
|
||||||
CtaButtonRow(
|
item {
|
||||||
leftButton = {
|
CtaButtonRow(
|
||||||
ActionButton(
|
leftButton = {
|
||||||
stringResource(R.string.string_learn_more),
|
ActionButton(
|
||||||
onClick = onLearnMore
|
stringResource(R.string.string_learn_more),
|
||||||
)
|
onClick = onLearnMore
|
||||||
},
|
)
|
||||||
rightButton = {
|
},
|
||||||
ConfirmButton(
|
rightButton = {
|
||||||
stringResource(R.string.string_continue),
|
ConfirmButton(
|
||||||
onClick = onConfirm
|
stringResource(R.string.string_continue),
|
||||||
)
|
onClick = onConfirm
|
||||||
},
|
)
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,30 +231,32 @@ fun ProviderSelectionCard(
|
|||||||
onMoreOptionsSelected: () -> Unit,
|
onMoreOptionsSelected: () -> Unit,
|
||||||
) {
|
) {
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap())
|
item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) }
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
HeadlineText(
|
item {
|
||||||
text = stringResource(
|
HeadlineText(
|
||||||
R.string.choose_provider_title,
|
text = stringResource(
|
||||||
when (requestDisplayInfo.type) {
|
R.string.choose_provider_title,
|
||||||
CredentialType.PASSKEY ->
|
when (requestDisplayInfo.type) {
|
||||||
stringResource(R.string.passkeys)
|
CredentialType.PASSKEY ->
|
||||||
CredentialType.PASSWORD ->
|
stringResource(R.string.passkeys)
|
||||||
stringResource(R.string.passwords)
|
CredentialType.PASSWORD ->
|
||||||
CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
|
stringResource(R.string.passwords)
|
||||||
}
|
CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
|
|
||||||
BodyMediumText(text = stringResource(R.string.choose_provider_body))
|
item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) }
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
CredentialContainerCard {
|
item {
|
||||||
LazyColumn(
|
CredentialContainerCard {
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
Column(
|
||||||
) {
|
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||||
sortedCreateOptionsPairs.forEach { entry ->
|
) {
|
||||||
item {
|
sortedCreateOptionsPairs.forEach { entry ->
|
||||||
MoreOptionsInfoRow(
|
MoreOptionsInfoRow(
|
||||||
requestDisplayInfo = requestDisplayInfo,
|
requestDisplayInfo = requestDisplayInfo,
|
||||||
providerInfo = entry.second,
|
providerInfo = entry.second,
|
||||||
@@ -256,8 +271,6 @@ fun ProviderSelectionCard(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
item {
|
|
||||||
MoreOptionsDisabledProvidersRow(
|
MoreOptionsDisabledProvidersRow(
|
||||||
disabledProviders = disabledProviderList,
|
disabledProviders = disabledProviderList,
|
||||||
onDisabledProvidersSelected = onDisabledProvidersSelected,
|
onDisabledProvidersSelected = onDisabledProvidersSelected,
|
||||||
@@ -266,15 +279,17 @@ fun ProviderSelectionCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasRemoteEntry) {
|
if (hasRemoteEntry) {
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
CtaButtonRow(
|
item {
|
||||||
leftButton = {
|
CtaButtonRow(
|
||||||
ActionButton(
|
leftButton = {
|
||||||
stringResource(R.string.string_more_options),
|
ActionButton(
|
||||||
onMoreOptionsSelected
|
stringResource(R.string.string_more_options),
|
||||||
)
|
onMoreOptionsSelected
|
||||||
}
|
)
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,14 +325,14 @@ fun MoreOptionsSelectionCard(
|
|||||||
else onBackCreationSelectionButtonSelected,
|
else onBackCreationSelectionButtonSelected,
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
CredentialContainerCard {
|
item {
|
||||||
EntryListColumn {
|
CredentialContainerCard {
|
||||||
// Only in the flows with default provider(not first time use) we can show the
|
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||||
// createOptions here, or they will be shown on ProviderSelectionCard
|
// Only in the flows with default provider(not first time use) we can show the
|
||||||
if (hasDefaultProvider) {
|
// createOptions here, or they will be shown on ProviderSelectionCard
|
||||||
sortedCreateOptionsPairs.forEach { entry ->
|
if (hasDefaultProvider) {
|
||||||
item {
|
sortedCreateOptionsPairs.forEach { entry ->
|
||||||
MoreOptionsInfoRow(
|
MoreOptionsInfoRow(
|
||||||
requestDisplayInfo = requestDisplayInfo,
|
requestDisplayInfo = requestDisplayInfo,
|
||||||
providerInfo = entry.second,
|
providerInfo = entry.second,
|
||||||
@@ -329,26 +344,23 @@ fun MoreOptionsSelectionCard(
|
|||||||
entry.first
|
entry.first
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
item {
|
|
||||||
MoreOptionsDisabledProvidersRow(
|
MoreOptionsDisabledProvidersRow(
|
||||||
disabledProviders = disabledProviderList,
|
disabledProviders = disabledProviderList,
|
||||||
onDisabledProvidersSelected =
|
onDisabledProvidersSelected =
|
||||||
onDisabledProvidersSelected,
|
onDisabledProvidersSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
enabledProviderList.forEach {
|
||||||
enabledProviderList.forEach {
|
if (it.remoteEntry != null) {
|
||||||
if (it.remoteEntry != null) {
|
|
||||||
item {
|
|
||||||
RemoteEntryRow(
|
RemoteEntryRow(
|
||||||
remoteInfo = it.remoteEntry!!,
|
remoteInfo = it.remoteEntry!!,
|
||||||
onRemoteEntrySelected = onRemoteEntrySelected,
|
onRemoteEntrySelected = onRemoteEntrySelected,
|
||||||
)
|
)
|
||||||
|
return@forEach
|
||||||
}
|
}
|
||||||
return@forEach
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,30 +375,34 @@ fun MoreOptionsRowIntroCard(
|
|||||||
onUseOnceSelected: () -> Unit,
|
onUseOnceSelected: () -> Unit,
|
||||||
) {
|
) {
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
HeadlineIcon(imageVector = Icons.Outlined.NewReleases)
|
item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) }
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
HeadlineText(
|
item {
|
||||||
text = stringResource(
|
HeadlineText(
|
||||||
R.string.use_provider_for_all_title,
|
text = stringResource(
|
||||||
providerInfo.displayName
|
R.string.use_provider_for_all_title,
|
||||||
|
providerInfo.displayName
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
BodyMediumText(text = stringResource(R.string.use_provider_for_all_description))
|
item { BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) }
|
||||||
CtaButtonRow(
|
item {
|
||||||
leftButton = {
|
CtaButtonRow(
|
||||||
ActionButton(
|
leftButton = {
|
||||||
stringResource(R.string.use_once),
|
ActionButton(
|
||||||
onClick = onUseOnceSelected
|
stringResource(R.string.use_once),
|
||||||
)
|
onClick = onUseOnceSelected
|
||||||
},
|
)
|
||||||
rightButton = {
|
},
|
||||||
ConfirmButton(
|
rightButton = {
|
||||||
stringResource(R.string.set_as_default),
|
ConfirmButton(
|
||||||
onClick = onChangeDefaultSelected
|
stringResource(R.string.set_as_default),
|
||||||
)
|
onClick = onChangeDefaultSelected
|
||||||
},
|
)
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,38 +418,44 @@ fun CreationSelectionCard(
|
|||||||
hasDefaultProvider: Boolean,
|
hasDefaultProvider: Boolean,
|
||||||
) {
|
) {
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
HeadlineIcon(
|
item {
|
||||||
bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
|
HeadlineIcon(
|
||||||
tint = Color.Unspecified,
|
bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
|
||||||
)
|
tint = Color.Unspecified,
|
||||||
Divider(thickness = 4.dp, color = Color.Transparent)
|
|
||||||
LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName)
|
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
|
||||||
HeadlineText(
|
|
||||||
text = when (requestDisplayInfo.type) {
|
|
||||||
CredentialType.PASSKEY -> stringResource(
|
|
||||||
R.string.choose_create_option_passkey_title,
|
|
||||||
requestDisplayInfo.appName
|
|
||||||
)
|
|
||||||
CredentialType.PASSWORD -> stringResource(
|
|
||||||
R.string.choose_create_option_password_title,
|
|
||||||
requestDisplayInfo.appName
|
|
||||||
)
|
|
||||||
CredentialType.UNKNOWN -> stringResource(
|
|
||||||
R.string.choose_create_option_sign_in_title,
|
|
||||||
requestDisplayInfo.appName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
|
||||||
CredentialContainerCard {
|
|
||||||
PrimaryCreateOptionRow(
|
|
||||||
requestDisplayInfo = requestDisplayInfo,
|
|
||||||
entryInfo = createOptionInfo,
|
|
||||||
onOptionSelected = onOptionSelected
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
item { Divider(thickness = 4.dp, color = Color.Transparent) }
|
||||||
|
item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) }
|
||||||
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
|
item {
|
||||||
|
HeadlineText(
|
||||||
|
text = when (requestDisplayInfo.type) {
|
||||||
|
CredentialType.PASSKEY -> stringResource(
|
||||||
|
R.string.choose_create_option_passkey_title,
|
||||||
|
requestDisplayInfo.appName
|
||||||
|
)
|
||||||
|
CredentialType.PASSWORD -> stringResource(
|
||||||
|
R.string.choose_create_option_password_title,
|
||||||
|
requestDisplayInfo.appName
|
||||||
|
)
|
||||||
|
CredentialType.UNKNOWN -> stringResource(
|
||||||
|
R.string.choose_create_option_sign_in_title,
|
||||||
|
requestDisplayInfo.appName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
|
item {
|
||||||
|
CredentialContainerCard {
|
||||||
|
PrimaryCreateOptionRow(
|
||||||
|
requestDisplayInfo = requestDisplayInfo,
|
||||||
|
entryInfo = createOptionInfo,
|
||||||
|
onOptionSelected = onOptionSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
var createOptionsSize = 0
|
var createOptionsSize = 0
|
||||||
var remoteEntry: RemoteInfo? = null
|
var remoteEntry: RemoteInfo? = null
|
||||||
enabledProviderList.forEach { enabledProvider ->
|
enabledProviderList.forEach { enabledProvider ->
|
||||||
@@ -450,29 +472,33 @@ fun CreationSelectionCard(
|
|||||||
} else {
|
} else {
|
||||||
createOptionsSize > 1 || remoteEntry != null
|
createOptionsSize > 1 || remoteEntry != null
|
||||||
}
|
}
|
||||||
CtaButtonRow(
|
item {
|
||||||
leftButton = if (shouldShowMoreOptionsButton) {
|
CtaButtonRow(
|
||||||
{
|
leftButton = if (shouldShowMoreOptionsButton) {
|
||||||
ActionButton(
|
{
|
||||||
stringResource(R.string.string_more_options),
|
ActionButton(
|
||||||
onMoreOptionsSelected
|
stringResource(R.string.string_more_options),
|
||||||
|
onMoreOptionsSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
rightButton = {
|
||||||
|
ConfirmButton(
|
||||||
|
stringResource(R.string.string_continue),
|
||||||
|
onClick = onConfirm
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
} else null,
|
|
||||||
rightButton = {
|
|
||||||
ConfirmButton(
|
|
||||||
stringResource(R.string.string_continue),
|
|
||||||
onClick = onConfirm
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if (createOptionInfo.footerDescription != null) {
|
|
||||||
Divider(
|
|
||||||
thickness = 1.dp,
|
|
||||||
color = MaterialTheme.colorScheme.outlineVariant,
|
|
||||||
modifier = Modifier.padding(vertical = 16.dp)
|
|
||||||
)
|
)
|
||||||
BodySmallText(text = createOptionInfo.footerDescription)
|
}
|
||||||
|
if (createOptionInfo.footerDescription != null) {
|
||||||
|
item {
|
||||||
|
Divider(
|
||||||
|
thickness = 1.dp,
|
||||||
|
color = MaterialTheme.colorScheme.outlineVariant,
|
||||||
|
modifier = Modifier.padding(vertical = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item { BodySmallText(text = createOptionInfo.footerDescription) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,29 +511,30 @@ fun ExternalOnlySelectionCard(
|
|||||||
onConfirm: () -> Unit,
|
onConfirm: () -> Unit,
|
||||||
) {
|
) {
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices))
|
item { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) }
|
||||||
Divider(thickness = 16.dp, color = Color.Transparent)
|
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
||||||
HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title))
|
item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) }
|
||||||
Divider(
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
thickness = 24.dp,
|
item {
|
||||||
color = Color.Transparent
|
CredentialContainerCard {
|
||||||
)
|
PrimaryCreateOptionRow(
|
||||||
CredentialContainerCard {
|
requestDisplayInfo = requestDisplayInfo,
|
||||||
PrimaryCreateOptionRow(
|
entryInfo = activeRemoteEntry,
|
||||||
requestDisplayInfo = requestDisplayInfo,
|
onOptionSelected = onOptionSelected
|
||||||
entryInfo = activeRemoteEntry,
|
)
|
||||||
onOptionSelected = onOptionSelected
|
}
|
||||||
|
}
|
||||||
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
|
item {
|
||||||
|
CtaButtonRow(
|
||||||
|
rightButton = {
|
||||||
|
ConfirmButton(
|
||||||
|
stringResource(R.string.string_continue),
|
||||||
|
onClick = onConfirm
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
|
||||||
CtaButtonRow(
|
|
||||||
rightButton = {
|
|
||||||
ConfirmButton(
|
|
||||||
stringResource(R.string.string_continue),
|
|
||||||
onClick = onConfirm
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,40 +542,38 @@ fun ExternalOnlySelectionCard(
|
|||||||
fun MoreAboutPasskeysIntroCard(
|
fun MoreAboutPasskeysIntroCard(
|
||||||
onBackPasskeyIntroButtonSelected: () -> Unit,
|
onBackPasskeyIntroButtonSelected: () -> Unit,
|
||||||
) {
|
) {
|
||||||
SheetContainerCard(topAppBar = {
|
SheetContainerCard(
|
||||||
MoreOptionTopAppBar(
|
topAppBar = {
|
||||||
text = stringResource(R.string.more_about_passkeys_title),
|
MoreOptionTopAppBar(
|
||||||
onNavigationIconClicked = onBackPasskeyIntroButtonSelected,
|
text = stringResource(R.string.more_about_passkeys_title),
|
||||||
)
|
onNavigationIconClicked = onBackPasskeyIntroButtonSelected,
|
||||||
}) {
|
)
|
||||||
LazyColumn(
|
},
|
||||||
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
|
contentVerticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
) {
|
||||||
) {
|
item {
|
||||||
item {
|
MoreAboutPasskeySectionHeader(
|
||||||
MoreAboutPasskeySectionHeader(
|
text = stringResource(R.string.passwordless_technology_title)
|
||||||
text = stringResource(R.string.passwordless_technology_title)
|
)
|
||||||
)
|
BodyMediumText(text = stringResource(R.string.passwordless_technology_detail))
|
||||||
BodyMediumText(text = stringResource(R.string.passwordless_technology_detail))
|
}
|
||||||
}
|
item {
|
||||||
item {
|
MoreAboutPasskeySectionHeader(
|
||||||
MoreAboutPasskeySectionHeader(
|
text = stringResource(R.string.public_key_cryptography_title)
|
||||||
text = stringResource(R.string.public_key_cryptography_title)
|
)
|
||||||
)
|
BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail))
|
||||||
BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail))
|
}
|
||||||
}
|
item {
|
||||||
item {
|
MoreAboutPasskeySectionHeader(
|
||||||
MoreAboutPasskeySectionHeader(
|
text = stringResource(R.string.improved_account_security_title)
|
||||||
text = stringResource(R.string.improved_account_security_title)
|
)
|
||||||
)
|
BodyMediumText(text = stringResource(R.string.improved_account_security_detail))
|
||||||
BodyMediumText(text = stringResource(R.string.improved_account_security_detail))
|
}
|
||||||
}
|
item {
|
||||||
item {
|
MoreAboutPasskeySectionHeader(
|
||||||
MoreAboutPasskeySectionHeader(
|
text = stringResource(R.string.seamless_transition_title)
|
||||||
text = stringResource(R.string.seamless_transition_title)
|
)
|
||||||
)
|
BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
|
||||||
BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@@ -141,70 +140,72 @@ fun PrimarySelectionCard(
|
|||||||
providerDisplayInfo.sortedUserNameToCredentialEntryList
|
providerDisplayInfo.sortedUserNameToCredentialEntryList
|
||||||
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
|
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
HeadlineText(
|
item {
|
||||||
text = stringResource(
|
HeadlineText(
|
||||||
if (sortedUserNameToCredentialEntryList
|
text = stringResource(
|
||||||
.size == 1 && authenticationEntryList.isEmpty()
|
if (sortedUserNameToCredentialEntryList
|
||||||
) {
|
.size == 1 && authenticationEntryList.isEmpty()
|
||||||
if (sortedUserNameToCredentialEntryList.first()
|
) {
|
||||||
.sortedCredentialEntryList.first().credentialType
|
if (sortedUserNameToCredentialEntryList.first()
|
||||||
== CredentialType.PASSKEY
|
.sortedCredentialEntryList.first().credentialType
|
||||||
) R.string.get_dialog_title_use_passkey_for
|
== CredentialType.PASSKEY
|
||||||
else R.string.get_dialog_title_use_sign_in_for
|
) R.string.get_dialog_title_use_passkey_for
|
||||||
} else if (
|
else R.string.get_dialog_title_use_sign_in_for
|
||||||
sortedUserNameToCredentialEntryList
|
} else if (
|
||||||
.isEmpty() && authenticationEntryList.size == 1
|
sortedUserNameToCredentialEntryList
|
||||||
) {
|
.isEmpty() && authenticationEntryList.size == 1
|
||||||
R.string.get_dialog_title_use_sign_in_for
|
) {
|
||||||
} else R.string.get_dialog_title_choose_sign_in_for,
|
R.string.get_dialog_title_use_sign_in_for
|
||||||
requestDisplayInfo.appName
|
} else R.string.get_dialog_title_choose_sign_in_for,
|
||||||
),
|
requestDisplayInfo.appName
|
||||||
)
|
),
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
)
|
||||||
CredentialContainerCard {
|
}
|
||||||
val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
val authenticationEntrySize = authenticationEntryList.size
|
item {
|
||||||
LazyColumn(
|
CredentialContainerCard {
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||||
) {
|
val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size
|
||||||
// Show max 4 entries in this primary page
|
val authenticationEntrySize = authenticationEntryList.size
|
||||||
if (usernameForCredentialSize + authenticationEntrySize <= 4) {
|
// Show max 4 entries in this primary page
|
||||||
items(sortedUserNameToCredentialEntryList) {
|
if (usernameForCredentialSize + authenticationEntrySize <= 4) {
|
||||||
CredentialEntryRow(
|
sortedUserNameToCredentialEntryList.forEach {
|
||||||
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
CredentialEntryRow(
|
||||||
onEntrySelected = onEntrySelected,
|
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
||||||
)
|
onEntrySelected = onEntrySelected,
|
||||||
}
|
)
|
||||||
items(authenticationEntryList) {
|
}
|
||||||
AuthenticationEntryRow(
|
authenticationEntryList.forEach {
|
||||||
authenticationEntryInfo = it,
|
AuthenticationEntryRow(
|
||||||
onEntrySelected = onEntrySelected,
|
authenticationEntryInfo = it,
|
||||||
)
|
onEntrySelected = onEntrySelected,
|
||||||
}
|
)
|
||||||
} else if (usernameForCredentialSize < 4) {
|
}
|
||||||
items(sortedUserNameToCredentialEntryList) {
|
} else if (usernameForCredentialSize < 4) {
|
||||||
CredentialEntryRow(
|
sortedUserNameToCredentialEntryList.forEach {
|
||||||
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
CredentialEntryRow(
|
||||||
onEntrySelected = onEntrySelected,
|
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
||||||
)
|
onEntrySelected = onEntrySelected,
|
||||||
}
|
)
|
||||||
items(authenticationEntryList.take(4 - usernameForCredentialSize)) {
|
}
|
||||||
AuthenticationEntryRow(
|
authenticationEntryList.take(4 - usernameForCredentialSize).forEach {
|
||||||
authenticationEntryInfo = it,
|
AuthenticationEntryRow(
|
||||||
onEntrySelected = onEntrySelected,
|
authenticationEntryInfo = it,
|
||||||
)
|
onEntrySelected = onEntrySelected,
|
||||||
}
|
)
|
||||||
} else {
|
}
|
||||||
items(sortedUserNameToCredentialEntryList.take(4)) {
|
} else {
|
||||||
CredentialEntryRow(
|
sortedUserNameToCredentialEntryList.take(4).forEach {
|
||||||
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
CredentialEntryRow(
|
||||||
onEntrySelected = onEntrySelected,
|
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
||||||
)
|
onEntrySelected = onEntrySelected,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Divider(thickness = 24.dp, color = Color.Transparent)
|
item { Divider(thickness = 24.dp, color = Color.Transparent) }
|
||||||
var totalEntriesCount = sortedUserNameToCredentialEntryList
|
var totalEntriesCount = sortedUserNameToCredentialEntryList
|
||||||
.flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList
|
.flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList
|
||||||
.size + providerInfoList.flatMap { it.actionEntryList }.size
|
.size + providerInfoList.flatMap { it.actionEntryList }.size
|
||||||
@@ -212,24 +213,26 @@ fun PrimarySelectionCard(
|
|||||||
// Row horizontalArrangement differs on only one actionButton(should place on most
|
// Row horizontalArrangement differs on only one actionButton(should place on most
|
||||||
// left)/only one confirmButton(should place on most right)/two buttons exist the same
|
// left)/only one confirmButton(should place on most right)/two buttons exist the same
|
||||||
// time(should be one on the left, one on the right)
|
// time(should be one on the left, one on the right)
|
||||||
CtaButtonRow(
|
item {
|
||||||
leftButton = if (totalEntriesCount > 1) {
|
CtaButtonRow(
|
||||||
{
|
leftButton = if (totalEntriesCount > 1) {
|
||||||
ActionButton(
|
{
|
||||||
stringResource(R.string.get_dialog_use_saved_passkey_for),
|
ActionButton(
|
||||||
onMoreOptionSelected
|
stringResource(R.string.get_dialog_use_saved_passkey_for),
|
||||||
)
|
onMoreOptionSelected
|
||||||
}
|
)
|
||||||
} else null,
|
}
|
||||||
rightButton = if (activeEntry != null) { // Only one sign-in options exist
|
} else null,
|
||||||
{
|
rightButton = if (activeEntry != null) { // Only one sign-in options exist
|
||||||
ConfirmButton(
|
{
|
||||||
stringResource(R.string.string_continue),
|
ConfirmButton(
|
||||||
onClick = onConfirm
|
stringResource(R.string.string_continue),
|
||||||
)
|
onClick = onConfirm
|
||||||
}
|
)
|
||||||
} else null,
|
}
|
||||||
)
|
} else null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,48 +255,46 @@ fun AllSignInOptionCard(
|
|||||||
onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked,
|
onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked,
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
LazyColumn {
|
// For username
|
||||||
// For username
|
items(sortedUserNameToCredentialEntryList) { item ->
|
||||||
items(sortedUserNameToCredentialEntryList) { item ->
|
PerUserNameCredentials(
|
||||||
PerUserNameCredentials(
|
perUserNameCredentialEntryList = item,
|
||||||
perUserNameCredentialEntryList = item,
|
onEntrySelected = onEntrySelected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Locked password manager
|
||||||
|
if (authenticationEntryList.isNotEmpty()) {
|
||||||
|
item {
|
||||||
|
LockedCredentials(
|
||||||
|
authenticationEntryList = authenticationEntryList,
|
||||||
onEntrySelected = onEntrySelected,
|
onEntrySelected = onEntrySelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Locked password manager
|
}
|
||||||
if (authenticationEntryList.isNotEmpty()) {
|
// From another device
|
||||||
item {
|
val remoteEntry = providerDisplayInfo.remoteEntry
|
||||||
LockedCredentials(
|
if (remoteEntry != null) {
|
||||||
authenticationEntryList = authenticationEntryList,
|
|
||||||
onEntrySelected = onEntrySelected,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// From another device
|
|
||||||
val remoteEntry = providerDisplayInfo.remoteEntry
|
|
||||||
if (remoteEntry != null) {
|
|
||||||
item {
|
|
||||||
RemoteEntryCard(
|
|
||||||
remoteEntry = remoteEntry,
|
|
||||||
onEntrySelected = onEntrySelected,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item {
|
item {
|
||||||
Divider(
|
RemoteEntryCard(
|
||||||
thickness = 1.dp,
|
remoteEntry = remoteEntry,
|
||||||
color = Color.LightGray,
|
onEntrySelected = onEntrySelected,
|
||||||
modifier = Modifier.padding(top = 16.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// Manage sign-ins (action chips)
|
|
||||||
item {
|
|
||||||
ActionChips(
|
|
||||||
providerInfoList = providerInfoList,
|
|
||||||
onEntrySelected = onEntrySelected
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
Divider(
|
||||||
|
thickness = 1.dp,
|
||||||
|
color = Color.LightGray,
|
||||||
|
modifier = Modifier.padding(top = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Manage sign-ins (action chips)
|
||||||
|
item {
|
||||||
|
ActionChips(
|
||||||
|
providerInfoList = providerInfoList,
|
||||||
|
onEntrySelected = onEntrySelected
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user