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

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

Merge "Update get flow snackBar page"

parents 7e3c1224 1abbd70f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -80,6 +80,8 @@
  <string name="close_sheet">"Close sheet"</string>
  <!-- Spoken content description of the back arrow button. -->
  <string name="accessibility_back_arrow_button">"Go back to the previous page"</string>
  <!-- Spoken content description of the close button. -->
  <string name="accessibility_close_button">"Close the Credential Manager action suggestion appearing at the bottom of the screen"</string>

  <!-- Strings for the get flow. -->
  <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
+1 −4
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RemoteInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
@@ -124,11 +123,9 @@ class CredentialManagerRepo(
    val providerEnabledList = GetFlowUtils.toProviderList(
    // TODO: handle runtime cast error
      providerEnabledList as List<GetCredentialProviderData>, context)
    // TODO: covert from real requestInfo
    val requestDisplayInfo = com.android.credentialmanager.getflow.RequestDisplayInfo("the app")
    val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo)
    return GetCredentialUiState(
      providerEnabledList,
      GetScreenState.PRIMARY_SELECTION,
      requestDisplayInfo,
    )
  }
+8 −0
Original line number Diff line number Diff line
@@ -88,6 +88,14 @@ class GetFlowUtils {
      }
    }

    fun toRequestDisplayInfo(
      requestInfo: RequestInfo,
    ): com.android.credentialmanager.getflow.RequestDisplayInfo {
      return com.android.credentialmanager.getflow.RequestDisplayInfo(
        appDomainName = requestInfo.appPackageName
      )
    }


    /* From service data structure to UI credential entry list representation. */
    private fun getCredentialOptionInfoList(
+85 −33
Original line number Diff line number Diff line
@@ -32,15 +32,19 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Snackbar
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@@ -65,6 +69,7 @@ import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.common.ui.TransparentBackgroundEntry
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
import com.android.credentialmanager.ui.theme.EntryShape
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme

@Composable
fun GetCredentialScreen(
@@ -75,26 +80,31 @@ fun GetCredentialScreen(
        initialValue = ModalBottomSheetValue.Expanded,
        skipHalfExpanded = true
    )
    val uiState = viewModel.uiState
    if (uiState.currentScreenState != GetScreenState.REMOTE_ONLY) {
        ModalBottomSheetLayout(
            sheetBackgroundColor = MaterialTheme.colorScheme.surface,
            modifier = Modifier.background(Color.Transparent),
            sheetState = state,
            sheetContent = {
            val uiState = viewModel.uiState
                // TODO: hide UI at top level
                if (!uiState.hidden) {
                when (uiState.currentScreenState) {
                    GetScreenState.PRIMARY_SELECTION -> PrimarySelectionCard(
                    if (uiState.currentScreenState == GetScreenState.PRIMARY_SELECTION) {
                        PrimarySelectionCard(
                            requestDisplayInfo = uiState.requestDisplayInfo,
                            providerDisplayInfo = uiState.providerDisplayInfo,
                            onEntrySelected = viewModel::onEntrySelected,
                            onCancel = viewModel::onCancel,
                            onMoreOptionSelected = viewModel::onMoreOptionSelected,
                        )
                    GetScreenState.ALL_SIGN_IN_OPTIONS -> AllSignInOptionCard(
                    } else {
                        AllSignInOptionCard(
                            providerInfoList = uiState.providerInfoList,
                            providerDisplayInfo = uiState.providerDisplayInfo,
                            onEntrySelected = viewModel::onEntrySelected,
                            onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
                            onCancel = viewModel::onCancel,
                            isNoAccount = uiState.isNoAccount,
                        )
                    }
                } else if (uiState.selectedEntry != null && !uiState.providerActivityPending) {
@@ -109,6 +119,12 @@ fun GetCredentialScreen(
                viewModel.onCancel()
            }
        }
    } else {
        SnackBarScreen(
            onClick = viewModel::onMoreOptionOnSnackBarSelected,
            onCancel = viewModel::onCancel,
        )
    }
}

/** Draws the primary credential selection page. */
@@ -195,6 +211,8 @@ fun AllSignInOptionCard(
    providerDisplayInfo: ProviderDisplayInfo,
    onEntrySelected: (EntryInfo) -> Unit,
    onBackButtonClicked: () -> Unit,
    onCancel: () -> Unit,
    isNoAccount: Boolean,
) {
    val sortedUserNameToCredentialEntryList =
        providerDisplayInfo.sortedUserNameToCredentialEntryList
@@ -212,7 +230,7 @@ fun AllSignInOptionCard(
                    )
                },
                navigationIcon = {
                    IconButton(onClick = onBackButtonClicked) {
                    IconButton(onClick = if (isNoAccount) onCancel else onBackButtonClicked) {
                        Icon(
                            Icons.Filled.ArrowBack,
                            contentDescription = stringResource(
@@ -518,3 +536,37 @@ fun SignInAnotherWayRow(onSelect: () -> Unit) {
        }
    )
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SnackBarScreen(
    onClick: (Boolean) -> Unit,
    onCancel: () -> Unit,
) {
    // TODO: Change the height, width and position according to the design
    Snackbar (
        modifier = Modifier.padding(horizontal = 80.dp).padding(top = 700.dp),
        shape = EntryShape.FullMediumRoundedCorner,
        containerColor = LocalAndroidColorScheme.current.colorBackground,
        contentColor = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
    ) {
        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
        ) {
            TextButton(
                onClick = {onClick(true)},
            ) {
                Text(text = stringResource(R.string.get_dialog_use_saved_passkey_for))
            }
            IconButton(onClick = onCancel) {
                Icon(
                    Icons.Filled.Close,
                    contentDescription = stringResource(
                        R.string.accessibility_close_button
                    )
                )
            }
        }
    }
}
 No newline at end of file
+28 −1
Original line number Diff line number Diff line
@@ -36,12 +36,13 @@ import com.android.internal.util.Preconditions

data class GetCredentialUiState(
  val providerInfoList: List<ProviderInfo>,
  val currentScreenState: GetScreenState,
  val requestDisplayInfo: RequestDisplayInfo,
  val currentScreenState: GetScreenState = toGetScreenState(providerInfoList),
  val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
  val selectedEntry: EntryInfo? = null,
  val hidden: Boolean = false,
  val providerActivityPending: Boolean = false,
  val isNoAccount: Boolean = false,
)

class GetCredentialViewModel(
@@ -127,6 +128,14 @@ class GetCredentialViewModel(
    )
  }

  fun onMoreOptionOnSnackBarSelected(isNoAccount: Boolean) {
    Log.d("Account Selector", "More Option on snackBar selected")
    uiState = uiState.copy(
      currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS,
      isNoAccount = isNoAccount,
    )
  }

  fun onBackToPrimarySelectionScreen() {
    uiState = uiState.copy(
      currentScreenState = GetScreenState.PRIMARY_SELECTION
@@ -192,6 +201,24 @@ private fun toProviderDisplayInfo(
  )
}

private fun toGetScreenState(
  providerInfoList: List<ProviderInfo>
): GetScreenState {
  var noLocalAccount = true
  var remoteInfo: RemoteEntryInfo? = null
  providerInfoList.forEach{providerInfo -> if (
    providerInfo.credentialEntryList.isNotEmpty() || providerInfo.authenticationEntry != null
  ) { noLocalAccount = false }
    // TODO: handle the error situation that if multiple remoteInfos exists
    if (providerInfo.remoteEntry != null) {
      remoteInfo = providerInfo.remoteEntry
    }
  }

  return if (noLocalAccount && remoteInfo != null)
    GetScreenState.REMOTE_ONLY else GetScreenState.PRIMARY_SELECTION
}

internal class CredentialEntryInfoComparator : Comparator<CredentialEntryInfo> {
  override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
    // First order by last used timestamp
Loading