Log the warning if logo description exceeds char limit.
1. Remove the exception for logo description char limit and log a warning instead. 2. Truncate if the logo description exceeds 30 characters 3. Set title's top constraint on both logo icon and logo description for non-icon or non-description cases. Flag: android.hardware.biometrics.custom_biometric_prompt Bug: 355677518 Test: manual test on test app Test: atest BiometricPromptTest Test: atest BiometricPromptScreenshotTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:86e209599423f7a69f1466eadd71e94041164b4a) Merged-In: Icb07acd88138a7519f73d3df9ab323220d99dfe7 Change-Id: Icb07acd88138a7519f73d3df9ab323220d99dfe7
This commit is contained in:
parent
045f396b67
commit
8cf068e046
5 changed files with 35 additions and 31 deletions
|
@ -240,18 +240,19 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
|
|||
*
|
||||
* @param logoDescription The logo description text that will be shown on the prompt.
|
||||
* @return This builder.
|
||||
* @throws IllegalArgumentException If logo description is null or exceeds certain character
|
||||
* limit.
|
||||
* @throws IllegalArgumentException If logo description is null.
|
||||
*/
|
||||
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
|
||||
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
|
||||
@NonNull
|
||||
public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
|
||||
if (logoDescription == null
|
||||
|| logoDescription.length() > MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER) {
|
||||
throw new IllegalArgumentException(
|
||||
"Logo description passed in can not be null or exceed "
|
||||
+ MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER + " character number.");
|
||||
if (logoDescription == null || logoDescription.isEmpty()) {
|
||||
throw new IllegalArgumentException("Logo description passed in can not be null");
|
||||
}
|
||||
if (logoDescription.length() > MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER) {
|
||||
Log.w(TAG,
|
||||
"Logo description passed in exceeds" + MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER
|
||||
+ " character number and may be truncated.");
|
||||
}
|
||||
mPromptInfo.setLogoDescription(logoDescription);
|
||||
return this;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package android.hardware.biometrics;
|
||||
|
||||
import static android.hardware.biometrics.BiometricPrompt.MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER;
|
||||
import static android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.MAX_DESCRIPTION_CHARACTER_NUMBER;
|
||||
import static android.hardware.biometrics.PromptVerticalListContentView.MAX_EACH_ITEM_CHARACTER_NUMBER;
|
||||
import static android.hardware.biometrics.PromptVerticalListContentView.MAX_ITEM_NUMBER;
|
||||
|
@ -116,19 +115,7 @@ public class BiometricPromptTest {
|
|||
() -> new BiometricPrompt.Builder(mContext).setLogoDescription(null)
|
||||
);
|
||||
|
||||
assertThat(e).hasMessageThat().contains(
|
||||
"Logo description passed in can not be null or exceed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogoDescription_charLimit() {
|
||||
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
|
||||
() -> new BiometricPrompt.Builder(mContext).setLogoDescription(
|
||||
generateRandomString(MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER + 1))
|
||||
);
|
||||
|
||||
assertThat(e).hasMessageThat().contains(
|
||||
"Logo description passed in can not be null or exceed");
|
||||
assertThat(e).hasMessageThat().isEqualTo("Logo description passed in can not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -87,20 +87,21 @@ android:layout_height="match_parent">
|
|||
android:id="@+id/logo_description"
|
||||
style="@style/TextAppearance.AuthCredential.LogoDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/biometric_prompt_logo_size"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
android:paddingStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/logo"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_goneMarginStart="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/logo"
|
||||
app:layout_constraintTop_toTopOf="@+id/logo" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.AuthCredential.Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:gravity="@integer/biometric_dialog_text_gravity"
|
||||
android:paddingHorizontal="0dp"
|
||||
android:textAlignment="viewStart"
|
||||
|
@ -108,7 +109,7 @@ android:layout_height="match_parent">
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/logo"
|
||||
app:layout_constraintTop_toBottomOf="@+id/logoBarrier"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
|
@ -158,6 +159,15 @@ android:layout_height="match_parent">
|
|||
app:layout_constraintTop_toBottomOf="@+id/subtitle"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/logoBarrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierMargin="12dp"
|
||||
app:barrierAllowsGoneWidgets="false"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="logo_description, logo" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/contentBarrier"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -192,11 +192,10 @@
|
|||
|
||||
<style name="TextAppearance.AuthCredential.LogoDescription" parent="TextAppearance.Material3.LabelLarge" >
|
||||
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
|
||||
<item name="android:ellipsize">marquee</item>
|
||||
<item name="android:gravity">@integer/biometric_dialog_text_gravity</item>
|
||||
<item name="android:marqueeRepeatLimit">1</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
<item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.AuthCredential.Title" parent="TextAppearance.Material3.HeadlineSmall" >
|
||||
|
|
|
@ -64,6 +64,7 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val TAG = "BiometricViewBinder"
|
||||
private const val MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER = 30
|
||||
|
||||
/** Top-most view binder for BiometricPrompt views. */
|
||||
object BiometricViewBinder {
|
||||
|
@ -167,7 +168,10 @@ object BiometricViewBinder {
|
|||
}
|
||||
|
||||
logoView.setImageDrawable(viewModel.logo.first())
|
||||
logoDescriptionView.text = viewModel.logoDescription.first()
|
||||
// The ellipsize effect on xml happens only when the TextView does not have any free
|
||||
// space on the screen to show the text. So we need to manually truncate.
|
||||
logoDescriptionView.text =
|
||||
viewModel.logoDescription.first().ellipsize(MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER)
|
||||
titleView.text = viewModel.title.first()
|
||||
subtitleView.text = viewModel.subtitle.first()
|
||||
descriptionView.text = viewModel.description.first()
|
||||
|
@ -684,6 +688,9 @@ private fun BiometricModalities.asDefaultHelpMessage(context: Context): String =
|
|||
else -> ""
|
||||
}
|
||||
|
||||
private fun String.ellipsize(cutOffLength: Int) =
|
||||
if (length <= cutOffLength) this else replaceRange(cutOffLength, length, "...")
|
||||
|
||||
private fun Boolean.asVisibleOrGone(): Int = if (this) View.VISIBLE else View.GONE
|
||||
|
||||
private fun Boolean.asVisibleOrHidden(): Int = if (this) View.VISIBLE else View.INVISIBLE
|
||||
|
|
Loading…
Reference in a new issue