From 355002929752b5aab67ba574cdaee5ebf7f13833 Mon Sep 17 00:00:00 2001 From: MiltonWu Date: Wed, 11 Oct 2023 08:13:14 +0000 Subject: [PATCH] fingerprint pre-enroll calibration Add calibration interface and dialog Test: Manually doing enrollment without any exception Bug: 301226085 Change-Id: Ibe0d205e87f75bb1d5bd1c646deb8501d96a4a5a --- ...iometrics_integration_declarations.aconfig | 7 ++ .../biometrics/BiometricEnrollBase.java | 1 + .../FingerprintEnrollFindSensor.java | 97 ++++++++++++++++--- .../FingerprintEnrollIntroduction.java | 30 ++++++ .../FingerprintFeatureProvider.java | 12 +++ .../FingerprintFeatureProviderImpl.java | 8 ++ .../UdfpsEnrollCalibrationDialog.kt | 58 +++++++++++ .../fingerprint/UdfpsEnrollCalibrator.kt | 36 +++++++ 8 files changed, 235 insertions(+), 14 deletions(-) create mode 100644 src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt create mode 100644 src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt diff --git a/aconfig/settings_biometrics_integration_declarations.aconfig b/aconfig/settings_biometrics_integration_declarations.aconfig index 529e126e41c..bc437f296cf 100644 --- a/aconfig/settings_biometrics_integration_declarations.aconfig +++ b/aconfig/settings_biometrics_integration_declarations.aconfig @@ -7,3 +7,10 @@ flag { bug: "288155127" } +flag { + name: "udfps_enroll_calibration" + namespace: "biometrics_integration" + description: "This flag controls whether the fps enroll calibration feature should be enabled" + bug: "301226085" +} + diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java index c9c8cff034c..7df1fe18e78 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollBase.java +++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java @@ -67,6 +67,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity { public static final String EXTRA_FINISHED_ENROLL_FACE = "finished_enrolling_face"; public static final String EXTRA_FINISHED_ENROLL_FINGERPRINT = "finished_enrolling_fingerprint"; public static final String EXTRA_LAUNCHED_POSTURE_GUIDANCE = "launched_posture_guidance"; + public static final String KEY_CALIBRATOR_UUID = "calibrator_uuid"; /** * Used by the choose fingerprint wizard to indicate the wizard is diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java index cfd57d7c81c..276845cf1eb 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java @@ -25,21 +25,27 @@ import android.content.res.Resources; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.OrientationEventListener; import android.view.Surface; import android.view.View; -import android.view.View.OnClickListener; import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricEnrollSidecar; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.biometrics.fingerprint.UdfpsEnrollCalibrator.Result; +import com.android.settings.biometrics.fingerprint.UdfpsEnrollCalibrator.Status; +import com.android.settings.flags.Flags; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settingslib.widget.LottieColorUtils; import com.android.systemui.unfold.compat.ScreenSizeFoldProvider; @@ -50,6 +56,7 @@ import com.google.android.setupcompat.template.FooterBarMixin; import com.google.android.setupcompat.template.FooterButton; import java.util.List; +import java.util.UUID; /** * Activity explaining the fingerprint sensor location for fingerprint enrollment. @@ -76,6 +83,10 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements private ScreenSizeFoldProvider mScreenSizeFoldProvider; private boolean mIsFolded; private boolean mIsReverseDefaultRotation; + @Nullable + private UdfpsEnrollCalibrator mCalibrator; + @Nullable + private Observer mCalibratorStatusObserver; @Override protected void onCreate(Bundle savedInstanceState) { @@ -107,14 +118,6 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements if (mCanAssumeUdfps) { setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title); setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message); - mFooterBarMixin.setPrimaryButton( - new FooterButton.Builder(this) - .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button) - .setListener(this::onStartButtonClick) - .setButtonType(FooterButton.ButtonType.NEXT) - .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) - .build() - ); mIllustrationLottie = findViewById(R.id.illustration_lottie); AccessibilityManager am = getSystemService(AccessibilityManager.class); @@ -167,12 +170,20 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements mAnimation = null; if (mCanAssumeUdfps) { - mIllustrationLottie.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onStartButtonClick(v); + if (Flags.udfpsEnrollCalibration()) { + mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider() + .getUdfpsEnrollCalibrator( + (savedInstanceState != null) + ? savedInstanceState.getParcelable(KEY_CALIBRATOR_UUID, UUID.class) + : getIntent().getSerializableExtra(KEY_CALIBRATOR_UUID, UUID.class) + ); + if (mCalibrator == null + || mCalibrator.getStatusLiveData().getValue() == Status.FINISHED) { + enableUdfpsLottieAndNextButton(); } - }); + } else { + enableUdfpsLottieAndNextButton(); + } } else if (!mCanAssumeSfps) { View animationView = findViewById(R.id.fingerprint_sensor_location_animation); if (animationView instanceof FingerprintFindSensorAnimation) { @@ -181,6 +192,20 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements } } + private void enableUdfpsLottieAndNextButton() { + mFooterBarMixin.setPrimaryButton( + new FooterButton.Builder(this) + .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button) + .setListener(this::onStartButtonClick) + .setButtonType(FooterButton.ButtonType.NEXT) + .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) + .build() + ); + if (mIllustrationLottie != null) { + mIllustrationLottie.setOnClickListener(this::onStartButtonClick); + } + } + private int getRotationFromDefault(int rotation) { if (mIsReverseDefaultRotation) { return (rotation + 1) % 4; @@ -258,6 +283,11 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked); + if (Flags.udfpsEnrollCalibration()) { + if (mCalibrator != null) { + outState.putSerializable(KEY_CALIBRATOR_UUID, mCalibrator.getUuid()); + } + } } @Override @@ -287,6 +317,39 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements if (mAnimation != null) { mAnimation.startAnimation(); } + if (Flags.udfpsEnrollCalibration()) { + if (mCalibrator != null) { + final Status current = mCalibrator.getStatusLiveData().getValue(); + if (current == Status.PROCESSING) { + if (mCalibratorStatusObserver == null) { + mCalibratorStatusObserver = status -> { + if (status == Status.GOT_RESULT) { + onGotCalibrationResult(); + } + }; + } + mCalibrator.getStatusLiveData().observe(this, mCalibratorStatusObserver); + } else if (current == Status.GOT_RESULT) { + onGotCalibrationResult(); + } + } + } + } + + private void onGotCalibrationResult() { + if (Flags.udfpsEnrollCalibration()) { + if (mCalibrator != null) { + mCalibrator.setFinished(); + if (mCalibrator.getResult() == Result.NEED_CALIBRATION) { + UdfpsEnrollCalibrationDialog.newInstance( + mCalibrator.getCalibrationDialogTitleTextId(), + mCalibrator.getCalibrationDialogMessageTextId(), + mCalibrator.getCalibrationDialogDismissButtonTextId() + ).show(getSupportFragmentManager(), "findsensor-calibration-dialog"); + } + } + new Handler(Looper.getMainLooper()).post(this::enableUdfpsLottieAndNextButton); + } } private void stopLookingForFingerprint() { @@ -344,6 +407,12 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements if (mAnimation != null) { mAnimation.pauseAnimation(); } + if (Flags.udfpsEnrollCalibration()) { + if (mCalibrator != null && mCalibratorStatusObserver != null) { + mCalibrator.getStatusLiveData().removeObserver(mCalibratorStatusObserver); + mCalibratorStatusObserver = null; + } + } } @Override diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java index df23a5c76b1..dc3c65ed674 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java @@ -45,6 +45,8 @@ import com.android.settings.biometrics.BiometricEnrollIntroduction; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.GatekeeperPasswordProvider; import com.android.settings.biometrics.MultiBiometricEnrollHelper; +import com.android.settings.flags.Flags; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtilsInternal; @@ -55,6 +57,7 @@ import com.google.android.setupdesign.span.LinkSpan; import com.google.android.setupdesign.util.DeviceHelper; import java.util.List; +import java.util.UUID; public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { @@ -67,6 +70,8 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { private DevicePolicyManager mDevicePolicyManager; private boolean mCanAssumeUdfps; + @Nullable + private UdfpsEnrollCalibrator mCalibrator; @Override protected void onCreate(Bundle savedInstanceState) { @@ -85,6 +90,16 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { mDevicePolicyManager = getSystemService(DevicePolicyManager.class); + if (Flags.udfpsEnrollCalibration()) { + mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider() + .getUdfpsEnrollCalibrator( + (savedInstanceState != null) + ? savedInstanceState.getParcelable( + KEY_CALIBRATOR_UUID, UUID.class) + : null + ); + } + final ImageView iconFingerprint = findViewById(R.id.icon_fingerprint); final ImageView iconDeviceLocked = findViewById(R.id.icon_device_locked); final ImageView iconTrashCan = findViewById(R.id.icon_trash_can); @@ -155,6 +170,16 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { } } + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (Flags.udfpsEnrollCalibration()) { + if (mCalibrator != null) { + outState.putSerializable(KEY_CALIBRATOR_UUID, mCalibrator.getUuid()); + } + } + } + @Override protected void initViews() { setDescriptionText(getString( @@ -364,6 +389,11 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, BiometricUtils.getGatekeeperPasswordHandle(getIntent())); } + if (Flags.udfpsEnrollCalibration()) { + if (mCalibrator != null) { + intent.putExtra(KEY_CALIBRATOR_UUID, mCalibrator.getUuid()); + } + } return intent; } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java index 906f95a57a9..5a2bf8bad59 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java @@ -16,12 +16,24 @@ package com.android.settings.biometrics.fingerprint; +import androidx.annotation.Nullable; + import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature; +import java.util.UUID; + public interface FingerprintFeatureProvider { /** * Gets the feature implementation of SFPS enrollment. * @return the feature implementation */ SfpsEnrollmentFeature getSfpsEnrollmentFeature(); + + /** + * Gets calibrator to calibrate the FPS before enrolling udfps + * @param uuid unique id for passed between different activities + * @return udfps calibrator + */ + @Nullable + UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@Nullable UUID uuid); } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java index 9745ca3fd7e..1baabc66ecc 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java @@ -21,6 +21,8 @@ import androidx.annotation.Nullable; import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature; import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl; +import java.util.UUID; + public class FingerprintFeatureProviderImpl implements FingerprintFeatureProvider { @Nullable @@ -33,4 +35,10 @@ public class FingerprintFeatureProviderImpl implements FingerprintFeatureProvide } return mSfpsEnrollmentFeatureImpl; } + + @Nullable + @Override + public UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@Nullable UUID uuid) { + return null; + } } diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt new file mode 100644 index 00000000000..892996a8d43 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 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.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import androidx.annotation.StringRes +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.android.settings.R + +class UdfpsEnrollCalibrationDialog : DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = + AlertDialog.Builder(requireActivity(), R.style.Theme_AlertDialog) + .setTitle(arguments!!.getInt(KEY_TITLE_TEXT_ID)) + .setMessage(arguments!!.getInt(KEY_MESSAGE_TEXT_ID)) + .setPositiveButton(arguments!!.getInt(KEY_DISMISS_BUTTON_TEXT_ID)) { + dialog: DialogInterface?, _: Int -> dialog?.dismiss() + } + .create().also { + isCancelable = false + } + + companion object { + + private const val KEY_TITLE_TEXT_ID = "title_text_id" + private const val KEY_MESSAGE_TEXT_ID = "message_text_id" + private const val KEY_DISMISS_BUTTON_TEXT_ID = "dismiss_button_text_id" + + @JvmStatic + fun newInstance( + @StringRes titleTextId: Int, + @StringRes messageTextId: Int, + @StringRes dismissButtonTextId: Int + ) = UdfpsEnrollCalibrationDialog().apply { + arguments = Bundle().apply { + putInt(KEY_TITLE_TEXT_ID, titleTextId) + putInt(KEY_MESSAGE_TEXT_ID, messageTextId) + putInt(KEY_DISMISS_BUTTON_TEXT_ID, dismissButtonTextId) + } + } + } +} diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt new file mode 100644 index 00000000000..c0626d3e694 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt @@ -0,0 +1,36 @@ +package com.android.settings.biometrics.fingerprint + +import androidx.annotation.StringRes +import androidx.lifecycle.LiveData +import java.util.UUID + +interface UdfpsEnrollCalibrator { + + enum class Status { + PROCESSING, + GOT_RESULT, + FINISHED, + } + + enum class Result { + NEED_CALIBRATION, + NO_NEED_CALIBRATION, + } + + val uuid: UUID + + val statusLiveData: LiveData + + val result: Result? + + fun setFinished() + + @get:StringRes + val calibrationDialogTitleTextId: Int + + @get:StringRes + val calibrationDialogMessageTextId: Int + + @get:StringRes + val calibrationDialogDismissButtonTextId: Int +} \ No newline at end of file