summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt223
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt51
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt55
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt100
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt48
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt226
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt61
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt150
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt89
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt80
10 files changed, 720 insertions, 363 deletions
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 37453c9bc8d4..d7ce5325d22d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -47,6 +47,10 @@ import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
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
// Consider repo per screen, similar to view model?
class CredentialManagerRepo(
@@ -63,7 +67,7 @@ class CredentialManagerRepo(
requestInfo = intent.extras?.getParcelable(
RequestInfo.EXTRA_REQUEST_INFO,
RequestInfo::class.java
- ) ?: testCreatePasskeyRequestInfo()
+ ) ?: testGetRequestInfo()
providerEnabledList = when (requestInfo.type) {
RequestInfo.TYPE_CREATE ->
@@ -258,137 +262,108 @@ class CredentialManagerRepo(
)
}
- private fun newActionEntry(
- key: String,
- subkey: String,
- credentialType: String,
- text: String,
- subtext: String? = null,
- ): Entry {
- val slice = Slice.Builder(
- Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
- ).addText(
- text, null, listOf(Entry.HINT_ACTION_TITLE)
- )
- if (subtext != null) {
- slice.addText(subtext, null, listOf(Entry.HINT_ACTION_SUBTEXT))
+ 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)
+ )
}
- return Entry(
- key,
- subkey,
- slice.build()
- )
- }
- private fun newAuthenticationEntry(
- key: String,
- subkey: String,
- credentialType: String,
- ): Entry {
- val slice = Slice.Builder(
- Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
- )
- return Entry(
- key,
- subkey,
- slice.build()
- )
- }
+ private fun newAuthenticationEntry(
+ key: String,
+ subkey: String,
+ credentialType: String,
+ ): Entry {
+ val slice = Slice.Builder(
+ Entry.CREDENTIAL_MANAGER_ENTRY_URI, 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")
+ 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 pendingIntent = PendingIntent.getActivity(context, 1,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ or PendingIntent.FLAG_ONE_SHOT))
- val slice = Slice.Builder(
- Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
- ).addText(
- credentialTypeDisplayName, null, listOf(Entry.HINT_CREDENTIAL_TYPE_DISPLAY_NAME)
- ).addText(
- userName, null, listOf(Entry.HINT_USER_NAME)
- ).addIcon(
- Icon.createWithResource(context, R.drawable.ic_passkey),
- null,
- listOf(Entry.HINT_PROFILE_ICON))
- if (userDisplayName != null) {
- slice.addText(userDisplayName, null, listOf(Entry.HINT_PASSKEY_USER_DISPLAY_NAME))
- }
- if (lastUsedTimeMillis != null) {
- slice.addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
+ val credentialEntry = CredentialEntry(credentialType, credentialTypeDisplayName, userName,
+ userDisplayName, pendingIntent, lastUsedTimeMillis
+ ?: 0L, Icon.createWithResource(context, R.drawable.ic_passkey),
+ false)
+
+ return Entry(
+ key,
+ subkey,
+ CredentialEntry.toSlice(credentialEntry),
+ pendingIntent,
+ null
+ )
}
- return Entry(
- key,
- subkey,
- slice.build(),
- pendingIntent,
- null
- )
- }
- private fun newCreateEntry(
- key: String,
- subkey: String,
- providerDisplayName: String,
- passwordCount: Int,
- passkeyCount: Int,
- totalCredentialCount: Int,
- 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 createPasswordRequest = android.service.credentials.CreateCredentialRequest(
- android.service.credentials.CallingAppInfo(
- context.applicationInfo.packageName, ArraySet<Signature>()),
- TYPE_PASSWORD_CREDENTIAL,
- toBundle("beckett-bakert@gmail.com", "password123")
- )
- val fillInIntent = Intent().putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
- createPasswordRequest)
+ private fun newCreateEntry(
+ key: String,
+ subkey: String,
+ providerDisplayName: String,
+ passwordCount: Int,
+ passkeyCount: Int,
+ totalCredentialCount: Int,
+ 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 createPasswordRequest = android.service.credentials.CreateCredentialRequest(
+ android.service.credentials.CallingAppInfo(
+ context.applicationInfo.packageName, ArraySet<Signature>()),
+ TYPE_PASSWORD_CREDENTIAL,
+ toBundle("beckett-bakert@gmail.com", "password123")
+ )
+ val fillInIntent = Intent().putExtra(
+ CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
+ createPasswordRequest)
- val slice = Slice.Builder(
- Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
- ).addText(
- providerDisplayName, null, listOf(Entry.HINT_USER_PROVIDER_ACCOUNT_NAME))
- .addIcon(
- Icon.createWithResource(context, R.drawable.ic_passkey),
- null,
- listOf(Entry.HINT_CREDENTIAL_TYPE_ICON))
- .addIcon(
- Icon.createWithResource(context, R.drawable.ic_profile),
- null,
- listOf(Entry.HINT_PROFILE_ICON))
- .addInt(
- passwordCount, null, listOf(Entry.HINT_PASSWORD_COUNT))
- .addInt(
- passkeyCount, null, listOf(Entry.HINT_PASSKEY_COUNT))
- .addInt(
- totalCredentialCount, null, listOf(Entry.HINT_TOTAL_CREDENTIAL_COUNT))
- .addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
- .build()
- return Entry(
- key,
- subkey,
- slice,
- pendingIntent,
- fillInIntent,
- )
- }
+ val createEntry = CreateEntry(
+ providerDisplayName, pendingIntent,
+ Icon.createWithResource(context, R.drawable.ic_profile), lastUsedTimeMillis,
+ listOf(
+ CredentialCountInformation.createPasswordCountInformation(passwordCount),
+ CredentialCountInformation.createPublicKeyCountInformation(passkeyCount),
+ ))
+ return Entry(
+ key,
+ subkey,
+ CreateEntry.toSlice(createEntry),
+ pendingIntent,
+ fillInIntent,
+ )
+ }
private fun newRemoteEntry(
key: String,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index db676b297c1b..22b2be9fc0d0 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -42,9 +42,10 @@ 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.ActionUi
-import com.android.credentialmanager.jetpack.provider.CredentialEntryUi
-import com.android.credentialmanager.jetpack.provider.SaveEntryUi
+import com.android.credentialmanager.jetpack.provider.Action
+import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
+import com.android.credentialmanager.jetpack.provider.CredentialEntry
+import com.android.credentialmanager.jetpack.provider.CreateEntry
import org.json.JSONObject
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
@@ -107,7 +108,8 @@ class GetFlowUtils {
context: Context,
): List<CredentialEntryInfo> {
return credentialEntries.map {
- val credentialEntryUi = CredentialEntryUi.fromSlice(it.slice)
+ // TODO: handle NPE gracefully
+ val credentialEntry = CredentialEntry.fromSlice(it.slice)!!
// Consider directly move the UI object into the class.
return@map CredentialEntryInfo(
@@ -116,14 +118,14 @@ class GetFlowUtils {
entrySubkey = it.subkey,
pendingIntent = it.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
- credentialType = credentialEntryUi.credentialType.toString(),
- credentialTypeDisplayName = credentialEntryUi.credentialTypeDisplayName.toString(),
- userName = credentialEntryUi.userName.toString(),
- displayName = credentialEntryUi.userDisplayName?.toString(),
+ credentialType = credentialEntry.type.toString(),
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
// TODO: proper fallback
- icon = credentialEntryUi.entryIcon?.loadDrawable(context)
- ?: context.getDrawable(R.drawable.ic_other_sign_in)!!,
- lastUsedTimeMillis = credentialEntryUi.lastUsedTimeMillis,
+ icon = credentialEntry.icon?.loadDrawable(context)
+ ?: context.getDrawable(R.drawable.ic_other_sign_in)!!,
+ lastUsedTimeMillis = credentialEntry.lastUsedTimeMillis,
)
}
}
@@ -170,7 +172,8 @@ class GetFlowUtils {
providerIcon: Drawable,
): List<ActionEntryInfo> {
return actionEntries.map {
- val actionEntryUi = ActionUi.fromSlice(it.slice)
+ // TODO: handle NPE gracefully
+ val actionEntryUi = Action.fromSlice(it.slice)!!
return@map ActionEntryInfo(
providerId = providerId,
@@ -178,10 +181,10 @@ class GetFlowUtils {
entrySubkey = it.subkey,
pendingIntent = it.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
- title = actionEntryUi.text.toString(),
+ title = actionEntryUi.title.toString(),
// TODO: gracefully fail
icon = providerIcon,
- subTitle = actionEntryUi.subtext?.toString(),
+ subTitle = actionEntryUi.subTitle?.toString(),
)
}
}
@@ -383,7 +386,8 @@ class CreateFlowUtils {
context: Context,
): List<CreateOptionInfo> {
return creationEntries.map {
- val saveEntryUi = SaveEntryUi.fromSlice(it.slice)
+ // TODO: handle NPE gracefully
+ val createEntry = CreateEntry.fromSlice(it.slice)!!
return@map CreateOptionInfo(
// TODO: remove fallbacks
@@ -392,13 +396,16 @@ class CreateFlowUtils {
entrySubkey = it.subkey,
pendingIntent = it.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
- userProviderDisplayName = saveEntryUi.userProviderAccountName as String,
- profileIcon = saveEntryUi.profileIcon?.loadDrawable(context)
- ?: requestDisplayInfo.typeIcon,
- passwordCount = saveEntryUi.passwordCount ?: 0,
- passkeyCount = saveEntryUi.passkeyCount ?: 0,
- totalCredentialCount = saveEntryUi.totalCredentialCount ?: 0,
- lastUsedTimeMillis = saveEntryUi.lastUsedTimeMillis ?: 0,
+ userProviderDisplayName = createEntry.accountName.toString(),
+ profileIcon = createEntry.icon?.loadDrawable(context)
+ ?: requestDisplayInfo.typeIcon,
+ passwordCount = CredentialCountInformation.getPasswordCount(
+ createEntry.credentialCountInformationList) ?: 0,
+ passkeyCount = CredentialCountInformation.getPasskeyCount(
+ createEntry.credentialCountInformationList) ?: 0,
+ totalCredentialCount = CredentialCountInformation.getTotalCount(
+ createEntry.credentialCountInformationList) ?: 0,
+ lastUsedTimeMillis = createEntry.lastUsedTimeMillis ?: 0,
)
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt
new file mode 100644
index 000000000000..165885843a35
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt
@@ -0,0 +1,55 @@
+/*
+ * 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/provider/Action.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt
new file mode 100644
index 000000000000..1abf9113845f
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt
@@ -0,0 +1,100 @@
+/*
+ * 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/ActionUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
deleted file mode 100644
index 19c5c2dfa4fe..000000000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
+++ /dev/null
@@ -1,48 +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.slice.Slice
-import android.credentials.ui.Entry
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class ActionUi(
- val text: CharSequence,
- val subtext: CharSequence?,
-) {
- companion object {
- fun fromSlice(slice: Slice): ActionUi {
- var text: CharSequence? = null
- var subtext: CharSequence? = null
-
- val items = slice.items
- items.forEach {
- if (it.hasHint(Entry.HINT_ACTION_TITLE)) {
- text = it.text
- } else if (it.hasHint(Entry.HINT_ACTION_SUBTEXT)) {
- subtext = it.text
- }
- }
- // TODO: fail NPE more elegantly.
- return ActionUi(text!!, subtext)
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt
new file mode 100644
index 000000000000..bed02f8e22a4
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt
@@ -0,0 +1,226 @@
+/*
+ * 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>
+) {
+
+ 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
+
+ /** 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
+ }
+
+ /**
+ * Builds an instance of [CreateEntry]
+ *
+ * @throws IllegalArgumentException If [accountName] is empty
+ */
+ fun build(): CreateEntry {
+ return CreateEntry(accountName, pendingIntent, icon, lastUsedTimeMillis,
+ credentialCountInformationList)
+ }
+ }
+
+ 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"
+
+ @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)
+ }
+ 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
+
+ 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
+ }
+ }
+
+ return try {
+ CreateEntry(accountName, pendingIntent, icon,
+ lastUsedTimeMillis, credentialCountInfo)
+ } 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
new file mode 100644
index 000000000000..aa77b74ce76a
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt
@@ -0,0 +1,61 @@
+/*
+ * 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
new file mode 100644
index 000000000000..61a104bd8e3a
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt
@@ -0,0 +1,150 @@
+/*
+ * 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
+ }
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
deleted file mode 100644
index 47b5af0d7fe6..000000000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
+++ /dev/null
@@ -1,89 +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.graphics.drawable.Icon
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class CredentialEntryUi(
- val credentialType: CharSequence,
- val credentialTypeDisplayName: CharSequence,
- val userName: CharSequence,
- val userDisplayName: CharSequence?,
- val entryIcon: Icon?,
- val lastUsedTimeMillis: Long?,
- // TODO: Remove note
- val note: CharSequence?,
-) {
- companion object {
- // Copied over from jetpack
- const val SLICE_HINT_TYPE_DISPLAY_NAME =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
- const val SLICE_HINT_USERNAME =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
- const val SLICE_HINT_DISPLAYNAME =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
- const val SLICE_HINT_LAST_USED_TIME_MILLIS =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
- const val SLICE_HINT_ICON =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
- const val SLICE_HINT_PENDING_INTENT =
- "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
-
- /**
- * Returns an instance of [CredentialEntryUi] derived from a [Slice] object.
- *
- * @param slice the [Slice] object constructed through jetpack library
- */
- @JvmStatic
- fun fromSlice(slice: Slice): CredentialEntryUi {
- var username: CharSequence? = null
- var displayName: CharSequence = ""
- var icon: Icon? = null
- var pendingIntent: PendingIntent? = null
- var lastUsedTimeMillis: Long = 0
- var note: CharSequence? = null
- var typeDisplayName: CharSequence = ""
-
- 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
- }
- }
- return CredentialEntryUi(
- slice.spec!!.type, typeDisplayName, username!!, displayName, icon,
- lastUsedTimeMillis, note,
- )
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
deleted file mode 100644
index 313f0f97e246..000000000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
+++ /dev/null
@@ -1,80 +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.graphics.drawable.Icon
-
-/**
- * UI representation for a save entry used during the create credential flow.
- *
- * TODO: move to jetpack.
- */
-class SaveEntryUi(
- val userProviderAccountName: CharSequence?,
- val credentialTypeIcon: Icon?,
- val profileIcon: Icon?,
- val passwordCount: Int?,
- val passkeyCount: Int?,
- val totalCredentialCount: Int?,
- val lastUsedTimeMillis: Long?,
-) {
- companion object {
- const val SLICE_HINT_ACCOUNT_NAME =
- "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
- const val SLICE_HINT_ICON =
- "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
- const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
- "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
- const val SLICE_HINT_LAST_USED_TIME_MILLIS =
- "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
- const val SLICE_HINT_PENDING_INTENT =
- "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
-
- /**
- * Returns an instance of [SaveEntryUi] derived from a [Slice] object.
- *
- * @param slice the [Slice] object constructed through the jetpack library
- */
- @JvmStatic
- fun fromSlice(slice: Slice): SaveEntryUi {
- var accountName: CharSequence? = null
- var icon: Icon? = null
- var pendingIntent: PendingIntent? = null
- var lastUsedTimeMillis: Long = 0
-
- 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_LAST_USED_TIME_MILLIS)) {
- lastUsedTimeMillis = it.long
- }
- }
-
- return SaveEntryUi(
- // TODO: Add count parsing
- accountName!!, icon, icon,
- 0, 0, 0, lastUsedTimeMillis,
- )
- }
- }
-}