Merge "Add remote entry for get flow."

This commit is contained in:
Helen Qin
2022-11-17 18:08:31 +00:00
committed by Android (Google) Code Review
6 changed files with 97 additions and 10 deletions

View File

@@ -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>

View File

@@ -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(

View File

@@ -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<Entry>,

View File

@@ -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<AuthenticationEntryInfo>,

View File

@@ -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>,
@@ -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(
@@ -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),
)
}

View File

@@ -28,8 +28,8 @@ data class ProviderInfo(
val displayName: String,
val credentialEntryList: List<CredentialEntryInfo>,
val authenticationEntry: AuthenticationEntryInfo?,
val remoteEntry: RemoteEntryInfo?,
val actionEntryList: List<ActionEntryInfo>,
// 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<PerUserNameCredentialEntryList>,
val authenticationEntryList: List<AuthenticationEntryInfo>,
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,