Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96d5bac2 authored by Qinmei Du's avatar Qinmei Du
Browse files

Add choose other password manager row in the more options screen

screenshot: https://screenshot.googleplex.com/4LX6xuETvKHRgCv

Test: deployed locally

Bug: 253157211
Change-Id: Ia2f3ba2d9eb86dc2216b5f2fcafa5e8aace9e5f0
parent cef8c91b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
  <string name="passkeys">passkeys</string>
  <string name="passwords">passwords</string>
  <string name="sign_ins">sign-ins</string>
  <string name="other_password_manager">Other password manager</string>
  <string name="createOptionInfo_icon_description">CreateOptionInfo credentialType icon</string>
  <!-- Spoken content description of an element which will close the sheet when clicked. -->
  <string name="close_sheet">"Close sheet"</string>
+33 −14
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.credentials.ui.Constants
import android.credentials.ui.Entry
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.DisabledProviderData
import android.credentials.ui.ProviderData
import android.credentials.ui.RequestInfo
import android.credentials.ui.BaseDialogResult
@@ -39,7 +40,7 @@ import android.os.ResultReceiver
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreatePasskeyUiState
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.ProviderInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState
@@ -51,7 +52,8 @@ class CredentialManagerRepo(
  intent: Intent,
) {
  private val requestInfo: RequestInfo
  private val providerList: List<ProviderData>
  private val providerEnabledList: List<ProviderData>
  private val providerDisabledList: List<DisabledProviderData>
  // TODO: require non-null.
  val resultReceiver: ResultReceiver?

@@ -61,16 +63,16 @@ class CredentialManagerRepo(
      RequestInfo::class.java
    ) ?: testCreateRequestInfo()

    providerList = when (requestInfo.type) {
    providerEnabledList = when (requestInfo.type) {
      RequestInfo.TYPE_CREATE ->
        intent.extras?.getParcelableArrayList(
                ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
                CreateCredentialProviderData::class.java
        ) ?: testCreateCredentialProviderList()
        ) ?: testCreateCredentialEnabledProviderList()
      RequestInfo.TYPE_GET ->
        intent.extras?.getParcelableArrayList(
          ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
          GetCredentialProviderData::class.java
          DisabledProviderData::class.java
        ) ?: testGetCredentialProviderList()
      else -> {
        // TODO: fail gracefully
@@ -78,6 +80,12 @@ class CredentialManagerRepo(
      }
    }

    providerDisabledList =
      intent.extras?.getParcelableArrayList(
        ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST,
        DisabledProviderData::class.java
      ) ?: testDisabledProviderList()

    resultReceiver = intent.getParcelableExtra(
      Constants.EXTRA_RESULT_RECEIVER,
      ResultReceiver::class.java
@@ -103,25 +111,28 @@ class CredentialManagerRepo(
  }

  fun getCredentialInitialUiState(): GetCredentialUiState {
    val providerList = GetFlowUtils.toProviderList(
    val providerEnabledList = GetFlowUtils.toProviderList(
    // TODO: handle runtime cast error
    providerList as List<GetCredentialProviderData>, context)
      providerEnabledList as List<GetCredentialProviderData>, context)
    // TODO: covert from real requestInfo
    val requestDisplayInfo = com.android.credentialmanager.getflow.RequestDisplayInfo("tribank")
    return GetCredentialUiState(
      providerList,
      providerEnabledList,
      GetScreenState.PRIMARY_SELECTION,
      requestDisplayInfo,
    )
  }

  fun createPasskeyInitialUiState(): CreatePasskeyUiState {
    val providerList = CreateFlowUtils.toProviderList(
    val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
      // Handle runtime cast error
      providerList as List<CreateCredentialProviderData>, context)
      providerEnabledList as List<CreateCredentialProviderData>, context)
    val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
      // Handle runtime cast error
      providerDisabledList as List<DisabledProviderData>, context)
    var hasDefault = false
    var defaultProvider: ProviderInfo = providerList.first()
    providerList.forEach{providerInfo -> providerInfo.createOptions =
    var defaultProvider: EnabledProviderInfo = providerEnabledList.first()
    providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
      providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
      if (providerInfo.isDefault) {hasDefault = true; defaultProvider = providerInfo} }
    // TODO: covert from real requestInfo
@@ -131,7 +142,8 @@ class CredentialManagerRepo(
      TYPE_PUBLIC_KEY_CREDENTIAL,
      "tribank")
    return CreatePasskeyUiState(
      providers = providerList,
      enabledProviders = providerEnabledList,
      disabledProviders = providerDisabledList,
      if (hasDefault)
      {CreateScreenState.CREATION_OPTION_SELECTION} else {CreateScreenState.PASSKEY_INTRO},
      requestDisplayInfo,
@@ -157,7 +169,7 @@ class CredentialManagerRepo(
  }

  // TODO: below are prototype functionalities. To be removed for productionization.
  private fun testCreateCredentialProviderList(): List<CreateCredentialProviderData> {
  private fun testCreateCredentialEnabledProviderList(): List<CreateCredentialProviderData> {
    return listOf(
      CreateCredentialProviderData
        .Builder("com.google/com.google.CredentialManagerService")
@@ -185,6 +197,13 @@ class CredentialManagerRepo(
    )
  }

  private fun testDisabledProviderList(): List<DisabledProviderData> {
    return listOf(
      DisabledProviderData("LastPass"),
      DisabledProviderData("Xyzinstalledbutdisabled"),
    )
  }

  private fun testGetCredentialProviderList(): List<GetCredentialProviderData> {
    return listOf(
      GetCredentialProviderData.Builder("com.google/com.google.CredentialManagerService")
+18 −3
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context
import android.credentials.ui.Entry
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.getflow.ActionEntryInfo
import com.android.credentialmanager.getflow.AuthenticationEntryInfo
@@ -103,12 +104,12 @@ class GetFlowUtils {
class CreateFlowUtils {
  companion object {

    fun toProviderList(
    fun toEnabledProviderList(
      providerDataList: List<CreateCredentialProviderData>,
      context: Context,
    ): List<com.android.credentialmanager.createflow.ProviderInfo> {
    ): List<com.android.credentialmanager.createflow.EnabledProviderInfo> {
      return providerDataList.map {
        com.android.credentialmanager.createflow.ProviderInfo(
        com.android.credentialmanager.createflow.EnabledProviderInfo(
          // TODO: replace to extract from the service data structure when available
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = it.providerFlattenedComponentName,
@@ -119,6 +120,20 @@ class CreateFlowUtils {
      }
    }

    fun toDisabledProviderList(
      providerDataList: List<DisabledProviderData>,
      context: Context,
    ): List<com.android.credentialmanager.createflow.DisabledProviderInfo> {
      return providerDataList.map {
        com.android.credentialmanager.createflow.DisabledProviderInfo(
          // TODO: replace to extract from the service data structure when available
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = it.providerFlattenedComponentName,
          displayName = it.providerFlattenedComponentName,
        )
      }
    }

    private fun toCreationOptionInfoList(
      creationEntries: List<Entry>,
      context: Context,
+15 −3
Original line number Diff line number Diff line
@@ -18,13 +18,25 @@ package com.android.credentialmanager.createflow

import android.graphics.drawable.Drawable

data class ProviderInfo(
open class ProviderInfo(
  val icon: Drawable,
  val name: String,
  val displayName: String,
)

class EnabledProviderInfo(
  icon: Drawable,
  name: String,
  displayName: String,
  var createOptions: List<CreateOptionInfo>,
  val isDefault: Boolean,
)
) : ProviderInfo(icon, name, displayName)

class DisabledProviderInfo(
  icon: Drawable,
  name: String,
  displayName: String,
) : ProviderInfo(icon, name, displayName)

open class EntryInfo (
  val entryKey: String,
@@ -55,7 +67,7 @@ data class RequestDisplayInfo(
 * user selects a different entry on the more option page.
 */
data class ActiveEntry (
  val activeProvider: ProviderInfo,
  val activeProvider: EnabledProviderInfo,
  val activeEntryInfo: EntryInfo,
)

+58 −14
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@@ -59,7 +60,7 @@ fun CreatePasskeyScreen(
          onCancel = viewModel::onCancel,
        )
        CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
          providerList = uiState.providers,
          enabledProviderList = uiState.enabledProviders,
          onCancel = viewModel::onCancel,
          onProviderSelected = viewModel::onProviderSelected
        )
@@ -70,14 +71,16 @@ fun CreatePasskeyScreen(
          onOptionSelected = viewModel::onPrimaryCreateOptionInfoSelected,
          onConfirm = viewModel::onPrimaryCreateOptionInfoSelected,
          onCancel = viewModel::onCancel,
          multiProvider = uiState.providers.size > 1,
          multiProvider = uiState.enabledProviders.size > 1,
          onMoreOptionsSelected = viewModel::onMoreOptionsSelected
        )
        CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
            requestDisplayInfo = uiState.requestDisplayInfo,
            providerList = uiState.providers,
            enabledProviderList = uiState.enabledProviders,
            disabledProviderList = uiState.disabledProviders,
            onBackButtonSelected = viewModel::onBackButtonSelected,
            onOptionSelected = viewModel::onMoreOptionsRowSelected
            onOptionSelected = viewModel::onMoreOptionsRowSelected,
            onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected
          )
        CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
          providerInfo = uiState.activeEntry?.activeProvider!!,
@@ -153,7 +156,7 @@ fun ConfirmationCard(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProviderSelectionCard(
  providerList: List<ProviderInfo>,
  enabledProviderList: List<EnabledProviderInfo>,
  onProviderSelected: (String) -> Unit,
  onCancel: () -> Unit
) {
@@ -182,7 +185,7 @@ fun ProviderSelectionCard(
        LazyColumn(
          verticalArrangement = Arrangement.spacedBy(2.dp)
        ) {
          providerList.forEach {
          enabledProviderList.forEach {
            item {
              ProviderRow(providerInfo = it, onProviderSelected = onProviderSelected)
            }
@@ -212,9 +215,11 @@ fun ProviderSelectionCard(
@Composable
fun MoreOptionsSelectionCard(
  requestDisplayInfo: RequestDisplayInfo,
  providerList: List<ProviderInfo>,
  enabledProviderList: List<EnabledProviderInfo>,
  disabledProviderList: List<DisabledProviderInfo>,
  onBackButtonSelected: () -> Unit,
  onOptionSelected: (ActiveEntry) -> Unit
  onOptionSelected: (ActiveEntry) -> Unit,
  onDisabledPasswordManagerSelected: () -> Unit,
) {
  Card() {
    Column() {
@@ -250,18 +255,24 @@ fun MoreOptionsSelectionCard(
        LazyColumn(
          verticalArrangement = Arrangement.spacedBy(2.dp)
        ) {
          providerList.forEach { providerInfo ->
            providerInfo.createOptions.forEach { createOptionInfo ->
          enabledProviderList.forEach { enabledProviderInfo ->
            enabledProviderInfo.createOptions.forEach { createOptionInfo ->
              item {
                MoreOptionsInfoRow(
                  providerInfo = providerInfo,
                  providerInfo = enabledProviderInfo,
                  createOptionInfo = createOptionInfo,
                  onOptionSelected = {
                    onOptionSelected(ActiveEntry(providerInfo, createOptionInfo))
                    onOptionSelected(ActiveEntry(enabledProviderInfo, createOptionInfo))
                  })
              }
            }
          }
          item {
            MoreOptionsDisabledProvidersRow(
              disabledProviders = disabledProviderList,
              onDisabledPasswordManagerSelected = onDisabledPasswordManagerSelected,
            )
          }
        }
      }
      Divider(
@@ -276,7 +287,7 @@ fun MoreOptionsSelectionCard(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsRowIntroCard(
  providerInfo: ProviderInfo,
  providerInfo: EnabledProviderInfo,
  onDefaultOrNotSelected: () -> Unit,
) {
  Card() {
@@ -483,7 +494,7 @@ fun PrimaryCreateOptionRow(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsInfoRow(
  providerInfo: ProviderInfo,
  providerInfo: EnabledProviderInfo,
  createOptionInfo: CreateOptionInfo,
  onOptionSelected: () -> Unit
) {
@@ -547,3 +558,36 @@ fun MoreOptionsInfoRow(
        }
    )
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsDisabledProvidersRow(
  disabledProviders: List<ProviderInfo>,
  onDisabledPasswordManagerSelected: () -> Unit,
) {
  SuggestionChip(
    modifier = Modifier.fillMaxWidth(),
    onClick = onDisabledPasswordManagerSelected,
    icon = {
      Icon(
        Icons.Filled.Add,
        contentDescription = null
      )
    },
    shape = MaterialTheme.shapes.large,
    label = {
      Column() {
        Text(
          text = stringResource(R.string.other_password_manager),
          style = MaterialTheme.typography.titleLarge,
          modifier = Modifier.padding(top = 16.dp)
        )
        Text(
          text = disabledProviders.joinToString(separator = ", "){ it.displayName },
          style = MaterialTheme.typography.bodyMedium,
          modifier = Modifier.padding(bottom = 16.dp)
        )
      }
    }
  )
}
 No newline at end of file
Loading