Use the official jetpack lib.
Also included bug fixes of incorrectly showing 0 for absent credential
counts and unknown create credential flow always displaying fake request
info.
Fix: 255688790
Fix: 267575490
Fix: 261050124
Test: local deployment
Change-Id: I6659a9a94c14cd10de5641615177b891c9a472c2
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 2cb3468..00d42bd 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -31,6 +31,7 @@
"androidx.compose.ui_ui",
"androidx.compose.ui_ui-tooling",
"androidx.core_core-ktx",
+ "androidx.credentials_credentials",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-runtime-ktx",
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 7b53e26..c66ea5e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -16,12 +16,10 @@
package com.android.credentialmanager
-import android.app.PendingIntent
import android.app.slice.Slice
import android.app.slice.SliceSpec
import android.content.Context
import android.content.Intent
-import android.content.pm.SigningInfo
import android.credentials.CreateCredentialRequest
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.credentials.CredentialOption
@@ -40,18 +38,15 @@
import android.os.Binder
import android.os.Bundle
import android.os.ResultReceiver
-import android.service.credentials.CredentialProviderService
import com.android.credentialmanager.createflow.DisabledProviderInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
-import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toCredentialDataBundle
-import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import com.android.credentialmanager.jetpack.provider.Action
-import com.android.credentialmanager.jetpack.provider.CreateEntry
-import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
-import com.android.credentialmanager.jetpack.provider.CredentialEntry
+import androidx.credentials.CreateCredentialRequest.DisplayInfo
+import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.CreatePasswordRequest
+
+import java.time.Instant
// Consider repo per screen, similar to view model?
class CredentialManagerRepo(
@@ -180,14 +175,14 @@
.Builder("io.enpass.app")
.setSaveEntries(
listOf<Entry>(
- newCreateEntry(
+ CreateTestUtils.newCreateEntry(context,
"key1", "subkey-1", "elisa.beckett@gmail.com",
- 20, 7, 27, 10L,
- "Optional footer description"
+ 20, 7, 27, Instant.ofEpochSecond(10L),
+ "Legal note"
),
- newCreateEntry(
+ CreateTestUtils.newCreateEntry(context,
"key1", "subkey-2", "elisa.work@google.com",
- 20, 7, 27, 12L,
+ 20, 7, 27, Instant.ofEpochSecond(12L),
null
),
)
@@ -200,14 +195,14 @@
.Builder("com.dashlane")
.setSaveEntries(
listOf<Entry>(
- newCreateEntry(
+ CreateTestUtils.newCreateEntry(context,
"key1", "subkey-3", "elisa.beckett@dashlane.com",
- 20, 7, 27, 11L,
+ 20, 7, 27, Instant.ofEpochSecond(11L),
null
),
- newCreateEntry(
+ CreateTestUtils.newCreateEntry(context,
"key1", "subkey-4", "elisa.work@dashlane.com",
- 20, 7, 27, 14L,
+ 20, 7, 27, Instant.ofEpochSecond(14L),
null
),
)
@@ -228,33 +223,33 @@
GetCredentialProviderData.Builder("io.enpass.app")
.setCredentialEntries(
listOf<Entry>(
- newGetEntry(
- "key1", "subkey-1", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.family@outlook.com", null, 3L
+ GetTestUtils.newPasswordEntry(
+ context, "key1", "subkey-1", "elisa.family@outlook.com", null,
+ Instant.ofEpochSecond(8000L)
),
- newGetEntry(
- "key1", "subkey-1", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
- "elisa.bakery@gmail.com", "Elisa Beckett", 0L
+ GetTestUtils.newPasskeyEntry(
+ context, "key1", "subkey-1", "elisa.bakery@gmail.com", "Elisa Beckett",
+ null
),
- newGetEntry(
- "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.bakery@gmail.com", null, 10L
+ GetTestUtils.newPasswordEntry(
+ context, "key1", "subkey-2", "elisa.bakery@gmail.com", null,
+ Instant.ofEpochSecond(10000L)
),
- newGetEntry(
- "key1", "subkey-3", TYPE_PUBLIC_KEY_CREDENTIAL, "Passkey",
- "elisa.family@outlook.com", "Elisa Beckett", 1L
+ GetTestUtils.newPasskeyEntry(
+ context, "key1", "subkey-3", "elisa.family@outlook.com",
+ "Elisa Beckett", Instant.ofEpochSecond(500L)
),
)
).setAuthenticationEntry(
- newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
+ GetTestUtils.newAuthenticationEntry(context, "key2", "subkey-1")
).setActionChips(
listOf(
- newActionEntry(
- "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
+ GetTestUtils.newActionEntry(
+ context, "key3", "subkey-1",
"Open Google Password Manager", "elisa.beckett@gmail.com"
),
- newActionEntry(
- "key3", "subkey-2", TYPE_PASSWORD_CREDENTIAL,
+ GetTestUtils.newActionEntry(
+ context, "key3", "subkey-2",
"Open Google Password Manager", "beckett-family@gmail.com"
),
)
@@ -264,137 +259,29 @@
GetCredentialProviderData.Builder("com.dashlane")
.setCredentialEntries(
listOf<Entry>(
- newGetEntry(
- "key1", "subkey-2", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.family@outlook.com", null, 4L
+ GetTestUtils.newPasswordEntry(
+ context, "key1", "subkey-2", "elisa.family@outlook.com", null,
+ Instant.ofEpochSecond(9000L)
),
- newGetEntry(
- "key1", "subkey-3", TYPE_PASSWORD_CREDENTIAL, "Password",
- "elisa.work@outlook.com", null, 11L
+ GetTestUtils.newPasswordEntry(
+ context, "key1", "subkey-3", "elisa.work@outlook.com", null,
+ Instant.ofEpochSecond(11000L)
),
)
).setAuthenticationEntry(
- newAuthenticationEntry("key2", "subkey-1", TYPE_PASSWORD_CREDENTIAL)
+ GetTestUtils.newAuthenticationEntry(context, "key2", "subkey-1")
).setActionChips(
listOf(
- newActionEntry(
- "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL,
- "Open Enpass"
+ GetTestUtils.newActionEntry(
+ context, "key3", "subkey-1", "Open Enpass",
+ "Manage passwords"
),
)
).build(),
)
}
- private fun newActionEntry(
- key: String,
- subkey: String,
- credentialType: String,
- text: String,
- subtext: String? = null,
- ): Entry {
- val action = Action(text, subtext, null)
- return Entry(
- key,
- subkey,
- Action.toSlice(action)
- )
- }
-
- private fun newAuthenticationEntry(
- key: String,
- subkey: String,
- credentialType: String,
- ): Entry {
- val slice = Slice.Builder(
- Uri.EMPTY, SliceSpec(credentialType, 1)
- )
- return Entry(
- key,
- subkey,
- slice.build()
- )
- }
-
- private fun newGetEntry(
- key: String,
- subkey: String,
- credentialType: String,
- credentialTypeDisplayName: String,
- userName: String,
- userDisplayName: String?,
- lastUsedTimeMillis: Long?,
- ): Entry {
- val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
- .setPackage("com.androidauth.androidvault")
- intent.putExtra("provider_extra_sample", "testprovider")
-
- val pendingIntent = PendingIntent.getActivity(
- context, 1,
- intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- or PendingIntent.FLAG_ONE_SHOT)
- )
-
- val credentialEntry = CredentialEntry(
- credentialType, credentialTypeDisplayName, userName,
- userDisplayName, pendingIntent, lastUsedTimeMillis
- ?: 0L, null, false
- )
-
- return Entry(
- key,
- subkey,
- CredentialEntry.toSlice(credentialEntry),
- Intent()
- )
- }
-
- private fun newCreateEntry(
- key: String,
- subkey: String,
- providerDisplayName: String,
- passwordCount: Int,
- passkeyCount: Int,
- totalCredentialCount: Int,
- lastUsedTimeMillis: Long,
- footerDescription: String?,
- ): Entry {
- val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
- .setPackage("com.androidauth.androidvault")
- intent.putExtra("provider_extra_sample", "testprovider")
- val pendingIntent = PendingIntent.getActivity(
- context, 1,
- intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- or PendingIntent.FLAG_ONE_SHOT)
- )
- val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
- android.service.credentials.CallingAppInfo(
- context.applicationInfo.packageName, SigningInfo()
- ),
- TYPE_PASSWORD_CREDENTIAL,
- toCredentialDataBundle("beckett-bakert@gmail.com", "password123")
- )
- val fillInIntent = Intent().putExtra(
- CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
- createPasswordRequest
- )
-
- val createEntry = CreateEntry(
- providerDisplayName, pendingIntent,
- null, lastUsedTimeMillis,
- listOf(
- CredentialCountInformation.createPasswordCountInformation(passwordCount),
- CredentialCountInformation.createPublicKeyCountInformation(passkeyCount),
- ), footerDescription
- )
- return Entry(
- key,
- subkey,
- CreateEntry.toSlice(createEntry),
- fillInIntent
- )
- }
private fun newRemoteEntry(
key: String,
@@ -451,7 +338,7 @@
return RequestInfo.newCreateRequestInfo(
Binder(),
CreateCredentialRequest(
- TYPE_PUBLIC_KEY_CREDENTIAL,
+ "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
credentialData,
// TODO: populate with actual data
/*candidateQueryData=*/ Bundle(),
@@ -462,14 +349,13 @@
}
private fun testCreatePasswordRequestInfo(): RequestInfo {
- val data = toCredentialDataBundle("beckett-bakert@gmail.com", "password123")
+ val request = CreatePasswordRequest("beckett-bakert@gmail.com", "password123")
return RequestInfo.newCreateRequestInfo(
Binder(),
CreateCredentialRequest(
TYPE_PASSWORD_CREDENTIAL,
- data,
- // TODO: populate with actual data
- /*candidateQueryData=*/ Bundle(),
+ request.credentialData,
+ request.candidateQueryData,
/*isSystemProviderRequired=*/ false
),
"com.google.android.youtube"
@@ -478,6 +364,10 @@
private fun testCreateOtherCredentialRequestInfo(): RequestInfo {
val data = Bundle()
+ val displayInfo = DisplayInfo("my-username00", "Joe")
+ data.putBundle(
+ "androidx.credentials.BUNDLE_KEY_REQUEST_DISPLAY_INFO",
+ displayInfo.toBundle())
return RequestInfo.newCreateRequestInfo(
Binder(),
CreateCredentialRequest(
@@ -498,7 +388,7 @@
)
.addCredentialOption(
CredentialOption(
- TYPE_PUBLIC_KEY_CREDENTIAL,
+ "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
Bundle(),
Bundle(), /*isSystemProviderRequired=*/
false
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 3f705d6..d8420cd 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -16,18 +16,22 @@
package com.android.credentialmanager
+import android.app.slice.Slice
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.DisabledProviderData
import android.credentials.ui.Entry
import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.RequestInfo
import android.graphics.drawable.Drawable
+import android.service.credentials.CredentialEntry
import android.text.TextUtils
import android.util.Log
import com.android.credentialmanager.common.Constants
+import com.android.credentialmanager.common.CredentialType
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateOptionInfo
@@ -41,17 +45,22 @@
import com.android.credentialmanager.getflow.CredentialEntryInfo
import com.android.credentialmanager.getflow.ProviderInfo
import com.android.credentialmanager.getflow.RemoteEntryInfo
-import com.android.credentialmanager.jetpack.developer.CreateCredentialRequest
-import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest
-import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import com.android.credentialmanager.jetpack.provider.Action
-import com.android.credentialmanager.jetpack.provider.AuthenticationAction
-import com.android.credentialmanager.jetpack.provider.CreateEntry
-import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
-import com.android.credentialmanager.jetpack.provider.CredentialEntry
+import androidx.credentials.CreateCredentialRequest
+import androidx.credentials.CreateCustomCredentialRequest
+import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.CreateEntry
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.RemoteCreateEntry
+import androidx.credentials.provider.RemoteCredentialEntry
import org.json.JSONObject
+// TODO: remove all !! checks
private fun getAppLabel(
pm: PackageManager,
appPackageName: String
@@ -174,25 +183,76 @@
credentialEntries: List<Entry>,
context: Context,
): List<CredentialEntryInfo> {
- return credentialEntries.map {
- // TODO: handle NPE gracefully
- val credentialEntry = CredentialEntry.fromSlice(it.slice)!!
+ val result: MutableList<CredentialEntryInfo> = mutableListOf()
+ credentialEntries.forEach {
+ val credentialEntry = parseCredentialEntryFromSlice(it.slice)
+ when (credentialEntry) {
+ is PasswordCredentialEntry -> {
+ result.add(CredentialEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSWORD,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ ))
+ }
+ is PublicKeyCredentialEntry -> {
+ result.add(CredentialEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSKEY,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ ))
+ }
+ is CustomCredentialEntry -> {
+ result.add(CredentialEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.UNKNOWN,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.title.toString(),
+ displayName = credentialEntry.subtitle?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ ))
+ }
+ else -> Log.d(
+ Constants.LOG_TAG,
+ "Encountered unrecognized credential entry ${it.slice.spec?.type}"
+ )
+ }
+ }
+ // TODO: handle empty list due to parsing error.
+ return result
+ }
- // Consider directly move the UI object into the class.
- return@map CredentialEntryInfo(
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = credentialEntry.type,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- // TODO: proper fallback
- icon = credentialEntry.icon?.loadDrawable(context),
- lastUsedTimeMillis = credentialEntry.lastUsedTimeMillis,
- )
+ private fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
+ try {
+ when (slice.spec?.type) {
+ TYPE_PASSWORD_CREDENTIAL -> return PasswordCredentialEntry.fromSlice(slice)!!
+ TYPE_PUBLIC_KEY_CREDENTIAL -> return PublicKeyCredentialEntry.fromSlice(slice)!!
+ else -> return CustomCredentialEntry.fromSlice(slice)!!
+ }
+ } catch (e: Exception) {
+ // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+ // password / passkey parsing attempt.
+ return CustomCredentialEntry.fromSlice(slice)
}
}
@@ -205,18 +265,13 @@
if (authEntry == null) {
return null
}
- val authStructuredEntry = AuthenticationAction.fromSlice(
- authEntry!!.slice
- )
- if (authStructuredEntry == null) {
- return null
- }
-
+ val structuredAuthEntry =
+ AuthenticationAction.fromSlice(authEntry.slice) ?: return null
return AuthenticationEntryInfo(
providerId = providerId,
entryKey = authEntry.key,
entrySubkey = authEntry.subkey,
- pendingIntent = authStructuredEntry.pendingIntent,
+ pendingIntent = structuredAuthEntry.pendingIntent,
fillInIntent = authEntry.frameworkExtrasIntent,
title = providerDisplayName,
icon = providerIcon,
@@ -228,11 +283,13 @@
if (remoteEntry == null) {
return null
}
+ val structuredRemoteEntry = RemoteCredentialEntry.fromSlice(remoteEntry.slice)
+ ?: return null
return RemoteEntryInfo(
providerId = providerId,
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
- pendingIntent = remoteEntry.pendingIntent,
+ pendingIntent = structuredRemoteEntry.pendingIntent,
fillInIntent = remoteEntry.frameworkExtrasIntent,
)
}
@@ -242,22 +299,22 @@
actionEntries: List<Entry>,
providerIcon: Drawable,
): List<ActionEntryInfo> {
- return actionEntries.map {
- // TODO: handle NPE gracefully
- val actionEntryUi = Action.fromSlice(it.slice)!!
-
- return@map ActionEntryInfo(
+ val result: MutableList<ActionEntryInfo> = mutableListOf()
+ actionEntries.forEach {
+ val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
+ result.add(ActionEntryInfo(
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
pendingIntent = actionEntryUi.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
title = actionEntryUi.title.toString(),
- // TODO: gracefully fail
icon = providerIcon,
- subTitle = actionEntryUi.subTitle?.toString(),
- )
+ subTitle = actionEntryUi.subtitle?.toString(),
+ ))
}
+ // TODO: handle empty list
+ return result
}
}
}
@@ -316,22 +373,21 @@
): RequestDisplayInfo? {
val appLabel = getAppLabel(context.packageManager, requestInfo.appPackageName)
?: return null
- val createCredentialRequest = requestInfo.createCredentialRequest
- val createCredentialRequestJetpack = createCredentialRequest?.let {
- CreateCredentialRequest.createFrom(
- it.type, it.credentialData, it.candidateQueryData, it.isSystemProviderRequired
+ val createCredentialRequest = requestInfo.createCredentialRequest ?: return null
+ val createCredentialRequestJetpack = CreateCredentialRequest.createFrom(
+ createCredentialRequest.type,
+ createCredentialRequest.credentialData,
+ createCredentialRequest.candidateQueryData,
+ createCredentialRequest.isSystemProviderRequired
+ )
+ return when (createCredentialRequestJetpack) {
+ is CreatePasswordRequest -> RequestDisplayInfo(
+ createCredentialRequestJetpack.id,
+ createCredentialRequestJetpack.password,
+ CredentialType.PASSWORD,
+ appLabel,
+ context.getDrawable(R.drawable.ic_password)!!
)
- }
- when (createCredentialRequestJetpack) {
- is CreatePasswordRequest -> {
- return RequestDisplayInfo(
- createCredentialRequestJetpack.id,
- createCredentialRequestJetpack.password,
- createCredentialRequestJetpack.type,
- appLabel,
- context.getDrawable(R.drawable.ic_password)!!
- )
- }
is CreatePublicKeyCredentialRequest -> {
val requestJson = createCredentialRequestJetpack.requestJson
val json = JSONObject(requestJson)
@@ -342,24 +398,29 @@
name = user.getString("name")
displayName = user.getString("displayName")
}
- return RequestDisplayInfo(
+ RequestDisplayInfo(
name,
displayName,
- createCredentialRequestJetpack.type,
+ CredentialType.PASSKEY,
appLabel,
context.getDrawable(R.drawable.ic_passkey)!!
)
}
- // TODO: correctly parsing for other sign-ins
- else -> {
- return RequestDisplayInfo(
- "beckett-bakert@gmail.com",
- "Elisa Beckett",
- "other-sign-ins",
- appLabel.toString(),
- context.getDrawable(R.drawable.ic_other_sign_in)!!
+ is CreateCustomCredentialRequest -> {
+ // TODO: directly use the display info once made public
+ val displayInfo = CreateCredentialRequest.DisplayInfo
+ .parseFromCredentialDataBundle(createCredentialRequest.credentialData)
+ ?: return null
+ RequestDisplayInfo(
+ title = displayInfo.userId,
+ subtitle = displayInfo.userDisplayName,
+ type = CredentialType.UNKNOWN,
+ appName = appLabel,
+ typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context)
+ ?: context.getDrawable(R.drawable.ic_other_sign_in)!!
)
}
+ else -> null
}
}
@@ -408,7 +469,7 @@
currentScreenState = initialScreenState,
requestDisplayInfo = requestDisplayInfo,
sortedCreateOptionsPairs = createOptionsPairs.sortedWith(
- compareByDescending { it.first.lastUsedTimeMillis }
+ compareByDescending { it.first.lastUsedTime }
),
hasDefaultProvider = defaultProvider != null,
activeEntry = toActiveEntry(
@@ -430,7 +491,7 @@
isPasskeyFirstUse: Boolean,
): CreateScreenState? {
return if (isPasskeyFirstUse && requestDisplayInfo.type ==
- TYPE_PUBLIC_KEY_CREDENTIAL && !isOnPasskeyIntroStateAlready) {
+ CredentialType.PASSKEY && !isOnPasskeyIntroStateAlready) {
CreateScreenState.PASSKEY_INTRO
} else if ((defaultProvider == null || defaultProvider.createOptions.isEmpty()) &&
createOptionSize > 1) {
@@ -479,12 +540,10 @@
creationEntries: List<Entry>,
context: Context,
): List<CreateOptionInfo> {
- return creationEntries.map {
- // TODO: handle NPE gracefully
- val createEntry = CreateEntry.fromSlice(it.slice)!!
-
- return@map CreateOptionInfo(
- // TODO: remove fallbacks
+ val result: MutableList<CreateOptionInfo> = mutableListOf()
+ creationEntries.forEach {
+ val createEntry = CreateEntry.fromSlice(it.slice) ?: return@forEach
+ result.add(CreateOptionInfo(
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
@@ -492,32 +551,28 @@
fillInIntent = it.frameworkExtrasIntent,
userProviderDisplayName = createEntry.accountName.toString(),
profileIcon = createEntry.icon?.loadDrawable(context),
- passwordCount = CredentialCountInformation.getPasswordCount(
- createEntry.credentialCountInformationList
- ) ?: 0,
- passkeyCount = CredentialCountInformation.getPasskeyCount(
- createEntry.credentialCountInformationList
- ) ?: 0,
- totalCredentialCount = CredentialCountInformation.getTotalCount(
- createEntry.credentialCountInformationList
- ) ?: 0,
- lastUsedTimeMillis = createEntry.lastUsedTimeMillis ?: 0,
- footerDescription = createEntry.footerDescription?.toString()
- )
+ passwordCount = createEntry.getPasswordCredentialCount(),
+ passkeyCount = createEntry.getPublicKeyCredentialCount(),
+ totalCredentialCount = createEntry.getTotalCredentialCount(),
+ lastUsedTime = createEntry.lastUsedTime,
+ footerDescription = createEntry.description?.toString()
+ ))
}
+ return result
}
private fun toRemoteInfo(
providerId: String,
remoteEntry: Entry?,
): RemoteInfo? {
- // TODO: should also call fromSlice after getting the official jetpack code.
return if (remoteEntry != null) {
+ val structuredRemoteEntry = RemoteCreateEntry.fromSlice(remoteEntry.slice)
+ ?: return null
RemoteInfo(
providerId = providerId,
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
- pendingIntent = remoteEntry.pendingIntent,
+ pendingIntent = structuredRemoteEntry.pendingIntent,
fillInIntent = remoteEntry.frameworkExtrasIntent,
)
} else null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
new file mode 100644
index 0000000..e3bbaeb
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
@@ -0,0 +1,352 @@
+/*
+ * 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
+
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.content.Context
+import android.content.Intent
+import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
+import android.credentials.ui.Entry
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.provider.Settings
+import androidx.credentials.provider.CreateEntry
+
+import java.time.Instant
+
+// TODO: remove once testing is complete
+class GetTestUtils {
+ companion object {
+ internal fun newAuthenticationEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ ): Entry {
+ val slice = Slice.Builder(
+ Uri.EMPTY, SliceSpec("AuthenticationAction", 0)
+ )
+ val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ slice.addAction(
+ pendingIntent,
+ Slice.Builder(slice)
+ .addHints(listOf("androidx.credentials.provider.authenticationAction" +
+ ".SLICE_HINT_PENDING_INTENT"))
+ .build(),
+ /*subType=*/null
+ )
+ return Entry(
+ key,
+ subkey,
+ slice.build()
+ )
+ }
+
+ internal fun newActionEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ text: String,
+ subtext: String? = null,
+ ): Entry {
+ val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("Action", 0))
+ .addText(
+ text, /*subType=*/null,
+ listOf("androidx.credentials.provider.action.HINT_ACTION_TITLE")
+ )
+ .addText(
+ subtext, /*subType=*/null,
+ listOf("androidx.credentials.provider.action.HINT_ACTION_SUBTEXT")
+ )
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(
+ listOf("androidx.credentials.provider.action.SLICE_HINT_PENDING_INTENT")
+ )
+ .build(),
+ /*subType=*/null
+ )
+ return Entry(
+ key,
+ subkey,
+ sliceBuilder.build()
+ )
+ }
+
+ private const val SLICE_HINT_TYPE_DISPLAY_NAME =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+ private const val SLICE_HINT_TITLE =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_USER_NAME"
+ private const val SLICE_HINT_SUBTITLE =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+ private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+ private const val SLICE_HINT_ICON =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_PROFILE_ICON"
+ private const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_PENDING_INTENT"
+ private const val SLICE_HINT_AUTO_ALLOWED =
+ "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_AUTO_ALLOWED"
+ private const val AUTO_SELECT_TRUE_STRING = "true"
+ private const val AUTO_SELECT_FALSE_STRING = "false"
+
+ internal fun newPasswordEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ userName: String,
+ userDisplayName: String?,
+ lastUsedTime: Instant?,
+ ): Entry {
+ val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+ .setPackage("com.androidauth.androidvault")
+ intent.putExtra("provider_extra_sample", "testprovider")
+ val pendingIntent = PendingIntent.getActivity(
+ context, 1,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ or PendingIntent.FLAG_ONE_SHOT)
+ )
+ return Entry(
+ key,
+ subkey,
+ toPasswordSlice(userName, userDisplayName, pendingIntent, lastUsedTime),
+ Intent()
+ )
+ }
+
+ private fun toPasswordSlice(
+ title: CharSequence,
+ subTitle: CharSequence?,
+ pendingIntent: PendingIntent,
+ lastUsedTime: Instant?,
+ icon: Icon? = null,
+ isAutoSelectAllowed: Boolean = true
+ ): Slice {
+ val type = TYPE_PASSWORD_CREDENTIAL
+ val autoSelectAllowed = if (isAutoSelectAllowed) {
+ AUTO_SELECT_TRUE_STRING
+ } else {
+ AUTO_SELECT_FALSE_STRING
+ }
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ type, 1
+ )
+ )
+ .addText(
+ "Password", /*subType=*/null,
+ listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
+ )
+ .addText(
+ title, /*subType=*/null,
+ listOf(SLICE_HINT_TITLE)
+ )
+ .addText(
+ subTitle, /*subType=*/null,
+ listOf(SLICE_HINT_SUBTITLE)
+ )
+ .addText(
+ autoSelectAllowed, /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_ALLOWED)
+ )
+ if (lastUsedTime != null) {
+ sliceBuilder.addLong(
+ lastUsedTime.toEpochMilli(),
+ /*subType=*/null,
+ listOf(SLICE_HINT_LAST_USED_TIME_MILLIS)
+ )
+ }
+ if (icon != null) {
+ sliceBuilder.addIcon(
+ icon, /*subType=*/null,
+ listOf(SLICE_HINT_ICON)
+ )
+ }
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(listOf(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+
+
+ private const val PASSKEY_SLICE_HINT_TYPE_DISPLAY_NAME =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+ private const val PASSKEY_SLICE_HINT_TITLE =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_USER_NAME"
+ private const val PASSKEY_SLICE_HINT_SUBTITLE =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+ private const val PASSKEY_SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+ private const val PASSKEY_SLICE_HINT_ICON =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_PROFILE_ICON"
+ private const val PASSKEY_SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_PENDING_INTENT"
+ private const val PASSKEY_SLICE_HINT_AUTO_ALLOWED =
+ "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_AUTO_ALLOWED"
+
+ internal fun newPasskeyEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ userName: String,
+ userDisplayName: String?,
+ lastUsedTime: Instant?,
+ ): Entry {
+ val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+ .setPackage("com.androidauth.androidvault")
+ intent.putExtra("provider_extra_sample", "testprovider")
+ val pendingIntent = PendingIntent.getActivity(
+ context, 1,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ or PendingIntent.FLAG_ONE_SHOT)
+ )
+ return Entry(
+ key, subkey, toPasskeySlice(
+ userName, userDisplayName, pendingIntent, lastUsedTime
+ ),
+ Intent()
+ )
+ }
+
+ private fun toPasskeySlice(
+ title: CharSequence,
+ subTitle: CharSequence?,
+ pendingIntent: PendingIntent,
+ lastUsedTime: Instant?,
+ icon: Icon? = null,
+ isAutoSelectAllowed: Boolean = true
+ ): Slice {
+ val type = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
+ val autoSelectAllowed = if (isAutoSelectAllowed) {
+ AUTO_SELECT_TRUE_STRING
+ } else {
+ AUTO_SELECT_FALSE_STRING
+ }
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ type, 1
+ )
+ )
+ .addText(
+ "Passkey", /*subType=*/null,
+ listOf(PASSKEY_SLICE_HINT_TYPE_DISPLAY_NAME)
+ )
+ .addText(
+ title, /*subType=*/null,
+ listOf(PASSKEY_SLICE_HINT_TITLE)
+ )
+ .addText(
+ subTitle, /*subType=*/null,
+ listOf(PASSKEY_SLICE_HINT_SUBTITLE)
+ )
+ .addText(
+ autoSelectAllowed, /*subType=*/null,
+ listOf(PASSKEY_SLICE_HINT_AUTO_ALLOWED)
+ )
+ if (lastUsedTime != null) {
+ sliceBuilder.addLong(
+ lastUsedTime.toEpochMilli(),
+ /*subType=*/null,
+ listOf(PASSKEY_SLICE_HINT_LAST_USED_TIME_MILLIS)
+ )
+ }
+ if (icon != null) {
+ sliceBuilder.addIcon(
+ icon, /*subType=*/null,
+ listOf(PASSKEY_SLICE_HINT_ICON)
+ )
+ }
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(listOf(PASSKEY_SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+ }
+}
+
+class CreateTestUtils {
+ companion object {
+ private const val TYPE_TOTAL_CREDENTIAL = "TOTAL_CREDENTIAL_COUNT_TYPE"
+ private const val SLICE_HINT_ACCOUNT_NAME =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
+ private const val SLICE_HINT_NOTE =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_NOTE"
+ private const val SLICE_HINT_ICON =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
+ private const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
+ private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+ private const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
+
+ internal fun newCreateEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ providerUserDisplayName: String,
+ passwordCount: Int?,
+ passkeyCount: Int?,
+ totalCredentialCount: Int?,
+ lastUsedTime: Instant?,
+ footerDescription: String?,
+ ): Entry {
+ val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+ .setPackage("com.androidauth.androidvault")
+ intent.putExtra("provider_extra_sample", "testprovider")
+ val pendingIntent = PendingIntent.getActivity(
+ context, 1,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ or PendingIntent.FLAG_ONE_SHOT)
+ )
+ val credCountMap = mutableMapOf<String, Int>()
+ passwordCount?.let { credCountMap.put(TYPE_PASSWORD_CREDENTIAL, it) }
+ passkeyCount?.let {
+ credCountMap.put("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", it)
+ }
+ totalCredentialCount?.let { credCountMap.put(TYPE_TOTAL_CREDENTIAL, it) }
+ return Entry(
+ key,
+ subkey,
+ CreateEntry.toSlice(
+ providerUserDisplayName,
+ null,
+ footerDescription,
+ lastUsedTime,
+ credCountMap,
+ pendingIntent
+ ),
+ Intent()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
similarity index 64%
rename from packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
index 497c272..cc92f60 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,12 +14,8 @@
* limitations under the License.
*/
-package com.android.credentialmanager.jetpack.developer
+package com.android.credentialmanager.common
-/**
- * Internal exception used to indicate a parsing error while converting from a framework type to
- * a jetpack type.
- *
- * @hide
- */
-internal class FrameworkClassParsingException : Exception()
\ No newline at end of file
+enum class CredentialType {
+ UNKNOWN, PASSKEY, PASSWORD,
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 5e432b9..f8d008e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -2,7 +2,6 @@
package com.android.credentialmanager.createflow
-import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
@@ -37,6 +36,7 @@
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetLayout
import com.android.credentialmanager.common.material.ModalBottomSheetValue
@@ -50,7 +50,6 @@
import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.ui.theme.EntryShape
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -288,11 +287,11 @@
text = stringResource(
R.string.choose_provider_title,
when (requestDisplayInfo.type) {
- TYPE_PUBLIC_KEY_CREDENTIAL ->
+ CredentialType.PASSKEY ->
stringResource(R.string.passkeys)
- TYPE_PASSWORD_CREDENTIAL ->
+ CredentialType.PASSWORD ->
stringResource(R.string.passwords)
- else -> stringResource(R.string.sign_in_info)
+ CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
}),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 24.dp)
@@ -398,11 +397,11 @@
stringResource(
R.string.save_credential_to_title,
when (requestDisplayInfo.type) {
- TYPE_PUBLIC_KEY_CREDENTIAL ->
+ CredentialType.PASSKEY ->
stringResource(R.string.passkey)
- TYPE_PASSWORD_CREDENTIAL ->
+ CredentialType.PASSWORD ->
stringResource(R.string.password)
- else -> stringResource(R.string.sign_in_info)
+ CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
}),
style = MaterialTheme.typography.titleMedium,
)
@@ -571,15 +570,15 @@
)
TextOnSurface(
text = when (requestDisplayInfo.type) {
- TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(
+ CredentialType.PASSKEY -> stringResource(
R.string.choose_create_option_passkey_title,
requestDisplayInfo.appName
)
- TYPE_PASSWORD_CREDENTIAL -> stringResource(
+ CredentialType.PASSWORD -> stringResource(
R.string.choose_create_option_password_title,
requestDisplayInfo.appName
)
- else -> stringResource(
+ CredentialType.UNKNOWN -> stringResource(
R.string.choose_create_option_sign_in_title,
requestDisplayInfo.appName
)
@@ -828,7 +827,7 @@
Column() {
// TODO: Add the function to hide/view password when the type is create password
when (requestDisplayInfo.type) {
- TYPE_PUBLIC_KEY_CREDENTIAL -> {
+ CredentialType.PASSKEY -> {
TextOnSurfaceVariant(
text = requestDisplayInfo.title,
style = MaterialTheme.typography.titleLarge,
@@ -846,7 +845,7 @@
modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
)
}
- TYPE_PASSWORD_CREDENTIAL -> {
+ CredentialType.PASSWORD -> {
TextOnSurfaceVariant(
text = requestDisplayInfo.title,
style = MaterialTheme.typography.titleLarge,
@@ -859,7 +858,7 @@
modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
)
}
- else -> {
+ CredentialType.UNKNOWN -> {
if (requestDisplayInfo.subtitle != null) {
TextOnSurfaceVariant(
text = requestDisplayInfo.title,
@@ -917,8 +916,8 @@
modifier = Modifier.padding(start = 5.dp),
)
}
- if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL ||
- requestDisplayInfo.type == TYPE_PASSWORD_CREDENTIAL) {
+ if (requestDisplayInfo.type == CredentialType.PASSKEY ||
+ requestDisplayInfo.type == CredentialType.PASSWORD) {
if (createOptionInfo.passwordCount != null &&
createOptionInfo.passkeyCount != null
) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 12a5085..05be0a6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -21,6 +21,8 @@
import android.graphics.drawable.Drawable
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityState
+import com.android.credentialmanager.common.CredentialType
+import java.time.Instant
data class CreateCredentialUiState(
val enabledProviders: List<EnabledProviderInfo>,
@@ -68,18 +70,18 @@
)
class CreateOptionInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val userProviderDisplayName: String?,
- val profileIcon: Drawable?,
- val passwordCount: Int?,
- val passkeyCount: Int?,
- val totalCredentialCount: Int?,
- val lastUsedTimeMillis: Long?,
- val footerDescription: String?,
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val userProviderDisplayName: String?,
+ val profileIcon: Drawable?,
+ val passwordCount: Int?,
+ val passkeyCount: Int?,
+ val totalCredentialCount: Int?,
+ val lastUsedTime: Instant?,
+ val footerDescription: String?,
) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
class RemoteInfo(
@@ -93,7 +95,7 @@
data class RequestDisplayInfo(
val title: String,
val subtitle: String?,
- val type: String,
+ val type: CredentialType,
val appName: String,
val typeIcon: Drawable,
)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index b30d1ec..a5e19b6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -16,7 +16,6 @@
package com.android.credentialmanager.getflow
-import android.credentials.Credential
import android.text.TextUtils
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
@@ -59,6 +58,7 @@
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetLayout
import com.android.credentialmanager.common.material.ModalBottomSheetValue
@@ -71,7 +71,6 @@
import com.android.credentialmanager.common.ui.TextOnSurfaceVariant
import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.common.ui.TransparentBackgroundEntry
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
import com.android.credentialmanager.ui.theme.EntryShape
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
@@ -171,7 +170,7 @@
) {
if (sortedUserNameToCredentialEntryList.first()
.sortedCredentialEntryList.first().credentialType
- == PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
+ == CredentialType.PASSKEY
) R.string.get_dialog_title_use_passkey_for
else R.string.get_dialog_title_use_sign_in_for
} else if (
@@ -534,7 +533,7 @@
)
TextSecondary(
text = if (
- credentialEntryInfo.credentialType == Credential.TYPE_PASSWORD_CREDENTIAL) {
+ credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
"••••••••••••"
} else {
if (TextUtils.isEmpty(credentialEntryInfo.displayName))
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
index 7d2f0da..5741f36 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -26,11 +26,11 @@
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.android.credentialmanager.CredentialManagerRepo
+import com.android.credentialmanager.common.CredentialType
import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.ProviderActivityState
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
import com.android.internal.util.Preconditions
data class GetCredentialUiState(
@@ -258,9 +258,9 @@
override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
// First prefer passkey type for its security benefits
if (p0.credentialType != p1.credentialType) {
- if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p0.credentialType) {
+ if (CredentialType.PASSKEY == p0.credentialType) {
return -1
- } else if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p1.credentialType) {
+ } else if (CredentialType.PASSKEY == p1.credentialType) {
return 1
}
}
@@ -272,9 +272,9 @@
} else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
return -1
}
- } else if (p0.lastUsedTimeMillis != null && p0.lastUsedTimeMillis > 0) {
+ } else if (p0.lastUsedTimeMillis != null) {
return -1
- } else if (p1.lastUsedTimeMillis != null && p1.lastUsedTimeMillis > 0) {
+ } else if (p1.lastUsedTimeMillis != null) {
return 1
}
return 0
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 8ce31bd..4c05dea 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -19,6 +19,9 @@
import android.app.PendingIntent
import android.content.Intent
import android.graphics.drawable.Drawable
+import com.android.credentialmanager.common.CredentialType
+
+import java.time.Instant
data class ProviderInfo(
/**
@@ -62,13 +65,13 @@
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
/** Type of this credential used for sorting. Not localized so must not be directly displayed. */
- val credentialType: String,
+ val credentialType: CredentialType,
/** Localized type value of this credential used for display purpose. */
val credentialTypeDisplayName: String,
val userName: String,
val displayName: String?,
val icon: Drawable?,
- val lastUsedTimeMillis: Long?,
+ val lastUsedTimeMillis: Instant?,
) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
class AuthenticationEntryInfo(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt
deleted file mode 100644
index eaa2ad4..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.credentials.Credential
-import android.os.Bundle
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.BUNDLE_KEY_SUBTYPE
-
-/**
- * Base request class for registering a credential.
- *
- * @property type the credential type
- * @property data the request data in the [Bundle] format
- * @property requireSystemProvider true if must only be fulfilled by a system provider and false
- * otherwise
- */
-open class CreateCredentialRequest(
- open val type: String,
- open val credentialData: Bundle,
- open val candidateQueryData: Bundle,
- open val requireSystemProvider: Boolean
-) {
- companion object {
- @JvmStatic
- fun createFrom(
- type: String,
- credentialData: Bundle,
- candidateQueryData: Bundle,
- requireSystemProvider: Boolean
- ): CreateCredentialRequest {
- return try {
- when (type) {
- Credential.TYPE_PASSWORD_CREDENTIAL ->
- CreatePasswordRequest.createFrom(credentialData)
- PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
- when (credentialData.getString(BUNDLE_KEY_SUBTYPE)) {
- CreatePublicKeyCredentialRequest
- .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST ->
- CreatePublicKeyCredentialRequest.createFrom(credentialData)
- CreatePublicKeyCredentialRequestPrivileged
- .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIV ->
- CreatePublicKeyCredentialRequestPrivileged
- .createFrom(credentialData)
- else -> throw FrameworkClassParsingException()
- }
- else -> throw FrameworkClassParsingException()
- }
- } catch (e: FrameworkClassParsingException) {
- // Parsing failed but don't crash the process. Instead just output a request with
- // the raw framework values.
- CreateCustomCredentialRequest(
- type,
- credentialData,
- candidateQueryData,
- requireSystemProvider
- )
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCustomCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCustomCredentialRequest.kt
deleted file mode 100644
index 50da9a1..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCustomCredentialRequest.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.jetpack.developer
-
-import android.os.Bundle
-
-/**
- * Base custom create request class for registering a credential.
- *
- * An application can construct a subtype custom request and call
- * [CredentialManager.executeCreateCredential] to launch framework UI flows to collect consent and
- * any other metadata needed from the user to register a new user credential.
- *
- * @property type the credential type determined by the credential-type-specific subclass for custom
- * use cases
- * @property credentialData the full credential creation request data in the [Bundle] format for
- * custom use cases
- * @property candidateQueryData the partial request data in the [Bundle] format that will be sent to
- * the provider during the initial candidate query stage, which should not contain sensitive user
- * credential information
- * @property requireSystemProvider true if must only be fulfilled by a system provider and false
- * otherwise
- * @throws IllegalArgumentException If [type] is empty
- * @throws NullPointerException If [type] or [credentialData] are null
- */
-open class CreateCustomCredentialRequest(
- final override val type: String,
- final override val credentialData: Bundle,
- final override val candidateQueryData: Bundle,
- @get:JvmName("requireSystemProvider")
- final override val requireSystemProvider: Boolean
-) : CreateCredentialRequest(type, credentialData, candidateQueryData, requireSystemProvider) {
- init {
- require(type.isNotEmpty()) { "type should not be empty" }
- }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt
deleted file mode 100644
index bf0aa8a..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.credentials.Credential
-import android.os.Bundle
-
-/**
- * A request to save the user password credential with their password provider.
- *
- * @property id the user id associated with the password
- * @property password the password
- * @throws NullPointerException If [id] is null
- * @throws NullPointerException If [password] is null
- * @throws IllegalArgumentException If [password] is empty
- */
-class CreatePasswordRequest constructor(
- val id: String,
- val password: String,
-) : CreateCredentialRequest(
- type = Credential.TYPE_PASSWORD_CREDENTIAL,
- credentialData = toCredentialDataBundle(id, password),
- // No credential data should be sent during the query phase.
- candidateQueryData = Bundle(),
- requireSystemProvider = false,
-) {
-
- init {
- require(password.isNotEmpty()) { "password should not be empty" }
- }
-
- companion object {
- const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
- const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
-
- @JvmStatic
- internal fun toCredentialDataBundle(id: String, password: String): Bundle {
- val bundle = Bundle()
- bundle.putString(BUNDLE_KEY_ID, id)
- bundle.putString(BUNDLE_KEY_PASSWORD, password)
- return bundle
- }
-
- @JvmStatic
- internal fun toCandidateDataBundle(id: String): Bundle {
- val bundle = Bundle()
- bundle.putString(BUNDLE_KEY_ID, id)
- return bundle
- }
-
- @JvmStatic
- internal fun createFrom(data: Bundle): CreatePasswordRequest {
- try {
- val id = data.getString(BUNDLE_KEY_ID)
- val password = data.getString(BUNDLE_KEY_PASSWORD)
- return CreatePasswordRequest(id!!, password!!)
- } catch (e: Exception) {
- throw FrameworkClassParsingException()
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt
deleted file mode 100644
index f3d402a..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.BUNDLE_KEY_SUBTYPE
-
-/**
-* A request to register a passkey from the user's public key credential provider.
-*
-* @property requestJson the privileged request in JSON format in the standard webauthn web json
-* shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
-* @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
-* immediately when there is no available passkey registration offering instead of falling back to
-* discovering remote options, and false (default) otherwise
-* @throws NullPointerException If [requestJson] is null
-* @throws IllegalArgumentException If [requestJson] is empty
-*/
-class CreatePublicKeyCredentialRequest @JvmOverloads constructor(
- val requestJson: String,
- @get:JvmName("preferImmediatelyAvailableCredentials")
- val preferImmediatelyAvailableCredentials: Boolean = false
-) : CreateCredentialRequest(
- type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
- credentialData = toCredentialDataBundle(requestJson, preferImmediatelyAvailableCredentials),
- // The whole request data should be passed during the query phase.
- candidateQueryData = toCredentialDataBundle(requestJson, preferImmediatelyAvailableCredentials),
- requireSystemProvider = false,
-) {
-
- init {
- require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
- }
-
- /** @hide */
- companion object {
- const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
- internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
- internal const val BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST =
- "androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST"
-
- @JvmStatic
- internal fun toCredentialDataBundle(
- requestJson: String,
- preferImmediatelyAvailableCredentials: Boolean
- ): Bundle {
- val bundle = Bundle()
- bundle.putString(BUNDLE_KEY_SUBTYPE,
- BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST)
- bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials)
- return bundle
- }
-
- @JvmStatic
- internal fun toCandidateDataBundle(
- requestJson: String,
- preferImmediatelyAvailableCredentials: Boolean
- ): Bundle {
- val bundle = Bundle()
- bundle.putString(BUNDLE_KEY_SUBTYPE,
- BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST)
- bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials)
- return bundle
- }
-
- @Suppress("deprecation") // bundle.get() used for boolean value to prevent default
- // boolean value from being returned.
- @JvmStatic
- internal fun createFrom(data: Bundle): CreatePublicKeyCredentialRequest {
- try {
- val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
- val preferImmediatelyAvailableCredentials =
- data.get(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
- return CreatePublicKeyCredentialRequest(requestJson!!,
- (preferImmediatelyAvailableCredentials!!) as Boolean)
- } catch (e: Exception) {
- throw FrameworkClassParsingException()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt
deleted file mode 100644
index 85ab2d2..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.BUNDLE_KEY_SUBTYPE
-
-/**
- * A privileged request to register a passkey from the user’s public key credential provider, where
- * the caller can modify the rp. Only callers with privileged permission, e.g. user’s default
- * brower, caBLE, can use this. These permissions will be introduced in an upcoming release.
- * TODO("Add specific permission info/annotation")
- *
- * @property requestJson the privileged request in JSON format in the standard webauthn web json
- * shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
- * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
- * immediately when there is no available passkey registration offering instead of falling back to
- * discovering remote options, and false (default) otherwise
- * @property relyingParty the expected true RP ID which will override the one in the [requestJson],
- * where rp is defined [here](https://w3c.github.io/webauthn/#rp-id)
- * @property clientDataHash a hash that is used to verify the [relyingParty] Identity
- * @throws NullPointerException If any of [requestJson], [relyingParty], or [clientDataHash] is
- * null
- * @throws IllegalArgumentException If any of [requestJson], [relyingParty], or [clientDataHash] is
- * empty
- */
-class CreatePublicKeyCredentialRequestPrivileged @JvmOverloads constructor(
- val requestJson: String,
- val relyingParty: String,
- val clientDataHash: String,
- @get:JvmName("preferImmediatelyAvailableCredentials")
- val preferImmediatelyAvailableCredentials: Boolean = false
-) : CreateCredentialRequest(
- type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
- credentialData = toCredentialDataBundle(
- requestJson,
- relyingParty,
- clientDataHash,
- preferImmediatelyAvailableCredentials
- ),
- // The whole request data should be passed during the query phase.
- candidateQueryData = toCredentialDataBundle(
- requestJson, relyingParty, clientDataHash, preferImmediatelyAvailableCredentials
- ),
- requireSystemProvider = false,
-) {
-
- init {
- require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
- require(relyingParty.isNotEmpty()) { "rp must not be empty" }
- require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" }
- }
-
- /** A builder for [CreatePublicKeyCredentialRequestPrivileged]. */
- class Builder(
- private var requestJson: String,
- private var relyingParty: String,
- private var clientDataHash: String
- ) {
-
- private var preferImmediatelyAvailableCredentials: Boolean = false
-
- /**
- * Sets the privileged request in JSON format.
- */
- fun setRequestJson(requestJson: String): Builder {
- this.requestJson = requestJson
- return this
- }
-
- /**
- * Sets to true if you prefer the operation to return immediately when there is no available
- * passkey registration offering instead of falling back to discovering remote options, and
- * false otherwise.
- *
- * The default value is false.
- */
- @Suppress("MissingGetterMatchingBuilder")
- fun setPreferImmediatelyAvailableCredentials(
- preferImmediatelyAvailableCredentials: Boolean
- ): Builder {
- this.preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
- return this
- }
-
- /**
- * Sets the expected true RP ID which will override the one in the [requestJson].
- */
- fun setRelyingParty(relyingParty: String): Builder {
- this.relyingParty = relyingParty
- return this
- }
-
- /**
- * Sets a hash that is used to verify the [relyingParty] Identity.
- */
- fun setClientDataHash(clientDataHash: String): Builder {
- this.clientDataHash = clientDataHash
- return this
- }
-
- /** Builds a [CreatePublicKeyCredentialRequestPrivileged]. */
- fun build(): CreatePublicKeyCredentialRequestPrivileged {
- return CreatePublicKeyCredentialRequestPrivileged(
- this.requestJson,
- this.relyingParty, this.clientDataHash, this.preferImmediatelyAvailableCredentials
- )
- }
- }
-
- /** @hide */
- companion object {
- internal const val BUNDLE_KEY_RELYING_PARTY =
- "androidx.credentials.BUNDLE_KEY_RELYING_PARTY"
- internal const val BUNDLE_KEY_CLIENT_DATA_HASH =
- "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
- internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
-
- internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
-
- internal const val BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIV =
- "androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_" +
- "PRIVILEGED"
-
- @JvmStatic
- internal fun toCredentialDataBundle(
- requestJson: String,
- relyingParty: String,
- clientDataHash: String,
- preferImmediatelyAvailableCredentials: Boolean
- ): Bundle {
- val bundle = Bundle()
- bundle.putString(
- PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
- BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIV
- )
- bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putString(BUNDLE_KEY_RELYING_PARTY, relyingParty)
- bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
- bundle.putBoolean(
- BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials
- )
- return bundle
- }
-
- @Suppress("deprecation") // bundle.get() used for boolean value to prevent default
- // boolean value from being returned.
- @JvmStatic
- internal fun createFrom(data: Bundle): CreatePublicKeyCredentialRequestPrivileged {
- try {
- val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
- val rp = data.getString(BUNDLE_KEY_RELYING_PARTY)
- val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH)
- val preferImmediatelyAvailableCredentials =
- data.get(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
- return CreatePublicKeyCredentialRequestPrivileged(
- requestJson!!,
- rp!!,
- clientDataHash!!,
- (preferImmediatelyAvailableCredentials!!) as Boolean,
- )
- } catch (e: Exception) {
- throw FrameworkClassParsingException()
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt
deleted file mode 100644
index ee08e9e..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-
-/**
- * Base class for a credential with which the user consented to authenticate to the app.
- *
- * @property type the credential type
- * @property data the credential data in the [Bundle] format.
- */
-open class Credential(val type: String, val data: Bundle)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
deleted file mode 100644
index fc7b7de..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.credentials.Credential
-import android.os.Bundle
-
-/**
- * Base request class for getting a registered credential.
- *
- * @property type the credential type
- * @property data the request data in the [Bundle] format
- * @property requireSystemProvider true if must only be fulfilled by a system provider and false
- * otherwise
- */
-open class GetCredentialOption(
- open val type: String,
- open val requestData: Bundle,
- open val candidateQueryData: Bundle,
- open val requireSystemProvider: Boolean,
-) {
- companion object {
- @JvmStatic
- fun createFrom(
- type: String,
- requestData: Bundle,
- candidateQueryData: Bundle,
- requireSystemProvider: Boolean
- ): GetCredentialOption {
- return try {
- when (type) {
- Credential.TYPE_PASSWORD_CREDENTIAL ->
- GetPasswordOption.createFrom(requestData)
- PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
- when (requestData.getString(PublicKeyCredential.BUNDLE_KEY_SUBTYPE)) {
- GetPublicKeyCredentialOption
- .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION ->
- GetPublicKeyCredentialOption.createFrom(requestData)
- GetPublicKeyCredentialOptionPrivileged
- .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED ->
- GetPublicKeyCredentialOptionPrivileged.createFrom(requestData)
- else -> throw FrameworkClassParsingException()
- }
- else -> throw FrameworkClassParsingException()
- }
- } catch (e: FrameworkClassParsingException) {
- // Parsing failed but don't crash the process. Instead just output a request with
- // the raw framework values.
- GetCustomCredentialOption(
- type, requestData, candidateQueryData, requireSystemProvider)
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt
deleted file mode 100644
index fdd57ff..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-/**
- * Encapsulates a request to get a user credential.
- *
- * @property getCredentialOptions the list of [GetCredentialOption] from which the user can choose
- * one to authenticate to the app
- * @throws IllegalArgumentException If [getCredentialOptions] is empty
- */
-class GetCredentialRequest constructor(
- val getCredentialOptions: List<GetCredentialOption>,
-) {
-
- init {
- require(getCredentialOptions.isNotEmpty()) { "credentialRequests should not be empty" }
- }
-
- /** A builder for [GetCredentialRequest]. */
- class Builder {
- private var getCredentialOptions: MutableList<GetCredentialOption> = mutableListOf()
-
- /** Adds a specific type of [GetCredentialOption]. */
- fun addGetCredentialOption(getCredentialOption: GetCredentialOption): Builder {
- getCredentialOptions.add(getCredentialOption)
- return this
- }
-
- /** Sets the list of [GetCredentialOption]. */
- fun setGetCredentialOptions(getCredentialOptions: List<GetCredentialOption>): Builder {
- this.getCredentialOptions = getCredentialOptions.toMutableList()
- return this
- }
-
- /**
- * Builds a [GetCredentialRequest].
- *
- * @throws IllegalArgumentException If [getCredentialOptions] is empty
- */
- fun build(): GetCredentialRequest {
- return GetCredentialRequest(getCredentialOptions.toList())
- }
- }
-
- companion object {
- @JvmStatic
- fun createFrom(from: android.credentials.GetCredentialRequest): GetCredentialRequest {
- return GetCredentialRequest(
- from.credentialOptions.map {
- GetCredentialOption.createFrom(
- it.type,
- it.credentialRetrievalData,
- it.candidateQueryData,
- it.isSystemProviderRequired()
- )
- }
- )
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCustomCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCustomCredentialOption.kt
deleted file mode 100644
index 803885c..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCustomCredentialOption.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.jetpack.developer
-
-import android.os.Bundle
-
-/**
- * Allows extending custom versions of GetCredentialOptions for unique use cases.
- *
- * @property type the credential type determined by the credential-type-specific subclass
- * generated for custom use cases
- * @property requestData the request data in the [Bundle] format, generated for custom use cases
- * @property candidateQueryData the partial request data in the [Bundle] format that will be sent to
- * the provider during the initial candidate query stage, which should not contain sensitive user
- * information
- * @property requireSystemProvider true if must only be fulfilled by a system provider and false
- * otherwise
- * @throws IllegalArgumentException If [type] is empty
- * @throws NullPointerException If [requestData] or [type] is null
- */
-open class GetCustomCredentialOption(
- final override val type: String,
- final override val requestData: Bundle,
- final override val candidateQueryData: Bundle,
- @get:JvmName("requireSystemProvider")
- final override val requireSystemProvider: Boolean
-) : GetCredentialOption(
- type,
- requestData,
- candidateQueryData,
- requireSystemProvider
-) {
- init {
- require(type.isNotEmpty()) { "type should not be empty" }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt
deleted file mode 100644
index 2b9cfa3..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.credentials.Credential
-import android.os.Bundle
-
-/** A request to retrieve the user's saved application password from their password provider. */
-class GetPasswordOption : GetCredentialOption(
- Credential.TYPE_PASSWORD_CREDENTIAL,
- Bundle(),
- Bundle(),
- false,
-) {
- companion object {
- @JvmStatic
- fun createFrom(data: Bundle): GetPasswordOption {
- return GetPasswordOption()
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt
deleted file mode 100644
index 2f9b249..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-
-/**
- * A request to get passkeys from the user's public key credential provider.
- *
- * @property requestJson the privileged request in JSON format in the standard webauthn web json
- * shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
- * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
- * immediately when there is no available credential instead of falling back to discovering remote
- * credentials, and false (default) otherwise
- * @throws NullPointerException If [requestJson] is null
- * @throws IllegalArgumentException If [requestJson] is empty
- */
-class GetPublicKeyCredentialOption @JvmOverloads constructor(
- val requestJson: String,
- @get:JvmName("preferImmediatelyAvailableCredentials")
- val preferImmediatelyAvailableCredentials: Boolean = false,
-) : GetCredentialOption(
- type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
- requestData = toRequestDataBundle(requestJson, preferImmediatelyAvailableCredentials),
- candidateQueryData = toRequestDataBundle(requestJson, preferImmediatelyAvailableCredentials),
- requireSystemProvider = false
-) {
- init {
- require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
- }
-
- /** @hide */
- companion object {
- internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
- internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
- internal const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION =
- "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION"
-
- @JvmStatic
- internal fun toRequestDataBundle(
- requestJson: String,
- preferImmediatelyAvailableCredentials: Boolean
- ): Bundle {
- val bundle = Bundle()
- bundle.putString(
- PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
- BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION
- )
- bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials)
- return bundle
- }
-
- @Suppress("deprecation") // bundle.get() used for boolean value to prevent default
- // boolean value from being returned.
- @JvmStatic
- internal fun createFrom(data: Bundle): GetPublicKeyCredentialOption {
- try {
- val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
- val preferImmediatelyAvailableCredentials =
- data.get(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
- return GetPublicKeyCredentialOption(requestJson!!,
- (preferImmediatelyAvailableCredentials!!) as Boolean)
- } catch (e: Exception) {
- throw FrameworkClassParsingException()
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt
deleted file mode 100644
index 6f4782a..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-
-/**
- * A privileged request to get passkeys from the user's public key credential provider. The caller
- * can modify the RP. Only callers with privileged permission (e.g. user's public browser or caBLE)
- * can use this. These permissions will be introduced in an upcoming release.
- * TODO("Add specific permission info/annotation")
- *
- * @property requestJson the privileged request in JSON format in the standard webauthn web json
- * shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
- * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
- * immediately when there is no available credential instead of falling back to discovering remote
- * credentials, and false (default) otherwise
- * @property relyingParty the expected true RP ID which will override the one in the [requestJson],
- * where relyingParty is defined [here](https://w3c.github.io/webauthn/#rp-id) in more detail
- * @property clientDataHash a hash that is used to verify the [relyingParty] Identity
- * @throws NullPointerException If any of [requestJson], [relyingParty], or [clientDataHash]
- * is null
- * @throws IllegalArgumentException If any of [requestJson], [relyingParty], or [clientDataHash] is
- * empty
- */
-class GetPublicKeyCredentialOptionPrivileged @JvmOverloads constructor(
- val requestJson: String,
- val relyingParty: String,
- val clientDataHash: String,
- @get:JvmName("preferImmediatelyAvailableCredentials")
- val preferImmediatelyAvailableCredentials: Boolean = false
-) : GetCredentialOption(
- type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
- requestData = toBundle(
- requestJson,
- relyingParty,
- clientDataHash,
- preferImmediatelyAvailableCredentials
- ),
- candidateQueryData = toBundle(
- requestJson,
- relyingParty,
- clientDataHash,
- preferImmediatelyAvailableCredentials
- ),
- requireSystemProvider = false,
-) {
-
- init {
- require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
- require(relyingParty.isNotEmpty()) { "rp must not be empty" }
- require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" }
- }
-
- /** A builder for [GetPublicKeyCredentialOptionPrivileged]. */
- class Builder(
- private var requestJson: String,
- private var relyingParty: String,
- private var clientDataHash: String
- ) {
-
- private var preferImmediatelyAvailableCredentials: Boolean = false
-
- /**
- * Sets the privileged request in JSON format.
- */
- fun setRequestJson(requestJson: String): Builder {
- this.requestJson = requestJson
- return this
- }
-
- /**
- * Sets to true if you prefer the operation to return immediately when there is no available
- * credential instead of falling back to discovering remote credentials, and false
- * otherwise.
- *
- * The default value is false.
- */
- @Suppress("MissingGetterMatchingBuilder")
- fun setPreferImmediatelyAvailableCredentials(
- preferImmediatelyAvailableCredentials: Boolean
- ): Builder {
- this.preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
- return this
- }
-
- /**
- * Sets the expected true RP ID which will override the one in the [requestJson].
- */
- fun setRelyingParty(relyingParty: String): Builder {
- this.relyingParty = relyingParty
- return this
- }
-
- /**
- * Sets a hash that is used to verify the [relyingParty] Identity.
- */
- fun setClientDataHash(clientDataHash: String): Builder {
- this.clientDataHash = clientDataHash
- return this
- }
-
- /** Builds a [GetPublicKeyCredentialOptionPrivileged]. */
- fun build(): GetPublicKeyCredentialOptionPrivileged {
- return GetPublicKeyCredentialOptionPrivileged(
- this.requestJson,
- this.relyingParty, this.clientDataHash, this.preferImmediatelyAvailableCredentials
- )
- }
- }
-
- /** @hide */
- companion object {
- internal const val BUNDLE_KEY_RELYING_PARTY =
- "androidx.credentials.BUNDLE_KEY_RELYING_PARTY"
- internal const val BUNDLE_KEY_CLIENT_DATA_HASH =
- "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
- internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
- internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
- internal const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED =
- "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION" +
- "_PRIVILEGED"
-
- @JvmStatic
- internal fun toBundle(
- requestJson: String,
- relyingParty: String,
- clientDataHash: String,
- preferImmediatelyAvailableCredentials: Boolean
- ): Bundle {
- val bundle = Bundle()
- bundle.putString(
- PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
- BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED
- )
- bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putString(BUNDLE_KEY_RELYING_PARTY, relyingParty)
- bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
- bundle.putBoolean(
- BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials
- )
- return bundle
- }
-
- @Suppress("deprecation") // bundle.get() used for boolean value to prevent default
- // boolean value from being returned.
- @JvmStatic
- internal fun createFrom(data: Bundle): GetPublicKeyCredentialOptionPrivileged {
- try {
- val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
- val rp = data.getString(BUNDLE_KEY_RELYING_PARTY)
- val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH)
- val preferImmediatelyAvailableCredentials =
- data.get(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
- return GetPublicKeyCredentialOptionPrivileged(
- requestJson!!,
- rp!!,
- clientDataHash!!,
- (preferImmediatelyAvailableCredentials!!) as Boolean,
- )
- } catch (e: Exception) {
- throw FrameworkClassParsingException()
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt
deleted file mode 100644
index 1658858..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-
-class PasswordCredential constructor(
- val id: String,
- val password: String,
-) : Credential(android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL, toBundle(id, password)) {
-
- init {
- require(password.isNotEmpty()) { "password should not be empty" }
- }
-
- /** @hide */
- companion object {
-
- const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
- const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
-
- @JvmStatic
- internal fun toBundle(id: String, password: String): Bundle {
- val bundle = Bundle()
- bundle.putString(BUNDLE_KEY_ID, id)
- bundle.putString(BUNDLE_KEY_PASSWORD, password)
- return bundle
- }
-
- @JvmStatic
- internal fun createFrom(data: Bundle): PasswordCredential {
- try {
- val id = data.getString(BUNDLE_KEY_ID)
- val password = data.getString(BUNDLE_KEY_PASSWORD)
- return PasswordCredential(id!!, password!!)
- } catch (e: Exception) {
- throw FrameworkClassParsingException()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt
deleted file mode 100644
index 6a81167..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.developer
-
-import android.os.Bundle
-
-/**
- * Represents the user's passkey credential granted by the user for app sign-in.
- *
- * @property authenticationResponseJson the public key credential authentication response in
- * JSON format that follows the standard webauthn json format shown at
- * [this w3c link](https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson)
- * @throws NullPointerException If [authenticationResponseJson] is null. This is handled by the
- * kotlin runtime
- * @throws IllegalArgumentException If [authenticationResponseJson] is empty
- *
- * @hide
- */
-class PublicKeyCredential constructor(
- val authenticationResponseJson: String
-) : Credential(
- TYPE_PUBLIC_KEY_CREDENTIAL,
- toBundle(authenticationResponseJson)
-) {
-
- init {
- require(authenticationResponseJson.isNotEmpty()) {
- "authentication response JSON must not be empty" }
- }
- companion object {
- /** The type value for public key credential related operations. */
- const val TYPE_PUBLIC_KEY_CREDENTIAL: String =
- "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
- /** The Bundle key value for the public key credential subtype (privileged or regular). */
- internal const val BUNDLE_KEY_SUBTYPE = "androidx.credentials.BUNDLE_KEY_SUBTYPE"
- const val BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON =
- "androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON"
-
- @JvmStatic
- internal fun toBundle(authenticationResponseJson: String): Bundle {
- val bundle = Bundle()
- bundle.putString(BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, authenticationResponseJson)
- return bundle
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt
deleted file mode 100644
index 1abf911..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.provider
-
-import android.annotation.SuppressLint
-import android.app.PendingIntent
-import android.app.slice.Slice
-import android.app.slice.SliceSpec
-import android.net.Uri
-import android.util.Log
-import java.util.Collections
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class Action constructor(
- val title: CharSequence,
- val subTitle: CharSequence?,
- val pendingIntent: PendingIntent?,
-) {
-
- init {
- require(title.isNotEmpty()) { "title must not be empty" }
- }
-
- companion object {
- private const val TAG = "Action"
- internal const val SLICE_HINT_TITLE =
- "androidx.credentials.provider.action.HINT_ACTION_TITLE"
- internal const val SLICE_HINT_SUBTITLE =
- "androidx.credentials.provider.action.HINT_ACTION_SUBTEXT"
- internal const val SLICE_HINT_PENDING_INTENT =
- "androidx.credentials.provider.action.SLICE_HINT_PENDING_INTENT"
-
- @JvmStatic
- fun toSlice(action: Action): Slice {
- // TODO("Put the right spec and version value")
- val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
- .addText(action.title, /*subType=*/null,
- listOf(SLICE_HINT_TITLE))
- .addText(action.subTitle, /*subType=*/null,
- listOf(SLICE_HINT_SUBTITLE))
- if (action.pendingIntent != null) {
- sliceBuilder.addAction(action.pendingIntent,
- Slice.Builder(sliceBuilder)
- .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
- .build(),
- /*subType=*/null)
- }
- return sliceBuilder.build()
- }
-
- /**
- * Returns an instance of [Action] derived from a [Slice] object.
- *
- * @param slice the [Slice] object constructed through [toSlice]
- */
- @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
- @JvmStatic
- fun fromSlice(slice: Slice): Action? {
- // TODO("Put the right spec and version value")
- var title: CharSequence = ""
- var subTitle: CharSequence? = null
- var pendingIntent: PendingIntent? = null
-
- slice.items.forEach {
- if (it.hasHint(SLICE_HINT_TITLE)) {
- title = it.text
- } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
- subTitle = it.text
- } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
- pendingIntent = it.action
- }
- }
-
- return try {
- Action(title, subTitle, pendingIntent)
- } catch (e: Exception) {
- Log.i(TAG, "fromSlice failed with: " + e.message)
- null
- }
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/AuthenticationAction.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/AuthenticationAction.kt
deleted file mode 100644
index 283c7ba..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/AuthenticationAction.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.provider
-
-import android.app.PendingIntent
-import android.app.slice.Slice
-import android.util.Log
-import androidx.annotation.VisibleForTesting
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class AuthenticationAction constructor(
- val pendingIntent: PendingIntent
-) {
-
-
- companion object {
- private const val TAG = "AuthenticationAction"
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
- internal const val SLICE_HINT_PENDING_INTENT =
- "androidx.credentials.provider.authenticationAction.SLICE_HINT_PENDING_INTENT"
-
- @JvmStatic
- fun fromSlice(slice: Slice): AuthenticationAction? {
- slice.items.forEach {
- if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
- return try {
- AuthenticationAction(it.action)
- } catch (e: Exception) {
- Log.i(TAG, "fromSlice failed with: " + e.message)
- null
- }
- }
- }
- return null
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt
deleted file mode 100644
index 0ec91d6..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.provider
-
-import android.annotation.SuppressLint
-import android.app.PendingIntent
-import android.app.slice.Slice
-import android.app.slice.SliceSpec
-import android.graphics.drawable.Icon
-import android.net.Uri
-import android.os.Bundle
-import android.util.Log
-import java.util.Collections
-
-/**
- * UI representation for a save entry used during the create credential flow.
- *
- * TODO: move to jetpack.
- */
-class CreateEntry internal constructor(
- val accountName: CharSequence,
- val pendingIntent: PendingIntent?,
- val icon: Icon?,
- val lastUsedTimeMillis: Long,
- val credentialCountInformationList: List<CredentialCountInformation>,
- val footerDescription: CharSequence?,
-) {
-
- init {
- require(accountName.isNotEmpty()) { "accountName must not be empty" }
- }
-
- /**
- * A builder for [CreateEntry]
- *
- * @property accountName the name of the account where the credential will be registered
- * @property pendingIntent the [PendingIntent] that will be fired when the user selects
- * this entry
- *
- * @hide
- */
- class Builder constructor(
- private val accountName: CharSequence,
- private val pendingIntent: PendingIntent? = null
- ) {
-
- private var credentialCountInformationList: MutableList<CredentialCountInformation> =
- mutableListOf()
- private var icon: Icon? = null
- private var lastUsedTimeMillis: Long = 0
- private var footerDescription: CharSequence? = null
-
- /** Adds a [CredentialCountInformation] denoting a given credential
- * type and the count of credentials that the provider has stored for that
- * credential type.
- *
- * This information will be displayed on the [CreateEntry] to help the user
- * make a choice.
- */
- @Suppress("MissingGetterMatchingBuilder")
- fun addCredentialCountInformation(info: CredentialCountInformation): Builder {
- credentialCountInformationList.add(info)
- return this
- }
-
- /** Sets a list of [CredentialCountInformation]. Each item in the list denotes a given
- * credential type and the count of credentials that the provider has stored of that
- * credential type.
- *
- * This information will be displayed on the [CreateEntry] to help the user
- * make a choice.
- */
- fun setCredentialCountInformationList(infoList: List<CredentialCountInformation>): Builder {
- credentialCountInformationList = infoList as MutableList<CredentialCountInformation>
- return this
- }
-
- /** Sets an icon to be displayed with the entry on the UI */
- fun setIcon(icon: Icon?): Builder {
- this.icon = icon
- return this
- }
-
- /** Sets the last time this account was used */
- fun setLastUsedTimeMillis(lastUsedTimeMillis: Long): Builder {
- this.lastUsedTimeMillis = lastUsedTimeMillis
- return this
- }
-
- /** Sets the footer description of this */
- fun setFooterDescription(footerDescription: CharSequence): Builder {
- this.footerDescription = footerDescription
- return this
- }
-
- /**
- * Builds an instance of [CreateEntry]
- *
- * @throws IllegalArgumentException If [accountName] is empty
- */
- fun build(): CreateEntry {
- return CreateEntry(accountName, pendingIntent, icon, lastUsedTimeMillis,
- credentialCountInformationList, footerDescription)
- }
- }
-
- companion object {
- private const val TAG = "CreateEntry"
- internal const val SLICE_HINT_ACCOUNT_NAME =
- "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
- internal const val SLICE_HINT_ICON =
- "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
- internal const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
- "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
- internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
- "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
- internal const val SLICE_HINT_PENDING_INTENT =
- "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
- internal const val SLICE_HINT_FOOTER_DESCRIPTION =
- "androidx.credentials.provider.createEntry.SLICE_HINT_FOOTER_DESCRIPTION"
-
- @JvmStatic
- fun toSlice(createEntry: CreateEntry): Slice {
- // TODO("Use the right type and revision")
- val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
- sliceBuilder.addText(createEntry.accountName, /*subType=*/null,
- listOf(SLICE_HINT_ACCOUNT_NAME))
- .addLong(createEntry.lastUsedTimeMillis, /*subType=*/null, listOf(
- SLICE_HINT_LAST_USED_TIME_MILLIS))
- if (createEntry.icon != null) {
- sliceBuilder.addIcon(createEntry.icon, /*subType=*/null,
- listOf(SLICE_HINT_ICON))
- }
-
- val credentialCountBundle = convertCredentialCountInfoToBundle(
- createEntry.credentialCountInformationList)
- if (credentialCountBundle != null) {
- sliceBuilder.addBundle(convertCredentialCountInfoToBundle(
- createEntry.credentialCountInformationList), null, listOf(
- SLICE_HINT_CREDENTIAL_COUNT_INFORMATION))
- }
- if (createEntry.pendingIntent != null) {
- sliceBuilder.addAction(createEntry.pendingIntent,
- Slice.Builder(sliceBuilder)
- .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
- .build(),
- /*subType=*/null)
- }
- if (createEntry.footerDescription != null) {
- sliceBuilder.addText(createEntry.footerDescription, /*subType=*/null,
- listOf(SLICE_HINT_FOOTER_DESCRIPTION))
- }
- return sliceBuilder.build()
- }
-
- /**
- * Returns an instance of [CreateEntry] derived from a [Slice] object.
- *
- * @param slice the [Slice] object constructed through [toSlice]
- */
- @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
- @JvmStatic
- fun fromSlice(slice: Slice): CreateEntry? {
- // TODO("Put the right spec and version value")
- var accountName: CharSequence = ""
- var icon: Icon? = null
- var pendingIntent: PendingIntent? = null
- var credentialCountInfo: List<CredentialCountInformation> = listOf()
- var lastUsedTimeMillis: Long = 0
- var footerDescription: CharSequence? = null
-
- slice.items.forEach {
- if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {
- accountName = it.text
- } else if (it.hasHint(SLICE_HINT_ICON)) {
- icon = it.icon
- } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
- pendingIntent = it.action
- } else if (it.hasHint(SLICE_HINT_CREDENTIAL_COUNT_INFORMATION)) {
- credentialCountInfo = convertBundleToCredentialCountInfo(it.bundle)
- } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
- lastUsedTimeMillis = it.long
- } else if (it.hasHint(SLICE_HINT_FOOTER_DESCRIPTION)) {
- footerDescription = it.text
- }
- }
-
- return try {
- CreateEntry(accountName, pendingIntent, icon,
- lastUsedTimeMillis, credentialCountInfo, footerDescription)
- } catch (e: Exception) {
- Log.i(TAG, "fromSlice failed with: " + e.message)
- null
- }
- }
-
- @JvmStatic
- internal fun convertBundleToCredentialCountInfo(bundle: Bundle?):
- List<CredentialCountInformation> {
- val credentialCountList = ArrayList<CredentialCountInformation>()
- if (bundle == null) {
- return credentialCountList
- }
- bundle.keySet().forEach {
- try {
- credentialCountList.add(
- CredentialCountInformation(it, bundle.getInt(it)))
- } catch (e: Exception) {
- Log.i(TAG, "Issue unpacking credential count info bundle: " + e.message)
- }
- }
- return credentialCountList
- }
-
- @JvmStatic
- internal fun convertCredentialCountInfoToBundle(
- credentialCountInformationList: List<CredentialCountInformation>
- ): Bundle? {
- if (credentialCountInformationList.isEmpty()) {
- return null
- }
- val bundle = Bundle()
- credentialCountInformationList.forEach {
- bundle.putInt(it.type, it.count)
- }
- return bundle
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt
deleted file mode 100644
index aa77b74..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.provider
-
-import android.credentials.Credential
-import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
-
-class CredentialCountInformation constructor(
- val type: String,
- val count: Int
-) {
- companion object {
- @JvmStatic
- fun createPasswordCountInformation(count: Int): CredentialCountInformation {
- return CredentialCountInformation(Credential.TYPE_PASSWORD_CREDENTIAL, count)
- }
-
- @JvmStatic
- fun getPasswordCount(infos: List<CredentialCountInformation>): Int? {
- return getCountForType(infos, Credential.TYPE_PASSWORD_CREDENTIAL)
- }
-
- @JvmStatic
- fun createPublicKeyCountInformation(count: Int): CredentialCountInformation {
- return CredentialCountInformation(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, count)
- }
-
- @JvmStatic
- fun getPasskeyCount(infos: List<CredentialCountInformation>): Int? {
- return getCountForType(infos, PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
- }
-
- @JvmStatic
- fun createTotalCountInformation(count: Int): CredentialCountInformation {
- return CredentialCountInformation("TOTAL_COUNT", count)
- }
-
- @JvmStatic
- fun getTotalCount(infos: List<CredentialCountInformation>): Int? {
- return getCountForType(infos, "TOTAL_COUNT")
- }
-
- private fun getCountForType(infos: List<CredentialCountInformation>, type: String): Int? {
- return infos.firstOrNull { info -> info.type == type }?.count
- }
- }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt
deleted file mode 100644
index 61a104b..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2022 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.jetpack.provider
-
-import android.annotation.SuppressLint
-import android.app.PendingIntent
-import android.app.slice.Slice
-import android.app.slice.SliceSpec
-import android.graphics.drawable.Icon
-import android.net.Uri
-import android.util.Log
-import java.util.Collections
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-open class CredentialEntry constructor(
- // TODO("Add credential type display name for both CredentialEntry & CreateEntry")
- val type: String,
- val typeDisplayName: CharSequence,
- val username: CharSequence,
- val displayName: CharSequence?,
- val pendingIntent: PendingIntent?,
- // TODO("Consider using Instant or other strongly typed time data type")
- val lastUsedTimeMillis: Long,
- val icon: Icon?,
- var autoSelectAllowed: Boolean
-) {
- init {
- require(type.isNotEmpty()) { "type must not be empty" }
- require(username.isNotEmpty()) { "type must not be empty" }
- }
-
- companion object {
- private const val TAG = "CredentialEntry"
- internal const val SLICE_HINT_TYPE_DISPLAY_NAME =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
- internal const val SLICE_HINT_USERNAME =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
- internal const val SLICE_HINT_DISPLAYNAME =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
- internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
- internal const val SLICE_HINT_ICON =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
- internal const val SLICE_HINT_PENDING_INTENT =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
- internal const val SLICE_HINT_AUTO_ALLOWED =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
- internal const val AUTO_SELECT_TRUE_STRING = "true"
- internal const val AUTO_SELECT_FALSE_STRING = "false"
-
- @JvmStatic
- internal fun toSlice(credentialEntry: CredentialEntry): Slice {
- // TODO("Put the right revision value")
- val autoSelectAllowed = if (credentialEntry.autoSelectAllowed) {
- AUTO_SELECT_TRUE_STRING
- } else {
- AUTO_SELECT_FALSE_STRING
- }
- val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(
- credentialEntry.type, 1))
- .addText(credentialEntry.typeDisplayName, /*subType=*/null,
- listOf(SLICE_HINT_TYPE_DISPLAY_NAME))
- .addText(credentialEntry.username, /*subType=*/null,
- listOf(SLICE_HINT_USERNAME))
- .addText(credentialEntry.displayName, /*subType=*/null,
- listOf(SLICE_HINT_DISPLAYNAME))
- .addLong(credentialEntry.lastUsedTimeMillis, /*subType=*/null,
- listOf(SLICE_HINT_LAST_USED_TIME_MILLIS))
- .addText(autoSelectAllowed, /*subType=*/null,
- listOf(SLICE_HINT_AUTO_ALLOWED))
- if (credentialEntry.icon != null) {
- sliceBuilder.addIcon(credentialEntry.icon, /*subType=*/null,
- listOf(SLICE_HINT_ICON))
- }
- if (credentialEntry.pendingIntent != null) {
- sliceBuilder.addAction(credentialEntry.pendingIntent,
- Slice.Builder(sliceBuilder)
- .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
- .build(),
- /*subType=*/null)
- }
- return sliceBuilder.build()
- }
-
- /**
- * Returns an instance of [CredentialEntry] derived from a [Slice] object.
- *
- * @param slice the [Slice] object constructed through [toSlice]
- */
- @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
- @JvmStatic
- fun fromSlice(slice: Slice): CredentialEntry? {
- var typeDisplayName: CharSequence? = null
- var username: CharSequence? = null
- var displayName: CharSequence? = null
- var icon: Icon? = null
- var pendingIntent: PendingIntent? = null
- var lastUsedTimeMillis: Long = 0
- var autoSelectAllowed = false
-
- slice.items.forEach {
- if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
- typeDisplayName = it.text
- } else if (it.hasHint(SLICE_HINT_USERNAME)) {
- username = it.text
- } else if (it.hasHint(SLICE_HINT_DISPLAYNAME)) {
- displayName = it.text
- } else if (it.hasHint(SLICE_HINT_ICON)) {
- icon = it.icon
- } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
- pendingIntent = it.action
- } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
- lastUsedTimeMillis = it.long
- } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
- val autoSelectValue = it.text
- if (autoSelectValue == AUTO_SELECT_TRUE_STRING) {
- autoSelectAllowed = true
- }
- }
- }
-
- return try {
- CredentialEntry(slice.spec!!.type, typeDisplayName!!, username!!,
- displayName, pendingIntent,
- lastUsedTimeMillis, icon, autoSelectAllowed)
- } catch (e: Exception) {
- Log.i(TAG, "fromSlice failed with: " + e.message)
- null
- }
- }
- }
-}