summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java34
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt2
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml10
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_layout.xml9
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt43
11 files changed, 150 insertions, 4 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 04f907a59535..77e460038866 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18792,6 +18792,7 @@ package android.hardware.biometrics {
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
method @Nullable public CharSequence getDescription();
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.graphics.Bitmap getLogoBitmap();
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public String getLogoDescription();
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public int getLogoRes();
method @Nullable public CharSequence getNegativeButtonText();
method @Nullable public CharSequence getSubtitle();
@@ -18843,6 +18844,7 @@ package android.hardware.biometrics {
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index bdaf9d789960..d4c58b239c84 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -200,6 +200,25 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
return this;
}
+ /**
+ * Optional: Sets logo description text that will be shown on the prompt.
+ *
+ * <p> Note that using this method is not recommended in most scenarios because the calling
+ * application's name will be used by default. Setting the logo description is intended for
+ * large bundled applications that perform a wide range of functions and need to show
+ * distinct description for each function.
+ *
+ * @param logoDescription The logo description text that will be shown on the prompt.
+ * @return This builder.
+ */
+ @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @NonNull
+ public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
+ mPromptInfo.setLogoDescription(logoDescription);
+ return this;
+ }
+
/**
* Required: Sets the title that will be shown on the prompt.
@@ -743,7 +762,20 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
return mPromptInfo.getLogoBitmap();
}
-
+ /**
+ * Gets the logo description for the prompt, as set by
+ * {@link Builder#setDescription(CharSequence)}.
+ * Currently for system applications use only.
+ *
+ * @return The logo description of the prompt, or null if the prompt has no logo description
+ * set.
+ */
+ @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @Nullable
+ public String getLogoDescription() {
+ return mPromptInfo.getLogoDescription();
+ }
/**
* Gets the title for the prompt, as set by {@link Builder#setTitle(CharSequence)}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 0f9cadc52608..2236660ee388 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -34,6 +34,7 @@ public class PromptInfo implements Parcelable {
@DrawableRes private int mLogoRes = -1;
@Nullable private Bitmap mLogoBitmap;
+ @Nullable private String mLogoDescription;
@NonNull private CharSequence mTitle;
private boolean mUseDefaultTitle;
@Nullable private CharSequence mSubtitle;
@@ -62,6 +63,7 @@ public class PromptInfo implements Parcelable {
PromptInfo(Parcel in) {
mLogoRes = in.readInt();
mLogoBitmap = in.readTypedObject(Bitmap.CREATOR);
+ mLogoDescription = in.readString();
mTitle = in.readCharSequence();
mUseDefaultTitle = in.readBoolean();
mSubtitle = in.readCharSequence();
@@ -106,6 +108,7 @@ public class PromptInfo implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mLogoRes);
dest.writeTypedObject(mLogoBitmap, 0);
+ dest.writeString(mLogoDescription);
dest.writeCharSequence(mTitle);
dest.writeBoolean(mUseDefaultTitle);
dest.writeCharSequence(mSubtitle);
@@ -173,6 +176,8 @@ public class PromptInfo implements Parcelable {
return true;
} else if (mLogoBitmap != null) {
return true;
+ } else if (mLogoDescription != null) {
+ return true;
}
return false;
}
@@ -189,6 +194,10 @@ public class PromptInfo implements Parcelable {
checkOnlyOneLogoSet();
}
+ public void setLogoDescription(@NonNull String logoDescription) {
+ mLogoDescription = logoDescription;
+ }
+
public void setTitle(CharSequence title) {
mTitle = title;
}
@@ -282,6 +291,10 @@ public class PromptInfo implements Parcelable {
return mLogoBitmap;
}
+ public String getLogoDescription() {
+ return mLogoDescription;
+ }
+
public CharSequence getTitle() {
return mTitle;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 72e884e9e5d6..9c2791f5a257 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -120,6 +120,7 @@ internal fun Collection<SensorPropertiesInternal?>.extractAuthenticatorTypes():
internal fun promptInfo(
logoRes: Int = -1,
logoBitmap: Bitmap? = null,
+ logoDescription: String? = null,
title: String = "title",
subtitle: String = "sub",
description: String = "desc",
@@ -132,6 +133,7 @@ internal fun promptInfo(
val info = PromptInfo()
info.logoRes = logoRes
info.logoBitmap = logoBitmap
+ info.logoDescription = logoDescription
info.title = title
info.subtitle = subtitle
info.description = description
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index a877853eaec8..0ecdcfcbdd83 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -13,6 +13,16 @@ android:layout_height="match_parent">
android:scaleType="fitXY"
android:visibility="gone" />
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:singleLine="true"
+ android:marqueeRepeatLimit="1"
+ android:ellipsize="marquee"
+ android:visibility="gone"/>
+
<ImageView
android:id="@+id/background"
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
index 10f71134c4cc..e7590746207d 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
@@ -28,6 +28,15 @@
android:scaleType="fitXY"/>
<TextView
+ android:id="@+id/logo_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:singleLine="true"
+ android:marqueeRepeatLimit="1"
+ android:ellipsize="marquee"/>
+
+ <TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index c17c8dced668..6133a51c6497 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -40,6 +40,7 @@ sealed class BiometricPromptRequest(
val contentView: PromptContentView? = info.contentView
val logoRes: Int = info.logoRes
val logoBitmap: Bitmap? = info.logoBitmap
+ val logoDescription: String? = info.logoDescription
val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index efad21bda380..31aadf51c4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -95,6 +95,7 @@ object BiometricViewBinder {
view.resources.getColor(R.color.biometric_dialog_gray, view.context.theme)
val logoView = view.requireViewById<ImageView>(R.id.logo)
+ val logoDescriptionView = view.requireViewById<TextView>(R.id.logo_description)
val titleView = view.requireViewById<TextView>(R.id.title)
val subtitleView = view.requireViewById<TextView>(R.id.subtitle)
val descriptionView = view.requireViewById<TextView>(R.id.description)
@@ -104,6 +105,8 @@ object BiometricViewBinder {
// set selected to enable marquee unless a screen reader is enabled
logoView.isSelected =
!accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
+ logoDescriptionView.isSelected =
+ !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
titleView.isSelected =
!accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
subtitleView.isSelected =
@@ -165,6 +168,7 @@ object BiometricViewBinder {
}
logoView.setImageDrawable(viewModel.logo.first())
+ logoDescriptionView.text = viewModel.logoDescription.first()
titleView.text = viewModel.title.first()
subtitleView.text = viewModel.subtitle.first()
descriptionView.text = viewModel.description.first()
@@ -197,6 +201,7 @@ object BiometricViewBinder {
viewsToHideWhenSmall =
listOf(
logoView,
+ logoDescriptionView,
titleView,
subtitleView,
descriptionView,
@@ -205,6 +210,7 @@ object BiometricViewBinder {
viewsToFadeInOnSizeChange =
listOf(
logoView,
+ logoDescriptionView,
titleView,
subtitleView,
descriptionView,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index ef5c37eaa953..788991d2e45b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.viewmodel
import android.content.Context
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
@@ -280,8 +281,9 @@ constructor(
it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
else ->
try {
- context.packageManager.getApplicationIcon(it.opPackageName)
- } catch (e: PackageManager.NameNotFoundException) {
+ val info = context.getApplicationInfo(it.opPackageName)
+ context.packageManager.getApplicationIcon(info)
+ } catch (e: Exception) {
Log.w(TAG, "Cannot find icon for package " + it.opPackageName, e)
null
}
@@ -289,6 +291,25 @@ constructor(
}
.distinctUntilChanged()
+ /** Logo description for the prompt. */
+ val logoDescription: Flow<String> =
+ promptSelectorInteractor.prompt
+ .map {
+ when {
+ !customBiometricPrompt() || it == null -> ""
+ it.logoDescription != null -> it.logoDescription
+ else ->
+ try {
+ val info = context.getApplicationInfo(it.opPackageName)
+ context.packageManager.getApplicationLabel(info).toString()
+ } catch (e: Exception) {
+ Log.w(TAG, "Cannot find name for package " + it.opPackageName, e)
+ ""
+ }
+ }
+ }
+ .distinctUntilChanged()
+
/** Title for the prompt. */
val title: Flow<String> =
promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
@@ -682,6 +703,12 @@ constructor(
}
}
+private fun Context.getApplicationInfo(packageName: String): ApplicationInfo =
+ packageManager.getApplicationInfo(
+ packageName,
+ PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+ )
+
/** How the fingerprint sensor was started for the prompt. */
enum class FingerprintStartMode {
/** Fingerprint sensor has not started. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index a46167a423f1..8fab2332c00e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -26,6 +26,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {
@Test
fun biometricRequestFromPromptInfo() {
val logoRes = R.drawable.ic_cake
+ val logoDescription = "test cake"
val title = "what"
val subtitle = "a"
val description = "request"
@@ -41,6 +42,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {
BiometricPromptRequest.Biometric(
promptInfo(
logoRes = logoRes,
+ logoDescription = logoDescription,
title = title,
subtitle = subtitle,
description = description,
@@ -53,6 +55,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {
)
assertThat(request.logoRes).isEqualTo(logoRes)
+ assertThat(request.logoDescription).isEqualTo(logoDescription)
assertThat(request.title).isEqualTo(title)
assertThat(request.subtitle).isEqualTo(subtitle)
assertThat(request.description).isEqualTo(description)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 2e94d381b8dc..ff68fe3df9e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.ui.viewmodel
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
@@ -74,6 +75,8 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
@@ -95,6 +98,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var udfpsUtils: UdfpsUtils
@Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
+ @Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val testScope = TestScope()
@@ -102,6 +107,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
private val logoResFromApp = R.drawable.ic_cake
private val logoFromApp = context.getDrawable(logoResFromApp)
private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
+ private val defaultLogoDescription = "Test Android App"
+ private val logoDescriptionFromApp = "Test Cake App"
private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
private lateinit var promptRepository: FakePromptRepository
@@ -166,7 +173,14 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
iconViewModel = viewModel.iconViewModel
// Set up default logo icon and app customized icon
- whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
+ whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
+ .thenReturn(applicationInfoNoIcon)
+ whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
+ .thenReturn(applicationInfoWithIcon)
+ whenever(packageManager.getApplicationIcon(applicationInfoWithIcon))
+ .thenReturn(defaultLogoIcon)
+ whenever(packageManager.getApplicationLabel(applicationInfoWithIcon))
+ .thenReturn(defaultLogoDescription)
context.setMockPackageManager(packageManager)
val resources = context.getOrCreateTestableResources()
resources.addOverride(logoResFromApp, logoFromApp)
@@ -1277,6 +1291,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
}
+ @Test
+ fun logoDescriptionIsEmptyIfPackageNameNotFound() =
+ runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
+ mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ val logoDescription by collectLastValue(viewModel.logoDescription)
+ assertThat(logoDescription).isEqualTo("")
+ }
+
+ @Test
+ fun defaultLogoDescriptionIfNoLogoDescriptionSet() = runGenericTest {
+ mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ val logoDescription by collectLastValue(viewModel.logoDescription)
+ assertThat(logoDescription).isEqualTo(defaultLogoDescription)
+ }
+
+ @Test
+ fun logoDescriptionSetByApp() =
+ runGenericTest(logoDescription = logoDescriptionFromApp) {
+ mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ val logoDescription by collectLastValue(viewModel.logoDescription)
+ assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
+ }
+
/** Asserts that the selected buttons are visible now. */
private suspend fun TestScope.assertButtonsVisible(
tryAgain: Boolean = false,
@@ -1300,6 +1337,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
contentView: PromptContentView? = null,
logoRes: Int = -1,
logoBitmap: Bitmap? = null,
+ logoDescription: String? = null,
packageName: String = OP_PACKAGE_NAME,
block: suspend TestScope.() -> Unit,
) {
@@ -1312,6 +1350,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
contentViewFromApp = contentView,
logoResFromApp = logoRes,
logoBitmapFromApp = logoBitmap,
+ logoDescriptionFromApp = logoDescription,
packageName = packageName,
)
@@ -1492,12 +1531,14 @@ private fun PromptSelectorInteractor.initializePrompt(
contentViewFromApp: PromptContentView? = null,
logoResFromApp: Int = -1,
logoBitmapFromApp: Bitmap? = null,
+ logoDescriptionFromApp: String? = null,
packageName: String = OP_PACKAGE_NAME,
) {
val info =
PromptInfo().apply {
logoRes = logoResFromApp
logoBitmap = logoBitmapFromApp
+ logoDescription = logoDescriptionFromApp
title = "t"
subtitle = "s"
description = descriptionFromApp