diff --git a/res/drawable/ic_face.xml b/res/drawable/ic_face.xml new file mode 100644 index 00000000000..85549fbbe80 --- /dev/null +++ b/res/drawable/ic_face.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 80b0e8aad8e..e39a2d9df8e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -914,6 +914,14 @@ Delete your current face model to set up Face Unlock again.\n\nYour face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone or for authentication in apps. Use Face Unlock for + + + + + + + + Face @@ -948,6 +956,8 @@ Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone. Use Face Unlock to unlock your phone + + Add face diff --git a/res/xml/security_settings_face.xml b/res/xml/security_settings_face.xml index e32148c458b..41d535f83d3 100644 --- a/res/xml/security_settings_face.xml +++ b/res/xml/security_settings_face.xml @@ -19,6 +19,11 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/security_settings_face_preference_title"> + + + + + + + + android:layout="@layout/face_remove_button" + settings:isPreferenceVisible="false"/> + android:layout="@layout/face_enroll_button" + settings:isPreferenceVisible="false"/> mTogglePreferences; private Preference mRemoveButton; private Preference mEnrollButton; + private PreferenceCategory mFaceEnrolledCategory; + private Preference mFaceRemoveButton; + private Preference mFaceEnrollButton; private FaceFeatureProvider mFaceFeatureProvider; private boolean mConfirmingPassword; @@ -111,8 +122,7 @@ public class FaceSettings extends DashboardFragment { } // Hide the "remove" button and show the "set up face authentication" button. - mRemoveButton.setVisible(false); - mEnrollButton.setVisible(true); + updateFaceAddAndRemovePreference(false); }; private final FaceSettingsEnrollButtonPreferenceController.Listener mEnrollListener = intent -> @@ -193,6 +203,15 @@ public class FaceSettings extends DashboardFragment { : use(FaceSettingsLockscreenBypassPreferenceController.class); mLockscreenController.setUserId(mUserId); + final int descriptionResId = FeatureFactory.getFeatureFactory() + .getFaceFeatureProvider().getFaceSettingsFeatureProvider() + .getSettingPageDescription(); + if (ResourceId.isValid(descriptionResId)) { + final Preference preference = findPreference(PREF_KEY_FACE_DESCRIPTION); + preference.setTitle(descriptionResId); + preference.setVisible(true); + } + final PreferenceCategory managePref = findPreference(SECURITY_SETTINGS_FACE_MANAGE_CATEGORY); Preference keyguardPref = findPreference(FaceSettingsKeyguardPreferenceController.KEY); @@ -201,8 +220,13 @@ public class FaceSettings extends DashboardFragment { Preference confirmPref = findPreference(FaceSettingsConfirmPreferenceController.KEY); Preference bypassPref = findPreference(mLockscreenController.getPreferenceKey()); + Preference unlockKeyguard = findPreference( + use(FaceSettingsKeyguardUnlockPreferenceController.class).getPreferenceKey()); + Preference appsPref = findPreference( + use(FaceSettingsAppsPreferenceController.class).getPreferenceKey()); mTogglePreferences = new ArrayList<>( - Arrays.asList(keyguardPref, appPref, attentionPref, confirmPref, bypassPref)); + Arrays.asList(keyguardPref, appPref, attentionPref, confirmPref, bypassPref, + unlockKeyguard, appsPref)); if (RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( getContext(), DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) != null) { @@ -215,9 +239,18 @@ public class FaceSettings extends DashboardFragment { mRemoveButton = findPreference(FaceSettingsRemoveButtonPreferenceController.KEY); mEnrollButton = findPreference(FaceSettingsEnrollButtonPreferenceController.KEY); + mFaceEnrolledCategory = findPreference(PREF_KEY_FACE_ENROLLED_CATEGORY); + mFaceRemoveButton = findPreference(PREF_KEY_FACE_REMOVE); + mFaceRemoveButton.setIcon(R.drawable.ic_face); + mFaceRemoveButton.setOnPreferenceClickListener( + use(FaceSettingsRemoveButtonPreferenceController.class)); + mFaceEnrollButton = findPreference(PREF_KEY_FACE_ENROLL); + mFaceEnrollButton.setIcon(R.drawable.ic_add_24dp); + mFaceEnrollButton.setOnPreferenceClickListener( + use(FaceSettingsEnrollButtonPreferenceController.class)); + final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId); - mEnrollButton.setVisible(!hasEnrolled); - mRemoveButton.setVisible(hasEnrolled); + updateFaceAddAndRemovePreference(hasEnrolled); // There is no better way to do this :/ for (AbstractPreferenceController controller : mControllers) { @@ -255,8 +288,7 @@ public class FaceSettings extends DashboardFragment { public void onStart() { super.onStart(); final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId); - mEnrollButton.setVisible(!hasEnrolled); - mRemoveButton.setVisible(hasEnrolled); + updateFaceAddAndRemovePreference(hasEnrolled); // When the user has face id registered but failed enrolling in device lock state, // lead users directly to the confirm deletion dialog in Face Unlock settings. @@ -264,10 +296,16 @@ public class FaceSettings extends DashboardFragment { final boolean isReEnrollFaceUnlock = getIntent().getBooleanExtra( FaceSettings.KEY_RE_ENROLL_FACE, false); if (isReEnrollFaceUnlock) { - final Button removeBtn = ((LayoutPreference) mRemoveButton).findViewById( - R.id.security_settings_face_settings_remove_button); - if (removeBtn != null && removeBtn.isEnabled()) { - mRemoveController.onClick(removeBtn); + if (Flags.biometricsOnboardingEducation()) { + if (mFaceRemoveButton.isEnabled()) { + mRemoveController.onPreferenceClick(mFaceRemoveButton); + } + } else { + final Button removeBtn = ((LayoutPreference) mRemoveButton).findViewById( + R.id.security_settings_face_settings_remove_button); + if (removeBtn != null && removeBtn.isEnabled()) { + mRemoveController.onClick(removeBtn); + } } } } @@ -327,8 +365,7 @@ public class FaceSettings extends DashboardFragment { }); final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId); - mEnrollButton.setVisible(!hasEnrolled); - mRemoveButton.setVisible(hasEnrolled); + updateFaceAddAndRemovePreference(hasEnrolled); final Utils.BiometricStatus biometricAuthStatus = Utils.requestBiometricAuthenticationForMandatoryBiometrics(getActivity(), mBiometricsAuthenticationRequested, @@ -407,6 +444,17 @@ public class FaceSettings extends DashboardFragment { return mControllers; } + private void updateFaceAddAndRemovePreference(boolean hasEnrolled) { + if (Flags.biometricsOnboardingEducation()) { + mFaceEnrolledCategory.setVisible(true); + mFaceRemoveButton.setVisible(hasEnrolled); + mFaceEnrollButton.setVisible(!hasEnrolled); + } else { + mEnrollButton.setVisible(!hasEnrolled); + mRemoveButton.setVisible(hasEnrolled); + } + } + private static List buildPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); controllers.add(new FaceSettingsKeyguardPreferenceController(context)); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAppsPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAppsPreferenceController.java index f859060f83a..fc582d4b862 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsAppsPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsAppsPreferenceController.java @@ -23,6 +23,7 @@ import android.hardware.face.FaceManager; import android.provider.Settings; import androidx.annotation.NonNull; +import androidx.preference.Preference; import com.android.settings.Utils; import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils; @@ -52,6 +53,20 @@ public class FaceSettingsAppsPreferenceController extends isChecked ? ON : OFF, getUserId()); } + @Override + public void updateState(Preference preference) { + super.updateState(preference); + if (!FaceSettings.isFaceHardwareDetected(mContext)) { + preference.setEnabled(false); + } else if (!mFaceManager.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/face/FaceSettingsEnrollButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java index 738f92f0c88..a6f7a4fa3e7 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceController.java @@ -24,10 +24,12 @@ import android.content.Intent; import android.view.View; import android.widget.Button; +import androidx.annotation.NonNull; import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settings.flags.Flags; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.widget.LayoutPreference; @@ -39,10 +41,11 @@ import com.google.android.setupdesign.util.PartnerStyleHelper; * Preference controller that allows a user to enroll their face. */ public class FaceSettingsEnrollButtonPreferenceController extends BasePreferenceController - implements View.OnClickListener { + implements View.OnClickListener, Preference.OnPreferenceClickListener { private static final String TAG = "FaceSettings/Remove"; static final String KEY = "security_settings_face_enroll_faces_container"; + static final String KEY_1 = "security_settings_face_enroll"; private final Context mContext; @@ -53,7 +56,7 @@ public class FaceSettingsEnrollButtonPreferenceController extends BasePreference private Listener mListener; public FaceSettingsEnrollButtonPreferenceController(Context context) { - this(context, KEY); + this(context, Flags.biometricsOnboardingEducation() ? KEY_1 : KEY); } public FaceSettingsEnrollButtonPreferenceController(Context context, String preferenceKey) { @@ -62,25 +65,39 @@ public class FaceSettingsEnrollButtonPreferenceController extends BasePreference } @Override - public void updateState(Preference preference) { + public void updateState(@NonNull Preference preference) { super.updateState(preference); - - mButton = ((LayoutPreference) preference).findViewById( - R.id.security_settings_face_settings_enroll_button); - - if (PartnerStyleHelper.shouldApplyPartnerResource(mButton)) { - ButtonStyler.applyPartnerCustomizationPrimaryButtonStyle(mContext, mButton); - } - - mButton.setOnClickListener(this); final boolean isDeviceOwnerBlockingAuth = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( mContext, DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) != null; - mButton.setEnabled(!isDeviceOwnerBlockingAuth); + + if (Flags.biometricsOnboardingEducation()) { + preference.setEnabled(!isDeviceOwnerBlockingAuth); + } else { + mButton = ((LayoutPreference) preference).findViewById( + R.id.security_settings_face_settings_enroll_button); + + if (PartnerStyleHelper.shouldApplyPartnerResource(mButton)) { + ButtonStyler.applyPartnerCustomizationPrimaryButtonStyle(mContext, mButton); + } + + mButton.setOnClickListener(this); + mButton.setEnabled(!isDeviceOwnerBlockingAuth); + } } @Override public void onClick(View v) { + startEnrolling(); + } + + @Override + public boolean onPreferenceClick(@NonNull Preference preference) { + startEnrolling(); + return true; + } + + private void startEnrolling() { mIsClicked = true; final Intent intent = new Intent(); intent.setClassName(SETTINGS_PACKAGE_NAME, FaceEnroll.class.getName()); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsFeatureProvider.kt b/src/com/android/settings/biometrics/face/FaceSettingsFeatureProvider.kt new file mode 100644 index 00000000000..c69239898e8 --- /dev/null +++ b/src/com/android/settings/biometrics/face/FaceSettingsFeatureProvider.kt @@ -0,0 +1,55 @@ +/* + * 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.face + +/** + * Provide features for FaceSettings page. + */ +open class FaceSettingsFeatureProvider { + /** + * Get the description shown in the Face settings page. + */ + open fun getSettingPageDescription(): Int { + return 0 + } + + /** + * Get the footer description for face class3 shown in the Face settings page. + */ + open fun getSettingPageFooterDescriptionClass3(): Int { + return com.android.settings.R.string.security_settings_face_settings_footer_class3 + } + + /** + * Get the footer learn more description shown in the Face settings page. + */ + open fun getSettingPageFooterLearnMoreDescription(): Int { + return 0 + } + + /** + * Get the footer learn more URL. + */ + open fun getSettingPageFooterLearnMoreUrl(): Int { + return 0 + } + + companion object { + @JvmStatic + val instance = FaceSettingsFeatureProvider() + } +} \ No newline at end of file diff --git a/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java index d56c9b3ae4a..e64327c6193 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java @@ -19,11 +19,13 @@ package com.android.settings.biometrics.face; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.ResourceId; import android.hardware.biometrics.SensorProperties; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.util.Log; +import android.view.View; import androidx.annotation.NonNull; import androidx.preference.Preference; @@ -35,6 +37,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.HelpUtils; +import com.android.settingslib.widget.FooterPreference; import java.util.List; @@ -82,14 +85,15 @@ public class FaceSettingsFooterPreferenceController extends BasePreferenceContro mContext, mContext.getString(R.string.help_url_face), getClass().getName()); final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(mContext, ANNOTATION_URL, helpIntent); - - int footerRes; + final FaceSettingsFeatureProvider featureProvider = FeatureFactory.getFeatureFactory() + .getFaceFeatureProvider().getFaceSettingsFeatureProvider(); + final int footerRes; boolean isAttentionSupported = mProvider.isAttentionSupported(mContext); if (Utils.isPrivateProfile(mUserId, mContext)) { footerRes = R.string.private_space_face_settings_footer; } else if (mIsFaceStrong) { footerRes = isAttentionSupported - ? R.string.security_settings_face_settings_footer_class3 + ? featureProvider.getSettingPageFooterDescriptionClass3() : R.string.security_settings_face_settings_footer_attention_not_supported; } else { footerRes = isAttentionSupported @@ -98,6 +102,20 @@ public class FaceSettingsFooterPreferenceController extends BasePreferenceContro } preference.setTitle(AnnotationSpan.linkify( mContext.getText(footerRes), linkInfo)); + + final int learnMoreRes = featureProvider.getSettingPageFooterLearnMoreDescription(); + final int learnMoreUrlRes = featureProvider.getSettingPageFooterLearnMoreUrl(); + if (ResourceId.isValid(learnMoreRes) + && ResourceId.isValid(learnMoreUrlRes) + && preference instanceof FooterPreference) { + final Intent learnMoreIntent = HelpUtils.getHelpIntent( + mContext, mContext.getString(learnMoreUrlRes), getClass().getName()); + final View.OnClickListener learnMoreClickListener = (v) -> { + mContext.startActivityForResult(KEY, learnMoreIntent, 0, null); + }; + ((FooterPreference) preference).setLearnMoreAction(learnMoreClickListener); + ((FooterPreference) preference).setLearnMoreText(mContext.getString(learnMoreRes)); + } } public void setUserId(int userId) { diff --git a/src/com/android/settings/biometrics/face/FaceSettingsKeyguardUnlockPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsKeyguardUnlockPreferenceController.java index e6298c09190..db9175cbb9c 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsKeyguardUnlockPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsKeyguardUnlockPreferenceController.java @@ -19,9 +19,11 @@ package com.android.settings.biometrics.face; import static android.provider.Settings.Secure.FACE_KEYGUARD_ENABLED; import android.content.Context; +import android.hardware.face.FaceManager; import android.provider.Settings; import androidx.annotation.NonNull; +import androidx.preference.Preference; import com.android.settings.Utils; import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils; @@ -32,9 +34,12 @@ public class FaceSettingsKeyguardUnlockPreferenceController extends private static final int OFF = 0; private static final int DEFAULT = ON; + private FaceManager mFaceManager; + public FaceSettingsKeyguardUnlockPreferenceController( @NonNull Context context, @NonNull String key) { super(context, key); + mFaceManager = Utils.getFaceManagerOrNull(context); } @Override @@ -49,6 +54,20 @@ public class FaceSettingsKeyguardUnlockPreferenceController extends FACE_KEYGUARD_ENABLED, isChecked ? ON : OFF, getUserId()); } + @Override + public void updateState(Preference preference) { + super.updateState(preference); + if (!FaceSettings.isFaceHardwareDetected(mContext)) { + preference.setEnabled(false); + } else if (!mFaceManager.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/face/FaceSettingsRemoveButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java index ae5b62bcbd8..ea214fdfb8d 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java @@ -31,6 +31,7 @@ import android.widget.Button; import android.widget.Toast; import android.window.OnBackInvokedCallback; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -40,6 +41,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.flags.Flags; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.LayoutPreference; @@ -54,10 +56,11 @@ import java.util.List; * will likely change if multiple enrollments are allowed/supported. */ public class FaceSettingsRemoveButtonPreferenceController extends BasePreferenceController - implements View.OnClickListener { + implements View.OnClickListener, Preference.OnPreferenceClickListener { private static final String TAG = "FaceSettings/Remove"; static final String KEY = "security_settings_face_delete_faces_container"; + static final String KEY_1 = "security_settings_face_remove"; public static class ConfirmRemoveDialog extends InstrumentedDialogFragment implements OnBackInvokedCallback { @@ -173,7 +176,11 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference if (remaining == 0) { final List faces = mFaceManager.getEnrolledFaces(mUserId); if (!faces.isEmpty()) { - mButton.setEnabled(true); + if (Flags.biometricsOnboardingEducation()) { + mPreference.setEnabled(true); + } else { + mButton.setEnabled(true); + } } else { mRemoving = false; mListener.onRemoved(); @@ -189,7 +196,11 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference @Override public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { - mButton.setEnabled(false); + if (Flags.biometricsOnboardingEducation()) { + mPreference.setEnabled(false); + } else { + mButton.setEnabled(false); + } final List faces = mFaceManager.getEnrolledFaces(mUserId); if (faces.isEmpty()) { Log.e(TAG, "No faces"); @@ -202,7 +213,11 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference // Remove the first/only face mFaceUpdater.remove(faces.get(0), mUserId, mRemovalCallback); } else { - mButton.setEnabled(true); + if (Flags.biometricsOnboardingEducation()) { + mPreference.setEnabled(true); + } else { + mButton.setEnabled(true); + } mRemoving = false; } @@ -224,7 +239,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference } public FaceSettingsRemoveButtonPreferenceController(Context context) { - this(context, KEY); + this(context, Flags.biometricsOnboardingEducation() ? KEY_1 : KEY); } public void setUserId(int userId) { @@ -232,19 +247,21 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference } @Override - public void updateState(Preference preference) { + public void updateState(@NonNull Preference preference) { super.updateState(preference); mPreference = preference; - mButton = ((LayoutPreference) preference) - .findViewById(R.id.security_settings_face_settings_remove_button); + if (!Flags.biometricsOnboardingEducation()) { + mButton = ((LayoutPreference) preference) + .findViewById(R.id.security_settings_face_settings_remove_button); - if (PartnerStyleHelper.shouldApplyPartnerResource(mButton)) { - ButtonStyler.applyPartnerCustomizationPrimaryButtonStyle(mContext, mButton); + if (PartnerStyleHelper.shouldApplyPartnerResource(mButton)) { + ButtonStyler.applyPartnerCustomizationPrimaryButtonStyle(mContext, mButton); + } + + mButton.setOnClickListener(this); } - mButton.setOnClickListener(this); - // If there is already a ConfirmRemoveDialog showing, reset the listener since the // controller has been recreated. ConfirmRemoveDialog removeDialog = @@ -256,10 +273,19 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference removeDialog.setOnClickListener(mOnConfirmDialogClickListener); } - if (!FaceSettings.isFaceHardwareDetected(mContext)) { - mButton.setEnabled(false); + final boolean isFaceHardwareDetected = FaceSettings.isFaceHardwareDetected(mContext); + if (Flags.biometricsOnboardingEducation()) { + if (!isFaceHardwareDetected) { + mPreference.setEnabled(false); + } else { + mPreference.setEnabled(!mRemoving); + } } else { - mButton.setEnabled(!mRemoving); + if (!isFaceHardwareDetected) { + mButton.setEnabled(false); + } else { + mButton.setEnabled(!mRemoving); + } } } @@ -270,22 +296,32 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference @Override public String getPreferenceKey() { - return KEY; + return Flags.biometricsOnboardingEducation() ? KEY_1 : KEY; } @Override public void onClick(View v) { if (v == mButton) { - mMetricsFeatureProvider.logClickedPreference(mPreference, getMetricsCategory()); - mRemoving = true; - ConfirmRemoveDialog confirmRemoveDialog = - ConfirmRemoveDialog.newInstance(BiometricUtils.isConvenience(mFaceManager)); - confirmRemoveDialog.setOnClickListener(mOnConfirmDialogClickListener); - confirmRemoveDialog.show(mActivity.getSupportFragmentManager(), - ConfirmRemoveDialog.class.getName()); + showRemoveDialog(); } } + @Override + public boolean onPreferenceClick(@NonNull Preference preference) { + showRemoveDialog(); + return true; + } + + private void showRemoveDialog() { + mMetricsFeatureProvider.logClickedPreference(mPreference, getMetricsCategory()); + mRemoving = true; + ConfirmRemoveDialog confirmRemoveDialog = + ConfirmRemoveDialog.newInstance(BiometricUtils.isConvenience(mFaceManager)); + confirmRemoveDialog.setOnClickListener(mOnConfirmDialogClickListener); + confirmRemoveDialog.show(mActivity.getSupportFragmentManager(), + ConfirmRemoveDialog.class.getName()); + } + public void setListener(Listener listener) { mListener = listener; } diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java index 9de2fc2dcb8..12fd750e13a 100644 --- a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java @@ -32,6 +32,8 @@ import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.os.Looper; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.preference.Preference; import androidx.preference.PreferenceManager; @@ -60,6 +62,8 @@ import java.util.List; public class FaceSettingsFooterPreferenceControllerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String PREF_KEY = "security_face_footer"; @Mock private FaceManager mFaceManager; @@ -140,6 +144,7 @@ public class FaceSettingsFooterPreferenceControllerTest { } @Test + @DisableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) public void testString_faceClass3() throws RemoteException { setupHasFaceFeature(); displayFaceSettingsFooterPreferenceController(); diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceControllerTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceControllerTest.java index 4d21364f5ca..bf813909ed4 100644 --- a/tests/unit/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/biometrics/face/FaceSettingsEnrollButtonPreferenceControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.widget.Button; +import androidx.preference.Preference; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Before; @@ -43,6 +44,8 @@ public class FaceSettingsEnrollButtonPreferenceControllerTest { @Mock private Button mButton; @Mock + private Preference mPreference; + @Mock private FaceSettingsEnrollButtonPreferenceController.Listener mListener; private FaceSettingsEnrollButtonPreferenceController mController; @@ -65,4 +68,12 @@ public class FaceSettingsEnrollButtonPreferenceControllerTest { assertThat(mController.isClicked()).isTrue(); verify(mListener).onStartEnrolling(any()); } + + @Test + public void testOnPreferenceClick() { + mController.onPreferenceClick(mPreference); + + assertThat(mController.isClicked()).isTrue(); + verify(mListener).onStartEnrolling(any()); + } }