When a single entry is truncated on the front page, provide a "View more" button
For a single entry there is no "more option" page. However, the texts on that entry may be truncated to fit into the one-line requirement for the Credential Selector front page. Therefore, if that happens, render a "view more" that leads to the more option page with the same entry, but fully expanded in text. Bug: 277272017 Test: manual (see bug for recording) Change-Id: If5d88453c7d1905a72c7beaebbc2ece8ce212d02
This commit is contained in:
@@ -117,6 +117,8 @@
|
||||
<string name="get_dialog_sign_in_type_username_separator" translatable="false">" • "</string>
|
||||
<!-- This text is followed by a list of one or more options. [CHAR LIMIT=80] -->
|
||||
<string name="get_dialog_title_sign_in_options">Sign-in options</string>
|
||||
<!-- Button label for viewing the full information about an account. [CHAR LIMIT=80] -->
|
||||
<string name="button_label_view_more">View more</string>
|
||||
<!-- Column heading for displaying sign-ins for a specific username. [CHAR LIMIT=80] -->
|
||||
<string name="get_dialog_heading_for_username">For <xliff:g id="username" example="becket@gmail.com">%1$s</xliff:g></string>
|
||||
<!-- Column heading for displaying locked (that is, the user needs to first authenticate via pin, fingerprint, faceId, etc.) sign-ins. [CHAR LIMIT=80] -->
|
||||
|
||||
@@ -52,6 +52,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
@@ -80,6 +81,7 @@ fun Entry(
|
||||
/** If true, draws a trailing lock icon. */
|
||||
isLockedAuthEntry: Boolean = false,
|
||||
enforceOneLine: Boolean = false,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
) {
|
||||
val iconPadding = Modifier.wrapContentSize().padding(
|
||||
// Horizontal padding should be 16dp, but the suggestion chip itself
|
||||
@@ -104,7 +106,11 @@ fun Entry(
|
||||
) {
|
||||
// Apply weight so that the trailing icon can always show.
|
||||
Column(modifier = Modifier.wrapContentHeight().fillMaxWidth().weight(1f)) {
|
||||
SmallTitleText(text = entryHeadlineText, enforceOneLine = enforceOneLine)
|
||||
SmallTitleText(
|
||||
text = entryHeadlineText,
|
||||
enforceOneLine = enforceOneLine,
|
||||
onTextLayout = onTextLayout,
|
||||
)
|
||||
if (passwordValue != null) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
|
||||
@@ -142,10 +148,18 @@ fun Entry(
|
||||
)
|
||||
}
|
||||
} else if (entrySecondLineText != null) {
|
||||
BodySmallText(text = entrySecondLineText, enforceOneLine = enforceOneLine)
|
||||
BodySmallText(
|
||||
text = entrySecondLineText,
|
||||
enforceOneLine = enforceOneLine,
|
||||
onTextLayout = onTextLayout,
|
||||
)
|
||||
}
|
||||
if (entryThirdLineText != null) {
|
||||
BodySmallText(text = entryThirdLineText, enforceOneLine = enforceOneLine)
|
||||
BodySmallText(
|
||||
text = entryThirdLineText,
|
||||
enforceOneLine = enforceOneLine,
|
||||
onTextLayout = onTextLayout,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isLockedAuthEntry) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
||||
@@ -58,14 +59,20 @@ fun BodyMediumText(text: String, modifier: Modifier = Modifier) {
|
||||
* Body-small typography; on-surface-variant color.
|
||||
*/
|
||||
@Composable
|
||||
fun BodySmallText(text: String, modifier: Modifier = Modifier, enforceOneLine: Boolean = false) {
|
||||
fun BodySmallText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
enforceOneLine: Boolean = false,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier.wrapContentSize(),
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
|
||||
maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE,
|
||||
onTextLayout = onTextLayout,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -86,14 +93,20 @@ fun LargeTitleText(text: String, modifier: Modifier = Modifier) {
|
||||
* Title-small typography; on-surface color.
|
||||
*/
|
||||
@Composable
|
||||
fun SmallTitleText(text: String, modifier: Modifier = Modifier, enforceOneLine: Boolean = false) {
|
||||
fun SmallTitleText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
enforceOneLine: Boolean = false,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier.wrapContentSize(),
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
|
||||
maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE,
|
||||
onTextLayout = onTextLayout,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,15 @@ import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.android.credentialmanager.CredentialSelectorViewModel
|
||||
@@ -158,6 +162,7 @@ fun PrimarySelectionCard(
|
||||
onMoreOptionSelected: () -> Unit,
|
||||
onLog: @Composable (UiEventEnum) -> Unit,
|
||||
) {
|
||||
val showMoreForTruncatedEntry = remember { mutableStateOf(false) }
|
||||
val sortedUserNameToCredentialEntryList =
|
||||
providerDisplayInfo.sortedUserNameToCredentialEntryList
|
||||
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
|
||||
@@ -209,6 +214,8 @@ fun PrimarySelectionCard(
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size
|
||||
val authenticationEntrySize = authenticationEntryList.size
|
||||
// If true, render a view more button for the single truncated entry on the
|
||||
// front page.
|
||||
// Show max 4 entries in this primary page
|
||||
if (usernameForCredentialSize + authenticationEntrySize <= 4) {
|
||||
sortedUserNameToCredentialEntryList.forEach {
|
||||
@@ -216,6 +223,9 @@ fun PrimarySelectionCard(
|
||||
credentialEntryInfo = it.sortedCredentialEntryList.first(),
|
||||
onEntrySelected = onEntrySelected,
|
||||
enforceOneLine = true,
|
||||
onTextLayout = {
|
||||
showMoreForTruncatedEntry.value = it.hasVisualOverflow
|
||||
}
|
||||
)
|
||||
}
|
||||
authenticationEntryList.forEach {
|
||||
@@ -269,6 +279,13 @@ fun PrimarySelectionCard(
|
||||
onMoreOptionSelected
|
||||
)
|
||||
}
|
||||
} else if (showMoreForTruncatedEntry.value) {
|
||||
{
|
||||
ActionButton(
|
||||
stringResource(R.string.button_label_view_more),
|
||||
onMoreOptionSelected
|
||||
)
|
||||
}
|
||||
} else null,
|
||||
rightButton = if (activeEntry != null) { // Only one sign-in options exist
|
||||
{
|
||||
@@ -438,6 +455,7 @@ fun CredentialEntryRow(
|
||||
credentialEntryInfo: CredentialEntryInfo,
|
||||
onEntrySelected: (BaseEntry) -> Unit,
|
||||
enforceOneLine: Boolean = false,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
) {
|
||||
Entry(
|
||||
onClick = { onEntrySelected(credentialEntryInfo) },
|
||||
@@ -463,6 +481,7 @@ fun CredentialEntryRow(
|
||||
)
|
||||
},
|
||||
enforceOneLine = enforceOneLine,
|
||||
onTextLayout = onTextLayout,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user