diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/ui/Entry.java index b9ee72dcdcf86..33427d373219c 100644 --- a/core/java/android/credentials/ui/Entry.java +++ b/core/java/android/credentials/ui/Entry.java @@ -82,32 +82,46 @@ public class Entry implements Parcelable { public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION = "android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION"; - // TODO: change to string key + string subkey. - private final int mId; + @NonNull private final String mKey; + @NonNull private final String mSubkey; @NonNull private final Slice mSlice; protected Entry(@NonNull Parcel in) { - int entryId = in.readInt(); + String key = in.readString8(); + String subkey = in.readString8(); Slice slice = Slice.CREATOR.createFromParcel(in); - mId = entryId; + mKey = key; + AnnotationValidations.validate(NonNull.class, null, mKey); + mSubkey = subkey; + AnnotationValidations.validate(NonNull.class, null, mSubkey); mSlice = slice; AnnotationValidations.validate(NonNull.class, null, mSlice); } - public Entry(int id, @NonNull Slice slice) { - mId = id; + public Entry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice) { + mKey = key; + mSubkey = subkey; mSlice = slice; } /** - * Returns the id of this entry that's unique within the context of the CredentialManager + * Returns the identifier of this entry that's unique within the context of the CredentialManager * request. */ - public int getEntryId() { - return mId; + @NonNull + public String getKey() { + return mKey; + } + + /** + * Returns the sub-identifier of this entry that's unique within the context of the {@code key}. + */ + @NonNull + public String getSubkey() { + return mSubkey; } /** @@ -120,7 +134,8 @@ public class Entry implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mId); + dest.writeString8(mKey); + dest.writeString8(mSubkey); mSlice.writeToParcel(dest, flags); } diff --git a/core/java/android/credentials/ui/UserSelectionDialogResult.java b/core/java/android/credentials/ui/UserSelectionDialogResult.java index eb3a4a8cfcbd7..6025d78d1baee 100644 --- a/core/java/android/credentials/ui/UserSelectionDialogResult.java +++ b/core/java/android/credentials/ui/UserSelectionDialogResult.java @@ -54,18 +54,17 @@ public class UserSelectionDialogResult extends BaseDialogResult implements Parce private static final String EXTRA_USER_SELECTION_RESULT = "android.credentials.ui.extra.USER_SELECTION_RESULT"; - @NonNull - private final String mProviderId; - - // TODO: consider switching to string or other types, depending on the service implementation. - private final int mEntryId; + @NonNull private final String mProviderId; + @NonNull private final String mEntryKey; + @NonNull private final String mEntrySubkey; public UserSelectionDialogResult( @NonNull IBinder requestToken, @NonNull String providerId, - int entryId) { + @NonNull String entryKey, @NonNull String entrySubkey) { super(requestToken); mProviderId = providerId; - mEntryId = entryId; + mEntryKey = entryKey; + mEntrySubkey = entrySubkey; } /** Returns provider package name whose entry was selected by the user. */ @@ -74,26 +73,38 @@ public class UserSelectionDialogResult extends BaseDialogResult implements Parce return mProviderId; } - /** Returns the id of the visual entry that the user selected. */ - public int getEntryId() { - return mEntryId; + /** Returns the key of the visual entry that the user selected. */ + @NonNull + public String getEntryKey() { + return mEntryKey; + } + + /** Returns the subkey of the visual entry that the user selected. */ + @NonNull + public String getEntrySubkey() { + return mEntrySubkey; } protected UserSelectionDialogResult(@NonNull Parcel in) { super(in); String providerId = in.readString8(); - int entryId = in.readInt(); + String entryKey = in.readString8(); + String entrySubkey = in.readString8(); mProviderId = providerId; AnnotationValidations.validate(NonNull.class, null, mProviderId); - mEntryId = entryId; + mEntryKey = entryKey; + AnnotationValidations.validate(NonNull.class, null, mEntryKey); + mEntrySubkey = entrySubkey; + AnnotationValidations.validate(NonNull.class, null, mEntrySubkey); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString8(mProviderId); - dest.writeInt(mEntryId); + dest.writeString8(mEntryKey); + dest.writeString8(mEntrySubkey); } @Override diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 4e7ead6fc1b8e..56fb1a91aa90b 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -71,11 +71,12 @@ class CredentialManagerRepo( resultReceiver?.send(BaseDialogResult.RESULT_CODE_DIALOG_CANCELED, resultData) } - fun onOptionSelected(providerPackageName: String, entryId: Int) { + fun onOptionSelected(providerPackageName: String, entryKey: String, entrySubkey: String) { val userSelectionDialogResult = UserSelectionDialogResult( requestInfo.token, providerPackageName, - entryId + entryKey, + entrySubkey ) val resultData = Bundle() UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultData) @@ -137,16 +138,16 @@ class CredentialManagerRepo( Icon.createWithResource(context, R.drawable.ic_launcher_foreground)) .setCredentialEntries( listOf( - newEntry(1, "elisa.beckett@gmail.com", "Elisa Backett", - "20 passwords and 7 passkeys saved"), - newEntry(2, "elisa.work@google.com", "Elisa Backett Work", - "20 passwords and 7 passkeys saved"), + newEntry("key1", "subkey-1", "elisa.beckett@gmail.com", + "Elisa Backett", "20 passwords and 7 passkeys saved"), + newEntry("key1", "subkey-2", "elisa.work@google.com", + "Elisa Backett Work", "20 passwords and 7 passkeys saved"), ) ).setActionChips( listOf( - newEntry(3, "Go to Settings", "", + newEntry("key2", "subkey-1", "Go to Settings", "", "20 passwords and 7 passkeys saved"), - newEntry(4, "Switch Account", "", + newEntry("key2", "subkey-2", "Switch Account", "", "20 passwords and 7 passkeys saved"), ), ).build(), @@ -156,21 +157,28 @@ class CredentialManagerRepo( Icon.createWithResource(context, R.drawable.ic_launcher_foreground)) .setCredentialEntries( listOf( - newEntry(1, "elisa.beckett@dashlane.com", "Elisa Backett", - "20 passwords and 7 passkeys saved"), - newEntry(2, "elisa.work@dashlane.com", "Elisa Backett Work", - "20 passwords and 7 passkeys saved"), + newEntry("key1", "subkey-3", "elisa.beckett@dashlane.com", + "Elisa Backett", "20 passwords and 7 passkeys saved"), + newEntry("key1", "subkey-4", "elisa.work@dashlane.com", + "Elisa Backett Work", "20 passwords and 7 passkeys saved"), ) ).setActionChips( listOf( - newEntry(3, "Manage Accounts", "Manage your accounts in the dashlane app", + newEntry("key2", "subkey-3", "Manage Accounts", + "Manage your accounts in the dashlane app", "20 passwords and 7 passkeys saved"), ), ).build(), ) } - private fun newEntry(id: Int, title: String, subtitle: String, usageData: String): Entry { + private fun newEntry( + key: String, + subkey: String, + title: String, + subtitle: String, + usageData: String + ): Entry { val slice = Slice.Builder( Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1) ) @@ -183,7 +191,8 @@ class CredentialManagerRepo( .addText(usageData, Slice.SUBTYPE_MESSAGE, listOf(Entry.HINT_SUBTITLE)) .build() return Entry( - id, + key, + subkey, slice ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index f51f1ef7e21f4..e037db7aa0e2a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -59,7 +59,8 @@ class GetFlowUtils { ?: context.getDrawable(R.drawable.ic_passkey)!!, title = credentialEntryUi.userName.toString(), subtitle = credentialEntryUi.displayName?.toString() ?: "Unknown display name", - id = it.entryId, + entryKey = it.key, + entrySubkey = it.subkey, usageData = credentialEntryUi.usageData?.toString() ?: "Unknown usageData", ) } @@ -99,7 +100,8 @@ class CreateFlowUtils { ?: context.getDrawable(R.drawable.ic_passkey)!!, title = saveEntryUi.title.toString(), subtitle = saveEntryUi.subTitle?.toString() ?: "Unknown subtitle", - id = it.entryId, + entryKey = it.key, + entrySubkey = it.subkey, usageData = saveEntryUi.usageData?.toString() ?: "Unknown usageData", ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index fe78539b5a831..cb2bf10f2aef3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -30,7 +30,8 @@ data class CreateOptionInfo( val icon: Drawable, val title: String, val subtitle: String, - val id: Int, + val entryKey: String, + val entrySubkey: String, val usageData: String ) diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt index 11538e0cabc07..373fb80e87be2 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt @@ -63,32 +63,32 @@ fun CreatePasskeyScreen( val uiState = viewModel.uiState when (uiState.currentScreenState) { CreateScreenState.PASSKEY_INTRO -> ConfirmationCard( - onConfirm = {viewModel.onConfirmIntro()}, - onCancel = {viewModel.onCancel()}, + onConfirm = viewModel::onConfirmIntro, + onCancel = viewModel::onCancel, ) CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard( providerList = uiState.providers, - onCancel = {viewModel.onCancel()}, - onProviderSelected = {viewModel.onProviderSelected(it)} + onCancel = viewModel::onCancel, + onProviderSelected = viewModel::onProviderSelected ) CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard( requestDisplayInfo = uiState.requestDisplayInfo, providerInfo = uiState.activeEntry?.activeProvider!!, createOptionInfo = uiState.activeEntry.activeCreateOptionInfo, - onOptionSelected = {viewModel.onPrimaryCreateOptionInfoSelected()}, - onConfirm = {viewModel.onPrimaryCreateOptionInfoSelected()}, - onCancel = {viewModel.onCancel()}, + onOptionSelected = viewModel::onPrimaryCreateOptionInfoSelected, + onConfirm = viewModel::onPrimaryCreateOptionInfoSelected, + onCancel = viewModel::onCancel, multiProvider = uiState.providers.size > 1, - onMoreOptionsSelected = {viewModel.onMoreOptionsSelected()} + onMoreOptionsSelected = viewModel::onMoreOptionsSelected ) CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard( providerList = uiState.providers, - onBackButtonSelected = {viewModel.onBackButtonSelected()}, - onOptionSelected = {viewModel.onMoreOptionsRowSelected(it)} + onBackButtonSelected = viewModel::onBackButtonSelected, + onOptionSelected = viewModel::onMoreOptionsRowSelected ) CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard( providerInfo = uiState.activeEntry?.activeProvider!!, - onDefaultOrNotSelected = {viewModel.onDefaultOrNotSelected()} + onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected ) } }, @@ -491,40 +491,6 @@ fun CreationSelectionCard( } } -@ExperimentalMaterialApi -@Composable -fun CreateOptionRow(createOptionInfo: CreateOptionInfo, onOptionSelected: (Int) -> Unit) { - Chip( - modifier = Modifier.fillMaxWidth(), - onClick = {onOptionSelected(createOptionInfo.id)}, - leadingIcon = { - Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp), - bitmap = createOptionInfo.icon.toBitmap().asImageBitmap(), - // painter = painterResource(R.drawable.ic_passkey), - // TODO: add description. - contentDescription = "") - }, - colors = ChipDefaults.chipColors( - backgroundColor = Grey100, - leadingIconContentColor = Grey100 - ), - shape = Shapes.large - ) { - Column() { - Text( - text = createOptionInfo.title, - style = Typography.h6, - modifier = Modifier.padding(top = 16.dp) - ) - Text( - text = createOptionInfo.subtitle, - style = Typography.body2, - modifier = Modifier.padding(bottom = 16.dp) - ) - } - } -} - @ExperimentalMaterialApi @Composable fun PrimaryCreateOptionRow( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt index 38486e2c7d74c..615da4e9de0aa 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt @@ -73,11 +73,15 @@ class CreatePasskeyViewModel( ) } - fun onCreateOptionSelected(createOptionId: Int) { - Log.d("Account Selector", "Option selected for creation: $createOptionId") + fun onCreateOptionSelected(entryKey: String, entrySubkey: String) { + Log.d( + "Account Selector", + "Option selected for creation: {key = $entryKey, subkey = $entrySubkey}" + ) CredentialManagerRepo.getInstance().onOptionSelected( uiState.activeEntry?.activeProvider!!.name, - createOptionId + entryKey, + entrySubkey ) dialogResult.value = DialogResult( ResultState.COMPLETE, @@ -122,13 +126,21 @@ class CreatePasskeyViewModel( } fun onPrimaryCreateOptionInfoSelected() { - var createOptionId = uiState.activeEntry?.activeCreateOptionInfo?.id - Log.d("Account Selector", "Option selected for creation: $createOptionId") - if (createOptionId != null) { + var createOptionEntryKey = uiState.activeEntry?.activeCreateOptionInfo?.entryKey + var createOptionEntrySubkey = uiState.activeEntry?.activeCreateOptionInfo?.entrySubkey + Log.d( + "Account Selector", + "Option selected for creation: " + + "{key = $createOptionEntryKey, subkey = $createOptionEntrySubkey}" + ) + if (createOptionEntryKey != null && createOptionEntrySubkey != null) { CredentialManagerRepo.getInstance().onOptionSelected( uiState.activeEntry?.activeProvider!!.name, - createOptionId + createOptionEntryKey, + createOptionEntrySubkey ) + } else { + TODO("Gracefully handle illegal state.") } dialogResult.value = DialogResult( ResultState.COMPLETE, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 6cb1faffd24c6..e3398c0d6fde8 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -68,10 +68,10 @@ fun GetCredentialScreen( GetScreenState.CREDENTIAL_SELECTION -> CredentialSelectionCard( requestDisplayInfo = uiState.requestDisplayInfo, providerInfo = uiState.selectedProvider!!, - onCancel = {viewModel.onCancel()}, - onOptionSelected = {viewModel.onCredentailSelected(it)}, + onCancel = viewModel::onCancel, + onOptionSelected = viewModel::onCredentailSelected, multiProvider = uiState.providers.size > 1, - onMoreOptionSelected = {viewModel.onMoreOptionSelected()}, + onMoreOptionSelected = viewModel::onMoreOptionSelected, ) } }, @@ -90,7 +90,7 @@ fun GetCredentialScreen( fun CredentialSelectionCard( requestDisplayInfo: RequestDisplayInfo, providerInfo: ProviderInfo, - onOptionSelected: (Int) -> Unit, + onOptionSelected: (String, String) -> Unit, onCancel: () -> Unit, multiProvider: Boolean, onMoreOptionSelected: () -> Unit, @@ -165,11 +165,11 @@ fun CredentialSelectionCard( @Composable fun CredentialOptionRow( credentialOptionInfo: CredentialOptionInfo, - onOptionSelected: (Int) -> Unit + onOptionSelected: (String, String) -> Unit, ) { Chip( modifier = Modifier.fillMaxWidth(), - onClick = {onOptionSelected(credentialOptionInfo.id)}, + onClick = {onOptionSelected(credentialOptionInfo.entryKey, credentialOptionInfo.entrySubkey)}, leadingIcon = { Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp), bitmap = credentialOptionInfo.icon.toBitmap().asImageBitmap(), diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt index f610ebdd15673..7b6c30aca89e4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt @@ -49,11 +49,12 @@ class GetCredentialViewModel( return dialogResult } - fun onCredentailSelected(credentialId: Int) { - Log.d("Account Selector", "credential selected: $credentialId") + fun onCredentailSelected(entryKey: String, entrySubkey: String) { + Log.d("Account Selector", "credential selected: {key=$entryKey,subkey=$entrySubkey}") CredentialManagerRepo.getInstance().onOptionSelected( uiState.selectedProvider!!.name, - credentialId + entryKey, + entrySubkey ) dialogResult.value = DialogResult( ResultState.COMPLETE, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index eaadb68f1e505..b6ecd371e6582 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -30,7 +30,8 @@ data class CredentialOptionInfo( val icon: Drawable, val title: String, val subtitle: String, - val id: Int, + val entryKey: String, + val entrySubkey: String, val usageData: String )