4/n: Add basic enrollment for Face
Bug: 110589286
Test: fingerprint enrolling still works
Test: enrollment flow with and without a pin set up still works properly
Test: enrollment continues when configuration changes, stops otherwise
Change-Id: I39f76c7f1a16e9533cef573f87cf4b81cb20cb18
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3c635eb..35a619c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1570,6 +1570,8 @@
android:theme="@style/GlifTheme.Light"/>
<activity android:name=".biometrics.face.FaceEnrollIntroduction" android:exported="false" />
+ <activity android:name=".biometrics.face.FaceEnrollEnrolling" android:exported="false" />
+ <activity android:name=".biometrics.face.FaceEnrollFinish" android:exported="false" />
<activity android:name=".biometrics.fingerprint.FingerprintSettings" android:exported="false"/>
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFindSensor" android:exported="false"/>
diff --git a/res/layout/face_enroll_enrolling.xml b/res/layout/face_enroll_enrolling.xml
new file mode 100644
index 0000000..6ced80f
--- /dev/null
+++ b/res/layout/face_enroll_enrolling.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<com.android.setupwizardlib.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/setup_wizard_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ style="?attr/face_layout_theme"
+ app:suwFooter="@layout/face_enroll_enrolling_footer">
+
+ <LinearLayout
+ style="@style/SuwContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.setupwizardlib.view.FillContentLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <!-- TODO: replace this with actual content-->
+ <ImageView
+ style="@style/SuwContentIllustration"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@null"
+ android:src="@drawable/face_enroll_introduction" />
+
+ </com.android.setupwizardlib.view.FillContentLayout>
+
+ <TextView
+ style="@style/TextAppearance.FaceErrorText"
+ android:id="@+id/error_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:accessibilityLiveRegion="polite"
+ android:gravity="center"
+ android:visibility="invisible"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</com.android.setupwizardlib.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/face_enroll_enrolling_footer.xml b/res/layout/face_enroll_enrolling_footer.xml
new file mode 100644
index 0000000..e3c5872
--- /dev/null
+++ b/res/layout/face_enroll_enrolling_footer.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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
+ -->
+
+<!-- TODO: Use aapt:attr when it is fixed (b/36809755) -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/SuwGlifButtonBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ style="@style/SuwGlifButton.Secondary"
+ android:id="@+id/skip_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/security_settings_face_enroll_enrolling_skip" />
+
+</LinearLayout>
diff --git a/res/layout/face_enroll_finish.xml b/res/layout/face_enroll_finish.xml
new file mode 100644
index 0000000..9966497
--- /dev/null
+++ b/res/layout/face_enroll_finish.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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
+ -->
+
+<com.android.setupwizardlib.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/setup_wizard_layout"
+ style="?attr/face_layout_theme"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:suwFooter="@layout/face_enroll_finish_footer">
+
+ <LinearLayout
+ style="@style/SuwContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.setupwizardlib.view.FillContentLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <ImageView
+ style="@style/SuwContentIllustration"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@null"
+ android:src="@drawable/face_enroll_introduction" />
+
+ </com.android.setupwizardlib.view.FillContentLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/face_enroll_finish_footer.xml b/res/layout/face_enroll_finish_footer.xml
new file mode 100644
index 0000000..06d2639
--- /dev/null
+++ b/res/layout/face_enroll_finish_footer.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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
+ -->
+
+<!-- TODO: Use aapt:attr when it is fixed (b/36809755) -->
+<com.android.setupwizardlib.view.ButtonBarLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/SuwGlifButtonBar.Stackable"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <Button
+ style="@style/SuwGlifButton.Primary"
+ android:id="@+id/next_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/security_settings_face_enroll_done" />
+
+</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2d80b85..365ad28 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -897,12 +897,28 @@
<string name="security_settings_face_enroll_introduction_message_unlock_disabled">Use you</string>
<!-- Introduction detail message shwon in face enrollment screen in setup wizard. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_enroll_introduction_message_setup">Use your face to unlock your phone, authorize purchases, or sign in to apps</string>
+ <!-- Title shown in face enrollment dialog [CHAR LIMIT=40] -->
+ <string name="security_settings_face_enroll_repeat_title">Center your face in the circle</string>
+ <!-- Button text to skip enrollment of face [CHAR LIMIT=40] -->
+ <string name="security_settings_face_enroll_enrolling_skip">Do it later</string>
<!-- Text shown when "Add face" button is disabled -->
<string name="face_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
<!-- Text shown when users has enrolled a maximum number of faces [CHAR LIMIT=NONE] -->
<string name="face_intro_error_max">You\u2019ve added the maximum number of faces</string>
<!-- Text shown when an unknown error caused the device to be unable to add faces [CHAR LIMIT=NONE] -->
<string name="face_intro_error_unknown">Can\u2019t add more faces</string>
+ <!-- Dialog message for dialog which shows when face cannot be enrolled. [CHAR LIMIT=45] -->
+ <string name="security_settings_face_enroll_error_dialog_title">Enrollment was not completed</string>
+ <!-- Button text shown in face dialog shown when an error occurs during enrollment [CHAR LIMIT=22] -->
+ <string name="security_settings_face_enroll_dialog_ok">OK</string>
+ <!-- Dialog message for dialog which shows when face cannot be enrolled due to being idle too long. -->
+ <string name="security_settings_face_enroll_error_timeout_dialog_message">Face enrollment time limit reached. Try again.</string>
+ <!-- Dialog message for dialog which shows when face cannot be enrolled due to an internal error or face can't be read. -->
+ <string name="security_settings_face_enroll_error_generic_dialog_message">Face enrollment didn\'t work.</string>
+ <!-- Message shown in face enrollment dialog once enrollment is completed -->
+ <string name="security_settings_face_enroll_finish_title">All set. Looking good.</string>
+ <!-- Button text to exit face wizard after everything is done [CHAR LIMIT=15] -->
+ <string name="security_settings_face_enroll_done">Done</string>
<!-- Fingerprint enrollment and settings --><skip />
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4e5bdda..32cdb35 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -364,6 +364,11 @@
<item name="android:icon">@drawable/ic_fingerprint_header</item>
</style>
+ <style name="TextAppearance.FaceErrorText"
+ parent="android:TextAppearance.Material.Body1">
+ <item name="android:textColor">?android:attr/colorError</item>
+ </style>
+
<style name="FaceLayoutTheme">
<item name="android:icon">@drawable/ic_face_header</item>
</style>
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index c49ddd4..298891e 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -30,29 +30,33 @@
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
-import com.android.settings.biometrics.fingerprint.FingerprintSettings;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.setupwizardlib.GlifLayout;
/**
- * Base activity for all fingerprint enrollment steps.
+ * Base activity for all biometric enrollment steps.
*/
public abstract class BiometricEnrollBase extends InstrumentedActivity
implements View.OnClickListener {
public static final int RESULT_FINISHED = BiometricSettings.RESULT_FINISHED;
public static final int RESULT_SKIP = BiometricSettings.RESULT_SKIP;
public static final int RESULT_TIMEOUT = BiometricSettings.RESULT_TIMEOUT;
+ public static final String EXTRA_KEY_LAUNCHED_CONFIRM = "launched_confirm_lock";
+ public static final int CONFIRM_REQUEST = 1;
+ public static final int ENROLLING = 2;
+
+ protected boolean mLaunchedConfirmLock;
protected byte[] mToken;
protected int mUserId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mToken = getIntent().getByteArrayExtra(
- ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
+ mToken = getIntent().getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
if (savedInstanceState != null && mToken == null) {
+ mLaunchedConfirmLock = savedInstanceState.getBoolean(EXTRA_KEY_LAUNCHED_CONFIRM);
mToken = savedInstanceState.getByteArray(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
}
@@ -68,6 +72,7 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+ outState.putBoolean(EXTRA_KEY_LAUNCHED_CONFIRM, mLaunchedConfirmLock);
outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
}
@@ -77,6 +82,10 @@
initViews();
}
+ protected boolean shouldLaunchConfirmLock() {
+ return mToken == null && !mLaunchedConfirmLock;
+ }
+
protected void initViews() {
getWindow().setStatusBarColor(Color.TRANSPARENT);
Button nextButton = getNextButton();
@@ -129,4 +138,25 @@
}
return intent;
}
+
+ protected void launchConfirmLock(int titleResId, long challenge) {
+ ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
+ boolean launchedConfirmationActivity;
+ if (mUserId == UserHandle.USER_NULL) {
+ launchedConfirmationActivity = helper.launchConfirmationActivity(CONFIRM_REQUEST,
+ getString(titleResId),
+ null, null, challenge);
+ } else {
+ launchedConfirmationActivity = helper.launchConfirmationActivity(CONFIRM_REQUEST,
+ getString(titleResId),
+ null, null, challenge, mUserId);
+ }
+ if (!launchedConfirmationActivity) {
+ // This shouldn't happen, as we should only end up at this step if a lock thingy is
+ // already set.
+ finish();
+ } else {
+ mLaunchedConfirmLock = true;
+ }
+ }
}
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index 98775f2..beefb39 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -102,9 +102,11 @@
protected abstract String getExtraKeyForBiometric();
/**
- * @return the intent for proceeding to the next step of enrollment
+ * @return the intent for proceeding to the next step of enrollment. For Fingerprint, this
+ * should lead to the "Find Sensor" activity. For Face, this should lead to the "Enrolling"
+ * activity.
*/
- protected abstract Intent getFindSensorIntent();
+ protected abstract Intent getEnrollingIntent();
/**
* @param span
@@ -179,7 +181,7 @@
}
private void launchFindSensor(byte[] token) {
- Intent intent = getFindSensorIntent();
+ Intent intent = getEnrollingIntent();
if (token != null) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
diff --git a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
new file mode 100644
index 0000000..111fecd
--- /dev/null
+++ b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.settings.core.InstrumentedFragment;
+import com.android.settings.password.ChooseLockSettingsHelper;
+
+import java.util.ArrayList;
+
+/**
+ * Abstract sidecar fragment to handle the state around biometric enrollment. This sidecar manages
+ * the state of enrollment throughout the activity lifecycle so the app can continue after an
+ * event like rotation.
+ */
+public abstract class BiometricEnrollSidecar extends InstrumentedFragment {
+
+ public interface Listener {
+ void onEnrollmentHelp(CharSequence helpString);
+ void onEnrollmentError(int errMsgId, CharSequence errString);
+ void onEnrollmentProgressChange(int steps, int remaining);
+ }
+
+ private int mEnrollmentSteps = -1;
+ private int mEnrollmentRemaining = 0;
+ private Listener mListener;
+ private boolean mEnrolling;
+ private Handler mHandler = new Handler();
+ private boolean mDone;
+ private ArrayList<QueuedEvent> mQueuedEvents;
+
+ protected CancellationSignal mEnrollmentCancel;
+ protected byte[] mToken;
+ protected int mUserId;
+
+ private abstract class QueuedEvent {
+ public abstract void send(Listener listener);
+ }
+
+ private class QueuedEnrollmentProgress extends QueuedEvent {
+ int enrollmentSteps;
+ int remaining;
+ public QueuedEnrollmentProgress(int enrollmentSteps, int remaining) {
+ this.enrollmentSteps = enrollmentSteps;
+ this.remaining = remaining;
+ }
+
+ @Override
+ public void send(Listener listener) {
+ listener.onEnrollmentProgressChange(enrollmentSteps, remaining);
+ }
+ }
+
+ private class QueuedEnrollmentHelp extends QueuedEvent {
+ int helpMsgId;
+ CharSequence helpString;
+ public QueuedEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+ this.helpMsgId = helpMsgId;
+ this.helpString = helpString;
+ }
+
+ @Override
+ public void send(Listener listener) {
+ listener.onEnrollmentHelp(helpString);
+ }
+ }
+
+ private class QueuedEnrollmentError extends QueuedEvent {
+ int errMsgId;
+ CharSequence errString;
+ public QueuedEnrollmentError(int errMsgId, CharSequence errString) {
+ this.errMsgId = errMsgId;
+ this.errString = errString;
+ }
+
+ @Override
+ public void send(Listener listener) {
+ listener.onEnrollmentError(errMsgId, errString);
+ }
+ }
+
+ private final Runnable mTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cancelEnrollment();
+ }
+ };
+
+ public BiometricEnrollSidecar() {
+ mQueuedEvents = new ArrayList<>();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mToken = activity.getIntent().getByteArrayExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
+ mUserId = activity.getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (!mEnrolling) {
+ startEnrollment();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (!getActivity().isChangingConfigurations()) {
+ cancelEnrollment();
+ }
+ }
+
+ protected void startEnrollment() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ mEnrollmentSteps = -1;
+ mEnrollmentCancel = new CancellationSignal();
+ mEnrolling = true;
+ }
+
+ public boolean cancelEnrollment() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ if (mEnrolling) {
+ mEnrollmentCancel.cancel();
+ mEnrolling = false;
+ mEnrollmentSteps = -1;
+ return true;
+ }
+ return false;
+ }
+
+ protected void onEnrollmentProgress(int remaining) {
+ if (mEnrollmentSteps == -1) {
+ mEnrollmentSteps = remaining;
+ }
+ mEnrollmentRemaining = remaining;
+ mDone = remaining == 0;
+ if (mListener != null) {
+ mListener.onEnrollmentProgressChange(mEnrollmentSteps, remaining);
+ } else {
+ mQueuedEvents.add(new QueuedEnrollmentProgress(mEnrollmentSteps, remaining));
+ }
+ }
+
+ protected void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+ if (mListener != null) {
+ mListener.onEnrollmentHelp(helpString);
+ } else {
+ mQueuedEvents.add(new QueuedEnrollmentHelp(helpMsgId, helpString));
+ }
+ }
+
+ protected void onEnrollmentError(int errMsgId, CharSequence errString) {
+ if (mListener != null) {
+ mListener.onEnrollmentError(errMsgId, errString);
+ } else {
+ mQueuedEvents.add(new QueuedEnrollmentError(errMsgId, errString));
+ }
+ mEnrolling = false;
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ if (mListener != null) {
+ for (int i=0; i<mQueuedEvents.size(); i++) {
+ QueuedEvent event = mQueuedEvents.get(i);
+ event.send(mListener);
+ }
+ mQueuedEvents.clear();
+ }
+ }
+
+ public int getEnrollmentSteps() {
+ return mEnrollmentSteps;
+ }
+
+ public int getEnrollmentRemaining() {
+ return mEnrollmentRemaining;
+ }
+
+ public boolean isDone() {
+ return mDone;
+ }
+
+ public boolean isEnrolling() {
+ return mEnrolling;
+ }
+}
diff --git a/src/com/android/settings/biometrics/BiometricErrorDialog.java b/src/com/android/settings/biometrics/BiometricErrorDialog.java
new file mode 100644
index 0000000..4e073f1
--- /dev/null
+++ b/src/com/android/settings/biometrics/BiometricErrorDialog.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static com.android.settings.biometrics.BiometricSettings.RESULT_FINISHED;
+import static com.android.settings.biometrics.BiometricSettings.RESULT_TIMEOUT;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Bundle;
+
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Abstract dialog, shown when an error occurs during biometric enrollment.
+ */
+public abstract class BiometricErrorDialog extends InstrumentedDialogFragment {
+
+ public static final String KEY_ERROR_MSG = "error_msg";
+ public static final String KEY_ERROR_ID = "error_id";
+
+ public abstract int getTitleResId();
+ public abstract int getOkButtonTextResId();
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ CharSequence errorString = getArguments().getCharSequence(KEY_ERROR_MSG);
+ final int errMsgId = getArguments().getInt(KEY_ERROR_ID);
+
+ builder.setTitle(getTitleResId())
+ .setMessage(errorString)
+ .setCancelable(false)
+ .setPositiveButton(getOkButtonTextResId(),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ boolean wasTimeout =
+ errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
+ Activity activity = getActivity();
+ activity.setResult(wasTimeout ?
+ RESULT_TIMEOUT : RESULT_FINISHED);
+ activity.finish();
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+}
diff --git a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
new file mode 100644
index 0000000..ab3cd84
--- /dev/null
+++ b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.content.Intent;
+import android.os.UserHandle;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.password.ChooseLockSettingsHelper;
+
+/**
+ * Abstract base activity which handles the actual enrolling for biometrics.
+ */
+public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase
+ implements BiometricEnrollSidecar.Listener {
+
+ private static final String TAG_SIDECAR = "sidecar";
+
+ protected BiometricEnrollSidecar mSidecar;
+
+ /**
+ * @return the intent for the finish activity
+ */
+ protected abstract Intent getFinishIntent();
+
+ /**
+ * @return an instance of the biometric enroll sidecar
+ */
+ protected abstract BiometricEnrollSidecar getSidecar();
+
+ /**
+ * @return true if enrollment should start automatically.
+ */
+ protected abstract boolean shouldStartAutomatically();
+
+ /**
+ * @return true if enrollment should finish when onStop is called.
+ */
+ protected boolean shouldFinishOnStop() {
+ return true;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (shouldStartAutomatically()) {
+ startEnrollment();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mSidecar != null) {
+ mSidecar.setListener(null);
+ }
+
+ if (shouldFinishOnStop() && !isChangingConfigurations()) {
+ if (mSidecar != null) {
+ mSidecar.cancelEnrollment();
+ getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
+ }
+ finish();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mSidecar != null) {
+ mSidecar.setListener(null);
+ mSidecar.cancelEnrollment();
+ getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
+ mSidecar = null;
+ }
+ super.onBackPressed();
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.skip_button:
+ setResult(RESULT_SKIP);
+ finish();
+ break;
+ default:
+ super.onClick(v);
+ }
+ }
+
+ public void startEnrollment() {
+ mSidecar = (BiometricEnrollSidecar) getFragmentManager().findFragmentByTag(TAG_SIDECAR);
+ if (mSidecar == null) {
+ mSidecar = getSidecar();
+ getFragmentManager().beginTransaction().add(mSidecar, TAG_SIDECAR).commit();
+ }
+ mSidecar.setListener(this);
+ }
+
+ protected void launchFinish(byte[] token) {
+ Intent intent = getFinishIntent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+ if (mUserId != UserHandle.USER_NULL) {
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ }
+ startActivity(intent);
+ overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
+ finish();
+ }
+
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
new file mode 100644
index 0000000..ad92c8d
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 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.Intent;
+import android.hardware.face.FaceManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricEnrollSidecar;
+import com.android.settings.biometrics.BiometricsEnrollEnrolling;
+import com.android.settings.biometrics.BiometricErrorDialog;
+import com.android.settings.password.ChooseLockSettingsHelper;
+
+
+public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
+
+ private static final String TAG = "FaceEnrollEnrolling";
+ private static final boolean DEBUG = true;
+
+ private TextView mErrorText;
+ private Interpolator mLinearOutSlowInInterpolator;
+ private boolean mShouldFinishOnStop = true;
+
+ public static class FaceErrorDialog extends BiometricErrorDialog {
+ static FaceErrorDialog newInstance(CharSequence msg, int msgId) {
+ FaceErrorDialog dialog = new FaceErrorDialog();
+ Bundle args = new Bundle();
+ args.putCharSequence(KEY_ERROR_MSG, msg);
+ args.putInt(KEY_ERROR_ID, msgId);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DIALOG_FACE_ERROR;
+ }
+
+ @Override
+ public int getTitleResId() {
+ return R.string.security_settings_face_enroll_error_dialog_title;
+ }
+
+ @Override
+ public int getOkButtonTextResId() {
+ return R.string.security_settings_face_enroll_dialog_ok;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.face_enroll_enrolling);
+ setHeaderText(R.string.security_settings_face_enroll_repeat_title);
+ mErrorText = findViewById(R.id.error_text);
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+ this, android.R.interpolator.linear_out_slow_in);
+
+ Button skipButton = findViewById(R.id.skip_button);
+ skipButton.setOnClickListener(this);
+
+ if (shouldLaunchConfirmLock()) {
+ launchConfirmLock(R.string.security_settings_face_preference_title,
+ Utils.getFaceManagerOrNull(this).preEnroll());
+ mShouldFinishOnStop = false;
+ } else {
+ startEnrollment();
+ }
+ }
+
+ @Override
+ protected Intent getFinishIntent() {
+ return new Intent(this, FaceEnrollFinish.class);
+ }
+
+ @Override
+ protected BiometricEnrollSidecar getSidecar() {
+ return new FaceEnrollSidecar();
+ }
+
+ @Override
+ protected boolean shouldStartAutomatically() {
+ return false;
+ }
+
+ @Override
+ protected boolean shouldFinishOnStop() {
+ return mShouldFinishOnStop;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.FACE_ENROLL_ENROLLING;
+ }
+
+ @Override
+ public void onEnrollmentHelp(CharSequence helpString) {
+ if (!TextUtils.isEmpty(helpString)) {
+ showError(helpString);
+ }
+ }
+
+ @Override
+ public void onEnrollmentError(int errMsgId, CharSequence errString) {
+ int msgId;
+ switch (errMsgId) {
+ case FaceManager.FACE_ERROR_TIMEOUT:
+ msgId = R.string.security_settings_face_enroll_error_timeout_dialog_message;
+ break;
+ default:
+ msgId = R.string.security_settings_face_enroll_error_generic_dialog_message;
+ break;
+ }
+ showErrorDialog(getText(msgId), errMsgId);
+ }
+
+ @Override
+ public void onEnrollmentProgressChange(int steps, int remaining) {
+ if (DEBUG) {
+ Log.v(TAG, "Steps: " + steps + " Remaining: " + remaining);
+ }
+ // TODO: Update the actual animation
+ showError("Steps: " + steps + " Remaining: " + remaining);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == CONFIRM_REQUEST) {
+ if (resultCode == RESULT_OK && data != null) {
+ mShouldFinishOnStop = true;
+ mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
+ overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
+ getIntent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
+ startEnrollment();
+ } else {
+ finish();
+ }
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ private void showErrorDialog(CharSequence msg, int msgId) {
+ BiometricErrorDialog dialog = FaceErrorDialog.newInstance(msg, msgId);
+ dialog.show(getFragmentManager(), FaceErrorDialog.class.getName());
+ }
+
+ private void showError(CharSequence error) {
+ mErrorText.setText(error);
+ if (mErrorText.getVisibility() == View.INVISIBLE) {
+ mErrorText.setVisibility(View.VISIBLE);
+ mErrorText.setTranslationY(getResources().getDimensionPixelSize(
+ R.dimen.fingerprint_error_text_appear_distance));
+ mErrorText.setAlpha(0f);
+ mErrorText.animate()
+ .alpha(1f)
+ .translationY(0f)
+ .setDuration(200)
+ .setInterpolator(mLinearOutSlowInInterpolator)
+ .start();
+ } else {
+ mErrorText.animate().cancel();
+ mErrorText.setAlpha(1f);
+ mErrorText.setTranslationY(0f);
+ }
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollFinish.java b/src/com/android/settings/biometrics/face/FaceEnrollFinish.java
new file mode 100644
index 0000000..7062fe3
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollFinish.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.biometrics.BiometricEnrollBase;
+
+/**
+ * Activity which concludes face enrollment.
+ */
+public class FaceEnrollFinish extends BiometricEnrollBase {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.face_enroll_finish);
+ setHeaderText(R.string.security_settings_face_enroll_finish_title);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.FACE_ENROLL_FINISHED;
+ }
+
+ @Override
+ public void onNextButtonClick() {
+ setResult(RESULT_FINISHED);
+ finish();
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index b4a33f3..cd04b22 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -113,8 +113,8 @@
}
@Override
- protected Intent getFindSensorIntent() {
- return null; // TODO
+ protected Intent getEnrollingIntent() {
+ return new Intent(this, FaceEnrollEnrolling.class);
}
@Override
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
new file mode 100644
index 0000000..7b445e5
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.app.Activity;
+import android.hardware.face.FaceManager;
+import android.os.UserHandle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricEnrollSidecar;
+
+/**
+ * Sidecar fragment to handle the state around face enrollment
+ */
+public class FaceEnrollSidecar extends BiometricEnrollSidecar {
+
+ private FaceManager mFaceManager;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mFaceManager = Utils.getFaceManagerOrNull(activity);
+ }
+
+ @Override
+ public void startEnrollment() {
+ super.startEnrollment();
+ if (mUserId != UserHandle.USER_NULL) {
+ mFaceManager.setActiveUser(mUserId);
+ }
+ mFaceManager.enroll(mToken, mEnrollmentCancel,
+ 0 /* flags */, mUserId, mEnrollmentCallback);
+ }
+
+ private FaceManager.EnrollmentCallback mEnrollmentCallback
+ = new FaceManager.EnrollmentCallback() {
+
+ @Override
+ public void onEnrollmentProgress(int remaining) {
+ FaceEnrollSidecar.super.onEnrollmentProgress(remaining);
+ }
+
+ @Override
+ public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+ FaceEnrollSidecar.super.onEnrollmentHelp(helpMsgId, helpString);
+ }
+
+ @Override
+ public void onEnrollmentError(int errMsgId, CharSequence errString) {
+ FaceEnrollSidecar.super.onEnrollmentError(errMsgId, errString);
+ }
+ };
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.FACE_ENROLL_SIDECAR;
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 9b0d1a6..3d4c5d4 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
@@ -30,7 +29,6 @@
import android.hardware.fingerprint.FingerprintManager;
import android.media.AudioAttributes;
import android.os.Bundle;
-import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.TextUtils;
@@ -44,15 +42,15 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
-import com.android.settings.biometrics.BiometricEnrollBase;
+import com.android.settings.biometrics.BiometricEnrollSidecar;
+import com.android.settings.biometrics.BiometricErrorDialog;
+import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settings.password.ChooseLockSettingsHelper;
/**
* Activity which handles the actual enrolling for fingerprint.
*/
-public class FingerprintEnrollEnrolling extends BiometricEnrollBase
- implements FingerprintEnrollSidecar.Listener {
+public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
static final String TAG_SIDECAR = "sidecar";
@@ -93,13 +91,38 @@
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
private int mIconTouchCount;
- private FingerprintEnrollSidecar mSidecar;
private boolean mAnimationCancelled;
private AnimatedVectorDrawable mIconAnimationDrawable;
private AnimatedVectorDrawable mIconBackgroundBlinksDrawable;
private boolean mRestoring;
private Vibrator mVibrator;
+ public static class FingerprintErrorDialog extends BiometricErrorDialog {
+ static FingerprintErrorDialog newInstance(CharSequence msg, int msgId) {
+ FingerprintErrorDialog dialog = new FingerprintErrorDialog();
+ Bundle args = new Bundle();
+ args.putCharSequence(KEY_ERROR_MSG, msg);
+ args.putInt(KEY_ERROR_ID, msgId);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.DIALOG_FINGERPINT_ERROR;
+ }
+
+ @Override
+ public int getTitleResId() {
+ return R.string.security_settings_fingerprint_enroll_error_dialog_title;
+ }
+
+ @Override
+ public int getOkButtonTextResId() {
+ return R.string.security_settings_fingerprint_enroll_dialog_ok;
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -148,14 +171,18 @@
}
@Override
+ protected BiometricEnrollSidecar getSidecar() {
+ return new FingerprintEnrollSidecar();
+ }
+
+ @Override
+ protected boolean shouldStartAutomatically() {
+ return true;
+ }
+
+ @Override
protected void onStart() {
super.onStart();
- mSidecar = (FingerprintEnrollSidecar) getFragmentManager().findFragmentByTag(TAG_SIDECAR);
- if (mSidecar == null) {
- mSidecar = new FingerprintEnrollSidecar();
- getFragmentManager().beginTransaction().add(mSidecar, TAG_SIDECAR).commit();
- }
- mSidecar.setListener(this);
updateProgress(false /* animate */);
updateDescription();
if (mRestoring) {
@@ -182,40 +209,7 @@
@Override
protected void onStop() {
super.onStop();
- if (mSidecar != null) {
- mSidecar.setListener(null);
- }
stopIconAnimation();
- if (!isChangingConfigurations()) {
- if (mSidecar != null) {
- mSidecar.cancelEnrollment();
- getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
- }
- finish();
- }
- }
-
- @Override
- public void onBackPressed() {
- if (mSidecar != null) {
- mSidecar.setListener(null);
- mSidecar.cancelEnrollment();
- getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
- mSidecar = null;
- }
- super.onBackPressed();
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.skip_button:
- setResult(RESULT_SKIP);
- finish();
- break;
- default:
- super.onClick(v);
- }
}
private void animateProgress(int progress) {
@@ -235,20 +229,6 @@
mIconBackgroundBlinksDrawable.start();
}
- private void launchFinish(byte[] token) {
- Intent intent = getFinishIntent();
- intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
- | Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
- if (mUserId != UserHandle.USER_NULL) {
- intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
- }
- startActivity(intent);
- overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
- finish();
- }
-
protected Intent getFinishIntent() {
return new Intent(this, FingerprintEnrollFinish.class);
}
@@ -263,7 +243,6 @@
}
}
-
@Override
public void onEnrollmentHelp(CharSequence helpString) {
if (!TextUtils.isEmpty(helpString)) {
@@ -323,8 +302,8 @@
}
private void showErrorDialog(CharSequence msg, int msgId) {
- ErrorDialog dlg = ErrorDialog.newInstance(msg, msgId);
- dlg.show(getFragmentManager(), ErrorDialog.class.getName());
+ BiometricErrorDialog dlg = FingerprintErrorDialog.newInstance(msg, msgId);
+ dlg.show(getFragmentManager(), FingerprintErrorDialog.class.getName());
}
private void showIconTouchDialog() {
@@ -455,54 +434,4 @@
return MetricsEvent.DIALOG_FINGERPRINT_ICON_TOUCH;
}
}
-
- public static class ErrorDialog extends InstrumentedDialogFragment {
-
- /**
- * Create a new instance of ErrorDialog.
- *
- * @param msg the string to show for message text
- * @param msgId the FingerprintManager error id so we know the cause
- * @return a new ErrorDialog
- */
- static ErrorDialog newInstance(CharSequence msg, int msgId) {
- ErrorDialog dlg = new ErrorDialog();
- Bundle args = new Bundle();
- args.putCharSequence("error_msg", msg);
- args.putInt("error_id", msgId);
- dlg.setArguments(args);
- return dlg;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- CharSequence errorString = getArguments().getCharSequence("error_msg");
- final int errMsgId = getArguments().getInt("error_id");
- builder.setTitle(R.string.security_settings_fingerprint_enroll_error_dialog_title)
- .setMessage(errorString)
- .setCancelable(false)
- .setPositiveButton(R.string.security_settings_fingerprint_enroll_dialog_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- boolean wasTimeout =
- errMsgId == FingerprintManager.FINGERPRINT_ERROR_TIMEOUT;
- Activity activity = getActivity();
- activity.setResult(wasTimeout ?
- RESULT_TIMEOUT : RESULT_FINISHED);
- activity.finish();
- }
- });
- AlertDialog dialog = builder.create();
- dialog.setCanceledOnTouchOutside(false);
- return dialog;
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.DIALOG_FINGERPINT_ERROR;
- }
- }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 047dda8..93a8d6e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -19,16 +19,14 @@
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
-import android.os.UserHandle;
import android.view.View;
import android.widget.Button;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
-import com.android.settings.biometrics.fingerprint.FingerprintEnrollSidecar.Listener;
+import com.android.settings.biometrics.BiometricEnrollSidecar.Listener;
import com.android.settings.password.ChooseLockSettingsHelper;
import androidx.annotation.Nullable;
@@ -38,14 +36,9 @@
*/
public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
- @VisibleForTesting
- static final int CONFIRM_REQUEST = 1;
- private static final int ENROLLING = 2;
- public static final String EXTRA_KEY_LAUNCHED_CONFIRM = "launched_confirm_lock";
-
@Nullable
private FingerprintFindSensorAnimation mAnimation;
- private boolean mLaunchedConfirmLock;
+
private FingerprintEnrollSidecar mSidecar;
private boolean mNextClicked;
@@ -57,13 +50,10 @@
skipButton.setOnClickListener(this);
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
- if (savedInstanceState != null) {
- mLaunchedConfirmLock = savedInstanceState.getBoolean(EXTRA_KEY_LAUNCHED_CONFIRM);
- mToken = savedInstanceState.getByteArray(
- ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
- }
- if (mToken == null && !mLaunchedConfirmLock) {
- launchConfirmLock();
+
+ if (shouldLaunchConfirmLock()) {
+ launchConfirmLock(R.string.security_settings_fingerprint_preference_title,
+ Utils.getFingerprintManagerOrNull(this).preEnroll());
} else if (mToken != null) {
startLookingForFingerprint(); // already confirmed, so start looking for fingerprint
}
@@ -133,13 +123,6 @@
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(EXTRA_KEY_LAUNCHED_CONFIRM, mLaunchedConfirmLock);
- outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
- }
-
- @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.skip_button:
@@ -209,28 +192,6 @@
}
}
- private void launchConfirmLock() {
- long challenge = Utils.getFingerprintManagerOrNull(this).preEnroll();
- ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
- boolean launchedConfirmationActivity = false;
- if (mUserId == UserHandle.USER_NULL) {
- launchedConfirmationActivity = helper.launchConfirmationActivity(CONFIRM_REQUEST,
- getString(R.string.security_settings_fingerprint_preference_title),
- null, null, challenge);
- } else {
- launchedConfirmationActivity = helper.launchConfirmationActivity(CONFIRM_REQUEST,
- getString(R.string.security_settings_fingerprint_preference_title),
- null, null, challenge, mUserId);
- }
- if (!launchedConfirmationActivity) {
- // This shouldn't happen, as we should only end up at this step if a lock thingy is
- // already set.
- finish();
- } else {
- mLaunchedConfirmLock = true;
- }
- }
-
@Override
public int getMetricsCategory() {
return MetricsEvent.FINGERPRINT_FIND_SENSOR;
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index ed111f4..41bf86f 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -117,7 +117,7 @@
}
@Override
- protected Intent getFindSensorIntent() {
+ protected Intent getEnrollingIntent() {
return new Intent(this, FingerprintEnrollFindSensor.class);
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
index af56310..27d71cd 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
@@ -16,164 +16,35 @@
package com.android.settings.biometrics.fingerprint;
-import android.annotation.Nullable;
import android.app.Activity;
-import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Handler;
import android.os.UserHandle;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedFragment;
-import com.android.settings.password.ChooseLockSettingsHelper;
-
-import java.util.ArrayList;
+import com.android.settings.biometrics.BiometricEnrollSidecar;
/**
* Sidecar fragment to handle the state around fingerprint enrollment.
*/
-public class FingerprintEnrollSidecar extends InstrumentedFragment {
+public class FingerprintEnrollSidecar extends BiometricEnrollSidecar {
- private int mEnrollmentSteps = -1;
- private int mEnrollmentRemaining = 0;
- private Listener mListener;
- private boolean mEnrolling;
- private CancellationSignal mEnrollmentCancel;
- private Handler mHandler = new Handler();
- private byte[] mToken;
- private boolean mDone;
- private int mUserId;
private FingerprintManager mFingerprintManager;
- private ArrayList<QueuedEvent> mQueuedEvents;
-
- private abstract class QueuedEvent {
- public abstract void send(Listener listener);
- }
-
- private class QueuedEnrollmentProgress extends QueuedEvent {
- int enrollmentSteps;
- int remaining;
- public QueuedEnrollmentProgress(int enrollmentSteps, int remaining) {
- this.enrollmentSteps = enrollmentSteps;
- this.remaining = remaining;
- }
-
- @Override
- public void send(Listener listener) {
- listener.onEnrollmentProgressChange(enrollmentSteps, remaining);
- }
- }
-
- private class QueuedEnrollmentHelp extends QueuedEvent {
- int helpMsgId;
- CharSequence helpString;
- public QueuedEnrollmentHelp(int helpMsgId, CharSequence helpString) {
- this.helpMsgId = helpMsgId;
- this.helpString = helpString;
- }
-
- @Override
- public void send(Listener listener) {
- listener.onEnrollmentHelp(helpString);
- }
- }
-
- private class QueuedEnrollmentError extends QueuedEvent {
- int errMsgId;
- CharSequence errString;
- public QueuedEnrollmentError(int errMsgId, CharSequence errString) {
- this.errMsgId = errMsgId;
- this.errString = errString;
- }
-
- @Override
- public void send(Listener listener) {
- listener.onEnrollmentError(errMsgId, errString);
- }
- }
-
- public FingerprintEnrollSidecar() {
- mQueuedEvents = new ArrayList<>();
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- }
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mFingerprintManager = Utils.getFingerprintManagerOrNull(activity);
- mToken = activity.getIntent().getByteArrayExtra(
- ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
- mUserId = activity.getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
}
@Override
- public void onStart() {
- super.onStart();
- if (!mEnrolling) {
- startEnrollment();
- }
- }
-
- @Override
- public void onStop() {
- super.onStop();
- if (!getActivity().isChangingConfigurations()) {
- cancelEnrollment();
- }
- }
-
- private void startEnrollment() {
- mHandler.removeCallbacks(mTimeoutRunnable);
- mEnrollmentSteps = -1;
- mEnrollmentCancel = new CancellationSignal();
+ protected void startEnrollment() {
+ super.startEnrollment();
if (mUserId != UserHandle.USER_NULL) {
mFingerprintManager.setActiveUser(mUserId);
}
mFingerprintManager.enroll(mToken, mEnrollmentCancel,
0 /* flags */, mUserId, mEnrollmentCallback);
- mEnrolling = true;
- }
-
- boolean cancelEnrollment() {
- mHandler.removeCallbacks(mTimeoutRunnable);
- if (mEnrolling) {
- mEnrollmentCancel.cancel();
- mEnrolling = false;
- mEnrollmentSteps = -1;
- return true;
- }
- return false;
- }
-
- public void setListener(Listener listener) {
- mListener = listener;
- if (mListener != null) {
- for (int i=0; i<mQueuedEvents.size(); i++) {
- QueuedEvent event = mQueuedEvents.get(i);
- event.send(mListener);
- }
- mQueuedEvents.clear();
- }
- }
-
- public int getEnrollmentSteps() {
- return mEnrollmentSteps;
- }
-
- public int getEnrollmentRemaining() {
- return mEnrollmentRemaining;
- }
-
- public boolean isDone() {
- return mDone;
}
private FingerprintManager.EnrollmentCallback mEnrollmentCallback
@@ -181,42 +52,17 @@
@Override
public void onEnrollmentProgress(int remaining) {
- if (mEnrollmentSteps == -1) {
- mEnrollmentSteps = remaining;
- }
- mEnrollmentRemaining = remaining;
- mDone = remaining == 0;
- if (mListener != null) {
- mListener.onEnrollmentProgressChange(mEnrollmentSteps, remaining);
- } else {
- mQueuedEvents.add(new QueuedEnrollmentProgress(mEnrollmentSteps, remaining));
- }
+ FingerprintEnrollSidecar.super.onEnrollmentProgress(remaining);
}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
- if (mListener != null) {
- mListener.onEnrollmentHelp(helpString);
- } else {
- mQueuedEvents.add(new QueuedEnrollmentHelp(helpMsgId, helpString));
- }
+ FingerprintEnrollSidecar.super.onEnrollmentHelp(helpMsgId, helpString);
}
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
- if (mListener != null) {
- mListener.onEnrollmentError(errMsgId, errString);
- } else {
- mQueuedEvents.add(new QueuedEnrollmentError(errMsgId, errString));
- }
- mEnrolling = false;
- }
- };
-
- private final Runnable mTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- cancelEnrollment();
+ FingerprintEnrollSidecar.super.onEnrollmentError(errMsgId, errString);
}
};
@@ -224,14 +70,4 @@
public int getMetricsCategory() {
return MetricsEvent.FINGERPRINT_ENROLL_SIDECAR;
}
-
- public interface Listener {
- void onEnrollmentHelp(CharSequence helpString);
- void onEnrollmentError(int errMsgId, CharSequence errString);
- void onEnrollmentProgressChange(int steps, int remaining);
- }
-
- public boolean isEnrolling() {
- return mEnrolling;
- }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java
index 9fcbbf5..6ffc096 100644
--- a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -70,7 +70,7 @@
}
@Override
- protected Intent getFindSensorIntent() {
+ protected Intent getEnrollingIntent() {
final Intent intent = new Intent(this, SetupFingerprintEnrollFindSensor.class);
SetupWizardUtils.copySetupExtras(getIntent(), intent);
return intent;
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
index 70a68ec..147ed1d 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
@@ -157,7 +157,7 @@
@Test
public void onActivityResult_withNullIntentShouldNotCrash() {
// this should not crash
- mActivity.onActivityResult(FingerprintEnrollFindSensor.CONFIRM_REQUEST, Activity.RESULT_OK,
+ mActivity.onActivityResult(BiometricEnrollBase.CONFIRM_REQUEST, Activity.RESULT_OK,
null);
assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(Activity.RESULT_CANCELED);
}