diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 3d718a2d392dd..0e604ce2cf3af 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -45,6 +45,8 @@ import com.android.credentialmanager.getflow.GetCredentialUiState import androidx.credentials.CreateCredentialRequest.DisplayInfo import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.CreatePasswordRequest +import androidx.credentials.GetPasswordOption +import androidx.credentials.GetPublicKeyCredentialOption import java.time.Instant @@ -71,7 +73,7 @@ class CredentialManagerRepo( requestInfo = intent.extras?.getParcelable( RequestInfo.EXTRA_REQUEST_INFO, RequestInfo::class.java - ) ?: testCreatePasswordRequestInfo() + ) ?: testCreatePasskeyRequestInfo() val originName: String? = when (requestInfo.type) { RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin @@ -120,8 +122,7 @@ class CredentialManagerRepo( providerDisableListUiState, defaultProviderId, requestDisplayInfoUiState, - /** isOnPasskeyIntroStateAlready */ - false, + isOnPasskeyIntroStateAlready = false, isPasskeyFirstUse )!!, getCredentialUiState = null, @@ -400,7 +401,8 @@ class CredentialManagerRepo( " \"authenticatorSelection\": {\n" + " \"residentKey\": \"required\",\n" + " \"requireResidentKey\": true\n" + - " }}" + " }}", + preferImmediatelyAvailableCredentials = true, ) val credentialData = request.credentialData return RequestInfo.newCreateRequestInfo( @@ -446,19 +448,28 @@ class CredentialManagerRepo( } private fun testGetRequestInfo(): RequestInfo { + val passwordOption = GetPasswordOption() + val passkeyOption = GetPublicKeyCredentialOption( + "json", preferImmediatelyAvailableCredentials = false) return RequestInfo.newGetRequestInfo( Binder(), GetCredentialRequest.Builder( Bundle() ).addCredentialOption( CredentialOption( - "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", - Bundle(), - Bundle(), /*isSystemProviderRequired=*/ - false + passwordOption.type, + passwordOption.requestData, + passwordOption.candidateQueryData, + passwordOption.isSystemProviderRequired ) - ) - .build(), + ).addCredentialOption( + CredentialOption( + passkeyOption.type, + passkeyOption.requestData, + passkeyOption.candidateQueryData, + passkeyOption.isSystemProviderRequired + ) + ).build(), "com.google.android.youtube" ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 9c9c2a3a1d103..a834994a5b0fd 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -50,7 +50,11 @@ import com.android.credentialmanager.getflow.RemoteEntryInfo import androidx.credentials.CreateCredentialRequest import androidx.credentials.CreateCustomCredentialRequest import androidx.credentials.CreatePasswordRequest +import androidx.credentials.CredentialOption 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.provider.Action import androidx.credentials.provider.AuthenticationAction @@ -172,10 +176,27 @@ class GetFlowUtils { context: Context, originName: String?, ): 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( appName = originName ?: getAppLabel(context.packageManager, requestInfo.appPackageName) - ?: return null + ?: return null, + preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials ) } @@ -415,24 +436,25 @@ class CreateFlowUtils { createCredentialRequestJetpack.password, CredentialType.PASSWORD, appLabel, - context.getDrawable(R.drawable.ic_password)!! + context.getDrawable(R.drawable.ic_password) ?: return null, + preferImmediatelyAvailableCredentials = false, ) is CreatePublicKeyCredentialRequest -> { - val requestJson = createCredentialRequestJetpack.requestJson - 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") - } - RequestDisplayInfo( - name, - displayName, - CredentialType.PASSKEY, - appLabel, - context.getDrawable(R.drawable.ic_passkey)!! + newRequestDisplayInfoFromPasskeyJson( + requestJson = createCredentialRequestJetpack.requestJson, + appLabel = appLabel, + context = context, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, + ) + } + is CreatePublicKeyCredentialRequestPrivileged -> { + newRequestDisplayInfoFromPasskeyJson( + requestJson = createCredentialRequestJetpack.requestJson, + appLabel = appLabel, + context = context, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, ) } is CreateCustomCredentialRequest -> { @@ -446,7 +468,8 @@ class CreateFlowUtils { type = CredentialType.UNKNOWN, appName = appLabel, 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 @@ -613,5 +636,29 @@ class CreateFlowUtils { ) } 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, + ) + } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index ce4e936446ff7..192fa15714c60 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -38,7 +38,9 @@ data class CreateCredentialUiState( ) internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean { - return state.sortedCreateOptionsPairs.isNotEmpty() + return state.sortedCreateOptionsPairs.isNotEmpty() || + (!state.requestDisplayInfo.preferImmediatelyAvailableCredentials && + state.remoteEntry != null) } open class ProviderInfo( @@ -104,6 +106,7 @@ data class RequestDisplayInfo( val type: CredentialType, val appName: String, val typeIcon: Drawable, + val preferImmediatelyAvailableCredentials: Boolean, ) /** diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index d1f0f8146be32..9727d3f39c4ad 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -37,7 +37,8 @@ data class GetCredentialUiState( internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean { return state.providerDisplayInfo.sortedUserNameToCredentialEntryList.isNotEmpty() || state.providerDisplayInfo.authenticationEntryList.isNotEmpty() || - state.providerDisplayInfo.remoteEntry != null + (state.providerDisplayInfo.remoteEntry != null && + !state.requestDisplayInfo.preferImmediatelyAvailableCredentials) } data class ProviderInfo( @@ -146,6 +147,7 @@ class ActionEntryInfo( data class RequestDisplayInfo( val appName: String, + val preferImmediatelyAvailableCredentials: Boolean, ) /** @@ -246,7 +248,6 @@ private fun toActiveEntry( private fun toGetScreenState( providerDisplayInfo: ProviderDisplayInfo ): GetScreenState { - return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() && providerDisplayInfo.remoteEntry == null && providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty })