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

Commit 7761b08c authored by Helen Qin's avatar Helen Qin Committed by Android (Google) Code Review
Browse files

Merge "Implemented the screen for when the only auth entries are all empty."

parents 3412574e 455a3095
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -92,8 +92,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>
  <!-- Spoken content description of the close "X" icon button. -->
  <string name="accessibility_close_button">Close</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] -->
@@ -120,6 +120,8 @@
  <string name="locked_credential_entry_label_subtext_tap_to_unlock">Tap to unlock</string>
  <!-- Explanatory label for a disabled button explaining that this option isn't viable because it does not contain any available credential (e.g. password, passkey, etc.) for the user. [CHAR LIMIT=120] -->
  <string name="locked_credential_entry_label_subtext_no_sign_in">No sign-in info</string>
  <!-- Label displayed when a selected option does not contain any viable credentials that can be used to log into the app. [CHAR LIMIT=80] -->
  <string name="no_sign_in_info_in">No sign-in info in <xliff:g id="source" example="becket@gmail.com">%1$s</xliff:g></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] -->
+5 −0
Original line number Diff line number Diff line
@@ -115,6 +115,11 @@ class CredentialSelectorViewModel(
        }
    }

    fun onLastLockedAuthEntryNotFoundError() {
        Log.d(Constants.LOG_TAG, "Unable to find the last unlocked entry")
        onInternalError()
    }

    private fun onInternalError() {
        Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state")
        credManRepo.onParsingFailureCancel()
+48 −7
Original line number Diff line number Diff line
@@ -79,7 +79,20 @@ fun GetCredentialScreen(
    getCredentialUiState: GetCredentialUiState,
    providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
    if (getCredentialUiState.currentScreenState != GetScreenState.REMOTE_ONLY) {
    if (getCredentialUiState.currentScreenState == GetScreenState.REMOTE_ONLY) {
        RemoteCredentialSnackBarScreen(
            onClick = viewModel::getFlowOnMoreOptionOnSnackBarSelected,
            onCancel = viewModel::onCancel,
        )
    } else if (getCredentialUiState.currentScreenState
        == GetScreenState.UNLOCKED_AUTH_ENTRIES_ONLY) {
        EmptyAuthEntrySnackBarScreen(
            authenticationEntryList =
            getCredentialUiState.providerDisplayInfo.authenticationEntryList,
            onCancel = viewModel::onCancel,
            onLastLokcedAuthEntryNotFound = viewModel::onLastLockedAuthEntryNotFoundError,
        )
    } else {
        ModalBottomSheet(
            sheetContent = {
                // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim
@@ -124,11 +137,6 @@ fun GetCredentialScreen(
            },
            onDismiss = viewModel::onCancel,
        )
    } else {
        SnackBarScreen(
            onClick = viewModel::getFlowOnMoreOptionOnSnackBarSelected,
            onCancel = viewModel::onCancel,
        )
    }
}

@@ -622,7 +630,7 @@ fun ActionEntryRow(

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SnackBarScreen(
fun RemoteCredentialSnackBarScreen(
    onClick: (Boolean) -> Unit,
    onCancel: () -> Unit,
) {
@@ -654,3 +662,36 @@ fun SnackBarScreen(
        Text(text = stringResource(R.string.get_dialog_use_saved_passkey_for))
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EmptyAuthEntrySnackBarScreen(
    authenticationEntryList: List<AuthenticationEntryInfo>,
    onCancel: () -> Unit,
    onLastLokcedAuthEntryNotFound: () -> Unit,
) {
    val lastLocked = authenticationEntryList.firstOrNull({it.isLastUnlocked})
    if (lastLocked == null) {
        onLastLokcedAuthEntryNotFound()
        return
    }

    // TODO: Change the height, width and position according to the design
    Snackbar(
        modifier = Modifier.padding(horizontal = 40.dp).padding(top = 700.dp),
        shape = EntryShape.FullMediumRoundedCorner,
        containerColor = LocalAndroidColorScheme.current.colorBackground,
        contentColor = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
        dismissAction = {
            IconButton(onClick = onCancel) {
                Icon(
                    Icons.Filled.Close,
                    contentDescription = stringResource(R.string.accessibility_close_button),
                    tint = LocalAndroidColorScheme.current.colorAccentTertiary
                )
            }
        },
    ) {
        Text(text = stringResource(R.string.no_sign_in_info_in, lastLocked.title))
    }
}
 No newline at end of file
+14 −16
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ import java.time.Instant
data class GetCredentialUiState(
    val providerInfoList: List<ProviderInfo>,
    val requestDisplayInfo: RequestDisplayInfo,
    val currentScreenState: GetScreenState = toGetScreenState(providerInfoList),
    val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
    val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
    val activeEntry: BaseEntry? = toActiveEntry(providerDisplayInfo),
    val isNoAccount: Boolean = false,
)
@@ -167,6 +167,9 @@ enum class GetScreenState {

    /** The snackbar only page when there's no account but only a remoteEntry. */
    REMOTE_ONLY,

    /** The snackbar when there are only auth entries and all of them turn out to be empty. */
    UNLOCKED_AUTH_ENTRIES_ONLY,
}

// IMPORTANT: new invocation should be mindful that this method will throw if more than 1 remote
@@ -174,7 +177,6 @@ enum class GetScreenState {
private fun toProviderDisplayInfo(
    providerInfoList: List<ProviderInfo>
): ProviderDisplayInfo {

    val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
    val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>()
    val remoteEntryList = mutableListOf<RemoteEntryInfo>()
@@ -241,22 +243,18 @@ private fun toActiveEntry(
}

private fun toGetScreenState(
    providerInfoList: List<ProviderInfo>
    providerDisplayInfo: ProviderDisplayInfo
): GetScreenState {
    var noLocalAccount = true
    var remoteInfo: RemoteEntryInfo? = null
    providerInfoList.forEach { providerInfo ->
        if (providerInfo.credentialEntryList.isNotEmpty() ||
            providerInfo.authenticationEntryList.isNotEmpty()) {
            noLocalAccount = false
        }
        if (providerInfo.remoteEntry != null) {
            remoteInfo = providerInfo.remoteEntry
        }
    }

    return if (noLocalAccount && remoteInfo != null)
        GetScreenState.REMOTE_ONLY else GetScreenState.PRIMARY_SELECTION
    return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() &&
        providerDisplayInfo.remoteEntry == null &&
        providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty })
        GetScreenState.UNLOCKED_AUTH_ENTRIES_ONLY
    else if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() &&
        providerDisplayInfo.authenticationEntryList.isEmpty() &&
        providerDisplayInfo.remoteEntry != null)
        GetScreenState.REMOTE_ONLY
    else GetScreenState.PRIMARY_SELECTION
}

internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<CredentialEntryInfo> {