Add the Another device row in the more options screen

screencast: https://screencast.googleplex.com/cast/NTEwMDE0ODM2MDIxNjU3NnxmODFmOGRjMy0zMw

Test: deployed locally

Bug: 253157211
Change-Id: Ib0572d4b1b0e1f576b1cc748252782f382f8a4b1
This commit is contained in:
Qinmei Du
2022-11-10 07:45:56 +00:00
parent 4e72b9846e
commit b78f3eed0e
8 changed files with 130 additions and 17 deletions

View File

@@ -131,6 +131,13 @@ public class CreateCredentialProviderData extends ProviderData implements Parcel
return this;
}
/** Sets the remote entry of the provider. */
@NonNull
public Builder setRemoteEntry(@Nullable Entry remoteEntry) {
mRemoteEntry = remoteEntry;
return this;
}
/** Builds a {@link CreateCredentialProviderData}. */
@NonNull
public CreateCredentialProviderData build() {

View File

@@ -0,0 +1,15 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="VectorPath"
android:name="vector"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:name="path"
android:pathData="M 7.6 4.72 L 7.6 7.6 L 4.72 7.6 L 4.72 4.72 L 7.6 4.72 Z M 9.04 3.28 L 3.28 3.28 L 3.28 9.04 L 9.04 9.04 L 9.04 3.28 Z M 7.6 12.4 L 7.6 15.28 L 4.72 15.28 L 4.72 12.4 L 7.6 12.4 Z M 9.04 10.96 L 3.28 10.96 L 3.28 16.72 L 9.04 16.72 L 9.04 10.96 Z M 15.28 4.72 L 15.28 7.6 L 12.4 7.6 L 12.4 4.72 L 15.28 4.72 Z M 16.72 3.28 L 10.96 3.28 L 10.96 9.04 L 16.72 9.04 L 16.72 3.28 Z M 10.96 10.96 L 12.4 10.96 L 12.4 12.4 L 10.96 12.4 L 10.96 10.96 Z M 12.4 12.4 L 13.84 12.4 L 13.84 13.84 L 12.4 13.84 L 12.4 12.4 Z M 13.84 10.96 L 15.28 10.96 L 15.28 12.4 L 13.84 12.4 L 13.84 10.96 Z M 10.96 13.84 L 12.4 13.84 L 12.4 15.28 L 10.96 15.28 L 10.96 13.84 Z M 12.4 15.28 L 13.84 15.28 L 13.84 16.72 L 12.4 16.72 L 12.4 15.28 Z M 13.84 13.84 L 15.28 13.84 L 15.28 15.28 L 13.84 15.28 L 13.84 13.84 Z M 15.28 12.4 L 16.72 12.4 L 16.72 13.84 L 15.28 13.84 L 15.28 12.4 Z M 15.28 15.28 L 16.72 15.28 L 16.72 16.72 L 15.28 16.72 L 15.28 15.28 Z M 19.6 5.2 L 17.68 5.2 L 17.68 2.32 L 14.8 2.32 L 14.8 0.4 L 19.6 0.4 L 19.6 5.2 Z M 19.6 19.6 L 19.6 14.8 L 17.68 14.8 L 17.68 17.68 L 14.8 17.68 L 14.8 19.6 L 19.6 19.6 Z M 0.4 19.6 L 5.2 19.6 L 5.2 17.68 L 2.32 17.68 L 2.32 14.8 L 0.4 14.8 L 0.4 19.6 Z M 0.4 0.4 L 0.4 5.2 L 2.32 5.2 L 2.32 2.32 L 5.2 2.32 L 5.2 0.4 L 0.4 0.4 Z"
android:fillColor="#000000"
android:strokeWidth="1"/>
</vector>

View File

@@ -28,6 +28,7 @@
<string name="passkey">passkey</string>
<string name="password">password</string>
<string name="sign_ins">sign-ins</string>
<string name="another_device">Another device</string>
<string name="other_password_manager">Other password manager</string>
<!-- Spoken content description of an element which will close the sheet when clicked. -->
<string name="close_sheet">"Close sheet"</string>

View File

@@ -181,6 +181,9 @@ class CredentialManagerRepo(
20, 7, 27, 11000),
)
)
.setRemoteEntry(
newRemoteEntry("key1", "subkey-1")
)
.setIsDefaultProvider(true)
.build(),
CreateCredentialProviderData
@@ -308,6 +311,19 @@ class CredentialManagerRepo(
)
}
private fun newRemoteEntry(
key: String,
subkey: String,
): Entry {
return Entry(
key,
subkey,
Slice.Builder(
Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
).build()
)
}
private fun testCreateRequestInfo(): RequestInfo {
val data = Bundle()
return RequestInfo.newCreateRequestInfo(

View File

@@ -23,6 +23,7 @@ import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.DisabledProviderData
import com.android.credentialmanager.createflow.CreateOptionInfo
import com.android.credentialmanager.createflow.RemoteInfo
import com.android.credentialmanager.getflow.ActionEntryInfo
import com.android.credentialmanager.getflow.AuthenticationEntryInfo
import com.android.credentialmanager.getflow.CredentialEntryInfo
@@ -120,6 +121,7 @@ class CreateFlowUtils {
displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
createOptions = toCreationOptionInfoList(it.saveEntries, context),
isDefault = it.isDefaultProvider,
remoteEntry = toRemoteInfo(it.remoteEntry),
)
}
}
@@ -164,5 +166,17 @@ class CreateFlowUtils {
)
}
}
private fun toRemoteInfo(
remoteEntry: Entry?,
): RemoteInfo? {
// TODO: should also call fromSlice after getting the official jetpack code.
return if (remoteEntry != null) {
RemoteInfo(
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
)
} else null
}
}
}

View File

@@ -30,6 +30,7 @@ class EnabledProviderInfo(
displayName: String,
var createOptions: List<CreateOptionInfo>,
val isDefault: Boolean,
var remoteEntry: RemoteInfo?,
) : ProviderInfo(icon, name, displayName)
class DisabledProviderInfo(
@@ -55,6 +56,11 @@ class CreateOptionInfo(
val lastUsedTimeMillis: Long?,
) : EntryInfo(entryKey, entrySubkey)
class RemoteInfo(
entryKey: String,
entrySubkey: String,
) : EntryInfo(entryKey, entrySubkey)
data class RequestDisplayInfo(
val userName: String,
val displayName: String,

View File

@@ -80,7 +80,8 @@ fun CreatePasskeyScreen(
disabledProviderList = uiState.disabledProviders,
onBackButtonSelected = viewModel::onBackButtonSelected,
onOptionSelected = viewModel::onMoreOptionsRowSelected,
onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected
onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected,
onRemoteEntrySelected = viewModel::onRemoteEntrySelected
)
CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
providerInfo = uiState.activeEntry?.activeProvider!!,
@@ -216,10 +217,11 @@ fun ProviderSelectionCard(
fun MoreOptionsSelectionCard(
requestDisplayInfo: RequestDisplayInfo,
enabledProviderList: List<EnabledProviderInfo>,
disabledProviderList: List<DisabledProviderInfo>,
disabledProviderList: List<DisabledProviderInfo>?,
onBackButtonSelected: () -> Unit,
onOptionSelected: (ActiveEntry) -> Unit,
onDisabledPasswordManagerSelected: () -> Unit,
onRemoteEntrySelected: () -> Unit,
) {
Card() {
Column() {
@@ -267,11 +269,26 @@ fun MoreOptionsSelectionCard(
}
}
}
item {
MoreOptionsDisabledProvidersRow(
disabledProviders = disabledProviderList,
onDisabledPasswordManagerSelected = onDisabledPasswordManagerSelected,
)
if (disabledProviderList != null) {
item {
MoreOptionsDisabledProvidersRow(
disabledProviders = disabledProviderList,
onDisabledPasswordManagerSelected = onDisabledPasswordManagerSelected,
)
}
}
var hasRemoteInfo = false
enabledProviderList.forEach {
if (it.remoteEntry != null) {
hasRemoteInfo = true
}
}
if (hasRemoteInfo) {
item {
RemoteEntryRow(
onRemoteEntrySelected = onRemoteEntrySelected,
)
}
}
}
}
@@ -498,7 +515,7 @@ fun MoreOptionsInfoRow(
modifier = Modifier.fillMaxWidth(),
onClick = onOptionSelected,
icon = {
Image(modifier = Modifier.size(32.dp, 32.dp).padding(start = 10.dp),
Image(modifier = Modifier.size(32.dp, 32.dp).padding(start = 16.dp),
bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
contentDescription = null)
},
@@ -508,12 +525,14 @@ fun MoreOptionsInfoRow(
Text(
text = providerInfo.displayName,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp)
modifier = Modifier.padding(top = 16.dp, start = 16.dp)
)
if (createOptionInfo.userProviderDisplayName != null) {
Text(
text = createOptionInfo.userProviderDisplayName,
style = MaterialTheme.typography.bodyMedium)
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(start = 16.dp)
)
}
if (createOptionInfo.passwordCount != null && createOptionInfo.passkeyCount != null) {
Text(
@@ -524,7 +543,7 @@ fun MoreOptionsInfoRow(
createOptionInfo.passkeyCount
),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
)
} else if (createOptionInfo.passwordCount != null) {
Text(
@@ -534,7 +553,7 @@ fun MoreOptionsInfoRow(
createOptionInfo.passwordCount
),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
)
} else if (createOptionInfo.passkeyCount != null) {
Text(
@@ -544,7 +563,7 @@ fun MoreOptionsInfoRow(
createOptionInfo.passkeyCount
),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
)
} else if (createOptionInfo.totalCredentialCount != null) {
// TODO: Handle the case when there is total count
@@ -567,7 +586,8 @@ fun MoreOptionsDisabledProvidersRow(
icon = {
Icon(
Icons.Filled.Add,
contentDescription = null
contentDescription = null,
modifier = Modifier.padding(start = 16.dp)
)
},
shape = MaterialTheme.shapes.large,
@@ -576,12 +596,42 @@ fun MoreOptionsDisabledProvidersRow(
Text(
text = stringResource(R.string.other_password_manager),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp)
modifier = Modifier.padding(top = 16.dp, start = 16.dp)
)
Text(
text = disabledProviders.joinToString(separator = ", "){ it.displayName },
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp)
)
}
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RemoteEntryRow(
onRemoteEntrySelected: () -> Unit,
) {
SuggestionChip(
modifier = Modifier.fillMaxWidth(),
onClick = onRemoteEntrySelected,
icon = {
Icon(
painter = painterResource(R.drawable.ic_other_devices),
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.padding(start = 18.dp)
)
},
shape = MaterialTheme.shapes.large,
label = {
Column() {
Text(
text = stringResource(R.string.another_device),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
.align(alignment = Alignment.CenterHorizontally)
)
}
}

View File

@@ -29,7 +29,7 @@ import com.android.credentialmanager.common.ResultState
data class CreatePasskeyUiState(
val enabledProviders: List<EnabledProviderInfo>,
val disabledProviders: List<DisabledProviderInfo>,
val disabledProviders: List<DisabledProviderInfo>? = null,
val currentScreenState: CreateScreenState,
val requestDisplayInfo: RequestDisplayInfo,
val activeEntry: ActiveEntry? = null,
@@ -103,6 +103,10 @@ class CreatePasskeyViewModel(
// TODO: Complete this function
}
fun onRemoteEntrySelected() {
// TODO: Complete this function
}
fun onCancel() {
CredentialManagerRepo.getInstance().onCancel()
dialogResult.value = DialogResult(ResultState.CANCELED)