Merge "[CredManUI] Honor preferImmediatelyAvailableCred" into udc-dev

This commit is contained in:
Helen Qin
2023-02-28 19:58:17 +00:00
committed by Android (Google) Code Review
4 changed files with 93 additions and 31 deletions

View File

@@ -45,6 +45,8 @@ import com.android.credentialmanager.getflow.GetCredentialUiState
import androidx.credentials.CreateCredentialRequest.DisplayInfo import androidx.credentials.CreateCredentialRequest.DisplayInfo
import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CreatePasswordRequest import androidx.credentials.CreatePasswordRequest
import androidx.credentials.GetPasswordOption
import androidx.credentials.GetPublicKeyCredentialOption
import java.time.Instant import java.time.Instant
@@ -71,7 +73,7 @@ class CredentialManagerRepo(
requestInfo = intent.extras?.getParcelable( requestInfo = intent.extras?.getParcelable(
RequestInfo.EXTRA_REQUEST_INFO, RequestInfo.EXTRA_REQUEST_INFO,
RequestInfo::class.java RequestInfo::class.java
) ?: testCreatePasswordRequestInfo() ) ?: testCreatePasskeyRequestInfo()
val originName: String? = when (requestInfo.type) { val originName: String? = when (requestInfo.type) {
RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin
@@ -120,8 +122,7 @@ class CredentialManagerRepo(
providerDisableListUiState, providerDisableListUiState,
defaultProviderId, defaultProviderId,
requestDisplayInfoUiState, requestDisplayInfoUiState,
/** isOnPasskeyIntroStateAlready */ isOnPasskeyIntroStateAlready = false,
false,
isPasskeyFirstUse isPasskeyFirstUse
)!!, )!!,
getCredentialUiState = null, getCredentialUiState = null,
@@ -400,7 +401,8 @@ class CredentialManagerRepo(
" \"authenticatorSelection\": {\n" + " \"authenticatorSelection\": {\n" +
" \"residentKey\": \"required\",\n" + " \"residentKey\": \"required\",\n" +
" \"requireResidentKey\": true\n" + " \"requireResidentKey\": true\n" +
" }}" " }}",
preferImmediatelyAvailableCredentials = true,
) )
val credentialData = request.credentialData val credentialData = request.credentialData
return RequestInfo.newCreateRequestInfo( return RequestInfo.newCreateRequestInfo(
@@ -446,19 +448,28 @@ class CredentialManagerRepo(
} }
private fun testGetRequestInfo(): RequestInfo { private fun testGetRequestInfo(): RequestInfo {
val passwordOption = GetPasswordOption()
val passkeyOption = GetPublicKeyCredentialOption(
"json", preferImmediatelyAvailableCredentials = false)
return RequestInfo.newGetRequestInfo( return RequestInfo.newGetRequestInfo(
Binder(), Binder(),
GetCredentialRequest.Builder( GetCredentialRequest.Builder(
Bundle() Bundle()
).addCredentialOption( ).addCredentialOption(
CredentialOption( CredentialOption(
"androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", passwordOption.type,
Bundle(), passwordOption.requestData,
Bundle(), /*isSystemProviderRequired=*/ passwordOption.candidateQueryData,
false passwordOption.isSystemProviderRequired
) )
) ).addCredentialOption(
.build(), CredentialOption(
passkeyOption.type,
passkeyOption.requestData,
passkeyOption.candidateQueryData,
passkeyOption.isSystemProviderRequired
)
).build(),
"com.google.android.youtube" "com.google.android.youtube"
) )
} }

View File

@@ -50,7 +50,11 @@ import com.android.credentialmanager.getflow.RemoteEntryInfo
import androidx.credentials.CreateCredentialRequest import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CredentialOption
import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CreatePublicKeyCredentialRequestPrivileged
import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.GetPublicKeyCredentialOptionPrivileged
import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import androidx.credentials.provider.Action import androidx.credentials.provider.Action
import androidx.credentials.provider.AuthenticationAction import androidx.credentials.provider.AuthenticationAction
@@ -172,10 +176,27 @@ class GetFlowUtils {
context: Context, context: Context,
originName: String?, originName: String?,
): com.android.credentialmanager.getflow.RequestDisplayInfo? { ): com.android.credentialmanager.getflow.RequestDisplayInfo? {
val getCredentialRequest = requestInfo.getCredentialRequest ?: return null
val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any {
val credentialOptionJetpack = CredentialOption.createFrom(
it.type,
it.credentialRetrievalData,
it.credentialRetrievalData,
it.isSystemProviderRequired
)
if (credentialOptionJetpack is GetPublicKeyCredentialOption) {
credentialOptionJetpack.preferImmediatelyAvailableCredentials
} else if (credentialOptionJetpack is GetPublicKeyCredentialOptionPrivileged) {
credentialOptionJetpack.preferImmediatelyAvailableCredentials
} else {
false
}
}
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)
?: return null ?: return null,
preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
) )
} }
@@ -415,24 +436,25 @@ class CreateFlowUtils {
createCredentialRequestJetpack.password, createCredentialRequestJetpack.password,
CredentialType.PASSWORD, CredentialType.PASSWORD,
appLabel, appLabel,
context.getDrawable(R.drawable.ic_password)!! context.getDrawable(R.drawable.ic_password) ?: return null,
preferImmediatelyAvailableCredentials = false,
) )
is CreatePublicKeyCredentialRequest -> { is CreatePublicKeyCredentialRequest -> {
val requestJson = createCredentialRequestJetpack.requestJson newRequestDisplayInfoFromPasskeyJson(
val json = JSONObject(requestJson) requestJson = createCredentialRequestJetpack.requestJson,
var name = "" appLabel = appLabel,
var displayName = "" context = context,
if (json.has("user")) { preferImmediatelyAvailableCredentials =
val user: JSONObject = json.getJSONObject("user") createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
name = user.getString("name") )
displayName = user.getString("displayName") }
} is CreatePublicKeyCredentialRequestPrivileged -> {
RequestDisplayInfo( newRequestDisplayInfoFromPasskeyJson(
name, requestJson = createCredentialRequestJetpack.requestJson,
displayName, appLabel = appLabel,
CredentialType.PASSKEY, context = context,
appLabel, preferImmediatelyAvailableCredentials =
context.getDrawable(R.drawable.ic_passkey)!! createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
) )
} }
is CreateCustomCredentialRequest -> { is CreateCustomCredentialRequest -> {
@@ -446,7 +468,8 @@ class CreateFlowUtils {
type = CredentialType.UNKNOWN, type = CredentialType.UNKNOWN,
appName = appLabel, appName = appLabel,
typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context) typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context)
?: context.getDrawable(R.drawable.ic_other_sign_in)!! ?: context.getDrawable(R.drawable.ic_other_sign_in) ?: return null,
preferImmediatelyAvailableCredentials = false,
) )
} }
else -> null else -> null
@@ -613,5 +636,29 @@ class CreateFlowUtils {
) )
} else null } else null
} }
private fun newRequestDisplayInfoFromPasskeyJson(
requestJson: String,
appLabel: String,
context: Context,
preferImmediatelyAvailableCredentials: Boolean,
): RequestDisplayInfo? {
val json = JSONObject(requestJson)
var name = ""
var displayName = ""
if (json.has("user")) {
val user: JSONObject = json.getJSONObject("user")
name = user.getString("name")
displayName = user.getString("displayName")
}
return RequestDisplayInfo(
name,
displayName,
CredentialType.PASSKEY,
appLabel,
context.getDrawable(R.drawable.ic_passkey) ?: return null,
preferImmediatelyAvailableCredentials,
)
}
} }
} }

View File

@@ -38,7 +38,9 @@ data class CreateCredentialUiState(
) )
internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean { internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean {
return state.sortedCreateOptionsPairs.isNotEmpty() return state.sortedCreateOptionsPairs.isNotEmpty() ||
(!state.requestDisplayInfo.preferImmediatelyAvailableCredentials &&
state.remoteEntry != null)
} }
open class ProviderInfo( open class ProviderInfo(
@@ -104,6 +106,7 @@ data class RequestDisplayInfo(
val type: CredentialType, val type: CredentialType,
val appName: String, val appName: String,
val typeIcon: Drawable, val typeIcon: Drawable,
val preferImmediatelyAvailableCredentials: Boolean,
) )
/** /**

View File

@@ -37,7 +37,8 @@ data class GetCredentialUiState(
internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean { internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean {
return state.providerDisplayInfo.sortedUserNameToCredentialEntryList.isNotEmpty() || return state.providerDisplayInfo.sortedUserNameToCredentialEntryList.isNotEmpty() ||
state.providerDisplayInfo.authenticationEntryList.isNotEmpty() || state.providerDisplayInfo.authenticationEntryList.isNotEmpty() ||
state.providerDisplayInfo.remoteEntry != null (state.providerDisplayInfo.remoteEntry != null &&
!state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
} }
data class ProviderInfo( data class ProviderInfo(
@@ -146,6 +147,7 @@ class ActionEntryInfo(
data class RequestDisplayInfo( data class RequestDisplayInfo(
val appName: String, val appName: String,
val preferImmediatelyAvailableCredentials: Boolean,
) )
/** /**
@@ -246,7 +248,6 @@ private fun toActiveEntry(
private fun toGetScreenState( private fun toGetScreenState(
providerDisplayInfo: ProviderDisplayInfo providerDisplayInfo: ProviderDisplayInfo
): GetScreenState { ): GetScreenState {
return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() && return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() &&
providerDisplayInfo.remoteEntry == null && providerDisplayInfo.remoteEntry == null &&
providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty }) providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty })