diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index dba61d66443..9a3a1796d60 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1570,6 +1570,8 @@
android:theme="@style/GlifTheme.Light"/>
+
+
diff --git a/res/layout/face_enroll_enrolling.xml b/res/layout/face_enroll_enrolling.xml
new file mode 100644
index 00000000000..6ced80f0413
--- /dev/null
+++ b/res/layout/face_enroll_enrolling.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 00000000000..e3c58720c07
--- /dev/null
+++ b/res/layout/face_enroll_enrolling_footer.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/face_enroll_finish.xml b/res/layout/face_enroll_finish.xml
new file mode 100644
index 00000000000..9966497bcb4
--- /dev/null
+++ b/res/layout/face_enroll_finish.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/face_enroll_finish_footer.xml b/res/layout/face_enroll_finish_footer.xml
new file mode 100644
index 00000000000..06d263906f2
--- /dev/null
+++ b/res/layout/face_enroll_finish_footer.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4f4369eb077..a4377b0a8ba 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -893,12 +893,28 @@
Use your face to unlock your phone or approve purchases.\n\nNote: You can\u2019t use your face to unlock this device. For more information, contact your organization\u2019s admin.
Use your face to unlock your phone, authorize purchases, or sign in to apps
+
+ Center your face in the circle
+
+ Do it later
You can add up to %d faces
You\u2019ve added the maximum number of faces
Can\u2019t add more faces
+
+ Enrollment was not completed
+
+ OK
+
+ Face enrollment time limit reached. Try again.
+
+ Face enrollment didn\'t work.
+
+ All set. Looking good.
+
+ Done
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8026405a510..e8a60ad0294 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -357,6 +357,11 @@
- @drawable/ic_fingerprint_header
+
+
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index c49ddd4841d..298891e569f 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -30,29 +30,33 @@ import android.widget.TextView;
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 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity
@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 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity
initViews();
}
+ protected boolean shouldLaunchConfirmLock() {
+ return mToken == null && !mLaunchedConfirmLock;
+ }
+
protected void initViews() {
getWindow().setStatusBarColor(Color.TRANSPARENT);
Button nextButton = getNextButton();
@@ -129,4 +138,25 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity
}
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 98775f21e1d..beefb39759d 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -102,9 +102,11 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
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 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
}
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 00000000000..111fecd881e
--- /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 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;
-
- 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