From 4bfbb8782a8046dfdc6c208188f1a938341767a5 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 29 Nov 2024 06:08:06 +0000 Subject: [PATCH] [Biometric Onboarding & Edu] Support check enrolled fingerprint - Add a new PreferenceItem for check enrolled fingerprint - Create a new DialogFragment for the check enrolled fingerprint with functions: - request an authentication to FingerprintManager - highlight the item when a authentication successes - show error text when authentication fails - close the authentication Bug: 370940762 Test: atest FingerprintSettingsFragmentTest Flag: com.android.settings.flags.biometrics_onboarding_education Change-Id: I90637e4ec20ea46e6f530ffd7ba79df9c31eda6b --- res/drawable/ic_check_list_24dp.xml | 25 +++ .../fingerprint_check_enrolled_dialog.xml | 47 +++++ res/values/strings.xml | 7 +- .../fingerprint/FingerprintSettings.java | 151 ++++++++++++++ .../fingerprint/UdfpsCheckEnrolledView.java | 186 ++++++++++++++++++ .../fingerprint/UdfpsFingerprintDrawable.java | 138 +++++++++++++ .../FingerprintSettingsFragmentTest.java | 43 ++++ 7 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 res/drawable/ic_check_list_24dp.xml create mode 100644 res/layout/fingerprint_check_enrolled_dialog.xml create mode 100644 src/com/android/settings/biometrics/fingerprint/UdfpsCheckEnrolledView.java create mode 100644 src/com/android/settings/biometrics/fingerprint/UdfpsFingerprintDrawable.java diff --git a/res/drawable/ic_check_list_24dp.xml b/res/drawable/ic_check_list_24dp.xml new file mode 100644 index 00000000000..4d8955cbccb --- /dev/null +++ b/res/drawable/ic_check_list_24dp.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/layout/fingerprint_check_enrolled_dialog.xml b/res/layout/fingerprint_check_enrolled_dialog.xml new file mode 100644 index 00000000000..5565829d825 --- /dev/null +++ b/res/layout/fingerprint_check_enrolled_dialog.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 7386eabaa38..2ef34a79340 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -921,6 +921,8 @@ When using Fingerprint Unlock Fingerprint for work + + Check enrolled fingerprints Add fingerprint @@ -974,7 +976,10 @@ For best results, use a screen protector that\u2019s Made for Google certified. With other screen protectors, your child\u2019s fingerprint may not work. - + + Touch the fingerprint sensor + + Fingerprint not recognized Watch Unlock diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index d8a14f1e450..223900c466d 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRI import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE; import static android.app.admin.DevicePolicyResources.UNDEFINED; import static android.hardware.biometrics.Flags.screenOffUnlockUdfps; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; import static com.android.settings.Utils.isPrivateProfile; @@ -41,6 +42,7 @@ import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; +import android.os.CancellationSignal; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; @@ -50,8 +52,15 @@ import android.text.InputFilter; import android.text.Spanned; import android.text.TextUtils; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowInsetsController; +import android.view.WindowManager; import android.widget.ImeAwareEditText; +import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -77,6 +86,7 @@ import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog; import com.android.settings.core.SettingsBaseActivity; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.overlay.FeatureFactory; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; @@ -236,6 +246,9 @@ public class FingerprintSettings extends SubSettings { private static final String TAG = "FingerprintSettings"; private static final String KEY_FINGERPRINT_ITEM_PREFIX = "key_fingerprint_item"; + + private static final String KEY_FINGERPRINT_CHECK_ENROLLED = + "key_fingerprint_check_enrolled"; @VisibleForTesting static final String KEY_FINGERPRINT_ADD = "key_fingerprint_add"; private static final String KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE = @@ -697,6 +710,17 @@ public class FingerprintSettings extends SubSettings { mFingerprintsEnrolledCategory.addPreference(pref); pref.setOnPreferenceChangeListener(this); } + if (Flags.biometricsOnboardingEducation() && isUdfps() && fingerprintCount > 0) { + // Setup check enrolled fingerprints preference + Preference pref = new Preference(root.getContext()); + pref.setKey(KEY_FINGERPRINT_CHECK_ENROLLED); + pref.setTitle(root.getContext().getString( + R.string.fingerprint_check_enrolled_title)); + pref.setIcon(R.drawable.ic_check_list_24dp); + pref.setVisible(true); + mFingerprintsEnrolledCategory.addPreference(pref); + pref.setOnPreferenceChangeListener(this); + } mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD); setupAddFingerprintPreference(); return keyToReturn; @@ -922,6 +946,8 @@ public class FingerprintSettings extends SubSettings { FingerprintPreference fpref = (FingerprintPreference) pref; final Fingerprint fp = fpref.getFingerprint(); showRenameDialog(fp); + } else if (KEY_FINGERPRINT_CHECK_ENROLLED.equals(key)) { + showCheckEnrolledDialog(); } return super.onPreferenceTreeClick(pref); } @@ -974,6 +1000,16 @@ public class FingerprintSettings extends SubSettings { mAuthenticateSidecar.stopAuthentication(); } + private void showCheckEnrolledDialog() { + final CheckEnrolledDialog checkEnrolledDialog = new CheckEnrolledDialog(); + final Bundle args = new Bundle(); + args.putInt(CheckEnrolledDialog.KEY_USER_ID, mUserId); + args.putParcelable(CheckEnrolledDialog.KEY_SENSOR_PROPERTIES, mSensorProperties.get(0)); + checkEnrolledDialog.setArguments(args); + checkEnrolledDialog.setTargetFragment(this, 0); + checkEnrolledDialog.show(getFragmentManager(), CheckEnrolledDialog.class.getName()); + } + @Override public boolean onPreferenceChange(Preference preference, Object value) { boolean result = true; @@ -1350,6 +1386,121 @@ public class FingerprintSettings extends SubSettings { return new InputFilter[]{filter}; } + public static class CheckEnrolledDialog extends InstrumentedDialogFragment { + + private static final String KEY_USER_ID = "user_id"; + private static final String KEY_SENSOR_PROPERTIES = "sensor_properties"; + private int mUserId; + private @Nullable CancellationSignal mCancellationSignal; + private @Nullable FingerprintSensorPropertiesInternal mSensorPropertiesInternal; + + @Override + public @NonNull View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate( + R.layout.fingerprint_check_enrolled_dialog, container, false); + } + + @Override + public @NonNull Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Dialog dialog = super.onCreateDialog(savedInstanceState); + if (dialog != null) { + mUserId = getArguments().getInt(KEY_USER_ID); + mSensorPropertiesInternal = + getArguments().getParcelable(KEY_SENSOR_PROPERTIES); + + // Remove the default dialog title bar + dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + + dialog.setOnShowListener(dialogInterface -> { + final UdfpsCheckEnrolledView v = + dialog.findViewById(R.id.udfps_check_enrolled_view); + v.setSensorProperties(mSensorPropertiesInternal); + }); + } + + return dialog; + } + + @Override + public void onStart() { + super.onStart(); + if (getDialog() == null) { + return; + } + + final Dialog dialog = getDialog(); + Window window = dialog.getWindow(); + WindowManager.LayoutParams params = window.getAttributes(); + + // Make the dialog fullscreen + params.width = WindowManager.LayoutParams.MATCH_PARENT; + params.height = WindowManager.LayoutParams.MATCH_PARENT; + params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + params.setFitInsetsTypes(0); + window.setAttributes(params); + window.getDecorView().getWindowInsetsController().hide( + WindowInsets.Type.statusBars()); + window.getDecorView().getWindowInsetsController().setSystemBarsBehavior( + WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + window.setBackgroundDrawableResource(android.R.color.black); + + final TextView message = + dialog.findViewById(R.id.udfps_fingerprint_sensor_message); + final Vibrator vibrator = getContext().getSystemService(Vibrator.class); + final FingerprintManager fpm = Utils.getFingerprintManagerOrNull(getContext()); + mCancellationSignal = new CancellationSignal(); + fpm.authenticate( + null /* crypto */, + mCancellationSignal, + new FingerprintManager.AuthenticationCallback() { + @Override + public void onAuthenticationError( + int errorCode, @NonNull CharSequence errString) { + dialog.dismiss(); + } + + @Override + public void onAuthenticationSucceeded( + @NonNull FingerprintManager.AuthenticationResult result) { + int fingerId = result.getFingerprint().getBiometricId(); + FingerprintSettingsFragment parent = + (FingerprintSettingsFragment) getTargetFragment(); + parent.highlightFingerprintItem(fingerId); + dialog.dismiss(); + } + + @Override + public void onAuthenticationFailed() { + vibrator.vibrate( + VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK)); + message.setText(R.string.fingerprint_check_enroll_not_recognized); + message.postDelayed(() -> { + message.setText(R.string.fingerprint_check_enroll_touch_sensor); + }, 2000); + } + }, + null /* handler */, + mUserId); + } + + @Override + public void onDismiss(@NonNull DialogInterface dialog) { + super.onDismiss(dialog); + if (mCancellationSignal != null) { + mCancellationSignal.cancel(); + mCancellationSignal = null; + } + } + + @Override + public int getMetricsCategory() { + return 0; + } + } + public static class RenameDialog extends InstrumentedDialogFragment { private Fingerprint mFp; diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsCheckEnrolledView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsCheckEnrolledView.java new file mode 100644 index 00000000000..52a28c75ea1 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsCheckEnrolledView.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2024 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 android.content.Context; +import android.graphics.Rect; +import android.graphics.RectF; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.util.AttributeSet; +import android.util.Log; +import android.util.RotationUtils; +import android.view.DisplayInfo; +import android.view.Surface; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settings.R; +import com.android.systemui.biometrics.UdfpsUtils; +import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; + +/** + * View corresponding with fingerprint_check_enrolled_dialog.xml + */ +public class UdfpsCheckEnrolledView extends RelativeLayout { + private static final String TAG = "UdfpsCheckEnrolledView"; + @NonNull + private final UdfpsFingerprintDrawable mFingerprintDrawable; + private ImageView mFingerprintView; + private UdfpsUtils mUdfpsUtils; + + private @Nullable Rect mSensorRect; + private @Nullable UdfpsOverlayParams mOverlayParams; + private @Nullable FingerprintSensorPropertiesInternal mSensorProperties; + + + public UdfpsCheckEnrolledView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + mFingerprintDrawable = new UdfpsFingerprintDrawable(mContext, attrs); + mUdfpsUtils = new UdfpsUtils(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mFingerprintView = findViewById(R.id.udfps_fingerprint_sensor_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); + } + + /** + * setup SensorProperties + */ + public void setSensorProperties(@Nullable FingerprintSensorPropertiesInternal properties) { + mSensorProperties = properties; + updateOverlayParams(); + } + + private void onSensorRectUpdated() { + updateDimensions(); + + if (mSensorRect == null || mOverlayParams == null) { + Log.e(TAG, "Fail to onSensorRectUpdated, mSensorRect/mOverlayParams null"); + return; + } + + // Updates sensor rect in relation to the overlay view + mSensorRect.set(0, 0, + mOverlayParams.getSensorBounds().width(), + mOverlayParams.getSensorBounds().height()); + mFingerprintDrawable.onSensorRectUpdated(new RectF(mSensorRect)); + } + + private void updateDimensions() { + if (mOverlayParams == null) { + Log.e(TAG, "Fail to updateDimensions for " + this + ", mOverlayParams null"); + return; + } + // Original sensorBounds assume portrait mode. + final Rect rotatedBounds = new Rect(mOverlayParams.getSensorBounds()); + int rotation = mOverlayParams.getRotation(); + if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { + RotationUtils.rotateBounds( + rotatedBounds, + mOverlayParams.getNaturalDisplayWidth(), + mOverlayParams.getNaturalDisplayHeight(), + rotation + ); + } + + RelativeLayout parent = ((RelativeLayout) getParent()); + if (parent == null) { + Log.e(TAG, "Fail to updateDimensions for " + this + ", parent null"); + return; + } + final int[] coords = parent.getLocationOnScreen(); + final int parentLeft = coords[0]; + final int parentTop = coords[1]; + final int parentRight = parentLeft + parent.getWidth(); + + // Update container view LayoutParams + RelativeLayout.LayoutParams checkEnrolledViewLp = + new RelativeLayout.LayoutParams(getWidth(), getHeight()); + checkEnrolledViewLp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + if (rotation == Surface.ROTATION_90) { + checkEnrolledViewLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + checkEnrolledViewLp.width = + rotatedBounds.width() + 2 * (parentRight - rotatedBounds.right); + } else { + checkEnrolledViewLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + checkEnrolledViewLp.width = rotatedBounds.width() + 2 * rotatedBounds.left; + } + setLayoutParams(checkEnrolledViewLp); + + // Update fingerprint view LayoutParams + RelativeLayout.LayoutParams fingerprintViewLp = new RelativeLayout.LayoutParams( + rotatedBounds.width(), rotatedBounds.height()); + fingerprintViewLp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + fingerprintViewLp.topMargin = rotatedBounds.top - parentTop; + if (rotation == Surface.ROTATION_90) { + fingerprintViewLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + fingerprintViewLp.rightMargin = parentRight - rotatedBounds.right; + } else { + fingerprintViewLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + fingerprintViewLp.leftMargin = rotatedBounds.left - parentLeft; + } + mFingerprintView.setLayoutParams(fingerprintViewLp); + } + + private void updateOverlayParams() { + + if (mSensorProperties == null) { + android.util.Log.e(TAG, "There is no sensor info!"); + return; + } + + DisplayInfo displayInfo = new DisplayInfo(); + if (getDisplay() == null) { + android.util.Log.e(TAG, "Can not get display"); + return; + } + getDisplay().getDisplayInfo(displayInfo); + Rect udfpsBounds = mSensorProperties.getLocation().getRect(); + float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo); + udfpsBounds.scale(scaleFactor); + + final Rect overlayBounds = new Rect( + 0, /* left */ + displayInfo.getNaturalHeight() / 2, /* top */ + displayInfo.getNaturalWidth(), /* right */ + displayInfo.getNaturalHeight() /* botom */); + + mOverlayParams = new UdfpsOverlayParams( + udfpsBounds, + overlayBounds, + displayInfo.getNaturalWidth(), + displayInfo.getNaturalHeight(), + scaleFactor, + displayInfo.rotation, + mSensorProperties.sensorType); + + post(() -> { + if (mOverlayParams == null) { + Log.e(TAG, "Fail to updateOverlayParams, mOverlayParams null"); + return; + } + mSensorRect = new Rect(mOverlayParams.getSensorBounds()); + onSensorRectUpdated(); + }); + } +} diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsFingerprintDrawable.java b/src/com/android/settings/biometrics/fingerprint/UdfpsFingerprintDrawable.java new file mode 100644 index 00000000000..e5ed6e16cc6 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsFingerprintDrawable.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 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 android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.PathShape; +import android.util.AttributeSet; +import android.util.PathParser; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settings.R; + +/** + * UDFPS fingerprint drawable + */ +public class UdfpsFingerprintDrawable extends Drawable { + private static final String TAG = "UdfpsFingerprintDrawable"; + + private static final float DEFAULT_STROKE_WIDTH = 3f; + + @NonNull + private final Paint mSensorOutlinePaint; + @NonNull + private final ShapeDrawable mFingerprintDrawable; + private int mAlpha; + + @Nullable + private RectF mSensorRect; + private int mEnrollIcon; + private int mOutlineColor; + + UdfpsFingerprintDrawable(@NonNull Context context, @Nullable AttributeSet attrs) { + mFingerprintDrawable = defaultFactory(context); + + loadResources(context, attrs); + mSensorOutlinePaint = new Paint(0 /* flags */); + mSensorOutlinePaint.setAntiAlias(true); + mSensorOutlinePaint.setColor(mOutlineColor); + mSensorOutlinePaint.setStyle(Paint.Style.FILL); + + mFingerprintDrawable.setTint(mEnrollIcon); + + setAlpha(255); + } + + /** The [sensorRect] coordinates for the sensor area. */ + void onSensorRectUpdated(@NonNull RectF sensorRect) { + int margin = ((int) sensorRect.height()) / 8; + Rect bounds = new Rect((int) (sensorRect.left) + margin, (int) (sensorRect.top) + margin, + (int) (sensorRect.right) - margin, (int) (sensorRect.bottom) - margin); + updateFingerprintIconBounds(bounds); + mSensorRect = sensorRect; + } + + void updateFingerprintIconBounds(@NonNull Rect bounds) { + mFingerprintDrawable.setBounds(bounds); + invalidateSelf(); + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (mSensorRect != null) { + canvas.drawOval(mSensorRect, mSensorOutlinePaint); + } + mFingerprintDrawable.draw(canvas); + mFingerprintDrawable.setAlpha(getAlpha()); + mSensorOutlinePaint.setAlpha(getAlpha()); + } + + @Override + public void setAlpha(int alpha) { + mAlpha = alpha; + mFingerprintDrawable.setAlpha(alpha); + mSensorOutlinePaint.setAlpha(alpha); + invalidateSelf(); + } + + @Override + public int getAlpha() { + return mAlpha; + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + } + + @Override + public int getOpacity() { + return PixelFormat.OPAQUE; + } + + private ShapeDrawable defaultFactory(Context context) { + String fpPath = context.getResources().getString(R.string.config_udfpsIcon); + ShapeDrawable drawable = new ShapeDrawable( + new PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f) + ); + drawable.mutate(); + drawable.getPaint().setStyle(Paint.Style.STROKE); + drawable.getPaint().setStrokeCap(Paint.Cap.ROUND); + drawable.getPaint().setStrokeWidth(DEFAULT_STROKE_WIDTH); + return drawable; + } + + private void loadResources(Context context, @Nullable AttributeSet attrs) { + final TypedArray ta = context.obtainStyledAttributes(attrs, + R.styleable.BiometricsEnrollView, R.attr.biometricsEnrollStyle, + R.style.BiometricsEnrollStyle); + mEnrollIcon = ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollIcon, 0); + mOutlineColor = ta.getColor( + R.styleable.BiometricsEnrollView_biometricsMovingTargetFill, 0); + ta.recycle(); + } +} 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 1086f85d45c..a570baadd59 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java @@ -17,6 +17,7 @@ package com.android.settings.biometrics.fingerprint; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; +import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST; @@ -354,6 +355,48 @@ public class FingerprintSettingsFragmentTest { assertThat(addPref.isEnabled()).isTrue(); } + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCheckEnrolledShown_whenAtLeastOneFingerprintEnrolled_Udfps() { + final Fingerprint fingerprint = new Fingerprint("Test", 0, 0); + doReturn(List.of(fingerprint)).when(mFingerprintManager).getEnrolledFingerprints(anyInt()); + setUpFragment(false, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL, 5); + + shadowOf(Looper.getMainLooper()).idle(); + + final Preference checkEnrolledPerf = + mFragment.findPreference("key_fingerprint_check_enrolled"); + assertThat(checkEnrolledPerf).isNotNull(); + assertThat(checkEnrolledPerf.isVisible()).isTrue(); + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCheckEnrolledHide_whenNoFingerprintEnrolled_Udfps() { + doReturn(List.of()).when(mFingerprintManager).getEnrolledFingerprints(anyInt()); + setUpFragment(false, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL, 5); + + shadowOf(Looper.getMainLooper()).idle(); + + final Preference checkEnrolledPerf = + mFragment.findPreference("key_fingerprint_check_enrolled"); + assertThat(checkEnrolledPerf).isNull(); + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCheckEnrolledHide_nonUdfps() { + final Fingerprint fingerprint = new Fingerprint("Test", 0, 0); + doReturn(List.of(fingerprint)).when(mFingerprintManager).getEnrolledFingerprints(anyInt()); + setUpFragment(false, PRIMARY_USER_ID, TYPE_REAR, 5); + + shadowOf(Looper.getMainLooper()).idle(); + + final Preference checkEnrolledPerf = + mFragment.findPreference("key_fingerprint_check_enrolled"); + assertThat(checkEnrolledPerf).isNull(); + } + private void setSensor(@FingerprintSensorProperties.SensorType int sensorType, int maxFingerprints) { final ArrayList props = new ArrayList<>();