Loading packages/CredentialManager/res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -62,4 +62,8 @@ <string name="locked_credential_entry_label_subtext">Tap to unlock</string> <!-- Column heading for displaying action chips for managing sign-ins from each credential provider. [CHAR LIMIT=80] --> <string name="get_dialog_heading_manage_sign_ins">Manage sign-ins</string> <!-- Column heading for displaying option to use sign-ins saved on a different device. [CHAR LIMIT=80] --> <string name="get_dialog_heading_from_another_device">From another device</string> <!-- Headline text for an option to use sign-ins saved on a different device. [CHAR LIMIT=120] --> <string name="get_dialog_option_headline_use_a_different_device">Use a different device</string> </resources> No newline at end of file packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +3 −1 Original line number Diff line number Diff line Loading @@ -194,7 +194,7 @@ class CredentialManagerRepo( ) ) .setRemoteEntry( newRemoteEntry("key1", "subkey-1") newRemoteEntry("key2", "subkey-1") ) .setIsDefaultProvider(true) .build(), Loading Loading @@ -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( Loading packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +18 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -63,6 +64,7 @@ class GetFlowUtils { providerDisplayName, providerIcon, it.authenticationEntry), remoteEntry = getRemoteEntry(it.providerFlattenedComponentName, it.remoteEntry), actionEntryList = getActionEntryList( it.providerFlattenedComponentName, it.actionChips, context), ) Loading Loading @@ -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<Entry>, Loading packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +55 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -220,16 +221,25 @@ fun AllSignInOptionCard( ) } // Locked password manager item { 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) } Loading Loading @@ -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<AuthenticationEntryInfo>, Loading packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt +9 −0 Original line number Diff line number Diff line Loading @@ -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<ProviderInfo>, Loading Loading @@ -86,10 +87,14 @@ private fun toProviderDisplayInfo( val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>() val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>() val remoteEntryList = mutableListOf<RemoteEntryInfo>() providerInfoList.forEach { providerInfo -> if (providerInfo.authenticationEntry != null) { authenticationEntryList.add(providerInfo.authenticationEntry) } if (providerInfo.remoteEntry != null) { remoteEntryList.add(providerInfo.remoteEntry) } providerInfo.credentialEntryList.forEach { userNameToCredentialEntryMap.compute( Loading @@ -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() Loading @@ -122,6 +130,7 @@ private fun toProviderDisplayInfo( return ProviderDisplayInfo( sortedUserNameToCredentialEntryList = sortedUserNameToCredentialEntryList, authenticationEntryList = authenticationEntryList, remoteEntry = remoteEntryList.getOrNull(0), ) } Loading Loading
packages/CredentialManager/res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -62,4 +62,8 @@ <string name="locked_credential_entry_label_subtext">Tap to unlock</string> <!-- Column heading for displaying action chips for managing sign-ins from each credential provider. [CHAR LIMIT=80] --> <string name="get_dialog_heading_manage_sign_ins">Manage sign-ins</string> <!-- Column heading for displaying option to use sign-ins saved on a different device. [CHAR LIMIT=80] --> <string name="get_dialog_heading_from_another_device">From another device</string> <!-- Headline text for an option to use sign-ins saved on a different device. [CHAR LIMIT=120] --> <string name="get_dialog_option_headline_use_a_different_device">Use a different device</string> </resources> No newline at end of file
packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +3 −1 Original line number Diff line number Diff line Loading @@ -194,7 +194,7 @@ class CredentialManagerRepo( ) ) .setRemoteEntry( newRemoteEntry("key1", "subkey-1") newRemoteEntry("key2", "subkey-1") ) .setIsDefaultProvider(true) .build(), Loading Loading @@ -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( Loading
packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +18 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -63,6 +64,7 @@ class GetFlowUtils { providerDisplayName, providerIcon, it.authenticationEntry), remoteEntry = getRemoteEntry(it.providerFlattenedComponentName, it.remoteEntry), actionEntryList = getActionEntryList( it.providerFlattenedComponentName, it.actionChips, context), ) Loading Loading @@ -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<Entry>, Loading
packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +55 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -220,16 +221,25 @@ fun AllSignInOptionCard( ) } // Locked password manager item { 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) } Loading Loading @@ -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<AuthenticationEntryInfo>, Loading
packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt +9 −0 Original line number Diff line number Diff line Loading @@ -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<ProviderInfo>, Loading Loading @@ -86,10 +87,14 @@ private fun toProviderDisplayInfo( val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>() val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>() val remoteEntryList = mutableListOf<RemoteEntryInfo>() providerInfoList.forEach { providerInfo -> if (providerInfo.authenticationEntry != null) { authenticationEntryList.add(providerInfo.authenticationEntry) } if (providerInfo.remoteEntry != null) { remoteEntryList.add(providerInfo.remoteEntry) } providerInfo.credentialEntryList.forEach { userNameToCredentialEntryMap.compute( Loading @@ -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() Loading @@ -122,6 +130,7 @@ private fun toProviderDisplayInfo( return ProviderDisplayInfo( sortedUserNameToCredentialEntryList = sortedUserNameToCredentialEntryList, authenticationEntryList = authenticationEntryList, remoteEntry = remoteEntryList.getOrNull(0), ) } Loading