diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/ui/Entry.java index 122c54ad81442..b9ee72dcdcf86 100644 --- a/core/java/android/credentials/ui/Entry.java +++ b/core/java/android/credentials/ui/Entry.java @@ -30,12 +30,39 @@ import com.android.internal.util.AnnotationValidations; * @hide */ public class Entry implements Parcelable { - // TODO: move to jetpack. + // TODO: these constants should go to jetpack. public static final String VERSION = "v1"; public static final Uri CREDENTIAL_MANAGER_ENTRY_URI = Uri.parse("credentialmanager.slice"); - public static final String HINT_TITLE = "hint_title"; - public static final String HINT_SUBTITLE = "hint_subtitle"; - public static final String HINT_ICON = "hint_icon"; + // TODO: remove these hint constants and use the credential entry & action ones defined below. + public static final String HINT_TITLE = "HINT_TITLE"; + public static final String HINT_SUBTITLE = "HINT_SUBTITLE"; + public static final String HINT_ICON = "HINT_ICON"; + /** + * 1. CREDENTIAL ENTRY CONSTANTS + */ + // User profile picture associated with this credential entry. + public static final String HINT_PROFILE_ICON = "HINT_PROFILE_ICON"; + public static final String HINT_CREDENTIAL_TYPE_ICON = "HINT_CREDENTIAL_TYPE_ICON"; + // The user account name of this provider app associated with this entry. + // Note: this is independent from the request app. + public static final String HINT_USER_PROVIDER_ACCOUNT_NAME = "HINT_USER_PROVIDER_ACCOUNT_NAME"; + public static final String HINT_PASSWORD_COUNT = "HINT_PASSWORD_COUNT"; + public static final String HINT_PASSKEY_COUNT = "HINT_PASSKEY_COUNT"; + public static final String HINT_TOTAL_CREDENTIAL_COUNT = "HINT_TOTAL_CREDENTIAL_COUNT"; + public static final String HINT_LAST_USED_TIME_MILLIS = "HINT_LAST_USED_TIME_MILLIS"; + /** Below are only available for get flows. */ + public static final String HINT_NOTE = "HINT_NOTE"; + public static final String HINT_USER_NAME = "HINT_USER_NAME"; + public static final String HINT_CREDENTIAL_TYPE = "HINT_CREDENTIAL_TYPE"; + public static final String HINT_PASSKEY_USER_DISPLAY_NAME = "HINT_PASSKEY_USER_DISPLAY_NAME"; + public static final String HINT_PASSWORD_VALUE = "HINT_PASSWORD_VALUE"; + + /** + * 2. ACTION CONSTANTS + */ + public static final String HINT_ACTION_TITLE = "HINT_ACTION_TITLE"; + public static final String HINT_ACTION_SUBTEXT = "HINT_ACTION_SUBTEXT"; + public static final String HINT_ACTION_ICON = "HINT_ACTION_ICON"; /** * The intent extra key for the action chip {@code Entry} list when launching the UX activities. @@ -55,7 +82,7 @@ public class Entry implements Parcelable { public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION = "android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION"; - // TODO: may be changed to other type depending on the service implementation. + // TODO: change to string key + string subkey. private final int mId; @NonNull diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt new file mode 100644 index 0000000000000..d6f1b5f5c8e9b --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt @@ -0,0 +1,46 @@ +/* + * 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 + +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. + */ +abstract class CredentialEntryUi( + val credentialTypeIcon: Icon, + val profileIcon: Icon?, + val lastUsedTimeMillis: Long?, + val note: CharSequence?, +) { + companion object { + fun fromSlice(slice: Slice): CredentialEntryUi { + return when (slice.spec?.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> PasskeyCredentialEntryUi.fromSlice(slice) + TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntryUi.fromSlice(slice) + else -> throw IllegalArgumentException("Unexpected type: ${slice.spec?.type}") + } + } + + const val TYPE_PUBLIC_KEY_CREDENTIAL: String = + "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" + const val TYPE_PASSWORD_CREDENTIAL: String = "androidx.credentials.TYPE_PASSWORD" + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt new file mode 100644 index 0000000000000..bb3b206500b4a --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt @@ -0,0 +1,63 @@ +/* + * 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 + +import android.app.slice.Slice +import android.credentials.ui.Entry +import android.graphics.drawable.Icon + +class PasskeyCredentialEntryUi( + val userName: CharSequence, + val userDisplayName: CharSequence?, + credentialTypeIcon: Icon, + profileIcon: Icon?, + lastUsedTimeMillis: Long?, + note: CharSequence?, +) : CredentialEntryUi(credentialTypeIcon, profileIcon, lastUsedTimeMillis, note) { + companion object { + fun fromSlice(slice: Slice): CredentialEntryUi { + var userName: CharSequence? = null + var userDisplayName: CharSequence? = null + var credentialTypeIcon: Icon? = null + var profileIcon: Icon? = null + var lastUsedTimeMillis: Long? = null + var note: CharSequence? = null + + val items = slice.items + items.forEach { + if (it.hasHint(Entry.HINT_USER_NAME)) { + userName = it.text + } else if (it.hasHint(Entry.HINT_PASSKEY_USER_DISPLAY_NAME)) { + userDisplayName = it.text + } else if (it.hasHint(Entry.HINT_CREDENTIAL_TYPE_ICON)) { + credentialTypeIcon = it.icon + } else if (it.hasHint(Entry.HINT_PROFILE_ICON)) { + profileIcon = it.icon + } else if (it.hasHint(Entry.HINT_LAST_USED_TIME_MILLIS)) { + lastUsedTimeMillis = it.long + } else if (it.hasHint(Entry.HINT_NOTE)) { + note = it.text + } + } + // TODO: fail NPE more elegantly. + return PasskeyCredentialEntryUi( + userName!!, userDisplayName, credentialTypeIcon!!, + profileIcon, lastUsedTimeMillis, note, + ) + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt new file mode 100644 index 0000000000000..7311b7081343e --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt @@ -0,0 +1,68 @@ +/* + * 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 + +import android.app.slice.Slice +import android.credentials.ui.Entry +import android.graphics.drawable.Icon + +/** + * UI representation for a password credential entry used during the get credential flow. + * + * TODO: move to jetpack. + */ +class PasswordCredentialEntryUi( + val userName: CharSequence, + val password: CharSequence, + credentialTypeIcon: Icon, + profileIcon: Icon?, + lastUsedTimeMillis: Long?, + note: CharSequence?, +) : CredentialEntryUi(credentialTypeIcon, profileIcon, lastUsedTimeMillis, note) { + companion object { + fun fromSlice(slice: Slice): CredentialEntryUi { + var userName: CharSequence? = null + var password: CharSequence? = null + var credentialTypeIcon: Icon? = null + var profileIcon: Icon? = null + var lastUsedTimeMillis: Long? = null + var note: CharSequence? = null + + val items = slice.items + items.forEach { + if (it.hasHint(Entry.HINT_USER_NAME)) { + userName = it.text + } else if (it.hasHint(Entry.HINT_PASSWORD_VALUE)) { + password = it.text + } else if (it.hasHint(Entry.HINT_CREDENTIAL_TYPE_ICON)) { + credentialTypeIcon = it.icon + } else if (it.hasHint(Entry.HINT_PROFILE_ICON)) { + profileIcon = it.icon + } else if (it.hasHint(Entry.HINT_LAST_USED_TIME_MILLIS)) { + lastUsedTimeMillis = it.long + } else if (it.hasHint(Entry.HINT_NOTE)) { + note = it.text + } + } + // TODO: fail NPE more elegantly. + return PasswordCredentialEntryUi( + userName!!, password!!, credentialTypeIcon!!, + profileIcon, lastUsedTimeMillis, note, + ) + } + } +} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt new file mode 100644 index 0000000000000..fad3309fb86fa --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt @@ -0,0 +1,73 @@ +/* + * 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 + +import android.app.slice.Slice +import android.credentials.ui.Entry +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 { + fun fromSlice(slice: Slice): SaveEntryUi { + var userProviderAccountName: CharSequence? = null + var credentialTypeIcon: Icon? = null + var profileIcon: Icon? = null + var passwordCount: Int? = null + var passkeyCount: Int? = null + var totalCredentialCount: Int? = null + var lastUsedTimeMillis: Long? = null + + + val items = slice.items + items.forEach { + if (it.hasHint(Entry.HINT_USER_PROVIDER_ACCOUNT_NAME)) { + userProviderAccountName = it.text + } else if (it.hasHint(Entry.HINT_CREDENTIAL_TYPE_ICON)) { + credentialTypeIcon = it.icon + } else if (it.hasHint(Entry.HINT_PROFILE_ICON)) { + profileIcon = it.icon + } else if (it.hasHint(Entry.HINT_PASSWORD_COUNT)) { + passwordCount = it.int + } else if (it.hasHint(Entry.HINT_PASSKEY_COUNT)) { + passkeyCount = it.int + } else if (it.hasHint(Entry.HINT_TOTAL_CREDENTIAL_COUNT)) { + totalCredentialCount = it.int + } else if (it.hasHint(Entry.HINT_LAST_USED_TIME_MILLIS)) { + lastUsedTimeMillis = it.long + } + } + // TODO: fail NPE more elegantly. + return SaveEntryUi( + userProviderAccountName!!, credentialTypeIcon, profileIcon, + passwordCount, passkeyCount, totalCredentialCount, lastUsedTimeMillis, + ) + } + } +}