From 594e4c3b594f2d5082044a3b597dfbd5268de280 Mon Sep 17 00:00:00 2001 From: Helen Qin Date: Thu, 17 Nov 2022 09:25:12 +0000 Subject: [PATCH] Add remote entry for get flow. Screenshot: https://screenshot.googleplex.com/B6bf2UiuJaDinzs Bug: 253157237 Test: Local Deployment Change-Id: I341642a162a5c007de9d3a2f9fbfab119b1f1e1d --- .../CredentialManager/res/values/strings.xml | 4 ++ .../CredentialManagerRepo.kt | 4 +- .../credentialmanager/DataConverter.kt | 22 +++++-- .../getflow/GetCredentialComponents.kt | 59 +++++++++++++++++-- .../getflow/GetCredentialViewModel.kt | 9 +++ .../credentialmanager/getflow/GetModel.kt | 9 ++- 6 files changed, 97 insertions(+), 10 deletions(-) diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 2f6d1b4bea02e..114de89ce82ac 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -62,4 +62,8 @@ Tap to unlock Manage sign-ins + + From another device + + Use a different device \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index b848a47f37dea..7e69987f1bf88 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -194,7 +194,7 @@ class CredentialManagerRepo( ) ) .setRemoteEntry( - newRemoteEntry("key1", "subkey-1") + newRemoteEntry("key2", "subkey-1") ) .setIsDefaultProvider(true) .build(), @@ -252,6 +252,8 @@ class CredentialManagerRepo( "Open Google Password Manager", "beckett-family@gmail.com" ), ) + ).setRemoteEntry( + newRemoteEntry("key4", "subkey-1") ).build(), GetCredentialProviderData.Builder("com.dashlane") .setCredentialEntries( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index df797436baf69..fad9364e3c237 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -30,6 +30,7 @@ import com.android.credentialmanager.getflow.ActionEntryInfo import com.android.credentialmanager.getflow.AuthenticationEntryInfo import com.android.credentialmanager.getflow.CredentialEntryInfo import com.android.credentialmanager.getflow.ProviderInfo +import com.android.credentialmanager.getflow.RemoteEntryInfo import com.android.credentialmanager.jetpack.provider.ActionUi import com.android.credentialmanager.jetpack.provider.CredentialEntryUi import com.android.credentialmanager.jetpack.provider.SaveEntryUi @@ -59,10 +60,11 @@ class GetFlowUtils { credentialEntryList = getCredentialOptionInfoList( it.providerFlattenedComponentName, it.credentialEntries, context), authenticationEntry = getAuthenticationEntry( - it.providerFlattenedComponentName, - providerDisplayName, - providerIcon, - it.authenticationEntry), + it.providerFlattenedComponentName, + providerDisplayName, + providerIcon, + it.authenticationEntry), + remoteEntry = getRemoteEntry(it.providerFlattenedComponentName, it.remoteEntry), actionEntryList = getActionEntryList( it.providerFlattenedComponentName, it.actionChips, context), ) @@ -116,6 +118,18 @@ class GetFlowUtils { ) } + private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? { + // TODO: should also call fromSlice after getting the official jetpack code. + if (remoteEntry == null) { + return null + } + return RemoteEntryInfo( + providerId = providerId, + entryKey = remoteEntry.key, + entrySubkey = remoteEntry.subkey, + ) + } + private fun getActionEntryList( providerId: String, actionEntries: List, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 6fd51dd3b69a7..19a032fcf4b81 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -220,16 +221,25 @@ fun AllSignInOptionCard( ) } // Locked password manager - item { - if (!authenticationEntryList.isEmpty()) { + if (!authenticationEntryList.isEmpty()) { + item { LockedCredentials( authenticationEntryList = authenticationEntryList, onEntrySelected = onEntrySelected, ) } } - // TODO: Remote action - // Manage sign-ins + // From another device + val remoteEntry = providerDisplayInfo.remoteEntry + if (remoteEntry != null) { + item { + RemoteEntryCard( + remoteEntry = remoteEntry, + onEntrySelected = onEntrySelected, + ) + } + } + // Manage sign-ins (action chips) item { ActionChips(providerInfoList = providerInfoList, onEntrySelected = onEntrySelected) } @@ -270,6 +280,47 @@ fun ActionChips( } } +@Composable +fun RemoteEntryCard( + remoteEntry: RemoteEntryInfo, + onEntrySelected: (EntryInfo) -> Unit, +) { + Text( + text = stringResource(R.string.get_dialog_heading_from_another_device), + style = MaterialTheme.typography.labelLarge, + modifier = Modifier.padding(vertical = 8.dp) + ) + Card( + modifier = Modifier.fillMaxWidth().wrapContentHeight(), + shape = MaterialTheme.shapes.medium, + ) { + Column( + modifier = Modifier.fillMaxWidth().wrapContentHeight(), + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + Entry( + onClick = {onEntrySelected(remoteEntry)}, + icon = { + Icon( + painter = painterResource(R.drawable.ic_other_devices), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.padding(start = 18.dp) + ) + }, + label = { + Text( + text = stringResource(R.string.get_dialog_option_headline_use_a_different_device), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp) + .align(alignment = Alignment.CenterHorizontally) + ) + } + ) + } + } +} + @Composable fun LockedCredentials( authenticationEntryList: List, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt index f78456aab332c..22370a91cde48 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt @@ -27,6 +27,7 @@ import com.android.credentialmanager.CredentialManagerRepo import com.android.credentialmanager.common.DialogResult import com.android.credentialmanager.common.ResultState import com.android.credentialmanager.jetpack.developer.PublicKeyCredential +import com.android.internal.util.Preconditions data class GetCredentialUiState( val providerInfoList: List, @@ -86,10 +87,14 @@ private fun toProviderDisplayInfo( val userNameToCredentialEntryMap = mutableMapOf>() val authenticationEntryList = mutableListOf() + val remoteEntryList = mutableListOf() providerInfoList.forEach { providerInfo -> if (providerInfo.authenticationEntry != null) { authenticationEntryList.add(providerInfo.authenticationEntry) } + if (providerInfo.remoteEntry != null) { + remoteEntryList.add(providerInfo.remoteEntry) + } providerInfo.credentialEntryList.forEach { userNameToCredentialEntryMap.compute( @@ -105,6 +110,9 @@ private fun toProviderDisplayInfo( } } } + // There can only be at most one remote entry + // TODO: fail elegantly + Preconditions.checkState(remoteEntryList.size <= 1) // Compose sortedUserNameToCredentialEntryList val comparator = CredentialEntryInfoComparator() @@ -122,6 +130,7 @@ private fun toProviderDisplayInfo( return ProviderDisplayInfo( sortedUserNameToCredentialEntryList = sortedUserNameToCredentialEntryList, authenticationEntryList = authenticationEntryList, + remoteEntry = remoteEntryList.getOrNull(0), ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index c541e08ffbb47..76d9847f53569 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -28,8 +28,8 @@ data class ProviderInfo( val displayName: String, val credentialEntryList: List, val authenticationEntry: AuthenticationEntryInfo?, + val remoteEntry: RemoteEntryInfo?, val actionEntryList: List, - // TODO: add remote entry ) /** Display-centric data structure derived from the [ProviderInfo]. This abstraction is not grouping @@ -41,6 +41,7 @@ data class ProviderDisplayInfo( */ val sortedUserNameToCredentialEntryList: List, val authenticationEntryList: List, + val remoteEntry: RemoteEntryInfo? ) abstract class EntryInfo ( @@ -72,6 +73,12 @@ class AuthenticationEntryInfo( val icon: Drawable, ) : EntryInfo(providerId, entryKey, entrySubkey) +class RemoteEntryInfo( + providerId: String, + entryKey: String, + entrySubkey: String, +) : EntryInfo(providerId, entryKey, entrySubkey) + class ActionEntryInfo( providerId: String, entryKey: String,