From 76acc81fea5b6df2b81520d4f0f9521dd2f85957 Mon Sep 17 00:00:00 2001 From: Harsh Lal Date: Tue, 21 Mar 2023 17:09:02 +0000 Subject: [PATCH] Add fallback screen logic to CredentialSelectorActivity Bug: 274129098 Test: local-deployment Change-Id: Ic198f1d7d99347c4311250459431e3168008db67 --- .../CredentialManager/res/values/strings.xml | 2 + .../getflow/GetGenericCredentialComponents.kt | 124 +++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 1d069b69f061c..1e0c2cdb6d99b 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -103,6 +103,8 @@ Use your saved sign-in for %1$s? Choose a saved sign-in for %1$s + + Choose an option for %1$s? Sign in another way diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt index 8b95b5e46aa1f..ba48f2b0fc1b8 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt @@ -19,8 +19,31 @@ package com.android.credentialmanager.getflow import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult import androidx.activity.result.IntentSenderRequest +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Divider import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.core.graphics.drawable.toBitmap +import com.android.compose.rememberSystemUiController import com.android.credentialmanager.CredentialSelectorViewModel +import com.android.credentialmanager.R +import com.android.credentialmanager.common.BaseEntry +import com.android.credentialmanager.common.ProviderActivityState +import com.android.credentialmanager.common.ui.CredentialContainerCard +import com.android.credentialmanager.common.ui.HeadlineIcon +import com.android.credentialmanager.common.ui.HeadlineText +import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant +import com.android.credentialmanager.common.ui.ModalBottomSheet +import com.android.credentialmanager.common.ui.SheetContainerCard +import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor +import com.android.credentialmanager.logging.GetCredentialEvent +import com.android.internal.logging.UiEventLogger + @Composable fun GetGenericCredentialScreen( @@ -28,5 +51,102 @@ fun GetGenericCredentialScreen( getCredentialUiState: GetCredentialUiState, providerActivityLauncher: ManagedActivityResultLauncher ) { - // TODO(b/274129098): Implement Screen for mDocs -} \ No newline at end of file + val sysUiController = rememberSystemUiController() + setBottomSheetSystemBarsColor(sysUiController) + ModalBottomSheet( + sheetContent = { + when (viewModel.uiState.providerActivityState) { + ProviderActivityState.NOT_APPLICABLE -> { + PrimarySelectionCardGeneric( + requestDisplayInfo = getCredentialUiState.requestDisplayInfo, + providerDisplayInfo = getCredentialUiState.providerDisplayInfo, + providerInfoList = getCredentialUiState.providerInfoList, + onEntrySelected = viewModel::getFlowOnEntrySelected, + onLog = { viewModel.logUiEvent(it) }, + ) + viewModel.uiMetrics.log(GetCredentialEvent + .CREDMAN_GET_CRED_SCREEN_PRIMARY_SELECTION) + } + ProviderActivityState.READY_TO_LAUNCH -> { + // Launch only once per providerActivityState change so that the provider + // UI will not be accidentally launched twice. + LaunchedEffect(viewModel.uiState.providerActivityState) { + viewModel.launchProviderUi(providerActivityLauncher) + } + viewModel.uiMetrics.log(GetCredentialEvent + .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_READY_TO_LAUNCH) + } + ProviderActivityState.PENDING -> { + // Hide our content when the provider activity is active. + viewModel.uiMetrics.log(GetCredentialEvent + .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_PENDING) + } + } + }, + onDismiss = viewModel::onUserCancel, + ) +} + +@Composable +fun PrimarySelectionCardGeneric( + requestDisplayInfo: RequestDisplayInfo, + providerDisplayInfo: ProviderDisplayInfo, + providerInfoList: List, + onEntrySelected: (BaseEntry) -> Unit, + onLog: @Composable (UiEventLogger.UiEventEnum) -> Unit, +) { + val sortedUserNameToCredentialEntryList = + providerDisplayInfo.sortedUserNameToCredentialEntryList + SheetContainerCard { + // When only one provider (not counting the remote-only provider) exists, display that + // provider's icon + name up top. + if (providerInfoList.size <= 2) { // It's only possible to be the single provider case + // if we are started with no more than 2 providers. + val nonRemoteProviderList = providerInfoList.filter( + { it.credentialEntryList.isNotEmpty() || it.authenticationEntryList.isNotEmpty() } + ) + if (nonRemoteProviderList.size == 1) { + val providerInfo = nonRemoteProviderList.firstOrNull() // First should always work + // but just to be safe. + if (providerInfo != null) { + item { + HeadlineIcon( + bitmap = providerInfo.icon.toBitmap().asImageBitmap(), + tint = Color.Unspecified, + ) + } + item { Divider(thickness = 4.dp, color = Color.Transparent) } + item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + } + } + } + + item { + HeadlineText( + text = stringResource( + R.string.get_dialog_title_choose_option_for, + requestDisplayInfo.appName + ), + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + // Show max 4 entries in this primary page + sortedUserNameToCredentialEntryList.forEach { + // TODO(b/275375861): fallback UI merges entries by account names. + // Need a strategy to be able to show all entries. + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + enforceOneLine = true, + ) + } + } + } + } + } + onLog(GetCredentialEvent.CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD) +}