diff --git a/res/values/strings.xml b/res/values/strings.xml
index 80b0e8aad8e..e6f4341bb9e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -955,6 +955,8 @@
Fingerprint
Fingerprints
+
+
Use fingerprint to
diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml
index 701d493e749..2868351f802 100644
--- a/res/xml/security_settings_fingerprint.xml
+++ b/res/xml/security_settings_fingerprint.xml
@@ -19,6 +19,11 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/security_settings_fingerprint_preference_title">
+
+
items = mFingerprintManager.getEnrolledFingerprints(mUserId);
final int fingerprintCount = items.size();
for (int i = 0; i < fingerprintCount; i++) {
@@ -785,8 +800,7 @@ public class FingerprintSettings extends SubSettings {
mFingerprintsEnrolledCategory.addPreference(pref);
pref.setOnPreferenceChangeListener(this);
}
- mAddFingerprintPreference = findPreference(mIsExpressiveThemeStyle
- ? KEY_FINGERPRINT_ADD_EXPRESSIVE : KEY_FINGERPRINT_ADD);
+ mAddFingerprintPreference = findPreference(getAddFingerprintPreferenceKey());
setupAddFingerprintPreference();
return keyToReturn;
}
@@ -839,18 +853,31 @@ public class FingerprintSettings extends SubSettings {
}
private void updateFingerprintUnlockCategoryVisibility() {
- final boolean fingerprintUnlockCategoryAvailable =
- mFingerprintUnlockCategoryPreferenceController.isAvailable();
- if (mFingerprintUnlockCategory.isVisible() != fingerprintUnlockCategoryAvailable) {
- mFingerprintUnlockCategory.setVisible(fingerprintUnlockCategoryAvailable);
- }
+ final int categoryStatus =
+ mFingerprintUnlockCategoryPreferenceController.getAvailabilityStatus();
+ updatePreferenceVisibility(categoryStatus, mFingerprintUnlockCategory);
+
if (mRequireScreenOnToAuthPreferenceController != null) {
- mRequireScreenOnToAuthPreference.setVisible(
- mRequireScreenOnToAuthPreferenceController.isAvailable());
+ final int status =
+ mRequireScreenOnToAuthPreferenceController.getAvailabilityStatus();
+ updatePreferenceVisibility(status, mRequireScreenOnToAuthPreference);
}
if (mScreenOffUnlockUdfpsPreferenceController != null) {
- mScreenOffUnlockUdfpsPreference.setVisible(
- mScreenOffUnlockUdfpsPreferenceController.isAvailable());
+ final int status =
+ mScreenOffUnlockUdfpsPreferenceController.getAvailabilityStatus();
+ updatePreferenceVisibility(status, mScreenOffUnlockUdfpsPreference);
+ }
+ }
+
+ private void updatePreferenceVisibility(int availabilityStatus, Preference preference) {
+ if (availabilityStatus == AVAILABLE) {
+ preference.setVisible(true);
+ preference.setEnabled(true);
+ } else if (availabilityStatus == CONDITIONALLY_UNAVAILABLE) {
+ preference.setVisible(true);
+ preference.setEnabled(false);
+ } else {
+ preference.setVisible(false);
}
}
@@ -898,8 +925,29 @@ public class FingerprintSettings extends SubSettings {
.setOnPreferenceChangeListener(fingerprintAppController);
}
+ private void updateUseFingerprintToEnableStatus() {
+ final PreferenceCategory category =
+ findPreference(KEY_BIOMETRICS_USE_FINGERPRINT_TO_CATEGORY);
+ if (!category.isVisible()) {
+ return;
+ }
+ final boolean hasFingerprintEnrolled =
+ mFingerprintManager.getEnrolledFingerprints(mUserId).size() > 0;
+
+ final FingerprintSettingsKeyguardUnlockPreferenceController fpUnlockController =
+ use(FingerprintSettingsKeyguardUnlockPreferenceController.class);
+ findPreference(fpUnlockController.getPreferenceKey())
+ .setEnabled(hasFingerprintEnrolled);
+
+ final FingerprintSettingsAppsPreferenceController fingerprintAppController =
+ use(FingerprintSettingsAppsPreferenceController.class);
+ findPreference(fingerprintAppController.getPreferenceKey())
+ .setEnabled(hasFingerprintEnrolled);
+ }
+
private void updatePreferencesAfterFingerprintRemoved() {
updateAddPreference();
+ updateUseFingerprintToEnableStatus();
if (isSfps() || (screenOffUnlockUdfps() && isScreenOffUnlcokSupported())) {
updateFingerprintUnlockCategoryVisibility();
}
@@ -911,8 +959,7 @@ public class FingerprintSettings extends SubSettings {
return; // Activity went away
}
- mAddFingerprintPreference = findPreference(
- mIsExpressiveThemeStyle ? KEY_FINGERPRINT_ADD_EXPRESSIVE : KEY_FINGERPRINT_ADD);
+ mAddFingerprintPreference = findPreference(getAddFingerprintPreferenceKey());
if (mAddFingerprintPreference == null) {
return; // b/275519315 Skip if updateAddPreference() invoke before addPreference()
@@ -945,11 +992,11 @@ public class FingerprintSettings extends SubSettings {
findPreference(KEY_FINGERPRINT_ADD_EXPRESSIVE);
if (nonExpressiveBtnPreference != null) {
- nonExpressiveBtnPreference.setVisible(!mIsExpressiveThemeStyle);
+ nonExpressiveBtnPreference.setVisible(!shouldShowExpressiveAddFingerprintPref());
}
if (expressiveBtnPreference != null) {
- expressiveBtnPreference.setVisible(mIsExpressiveThemeStyle);
+ expressiveBtnPreference.setVisible(shouldShowExpressiveAddFingerprintPref());
}
}
@@ -1053,7 +1100,7 @@ public class FingerprintSettings extends SubSettings {
@Override
public boolean onPreferenceTreeClick(Preference pref) {
final String key = pref.getKey();
- if (!mIsExpressiveThemeStyle && KEY_FINGERPRINT_ADD.equals(key)) {
+ if (KEY_FINGERPRINT_ADD.equals(key)) {
mIsEnrolling = true;
Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME,
@@ -1451,6 +1498,16 @@ public class FingerprintSettings extends SubSettings {
return Utils.isPrivateProfile(mUserId, getContext());
}
+ private String getAddFingerprintPreferenceKey() {
+ return shouldShowExpressiveAddFingerprintPref()
+ ? KEY_FINGERPRINT_ADD_EXPRESSIVE : KEY_FINGERPRINT_ADD;
+ }
+
+ private boolean shouldShowExpressiveAddFingerprintPref() {
+ return Flags.biometricsOnboardingEducation() && mIsExpressiveThemeStyle
+ && mFingerprintManager.hasEnrolledTemplates(mUserId);
+ }
+
public static class DeleteFingerprintDialog extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsAppsPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsAppsPreferenceController.java
index 2cd92fc03e4..e53ba85bea5 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsAppsPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsAppsPreferenceController.java
@@ -23,6 +23,7 @@ import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings;
import androidx.annotation.NonNull;
+import androidx.preference.Preference;
import com.android.settings.Utils;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
@@ -53,6 +54,20 @@ public class FingerprintSettingsAppsPreferenceController
isChecked ? ON : OFF, getUserId());
}
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+ preference.setEnabled(false);
+ } else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) {
+ preference.setEnabled(false);
+ } else if (getRestrictingAdmin() != null) {
+ preference.setEnabled(false);
+ } else {
+ preference.setEnabled(true);
+ }
+ }
+
@Override
public int getAvailabilityStatus() {
final ActiveUnlockStatusUtils activeUnlockStatusUtils =
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFeatureProvider.kt b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFeatureProvider.kt
new file mode 100644
index 00000000000..3138a0bbe00
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFeatureProvider.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint
+
+import com.android.settings.R
+
+/**
+ * Provide features for FingerprintSettings page.
+ */
+open class FingerprintSettingsFeatureProvider {
+ /**
+ * Get the description shown in the FingerprintSetting page.
+ */
+ open fun getSettingPageDescription(): Int {
+ return 0
+ }
+
+ /**
+ * Get the learn more description shown in the footer of the FingerprintSetting page.
+ */
+ open fun getSettingPageFooterLearnMoreDescription(): Int {
+ return R.string.security_settings_fingerprint_settings_footer_learn_more
+ }
+
+ companion object {
+ @JvmStatic
+ val instance = FingerprintSettingsFeatureProvider()
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsKeyguardUnlockPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsKeyguardUnlockPreferenceController.java
index 55c75abbcae..6b17584320f 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsKeyguardUnlockPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsKeyguardUnlockPreferenceController.java
@@ -19,9 +19,11 @@ package com.android.settings.biometrics.fingerprint;
import static android.provider.Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED;
import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings;
import androidx.annotation.NonNull;
+import androidx.preference.Preference;
import com.android.settings.Utils;
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
@@ -33,9 +35,12 @@ public class FingerprintSettingsKeyguardUnlockPreferenceController
private static final int OFF = 0;
private static final int DEFAULT = ON;
+ private FingerprintManager mFingerprintManager;
+
public FingerprintSettingsKeyguardUnlockPreferenceController(
@NonNull Context context, @NonNull String key) {
super(context, key);
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
}
@Override
@@ -50,6 +55,20 @@ public class FingerprintSettingsKeyguardUnlockPreferenceController
FINGERPRINT_KEYGUARD_ENABLED, isChecked ? ON : OFF, getUserId());
}
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+ preference.setEnabled(false);
+ } else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) {
+ preference.setEnabled(false);
+ } else if (getRestrictingAdmin() != null) {
+ preference.setEnabled(false);
+ } else {
+ preference.setEnabled(true);
+ }
+ }
+
@Override
public int getAvailabilityStatus() {
final ActiveUnlockStatusUtils activeUnlockStatusUtils =
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
index 02825d2aa50..47ba523c7b2 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
@@ -135,16 +135,20 @@ public class FingerprintSettingsFragmentTest {
@Mock
private Vibrator mVibrator;
+ private FingerprintSettingsFeatureProvider mFingerprintSettingsFeatureProvider;
+
+ private FakeFeatureFactory mFakeFeatureFactory;
private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar;
private FingerprintRemoveSidecar mFingerprintRemoveSidecar;
@Before
public void setUp() {
ShadowUtils.setFingerprintManager(mFingerprintManager);
- FakeFeatureFactory.setupForTest();
+ mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mContext = spy(ApplicationProvider.getApplicationContext());
mFragment = spy(new FingerprintSettingsFragment());
+ mFingerprintSettingsFeatureProvider = new FingerprintSettingsFeatureProvider();
doReturn(mContext).when(mFragment).getContext();
doReturn(mBiometricManager).when(mContext).getSystemService(BiometricManager.class);
doReturn(true).when(mFingerprintManager).isHardwareDetected();
@@ -152,6 +156,9 @@ public class FingerprintSettingsFragmentTest {
when(mBiometricManager.canAuthenticate(PRIMARY_USER_ID,
BiometricManager.Authenticators.IDENTITY_CHECK))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ when(mFakeFeatureFactory.getFingerprintFeatureProvider()
+ .getFingerprintSettingsFeatureProvider())
+ .thenReturn(mFingerprintSettingsFeatureProvider);
}
@After