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

Commit 3708b378 authored by Qinmei Du's avatar Qinmei Du Committed by Android (Google) Code Review
Browse files

Merge "Add the screen when there's no create option for the default provider...

Merge "Add the screen when there's no create option for the default provider but there's remoteEntry"
parents 8dadc7e8 3b7090ae
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@
  <string name="save_password_to_title">Save password to</string>
  <!-- This appears as the title of the modal bottom sheet for users to choose other available places the created other credential types can be saved to. [CHAR LIMIT=200] -->
  <string name="save_sign_in_to_title">Save sign-in to</string>
  <!-- This appears as the title of the modal bottom sheet for users to choose to create a passkey on another device. [CHAR LIMIT=200] -->
  <string name="create_passkey_in_other_device_title">Create a passkey in another device?</string>
  <!-- This appears as the title of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=200] -->
  <string name="use_provider_for_all_title">Use <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g> for all your sign-ins?</string>
  <!-- TODO: Check the wording here. -->
+45 −12
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RemoteInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
@@ -142,25 +144,22 @@ class CredentialManagerRepo(
    val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
      // Handle runtime cast error
      providerDisabledList as List<DisabledProviderData>, context)
    var hasDefault = false
    var defaultProvider: EnabledProviderInfo = providerEnabledList.first()
    var defaultProvider: EnabledProviderInfo? = null
    var remoteEntry: RemoteInfo? = null
    providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
      providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
      if (providerInfo.isDefault) {hasDefault = true; defaultProvider = providerInfo} }
      if (providerInfo.isDefault) {defaultProvider = providerInfo}
      if (providerInfo.remoteEntry != null) {
        remoteEntry = providerInfo.remoteEntry!!
      }
    }
    return CreateCredentialUiState(
      enabledProviders = providerEnabledList,
      disabledProviders = providerDisabledList,
      // TODO: Add the screen when defaultProvider has no createOption but
      //  there's remoteInfo under other providers
      if (!hasDefault || defaultProvider.createOptions.isEmpty()) {
        if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL)
        {CreateScreenState.PASSKEY_INTRO} else {CreateScreenState.PROVIDER_SELECTION}
      } else {CreateScreenState.CREATION_OPTION_SELECTION},
      toCreateScreenState(requestDisplayInfo, defaultProvider, remoteEntry),
      requestDisplayInfo,
      false,
      if (hasDefault) {
        ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
      } else null
      toActiveEntry(defaultProvider, remoteEntry),
    )
  }

@@ -509,4 +508,38 @@ class CredentialManagerRepo(
      "tribank.us"
    )
  }

  private fun toCreateScreenState(
    requestDisplayInfo: RequestDisplayInfo,
    defaultProvider: EnabledProviderInfo?,
    remoteEntry: RemoteInfo?,
  ): CreateScreenState {
    return if (
      defaultProvider != null && defaultProvider.createOptions.isEmpty() && remoteEntry != null
    ){
      CreateScreenState.EXTERNAL_ONLY_SELECTION
    } else if (defaultProvider == null || defaultProvider.createOptions.isEmpty()) {
      if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL) {
        CreateScreenState.PASSKEY_INTRO
      } else {
        CreateScreenState.PROVIDER_SELECTION
      }
    } else {
      CreateScreenState.CREATION_OPTION_SELECTION
    }
  }

  private fun toActiveEntry(
    defaultProvider: EnabledProviderInfo?,
    remoteEntry: RemoteInfo?,
  ): ActiveEntry? {
    return if (
      defaultProvider != null && defaultProvider.createOptions.isNotEmpty()
    ) {
      ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
    } else if (
      defaultProvider != null && defaultProvider.createOptions.isEmpty() && remoteEntry != null) {
      ActiveEntry(defaultProvider, remoteEntry)
    } else null
  }
}
+81 −5
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ fun CreateCredentialScreen(
    viewModel.onEntrySelected(it, providerActivityLauncher)
  }
  val confirmEntryCallback: () -> Unit = {
    viewModel.onConfirmCreationSelected(providerActivityLauncher)
    viewModel.onConfirmEntrySelected(providerActivityLauncher)
  }
  val state = rememberModalBottomSheetState(
    initialValue = ModalBottomSheetValue.Expanded,
@@ -108,6 +108,13 @@ fun CreateCredentialScreen(
          providerInfo = uiState.activeEntry?.activeProvider!!,
          onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected
        )
        CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
          requestDisplayInfo = uiState.requestDisplayInfo,
          activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!,
          onOptionSelected = selectEntryCallback,
          onConfirm = confirmEntryCallback,
          onCancel = viewModel::onCancel,
        )
      }
    },
    scrimColor = MaterialTheme.colorScheme.scrim,
@@ -489,7 +496,7 @@ fun CreationSelectionCard(
      ) {
        PrimaryCreateOptionRow(
          requestDisplayInfo = requestDisplayInfo,
          createOptionInfo = createOptionInfo,
          entryInfo = createOptionInfo,
          onOptionSelected = onOptionSelected
        )
      }
@@ -560,18 +567,87 @@ fun CreationSelectionCard(
  }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExternalOnlySelectionCard(
  requestDisplayInfo: RequestDisplayInfo,
  activeRemoteEntry: EntryInfo,
  onOptionSelected: (EntryInfo) -> Unit,
  onConfirm: () -> Unit,
  onCancel: () -> Unit,
) {
  Card() {
    Column() {
      Icon(
        painter = painterResource(R.drawable.ic_other_devices),
        contentDescription = null,
        tint = Color.Unspecified,
        modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
          .padding(all = 24.dp).size(32.dp)
      )
      Text(
        text = stringResource(R.string.create_passkey_in_other_device_title),
        style = MaterialTheme.typography.titleMedium,
        modifier = Modifier.padding(horizontal = 24.dp)
          .align(alignment = Alignment.CenterHorizontally),
        textAlign = TextAlign.Center,
      )
      Divider(
        thickness = 24.dp,
        color = Color.Transparent
      )
      Card(
        shape = MaterialTheme.shapes.medium,
        modifier = Modifier
          .padding(horizontal = 24.dp)
          .align(alignment = Alignment.CenterHorizontally),
      ) {
        PrimaryCreateOptionRow(
          requestDisplayInfo = requestDisplayInfo,
          entryInfo = activeRemoteEntry,
          onOptionSelected = onOptionSelected
        )
      }
      Divider(
        thickness = 24.dp,
        color = Color.Transparent
      )
      Row(
        horizontalArrangement = Arrangement.SpaceBetween,
        modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
      ) {
        CancelButton(
          stringResource(R.string.string_cancel),
          onClick = onCancel
        )
        ConfirmButton(
          stringResource(R.string.string_continue),
          onClick = onConfirm
        )
      }
      Divider(
        thickness = 18.dp,
        color = Color.Transparent,
        modifier = Modifier.padding(bottom = 16.dp)
      )
    }
  }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PrimaryCreateOptionRow(
  requestDisplayInfo: RequestDisplayInfo,
  createOptionInfo: CreateOptionInfo,
  entryInfo: EntryInfo,
  onOptionSelected: (EntryInfo) -> Unit
) {
  Entry(
    onClick = {onOptionSelected(createOptionInfo)},
    onClick = {onOptionSelected(entryInfo)},
    icon = {
      Icon(
        bitmap = createOptionInfo.profileIcon.toBitmap().asImageBitmap(),
        bitmap = if (entryInfo is CreateOptionInfo) {
          entryInfo.profileIcon.toBitmap().asImageBitmap()
        } else {requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()},
        contentDescription = null,
        tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
        modifier = Modifier.padding(start = 18.dp).size(32.dp)
+1 −1
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@ class CreateCredentialViewModel(
    }
  }

  fun onConfirmCreationSelected(
  fun onConfirmEntrySelected(
    launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
  ) {
    val selectedEntry = uiState.activeEntry?.activeEntryInfo
+1 −0
Original line number Diff line number Diff line
@@ -95,4 +95,5 @@ enum class CreateScreenState {
  CREATION_OPTION_SELECTION,
  MORE_OPTIONS_SELECTION,
  MORE_OPTIONS_ROW_INTRO,
  EXTERNAL_ONLY_SELECTION,
}