[Biometric Onboarding & Edu] Update face settings page
- Added a feature provider for face settings page in FaceFeatureProvider
for customization
- When face is deleted, disabled the settings buttons instead of hiding
them.
- Updated new UX style for add/remove face button.
Bug: 370940762
Test: atest FaceSettingsEnrollButtonPreferenceControllerTest
FaceSettingsFooterPreferenceControllerTest
Test: manual - 1. Enroll face
2. Go Face Settings page and remove face
3. Enroll face again
Flag: com.android.settings.flags.biometrics_onboarding_education
Change-Id: I490e647523eeff2dd1a58aab07f638e3e5e0ffb8
This commit is contained in:
@@ -42,4 +42,13 @@ public interface FaceFeatureProvider {
|
||||
default FaceEnrollActivityClassProvider getEnrollActivityClassProvider() {
|
||||
return FaceEnrollActivityClassProvider.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the feature provider for FaceSettings page
|
||||
* @return the provider
|
||||
*/
|
||||
@NonNull
|
||||
default FaceSettingsFeatureProvider getFaceSettingsFeatureProvider() {
|
||||
return FaceSettingsFeatureProvider.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
52
src/com/android/settings/biometrics/face/FacePreference.java
Normal file
52
src/com/android/settings/biometrics/face/FacePreference.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.widget.TwoTargetPreference;
|
||||
|
||||
public class FacePreference extends TwoTargetPreference {
|
||||
|
||||
public FacePreference(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public FacePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public FacePreference(
|
||||
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public FacePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSecondTargetResId() {
|
||||
return R.layout.preference_widget_delete;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ResourceId;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
@@ -73,12 +74,19 @@ public class FaceSettings extends DashboardFragment {
|
||||
private static final String KEY_BIOMETRICS_SUCCESSFULLY_AUTHENTICATED =
|
||||
"biometrics_successfully_authenticated";
|
||||
|
||||
private static final String PREF_KEY_FACE_DESCRIPTION = "security_settings_face_description";
|
||||
private static final String PREF_KEY_DELETE_FACE_DATA =
|
||||
"security_settings_face_delete_faces_container";
|
||||
private static final String PREF_KEY_ENROLL_FACE_UNLOCK =
|
||||
"security_settings_face_enroll_faces_container";
|
||||
private static final String PREF_KEY_USE_FACE_TO_CATEGORY =
|
||||
"biometric_settings_use_face_to";
|
||||
private static final String PREF_KEY_FACE_ENROLLED_CATEGORY =
|
||||
"security_settings_face_enrolled_category";
|
||||
private static final String PREF_KEY_FACE_REMOVE =
|
||||
"security_settings_face_remove";
|
||||
private static final String PREF_KEY_FACE_ENROLL =
|
||||
"security_settings_face_enroll";
|
||||
public static final String SECURITY_SETTINGS_FACE_MANAGE_CATEGORY =
|
||||
"security_settings_face_manage_category";
|
||||
|
||||
@@ -98,6 +106,9 @@ public class FaceSettings extends DashboardFragment {
|
||||
private List<Preference> 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<AbstractPreferenceController> buildPreferenceControllers(Context context) {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
controllers.add(new FaceSettingsKeyguardPreferenceController(context));
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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<Face> 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<Face> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user