Loading packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt +7 −3 Original line number Diff line number Diff line Loading @@ -16,11 +16,13 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.padding 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.CardDefaults import androidx.compose.material3.MaterialTheme Loading @@ -40,7 +42,8 @@ import androidx.compose.ui.unit.dp fun SheetContainerCard( topAppBar: (@Composable () -> Unit)? = null, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit, contentVerticalArrangement: Arrangement.Vertical = Arrangement.Top, content: LazyListScope.() -> Unit, ) { Card( modifier = modifier.fillMaxWidth().wrapContentHeight(), Loading @@ -54,7 +57,7 @@ fun SheetContainerCard( if (topAppBar != null) { topAppBar() } Column( LazyColumn( modifier = Modifier.padding( start = 24.dp, end = 24.dp, Loading @@ -63,6 +66,7 @@ fun SheetContainerCard( ).fillMaxWidth().wrapContentHeight(), horizontalAlignment = Alignment.CenterHorizontally, content = content, verticalArrangement = contentVerticalArrangement, ) } } Loading packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.ktdeleted 100644 → 0 +0 −31 Original line number Diff line number Diff line /* * 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, ) } No newline at end of file packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +249 −224 Original line number Diff line number Diff line Loading @@ -8,11 +8,12 @@ import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.Image 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.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme Loading @@ -23,7 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap Loading @@ -43,7 +43,6 @@ import com.android.credentialmanager.common.ui.ConfirmButton import com.android.credentialmanager.common.ui.CredentialContainerCard import com.android.credentialmanager.common.ui.CtaButtonRow 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.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.ModalBottomSheet Loading Loading @@ -157,6 +156,7 @@ fun PasskeyIntroCard( onLearnMore: () -> Unit, ) { SheetContainerCard { item { val onboardingImageResource = remember { mutableStateOf(R.drawable.ic_passkeys_onboarding) } Loading @@ -165,31 +165,43 @@ fun PasskeyIntroCard( } 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 .align(alignment = Alignment.CenterHorizontally).size(316.dp, 168.dp) modifier = Modifier.size(316.dp, 168.dp) ) Divider(thickness = 16.dp, color = Color.Transparent) HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) Divider(thickness = 16.dp, color = Color.Transparent) } } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { PasskeyBenefitRow( leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password), text = stringResource(R.string.passkey_creation_intro_body_password), ) Divider(thickness = 16.dp, color = Color.Transparent) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { PasskeyBenefitRow( leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint), text = stringResource(R.string.passkey_creation_intro_body_fingerprint), ) Divider(thickness = 16.dp, color = Color.Transparent) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { 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) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CtaButtonRow( leftButton = { ActionButton( Loading @@ -206,6 +218,7 @@ fun PasskeyIntroCard( ) } } } @Composable fun ProviderSelectionCard( Loading @@ -218,8 +231,9 @@ fun ProviderSelectionCard( onMoreOptionsSelected: () -> Unit, ) { SheetContainerCard { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) Divider(thickness = 16.dp, color = Color.Transparent) item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText( text = stringResource( R.string.choose_provider_title, Loading @@ -232,16 +246,17 @@ fun ProviderSelectionCard( } ) ) Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } BodyMediumText(text = stringResource(R.string.choose_provider_body)) Divider(thickness = 16.dp, color = Color.Transparent) item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { CredentialContainerCard { LazyColumn( Column( verticalArrangement = Arrangement.spacedBy(2.dp) ) { sortedCreateOptionsPairs.forEach { entry -> item { MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, Loading @@ -256,8 +271,6 @@ fun ProviderSelectionCard( } ) } } item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, Loading @@ -266,7 +279,8 @@ fun ProviderSelectionCard( } } if (hasRemoteEntry) { Divider(thickness = 24.dp, color = Color.Transparent) item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CtaButtonRow( leftButton = { ActionButton( Loading @@ -278,6 +292,7 @@ fun ProviderSelectionCard( } } } } @Composable fun MoreOptionsSelectionCard( Loading Loading @@ -310,14 +325,14 @@ fun MoreOptionsSelectionCard( else onBackCreationSelectionButtonSelected, ) }) { Divider(thickness = 16.dp, color = Color.Transparent) item { Divider(thickness = 16.dp, color = Color.Transparent) } item { CredentialContainerCard { EntryListColumn { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { // Only in the flows with default provider(not first time use) we can show the // createOptions here, or they will be shown on ProviderSelectionCard if (hasDefaultProvider) { sortedCreateOptionsPairs.forEach { entry -> item { MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, Loading @@ -329,25 +344,21 @@ fun MoreOptionsSelectionCard( entry.first ) ) }) } ) } item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, ) } } enabledProviderList.forEach { if (it.remoteEntry != null) { item { RemoteEntryRow( remoteInfo = it.remoteEntry!!, onRemoteEntrySelected = onRemoteEntrySelected, ) } return@forEach } } Loading @@ -355,6 +366,7 @@ fun MoreOptionsSelectionCard( } } } } @Composable fun MoreOptionsRowIntroCard( Loading @@ -363,16 +375,19 @@ fun MoreOptionsRowIntroCard( onUseOnceSelected: () -> Unit, ) { SheetContainerCard { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) Divider(thickness = 24.dp, color = Color.Transparent) item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { HeadlineText( text = stringResource( R.string.use_provider_for_all_title, providerInfo.displayName ) ) Divider(thickness = 24.dp, color = Color.Transparent) BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) } item { CtaButtonRow( leftButton = { ActionButton( Loading @@ -389,6 +404,7 @@ fun MoreOptionsRowIntroCard( ) } } } @Composable fun CreationSelectionCard( Loading @@ -402,13 +418,16 @@ fun CreationSelectionCard( hasDefaultProvider: Boolean, ) { SheetContainerCard { item { HeadlineIcon( 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) } 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( Loading @@ -425,7 +444,9 @@ fun CreationSelectionCard( ) } ) Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { PrimaryCreateOptionRow( requestDisplayInfo = requestDisplayInfo, Loading @@ -433,7 +454,8 @@ fun CreationSelectionCard( onOptionSelected = onOptionSelected ) } Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } var createOptionsSize = 0 var remoteEntry: RemoteInfo? = null enabledProviderList.forEach { enabledProvider -> Loading @@ -450,6 +472,7 @@ fun CreationSelectionCard( } else { createOptionsSize > 1 || remoteEntry != null } item { CtaButtonRow( leftButton = if (shouldShowMoreOptionsButton) { { Loading @@ -466,13 +489,16 @@ fun CreationSelectionCard( ) }, ) } if (createOptionInfo.footerDescription != null) { item { Divider( thickness = 1.dp, color = MaterialTheme.colorScheme.outlineVariant, modifier = Modifier.padding(vertical = 16.dp) ) BodySmallText(text = createOptionInfo.footerDescription) } item { BodySmallText(text = createOptionInfo.footerDescription) } } } } Loading @@ -485,13 +511,11 @@ fun ExternalOnlySelectionCard( onConfirm: () -> Unit, ) { SheetContainerCard { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) Divider(thickness = 16.dp, color = Color.Transparent) HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) Divider( thickness = 24.dp, color = Color.Transparent ) item { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { PrimaryCreateOptionRow( requestDisplayInfo = requestDisplayInfo, Loading @@ -499,7 +523,9 @@ fun ExternalOnlySelectionCard( onOptionSelected = onOptionSelected ) } Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CtaButtonRow( rightButton = { ConfirmButton( Loading @@ -510,20 +536,20 @@ fun ExternalOnlySelectionCard( ) } } } @Composable fun MoreAboutPasskeysIntroCard( onBackPasskeyIntroButtonSelected: () -> Unit, ) { SheetContainerCard(topAppBar = { SheetContainerCard( topAppBar = { MoreOptionTopAppBar( text = stringResource(R.string.more_about_passkeys_title), onNavigationIconClicked = onBackPasskeyIntroButtonSelected, ) }) { LazyColumn( modifier = Modifier.fillMaxWidth().wrapContentHeight(), verticalArrangement = Arrangement.spacedBy(8.dp) }, contentVerticalArrangement = Arrangement.spacedBy(8.dp) ) { item { MoreAboutPasskeySectionHeader( Loading Loading @@ -551,7 +577,6 @@ fun MoreAboutPasskeysIntroCard( } } } } @Composable fun PrimaryCreateOptionRow( Loading packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +115 −114 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Divider import androidx.compose.material3.TextButton Loading Loading @@ -141,6 +140,7 @@ fun PrimarySelectionCard( providerDisplayInfo.sortedUserNameToCredentialEntryList val authenticationEntryList = providerDisplayInfo.authenticationEntryList SheetContainerCard { item { HeadlineText( text = stringResource( if (sortedUserNameToCredentialEntryList Loading @@ -160,42 +160,42 @@ fun PrimarySelectionCard( requestDisplayInfo.appName ), ) Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size val authenticationEntrySize = authenticationEntryList.size LazyColumn( verticalArrangement = Arrangement.spacedBy(2.dp) ) { // Show max 4 entries in this primary page if (usernameForCredentialSize + authenticationEntrySize <= 4) { items(sortedUserNameToCredentialEntryList) { sortedUserNameToCredentialEntryList.forEach { CredentialEntryRow( credentialEntryInfo = it.sortedCredentialEntryList.first(), onEntrySelected = onEntrySelected, ) } items(authenticationEntryList) { authenticationEntryList.forEach { AuthenticationEntryRow( authenticationEntryInfo = it, onEntrySelected = onEntrySelected, ) } } else if (usernameForCredentialSize < 4) { items(sortedUserNameToCredentialEntryList) { sortedUserNameToCredentialEntryList.forEach { CredentialEntryRow( credentialEntryInfo = it.sortedCredentialEntryList.first(), onEntrySelected = onEntrySelected, ) } items(authenticationEntryList.take(4 - usernameForCredentialSize)) { authenticationEntryList.take(4 - usernameForCredentialSize).forEach { AuthenticationEntryRow( authenticationEntryInfo = it, onEntrySelected = onEntrySelected, ) } } else { items(sortedUserNameToCredentialEntryList.take(4)) { sortedUserNameToCredentialEntryList.take(4).forEach { CredentialEntryRow( credentialEntryInfo = it.sortedCredentialEntryList.first(), onEntrySelected = onEntrySelected, Loading @@ -204,7 +204,8 @@ fun PrimarySelectionCard( } } } Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } var totalEntriesCount = sortedUserNameToCredentialEntryList .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList .size + providerInfoList.flatMap { it.actionEntryList }.size Loading @@ -212,6 +213,7 @@ fun PrimarySelectionCard( // 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 // time(should be one on the left, one on the right) item { CtaButtonRow( leftButton = if (totalEntriesCount > 1) { { Loading @@ -232,6 +234,7 @@ fun PrimarySelectionCard( ) } } } /** Draws the secondary credential selection page, where all sign-in options are listed. */ @Composable Loading @@ -252,7 +255,6 @@ fun AllSignInOptionCard( onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked, ) }) { LazyColumn { // For username items(sortedUserNameToCredentialEntryList) { item -> PerUserNameCredentials( Loading Loading @@ -295,7 +297,6 @@ fun AllSignInOptionCard( } } } } // TODO: create separate rows for primary and secondary pages. // TODO: reuse rows and columns across types. Loading Loading
packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt +7 −3 Original line number Diff line number Diff line Loading @@ -16,11 +16,13 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.padding 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.CardDefaults import androidx.compose.material3.MaterialTheme Loading @@ -40,7 +42,8 @@ import androidx.compose.ui.unit.dp fun SheetContainerCard( topAppBar: (@Composable () -> Unit)? = null, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit, contentVerticalArrangement: Arrangement.Vertical = Arrangement.Top, content: LazyListScope.() -> Unit, ) { Card( modifier = modifier.fillMaxWidth().wrapContentHeight(), Loading @@ -54,7 +57,7 @@ fun SheetContainerCard( if (topAppBar != null) { topAppBar() } Column( LazyColumn( modifier = Modifier.padding( start = 24.dp, end = 24.dp, Loading @@ -63,6 +66,7 @@ fun SheetContainerCard( ).fillMaxWidth().wrapContentHeight(), horizontalAlignment = Alignment.CenterHorizontally, content = content, verticalArrangement = contentVerticalArrangement, ) } } Loading
packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.ktdeleted 100644 → 0 +0 −31 Original line number Diff line number Diff line /* * 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, ) } No newline at end of file
packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +249 −224 Original line number Diff line number Diff line Loading @@ -8,11 +8,12 @@ import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.Image 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.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme Loading @@ -23,7 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap Loading @@ -43,7 +43,6 @@ import com.android.credentialmanager.common.ui.ConfirmButton import com.android.credentialmanager.common.ui.CredentialContainerCard import com.android.credentialmanager.common.ui.CtaButtonRow 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.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.ModalBottomSheet Loading Loading @@ -157,6 +156,7 @@ fun PasskeyIntroCard( onLearnMore: () -> Unit, ) { SheetContainerCard { item { val onboardingImageResource = remember { mutableStateOf(R.drawable.ic_passkeys_onboarding) } Loading @@ -165,31 +165,43 @@ fun PasskeyIntroCard( } 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 .align(alignment = Alignment.CenterHorizontally).size(316.dp, 168.dp) modifier = Modifier.size(316.dp, 168.dp) ) Divider(thickness = 16.dp, color = Color.Transparent) HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) Divider(thickness = 16.dp, color = Color.Transparent) } } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { PasskeyBenefitRow( leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password), text = stringResource(R.string.passkey_creation_intro_body_password), ) Divider(thickness = 16.dp, color = Color.Transparent) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { PasskeyBenefitRow( leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint), text = stringResource(R.string.passkey_creation_intro_body_fingerprint), ) Divider(thickness = 16.dp, color = Color.Transparent) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { 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) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CtaButtonRow( leftButton = { ActionButton( Loading @@ -206,6 +218,7 @@ fun PasskeyIntroCard( ) } } } @Composable fun ProviderSelectionCard( Loading @@ -218,8 +231,9 @@ fun ProviderSelectionCard( onMoreOptionsSelected: () -> Unit, ) { SheetContainerCard { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) Divider(thickness = 16.dp, color = Color.Transparent) item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText( text = stringResource( R.string.choose_provider_title, Loading @@ -232,16 +246,17 @@ fun ProviderSelectionCard( } ) ) Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } BodyMediumText(text = stringResource(R.string.choose_provider_body)) Divider(thickness = 16.dp, color = Color.Transparent) item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { CredentialContainerCard { LazyColumn( Column( verticalArrangement = Arrangement.spacedBy(2.dp) ) { sortedCreateOptionsPairs.forEach { entry -> item { MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, Loading @@ -256,8 +271,6 @@ fun ProviderSelectionCard( } ) } } item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, Loading @@ -266,7 +279,8 @@ fun ProviderSelectionCard( } } if (hasRemoteEntry) { Divider(thickness = 24.dp, color = Color.Transparent) item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CtaButtonRow( leftButton = { ActionButton( Loading @@ -278,6 +292,7 @@ fun ProviderSelectionCard( } } } } @Composable fun MoreOptionsSelectionCard( Loading Loading @@ -310,14 +325,14 @@ fun MoreOptionsSelectionCard( else onBackCreationSelectionButtonSelected, ) }) { Divider(thickness = 16.dp, color = Color.Transparent) item { Divider(thickness = 16.dp, color = Color.Transparent) } item { CredentialContainerCard { EntryListColumn { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { // Only in the flows with default provider(not first time use) we can show the // createOptions here, or they will be shown on ProviderSelectionCard if (hasDefaultProvider) { sortedCreateOptionsPairs.forEach { entry -> item { MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, Loading @@ -329,25 +344,21 @@ fun MoreOptionsSelectionCard( entry.first ) ) }) } ) } item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, ) } } enabledProviderList.forEach { if (it.remoteEntry != null) { item { RemoteEntryRow( remoteInfo = it.remoteEntry!!, onRemoteEntrySelected = onRemoteEntrySelected, ) } return@forEach } } Loading @@ -355,6 +366,7 @@ fun MoreOptionsSelectionCard( } } } } @Composable fun MoreOptionsRowIntroCard( Loading @@ -363,16 +375,19 @@ fun MoreOptionsRowIntroCard( onUseOnceSelected: () -> Unit, ) { SheetContainerCard { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) Divider(thickness = 24.dp, color = Color.Transparent) item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { HeadlineText( text = stringResource( R.string.use_provider_for_all_title, providerInfo.displayName ) ) Divider(thickness = 24.dp, color = Color.Transparent) BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) } item { CtaButtonRow( leftButton = { ActionButton( Loading @@ -389,6 +404,7 @@ fun MoreOptionsRowIntroCard( ) } } } @Composable fun CreationSelectionCard( Loading @@ -402,13 +418,16 @@ fun CreationSelectionCard( hasDefaultProvider: Boolean, ) { SheetContainerCard { item { HeadlineIcon( 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) } 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( Loading @@ -425,7 +444,9 @@ fun CreationSelectionCard( ) } ) Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { PrimaryCreateOptionRow( requestDisplayInfo = requestDisplayInfo, Loading @@ -433,7 +454,8 @@ fun CreationSelectionCard( onOptionSelected = onOptionSelected ) } Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } var createOptionsSize = 0 var remoteEntry: RemoteInfo? = null enabledProviderList.forEach { enabledProvider -> Loading @@ -450,6 +472,7 @@ fun CreationSelectionCard( } else { createOptionsSize > 1 || remoteEntry != null } item { CtaButtonRow( leftButton = if (shouldShowMoreOptionsButton) { { Loading @@ -466,13 +489,16 @@ fun CreationSelectionCard( ) }, ) } if (createOptionInfo.footerDescription != null) { item { Divider( thickness = 1.dp, color = MaterialTheme.colorScheme.outlineVariant, modifier = Modifier.padding(vertical = 16.dp) ) BodySmallText(text = createOptionInfo.footerDescription) } item { BodySmallText(text = createOptionInfo.footerDescription) } } } } Loading @@ -485,13 +511,11 @@ fun ExternalOnlySelectionCard( onConfirm: () -> Unit, ) { SheetContainerCard { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) Divider(thickness = 16.dp, color = Color.Transparent) HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) Divider( thickness = 24.dp, color = Color.Transparent ) item { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) } item { Divider(thickness = 16.dp, color = Color.Transparent) } item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { PrimaryCreateOptionRow( requestDisplayInfo = requestDisplayInfo, Loading @@ -499,7 +523,9 @@ fun ExternalOnlySelectionCard( onOptionSelected = onOptionSelected ) } Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CtaButtonRow( rightButton = { ConfirmButton( Loading @@ -510,20 +536,20 @@ fun ExternalOnlySelectionCard( ) } } } @Composable fun MoreAboutPasskeysIntroCard( onBackPasskeyIntroButtonSelected: () -> Unit, ) { SheetContainerCard(topAppBar = { SheetContainerCard( topAppBar = { MoreOptionTopAppBar( text = stringResource(R.string.more_about_passkeys_title), onNavigationIconClicked = onBackPasskeyIntroButtonSelected, ) }) { LazyColumn( modifier = Modifier.fillMaxWidth().wrapContentHeight(), verticalArrangement = Arrangement.spacedBy(8.dp) }, contentVerticalArrangement = Arrangement.spacedBy(8.dp) ) { item { MoreAboutPasskeySectionHeader( Loading Loading @@ -551,7 +577,6 @@ fun MoreAboutPasskeysIntroCard( } } } } @Composable fun PrimaryCreateOptionRow( Loading
packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +115 −114 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Divider import androidx.compose.material3.TextButton Loading Loading @@ -141,6 +140,7 @@ fun PrimarySelectionCard( providerDisplayInfo.sortedUserNameToCredentialEntryList val authenticationEntryList = providerDisplayInfo.authenticationEntryList SheetContainerCard { item { HeadlineText( text = stringResource( if (sortedUserNameToCredentialEntryList Loading @@ -160,42 +160,42 @@ fun PrimarySelectionCard( requestDisplayInfo.appName ), ) Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } item { CredentialContainerCard { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size val authenticationEntrySize = authenticationEntryList.size LazyColumn( verticalArrangement = Arrangement.spacedBy(2.dp) ) { // Show max 4 entries in this primary page if (usernameForCredentialSize + authenticationEntrySize <= 4) { items(sortedUserNameToCredentialEntryList) { sortedUserNameToCredentialEntryList.forEach { CredentialEntryRow( credentialEntryInfo = it.sortedCredentialEntryList.first(), onEntrySelected = onEntrySelected, ) } items(authenticationEntryList) { authenticationEntryList.forEach { AuthenticationEntryRow( authenticationEntryInfo = it, onEntrySelected = onEntrySelected, ) } } else if (usernameForCredentialSize < 4) { items(sortedUserNameToCredentialEntryList) { sortedUserNameToCredentialEntryList.forEach { CredentialEntryRow( credentialEntryInfo = it.sortedCredentialEntryList.first(), onEntrySelected = onEntrySelected, ) } items(authenticationEntryList.take(4 - usernameForCredentialSize)) { authenticationEntryList.take(4 - usernameForCredentialSize).forEach { AuthenticationEntryRow( authenticationEntryInfo = it, onEntrySelected = onEntrySelected, ) } } else { items(sortedUserNameToCredentialEntryList.take(4)) { sortedUserNameToCredentialEntryList.take(4).forEach { CredentialEntryRow( credentialEntryInfo = it.sortedCredentialEntryList.first(), onEntrySelected = onEntrySelected, Loading @@ -204,7 +204,8 @@ fun PrimarySelectionCard( } } } Divider(thickness = 24.dp, color = Color.Transparent) } item { Divider(thickness = 24.dp, color = Color.Transparent) } var totalEntriesCount = sortedUserNameToCredentialEntryList .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList .size + providerInfoList.flatMap { it.actionEntryList }.size Loading @@ -212,6 +213,7 @@ fun PrimarySelectionCard( // 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 // time(should be one on the left, one on the right) item { CtaButtonRow( leftButton = if (totalEntriesCount > 1) { { Loading @@ -232,6 +234,7 @@ fun PrimarySelectionCard( ) } } } /** Draws the secondary credential selection page, where all sign-in options are listed. */ @Composable Loading @@ -252,7 +255,6 @@ fun AllSignInOptionCard( onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked, ) }) { LazyColumn { // For username items(sortedUserNameToCredentialEntryList) { item -> PerUserNameCredentials( Loading Loading @@ -295,7 +297,6 @@ fun AllSignInOptionCard( } } } } // TODO: create separate rows for primary and secondary pages. // TODO: reuse rows and columns across types. Loading