Merge "[CredManUi] App can specify preferred top level branding for sign-in flow" into udc-dev
This commit is contained in:
@@ -46,6 +46,7 @@ import com.android.credentialmanager.getflow.AuthenticationEntryInfo
|
|||||||
import com.android.credentialmanager.getflow.CredentialEntryInfo
|
import com.android.credentialmanager.getflow.CredentialEntryInfo
|
||||||
import com.android.credentialmanager.getflow.ProviderInfo
|
import com.android.credentialmanager.getflow.ProviderInfo
|
||||||
import com.android.credentialmanager.getflow.RemoteEntryInfo
|
import com.android.credentialmanager.getflow.RemoteEntryInfo
|
||||||
|
import com.android.credentialmanager.getflow.TopBrandingContent
|
||||||
import androidx.credentials.CreateCredentialRequest
|
import androidx.credentials.CreateCredentialRequest
|
||||||
import androidx.credentials.CreateCustomCredentialRequest
|
import androidx.credentials.CreateCustomCredentialRequest
|
||||||
import androidx.credentials.CreatePasswordRequest
|
import androidx.credentials.CreatePasswordRequest
|
||||||
@@ -207,6 +208,23 @@ class GetFlowUtils {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val preferUiBrandingComponentName =
|
||||||
|
getCredentialRequest.data.getParcelable(
|
||||||
|
"androidx.credentials.BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME",
|
||||||
|
ComponentName::class.java
|
||||||
|
)
|
||||||
|
val preferTopBrandingContent: TopBrandingContent? =
|
||||||
|
if (preferUiBrandingComponentName == null) null
|
||||||
|
else {
|
||||||
|
val (displayName, icon) = getServiceLabelAndIcon(
|
||||||
|
context.packageManager, preferUiBrandingComponentName.flattenToString())
|
||||||
|
?: Pair(null, null)
|
||||||
|
if (displayName != null && icon != null) {
|
||||||
|
TopBrandingContent(icon, displayName)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
return com.android.credentialmanager.getflow.RequestDisplayInfo(
|
return com.android.credentialmanager.getflow.RequestDisplayInfo(
|
||||||
appName = originName
|
appName = originName
|
||||||
?: getAppLabel(context.packageManager, requestInfo.appPackageName)
|
?: getAppLabel(context.packageManager, requestInfo.appPackageName)
|
||||||
@@ -216,6 +234,7 @@ class GetFlowUtils {
|
|||||||
// TODO(b/276777444): replace with direct library constant reference once
|
// TODO(b/276777444): replace with direct library constant reference once
|
||||||
// exposed.
|
// exposed.
|
||||||
"androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"),
|
"androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"),
|
||||||
|
preferTopBrandingContent = preferTopBrandingContent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.credentialmanager.getflow
|
package com.android.credentialmanager.getflow
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
@@ -34,7 +35,6 @@ import androidx.compose.material3.Divider
|
|||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -168,26 +168,30 @@ fun PrimarySelectionCard(
|
|||||||
providerDisplayInfo.sortedUserNameToCredentialEntryList
|
providerDisplayInfo.sortedUserNameToCredentialEntryList
|
||||||
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
|
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
|
||||||
SheetContainerCard {
|
SheetContainerCard {
|
||||||
// When only one provider (not counting the remote-only provider) exists, display that
|
val preferTopBrandingContent = requestDisplayInfo.preferTopBrandingContent
|
||||||
// provider's icon + name up top.
|
if (preferTopBrandingContent != null) {
|
||||||
if (providerInfoList.size <= 2) { // It's only possible to be the single provider case
|
item {
|
||||||
// if we are started with no more than 2 providers.
|
HeadlineProviderIconAndName(
|
||||||
val nonRemoteProviderList = providerInfoList.filter(
|
preferTopBrandingContent.icon,
|
||||||
{ it.credentialEntryList.isNotEmpty() || it.authenticationEntryList.isNotEmpty() }
|
preferTopBrandingContent.displayName
|
||||||
)
|
)
|
||||||
if (nonRemoteProviderList.size == 1) {
|
}
|
||||||
val providerInfo = nonRemoteProviderList.firstOrNull() // First should always work
|
} else {
|
||||||
// but just to be safe.
|
// When only one provider (not counting the remote-only provider) exists, display that
|
||||||
|
// provider's icon + name up top.
|
||||||
|
val providersWithActualEntries = providerInfoList.filter {
|
||||||
|
it.credentialEntryList.isNotEmpty() || it.authenticationEntryList.isNotEmpty()
|
||||||
|
}
|
||||||
|
if (providersWithActualEntries.size == 1) {
|
||||||
|
// First should always work but just to be safe.
|
||||||
|
val providerInfo = providersWithActualEntries.firstOrNull()
|
||||||
if (providerInfo != null) {
|
if (providerInfo != null) {
|
||||||
item {
|
item {
|
||||||
HeadlineIcon(
|
HeadlineProviderIconAndName(
|
||||||
bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
|
providerInfo.icon,
|
||||||
tint = Color.Unspecified,
|
providerInfo.displayName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item { Divider(thickness = 4.dp, color = Color.Transparent) }
|
|
||||||
item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) }
|
|
||||||
item { Divider(thickness = 16.dp, color = Color.Transparent) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,8 +373,19 @@ fun AllSignInOptionCard(
|
|||||||
onLog(GetCredentialEvent.CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD)
|
onLog(GetCredentialEvent.CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: create separate rows for primary and secondary pages.
|
@Composable
|
||||||
// TODO: reuse rows and columns across types.
|
fun HeadlineProviderIconAndName(
|
||||||
|
icon: Drawable,
|
||||||
|
name: String,
|
||||||
|
) {
|
||||||
|
HeadlineIcon(
|
||||||
|
bitmap = icon.toBitmap().asImageBitmap(),
|
||||||
|
tint = Color.Unspecified,
|
||||||
|
)
|
||||||
|
Divider(thickness = 4.dp, color = Color.Transparent)
|
||||||
|
LargeLabelTextOnSurfaceVariant(text = name)
|
||||||
|
Divider(thickness = 16.dp, color = Color.Transparent)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ActionChips(
|
fun ActionChips(
|
||||||
|
|||||||
@@ -173,6 +173,13 @@ data class RequestDisplayInfo(
|
|||||||
val appName: String,
|
val appName: String,
|
||||||
val preferImmediatelyAvailableCredentials: Boolean,
|
val preferImmediatelyAvailableCredentials: Boolean,
|
||||||
val preferIdentityDocUi: Boolean,
|
val preferIdentityDocUi: Boolean,
|
||||||
|
// A top level branding icon + display name preferred by the app.
|
||||||
|
val preferTopBrandingContent: TopBrandingContent?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class TopBrandingContent(
|
||||||
|
val icon: Drawable,
|
||||||
|
val displayName: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user