Merge "[Biometric Onboarding & Edu] Support check enrolled fingerprint" into main
This commit is contained in:
25
res/drawable/ic_check_list_24dp.xml
Normal file
25
res/drawable/ic_check_list_24dp.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 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
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?android:attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:pathData="M222,760L80,618L136,562L221,647L391,477L447,534L222,760ZM222,440L80,298L136,242L221,327L391,157L447,214L222,440ZM520,680L520,600L880,600L880,680L520,680ZM520,360L520,280L880,280L880,360L520,360Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
47
res/layout/fingerprint_check_enrolled_dialog.xml
Normal file
47
res/layout/fingerprint_check_enrolled_dialog.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<com.android.settings.biometrics.fingerprint.UdfpsCheckEnrolledView
|
||||||
|
android:id="@+id/udfps_check_enrolled_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_centerHorizontal="true">
|
||||||
|
|
||||||
|
<!-- Fingerprint -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/udfps_fingerprint_sensor_view"
|
||||||
|
android:contentDescription="@string/accessibility_fingerprint_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/udfps_fingerprint_sensor_message"
|
||||||
|
android:layout_marginTop="80dp"
|
||||||
|
android:layout_below="@id/udfps_fingerprint_sensor_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:text="@string/fingerprint_check_enroll_touch_sensor"/>
|
||||||
|
|
||||||
|
</com.android.settings.biometrics.fingerprint.UdfpsCheckEnrolledView>
|
||||||
|
</RelativeLayout>
|
@@ -938,6 +938,8 @@
|
|||||||
<string name="security_settings_fingerprint_settings_preferences_category">When using Fingerprint Unlock</string>
|
<string name="security_settings_fingerprint_settings_preferences_category">When using Fingerprint Unlock</string>
|
||||||
<!-- Title shown for work menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
<!-- Title shown for work menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
||||||
<string name="security_settings_work_fingerprint_preference_title">Fingerprint for work</string>
|
<string name="security_settings_work_fingerprint_preference_title">Fingerprint for work</string>
|
||||||
|
<!-- Preference to check enrolled fingerprints -->
|
||||||
|
<string name="fingerprint_check_enrolled_title">Check enrolled fingerprints</string>
|
||||||
<!-- Preference to add another fingerprint -->
|
<!-- Preference to add another fingerprint -->
|
||||||
<string name="fingerprint_add_title">Add fingerprint</string>
|
<string name="fingerprint_add_title">Add fingerprint</string>
|
||||||
<!-- Message showing the current number of fingerprints set up. Shown for a menu item that launches fingerprint settings or enrollment. -->
|
<!-- Message showing the current number of fingerprints set up. Shown for a menu item that launches fingerprint settings or enrollment. -->
|
||||||
@@ -991,7 +993,10 @@
|
|||||||
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_6">For best results, use a screen protector that\u2019s Made for Google certified. With other screen protectors, your child\u2019s fingerprint may not work.</string>
|
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_6">For best results, use a screen protector that\u2019s Made for Google certified. With other screen protectors, your child\u2019s fingerprint may not work.</string>
|
||||||
<!-- Introduction detail message shown in fingerprint enrollment introduction to learn more about fingerprint [CHAR LIMIT=NONE]-->
|
<!-- Introduction detail message shown in fingerprint enrollment introduction to learn more about fingerprint [CHAR LIMIT=NONE]-->
|
||||||
<string name="security_settings_fingerprint_v2_enroll_introduction_message_learn_more"></string>
|
<string name="security_settings_fingerprint_v2_enroll_introduction_message_learn_more"></string>
|
||||||
|
<!-- Description shown on the check enrolled fingerprint dialog -->
|
||||||
|
<string name="fingerprint_check_enroll_touch_sensor">Touch the fingerprint sensor</string>
|
||||||
|
<!-- Description shown on the check enrolled fingerprint dialog -->
|
||||||
|
<string name="fingerprint_check_enroll_not_recognized">Fingerprint not recognized</string>
|
||||||
<!-- Watch unlock enrollment and settings --><skip />
|
<!-- Watch unlock enrollment and settings --><skip />
|
||||||
<!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
|
<!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
|
||||||
<string name ="security_settings_activeunlock_preference_title">Watch Unlock</string>
|
<string name ="security_settings_activeunlock_preference_title">Watch Unlock</string>
|
||||||
|
@@ -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.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE;
|
||||||
import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
||||||
import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
|
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.SETTINGS_PACKAGE_NAME;
|
||||||
import static com.android.settings.Utils.isPrivateProfile;
|
import static com.android.settings.Utils.isPrivateProfile;
|
||||||
@@ -40,6 +41,7 @@ import android.hardware.fingerprint.Fingerprint;
|
|||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
@@ -49,8 +51,15 @@ import android.text.InputFilter;
|
|||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
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.ImeAwareEditText;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -76,6 +85,7 @@ import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog;
|
|||||||
import com.android.settings.core.SettingsBaseActivity;
|
import com.android.settings.core.SettingsBaseActivity;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.password.ChooseLockGeneric;
|
import com.android.settings.password.ChooseLockGeneric;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
@@ -235,6 +245,9 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
|
|
||||||
private static final String TAG = "FingerprintSettings";
|
private static final String TAG = "FingerprintSettings";
|
||||||
private static final String KEY_FINGERPRINT_ITEM_PREFIX = "key_fingerprint_item";
|
private static final String KEY_FINGERPRINT_ITEM_PREFIX = "key_fingerprint_item";
|
||||||
|
|
||||||
|
private static final String KEY_FINGERPRINT_CHECK_ENROLLED =
|
||||||
|
"key_fingerprint_check_enrolled";
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String KEY_FINGERPRINT_ADD = "key_fingerprint_add";
|
static final String KEY_FINGERPRINT_ADD = "key_fingerprint_add";
|
||||||
private static final String KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE =
|
private static final String KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE =
|
||||||
@@ -691,6 +704,17 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
mFingerprintsEnrolledCategory.addPreference(pref);
|
mFingerprintsEnrolledCategory.addPreference(pref);
|
||||||
pref.setOnPreferenceChangeListener(this);
|
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);
|
mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD);
|
||||||
setupAddFingerprintPreference();
|
setupAddFingerprintPreference();
|
||||||
return keyToReturn;
|
return keyToReturn;
|
||||||
@@ -916,6 +940,8 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
FingerprintPreference fpref = (FingerprintPreference) pref;
|
FingerprintPreference fpref = (FingerprintPreference) pref;
|
||||||
final Fingerprint fp = fpref.getFingerprint();
|
final Fingerprint fp = fpref.getFingerprint();
|
||||||
showRenameDialog(fp);
|
showRenameDialog(fp);
|
||||||
|
} else if (KEY_FINGERPRINT_CHECK_ENROLLED.equals(key)) {
|
||||||
|
showCheckEnrolledDialog();
|
||||||
}
|
}
|
||||||
return super.onPreferenceTreeClick(pref);
|
return super.onPreferenceTreeClick(pref);
|
||||||
}
|
}
|
||||||
@@ -968,6 +994,16 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
mAuthenticateSidecar.stopAuthentication();
|
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
|
@Override
|
||||||
public boolean onPreferenceChange(Preference preference, Object value) {
|
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||||
boolean result = true;
|
boolean result = true;
|
||||||
@@ -1344,6 +1380,121 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
return new InputFilter[]{filter};
|
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 {
|
public static class RenameDialog extends InstrumentedDialogFragment {
|
||||||
|
|
||||||
private Fingerprint mFp;
|
private Fingerprint mFp;
|
||||||
|
@@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.biometrics.fingerprint;
|
package com.android.settings.biometrics.fingerprint;
|
||||||
|
|
||||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
|
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 android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
|
||||||
|
|
||||||
import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST;
|
import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST;
|
||||||
@@ -354,6 +355,48 @@ public class FingerprintSettingsFragmentTest {
|
|||||||
assertThat(addPref.isEnabled()).isTrue();
|
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,
|
private void setSensor(@FingerprintSensorProperties.SensorType int sensorType,
|
||||||
int maxFingerprints) {
|
int maxFingerprints) {
|
||||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||||
|
Reference in New Issue
Block a user