Update fingerprint enroll UI to spec.

Also adds sliding animations between the steps.

Bug: 20495507
Change-Id: I148d8d8b8082361149f02fd2dc4cead0ba3e2526
This commit is contained in:
Jorim Jaggi
2015-04-22 16:17:23 -07:00
parent 5c012ec482
commit 5ad75f07b1
39 changed files with 1285 additions and 766 deletions

View File

@@ -28,9 +28,9 @@ public final class ChooseLockSettingsHelper {
static final String EXTRA_KEY_TYPE = "type";
static final String EXTRA_KEY_PASSWORD = "password";
static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge";
static final String EXTRA_KEY_CHALLENGE = "challenge";
static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
public static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge";
public static final String EXTRA_KEY_CHALLENGE = "challenge";
public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
private LockPatternUtils mLockPatternUtils;
@@ -104,7 +104,7 @@ public final class ChooseLockSettingsHelper {
* @return true if one exists and we launched an activity to confirm it
* @see #onActivityResult(int, int, android.content.Intent)
*/
boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
public boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
@Nullable CharSequence header, @Nullable CharSequence description,
long challenge) {
return launchConfirmationActivity(request, title, header, description,

View File

@@ -17,13 +17,14 @@
package com.android.settings;
import android.annotation.Nullable;
import android.app.Fragment;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settings.fingerprint.FingerprintUiHelper;
/**
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
*/

View File

@@ -1,460 +0,0 @@
/*
* Copyright (C) 2015 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;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.PowerManager;
import android.os.SystemClock;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.LockPatternUtils;
/**
* Wizard to enroll a fingerprint
*/
public class FingerprintEnroll extends SettingsActivity {
/**
* Used by the choose fingerprint wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
* <p>
* Previously, each activity in the wizard would finish itself after
* starting the next activity. However, this leads to broken 'Back'
* behavior. So, now an activity does not finish itself until it gets this
* result.
*/
static final int RESULT_FINISHED = RESULT_FIRST_USER;
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, FingerprintEnrollFragment.class.getName());
return modIntent;
}
@Override
protected boolean isValidFragment(String fragmentName) {
if (FingerprintEnrollFragment.class.getName().equals(fragmentName)) return true;
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
CharSequence msg = getText(R.string.security_settings_fingerprint_preference_title);
setTitle(msg);
}
public static class FingerprintEnrollFragment extends InstrumentedFragment
implements View.OnClickListener {
private static final String EXTRA_PROGRESS = "progress";
private static final String EXTRA_STAGE = "stage";
private static final int PROGRESS_BAR_MAX = 10000;
private static final String TAG = "FingerprintEnroll";
private static final boolean DEBUG = true;
private static final int CONFIRM_REQUEST = 101;
private static final int CHOOSE_LOCK_GENERIC_REQUEST = 102;
private static final int FINISH_DELAY = 250;
private PowerManager mPowerManager;
private FingerprintManager mFingerprintManager;
private View mContentView;
private TextView mTitleText;
private TextView mMessageText;
private Stage mStage;
private int mEnrollmentSteps;
private boolean mEnrolling;
private ProgressBar mProgressBar;
private ImageView mFingerprintAnimator;
private ObjectAnimator mProgressAnim;
private byte[] mToken;
// Give the user a chance to see progress completed before jumping to the next stage.
Runnable mDelayedFinishRunnable = new Runnable() {
@Override
public void run() {
updateStage(Stage.EnrollingFinish);
}
};
private final AnimatorListener mProgressAnimationListener = new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { }
@Override
public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
mContentView.postDelayed(mDelayedFinishRunnable, FINISH_DELAY);
}
}
@Override
public void onAnimationCancel(Animator animation) { }
};
private CancellationSignal mEnrollmentCancel = new CancellationSignal();
// This contains a list of all views managed by the UI. Used to determine which views
// need to be shown/hidden at each stage. It should be the union of the lists that follow
private static final int MANAGED_VIEWS[] = {
R.id.fingerprint_sensor_location,
R.id.fingerprint_animator,
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_in_app_indicator,
R.id.fingerprint_enroll_button_add,
R.id.fingerprint_enroll_button_next,
R.id.fingerprint_progress_bar
};
private static final int VIEWS_ENROLL_ONBOARD[] = {
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_enroll_button_next
};
private static final int VIEWS_ENROLL_FIND_SENSOR[] = {
R.id.fingerprint_sensor_location,
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_enroll_button_next
};
private static final int VIEWS_ENROLL_START[] = {
R.id.fingerprint_animator,
};
private static final int VIEWS_ENROLL_REPEAT[] = {
R.id.fingerprint_animator,
R.id.fingerprint_progress_bar
};
private static final int VIEWS_ENROLL_FINISH[] = {
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_in_app_indicator,
R.id.fingerprint_enroll_button_add,
R.id.fingerprint_enroll_button_next
};
private static final boolean ALWAYS_SHOW_FIND_SCREEN = true;
@Override
protected int getMetricsCategory() {
return MetricsLogger.FINGERPRINT_ENROLL;
}
private enum Stage {
EnrollingOnboard(R.string.security_settings_fingerprint_enroll_onboard_title,
R.string.security_settings_fingerprint_enroll_onboard_message,
VIEWS_ENROLL_ONBOARD),
EnrollingFindSensor(R.string.security_settings_fingerprint_enroll_find_sensor_title,
R.string.security_settings_fingerprint_enroll_find_sensor_message,
VIEWS_ENROLL_FIND_SENSOR),
EnrollingStart(R.string.security_settings_fingerprint_enroll_start_title,
R.string.security_settings_fingerprint_enroll_start_message,
VIEWS_ENROLL_START),
EnrollingRepeat(R.string.security_settings_fingerprint_enroll_repeat_title,
R.string.security_settings_fingerprint_enroll_repeat_message,
VIEWS_ENROLL_REPEAT),
EnrollingFinish(R.string.security_settings_fingerprint_enroll_finish_title,
R.string.security_settings_fingerprint_enroll_finish_message,
VIEWS_ENROLL_FINISH);
Stage(int title, int message, int[] enabledViewIds) {
this.title = title;
this.message = message;
this.enabledViewIds = enabledViewIds;
}
public int title;
public int message;
public int[] enabledViewIds;
};
void updateStage(Stage stage) {
if (DEBUG) Log.v(TAG, "updateStage(" + stage.toString() + ")");
// Show/hide views
for (int i = 0; i < MANAGED_VIEWS.length; i++) {
mContentView.findViewById(MANAGED_VIEWS[i]).setVisibility(View.INVISIBLE);
}
for (int i = 0; i < stage.enabledViewIds.length; i++) {
mContentView.findViewById(stage.enabledViewIds[i]).setVisibility(View.VISIBLE);
}
setTitleMessage(stage.title);
setMessage(stage.message);
if (mStage != stage) {
onStageChanged(stage);
mStage = stage;
}
}
private void startFingerprintAnimator() {
final Drawable d = mFingerprintAnimator.getDrawable();
if (d instanceof AnimationDrawable) {
((AnimationDrawable) d).start();
}
}
private void stopFingerprintAnimator() {
final Drawable d = mFingerprintAnimator.getDrawable();
if (d instanceof AnimationDrawable) {
final AnimationDrawable drawable = (AnimationDrawable) d;
drawable.stop();
drawable.setLevel(0);
}
}
private void onStageChanged(Stage stage) {
// Update state
switch (stage) {
case EnrollingOnboard: // pass through
case EnrollingFindSensor:
mEnrollmentSteps = -1;
mEnrolling = false;
break;
case EnrollingStart:
mEnrollmentSteps = -1;
mFingerprintManager.enroll(mToken, mEnrollmentCancel, mEnrollmentCallback, 0);
mProgressBar.setProgress(0);
mEnrolling = true;
startFingerprintAnimator(); // XXX hack - this should follow fingerprint detection
break;
case EnrollingRepeat:
break;
case EnrollingFinish:
stopFingerprintAnimator(); // XXX hack - this should follow fingerprint detection
mEnrolling = false;
break;
default:
break;
}
}
private void cancelEnrollment() {
if (mEnrolling) {
if (DEBUG) Log.v(TAG, "Cancel enrollment\n");
mEnrollmentCancel.cancel();
mEnrolling = false;
}
}
@Override
public void onDetach() {
super.onDetach();
cancelEnrollment(); // Do a little cleanup
}
private void updateProgress(int progress) {
if (DEBUG) Log.v(TAG, "Progress: " + progress);
if (mProgressAnim != null) {
mProgressAnim.cancel();
}
ObjectAnimator anim = ObjectAnimator.ofInt(mProgressBar, "progress",
mProgressBar.getProgress(), progress);
anim.addListener(mProgressAnimationListener);
anim.start();
mProgressAnim = anim;
}
protected void setMessage(CharSequence msg) {
if (msg != null) mMessageText.setText(msg);
}
private void setMessage(int id) {
if (id != 0) mMessageText.setText(id);
}
private void setTitleMessage(int title) {
if (title != 0) mTitleText.setText(title);
}
private EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() {
@Override
public void onEnrollmentProgress(int remaining) {
if (DEBUG) Log.v(TAG, "onEnrollResult(id=" + ", rem=" + remaining);
if (mEnrollmentSteps == -1) {
mEnrollmentSteps = remaining;
updateStage(Stage.EnrollingRepeat);
}
if (remaining >= 0) {
int progress = Math.max(0, mEnrollmentSteps + 1 - remaining);
updateProgress(PROGRESS_BAR_MAX * progress / (mEnrollmentSteps + 1));
// Treat fingerprint like a touch event
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS);
}
}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
setMessage(helpString);
}
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
setMessage(errString);
}
};
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST
|| requestCode == CONFIRM_REQUEST) {
if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
// The lock pin/pattern/password was set. Start enrolling!
mToken = data.getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
updateStage(Stage.EnrollingFindSensor);
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final Activity activity = getActivity();
mFingerprintManager = (FingerprintManager)activity
.getSystemService(Context.FINGERPRINT_SERVICE);
mPowerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
mContentView = inflater.inflate(R.layout.fingerprint_enroll, null);
mTitleText = (TextView) mContentView.findViewById(R.id.fingerprint_enroll_title);
mMessageText = (TextView) mContentView.findViewById(R.id.fingerprint_enroll_message);
mProgressBar = (ProgressBar) mContentView.findViewById(R.id.fingerprint_progress_bar);
mFingerprintAnimator = (ImageView) mContentView.findViewById(R.id.fingerprint_animator);
final int buttons[] = {
R.id.fingerprint_enroll_button_add,
R.id.fingerprint_enroll_button_next };
for (int i = 0; i < buttons.length; i++) {
mContentView.findViewById(buttons[i]).setOnClickListener(this);
}
if (mToken == null) {
mToken = getActivity().getIntent().getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
}
if (mToken == null) {
// need to choose or confirm lock
updateStage(Stage.EnrollingOnboard);
} else if (ALWAYS_SHOW_FIND_SCREEN
|| mFingerprintManager.getEnrolledFingerprints().size() == 0) {
updateStage(Stage.EnrollingFindSensor);
} else {
updateStage(Stage.EnrollingStart);
}
return mContentView;
}
@Override
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_STAGE, mStage.toString());
outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
mToken);
if (mStage == Stage.EnrollingRepeat) {
outState.putInt(EXTRA_PROGRESS, mProgressBar.getProgress());
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
mToken = savedInstanceState.getByteArray(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
//probably orientation change
String stageSaved = savedInstanceState.getString(EXTRA_STAGE, null);
if (stageSaved != null) {
Stage stage = Stage.valueOf(stageSaved);
updateStage(stage);
if (stage == Stage.EnrollingRepeat) {
mProgressBar.setProgress(savedInstanceState.getInt(EXTRA_PROGRESS));
}
}
}
}
@Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.fingerprint_enroll_button_add:
updateStage(Stage.EnrollingStart);
break;
case R.id.fingerprint_enroll_button_next:
if (mStage == Stage.EnrollingOnboard) {
launchChooseOrConfirmLock();
} else if (mStage == Stage.EnrollingFindSensor) {
updateStage(Stage.EnrollingStart);
} else if (mStage == Stage.EnrollingFinish) {
getActivity().finish();
} else {
Log.v(TAG, "No idea what to do next!");
}
break;
}
}
private void launchChooseOrConfirmLock() {
Intent intent = new Intent();
long challenge = mFingerprintManager.preEnroll();
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);
if (!helper.launchConfirmationActivity(CONFIRM_REQUEST, null,
null, null, challenge)) {
intent.setClassName("com.android.settings", ChooseLockGeneric.class.getName());
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
}
}
}
}

View File

@@ -54,6 +54,9 @@ import android.util.Log;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.TrustAgentUtils.TrustAgentComponentInfo;
import com.android.settings.fingerprint.FingerprintEnrollFindSensor;
import com.android.settings.fingerprint.FingerprintEnrollOnboard;
import com.android.settings.fingerprint.FingerprintSettings;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Index;
import com.android.settings.search.Indexable;
@@ -345,14 +348,19 @@ public class SecuritySettings extends SettingsPreferenceFragment
final List<Fingerprint> items = fpm.getEnrolledFingerprints();
final int fingerprintCount = items != null ? items.size() : 0;
final String clazz;
boolean hasPassword = mChooseLockSettingsHelper.utils().getActivePasswordQuality()
!= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
if (fingerprintCount > 0) {
fingerprintPreference.setSummary(getResources().getQuantityString(
R.plurals.security_settings_fingerprint_preference_summary,
fingerprintCount, fingerprintCount));
clazz = FingerprintSettings.class.getName();
} else if (!hasPassword) {
// No fingerprints registered, launch into enrollment wizard.
clazz = FingerprintEnrollOnboard.class.getName();
} else {
// No fingerprints registered, launch directly into enrollment wizard
clazz = FingerprintEnroll.class.getName();
// Lock thingy is already set up, launch directly into find sensor step from wizard.
clazz = FingerprintEnrollFindSensor.class.getName();
}
intent.setClassName("com.android.settings", clazz);
fingerprintPreference.setIntent(intent);

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2015 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.fingerprint;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import com.android.settings.ChooseLockSettingsHelper;
import com.android.settings.R;
import com.android.setupwizardlib.SetupWizardLayout;
import com.android.setupwizardlib.view.NavigationBar;
/**
* Base activity for all fingerprint enrollment steps.
*/
public class FingerprintEnrollBase extends Activity implements View.OnClickListener {
/**
* Used by the choose fingerprint wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
* <p>
* Previously, each activity in the wizard would finish itself after
* starting the next activity. However, this leads to broken 'Back'
* behavior. So, now an activity does not finish itself until it gets this
* result.
*/
protected static final int RESULT_FINISHED = RESULT_FIRST_USER;
protected byte[] mToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.Theme_FingerprintEnroll);
mToken = getIntent().getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
if (savedInstanceState != null && mToken == null) {
mToken = savedInstanceState.getByteArray(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
getNavigationBar().setVisibility(View.GONE);
Button nextButton = getNextButton();
if (nextButton != null) {
nextButton.setOnClickListener(this);
}
}
protected NavigationBar getNavigationBar() {
return (NavigationBar) findViewById(R.id.suw_layout_navigation_bar);
}
protected SetupWizardLayout getSetupWizardLayout() {
return (SetupWizardLayout) findViewById(R.id.setup_wizard_layout);
}
protected void setHeaderText(int resId) {
getSetupWizardLayout().setHeaderText(getText(resId));
}
protected Button getNextButton() {
return (Button) findViewById(R.id.next_button);
}
@Override
public void onClick(View v) {
if (v == getNextButton()) {
onNextButtonClick();
}
}
protected void onNextButtonClick() {
}
protected Intent getEnrollingIntent() {
Intent intent = new Intent();
intent.setClassName("com.android.settings", FingerprintEnrollEnrolling.class.getName());
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
return intent;
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2015 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.fingerprint;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.PowerManager;
import android.os.SystemClock;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.settings.ChooseLockSettingsHelper;
import com.android.settings.R;
/**
* Activity which handles the actual enrolling for fingerprint.
*/
public class FingerprintEnrollEnrolling extends FingerprintEnrollBase {
private static final int PROGRESS_BAR_MAX = 10000;
private static final int FINISH_DELAY = 250;
private PowerManager mPowerManager;
private CancellationSignal mEnrollmentCancel = new CancellationSignal();
private int mEnrollmentSteps;
private boolean mEnrolling;
private ProgressBar mProgressBar;
private ImageView mFingerprintAnimator;
private ObjectAnimator mProgressAnim;
private TextView mStartMessage;
private TextView mRepeatMessage;
private TextView mErrorText;
private Interpolator mFastOutSlowInInterpolator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fingerprint_enroll_enrolling);
setHeaderText(R.string.security_settings_fingerprint_enroll_start_title);
mPowerManager = getSystemService(PowerManager.class);
mStartMessage = (TextView) findViewById(R.id.start_message);
mRepeatMessage = (TextView) findViewById(R.id.repeat_message);
mErrorText = (TextView) findViewById(R.id.error_text);
mProgressBar = (ProgressBar) findViewById(R.id.fingerprint_progress_bar);
mFingerprintAnimator = (ImageView) findViewById(R.id.fingerprint_animator);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
this, android.R.interpolator.fast_out_slow_in);
startEnrollment();
}
@Override
protected void onDestroy() {
super.onDestroy();
cancelEnrollment();
}
private void startEnrollment() {
mEnrollmentSteps = -1;
getSystemService(FingerprintManager.class).enroll(mToken, mEnrollmentCancel,
mEnrollmentCallback, 0);
mProgressBar.setProgress(0);
mEnrolling = true;
}
private void cancelEnrollment() {
if (mEnrolling) {
mEnrollmentCancel.cancel();
mEnrolling = false;
}
}
private void updateProgress(int progress) {
if (mProgressAnim != null) {
mProgressAnim.cancel();
}
ObjectAnimator anim = ObjectAnimator.ofInt(mProgressBar, "progress",
mProgressBar.getProgress(), progress);
anim.addListener(mProgressAnimationListener);
anim.setInterpolator(mFastOutSlowInInterpolator);
anim.setDuration(250);
anim.start();
mProgressAnim = anim;
}
private void launchFinish(byte[] token) {
Intent intent = new Intent();
intent.setClassName("com.android.settings", FingerprintEnrollFinish.class.getName());
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
startActivity(intent);
setResult(RESULT_FINISHED);
finish();
}
private void updateDescription() {
if (mEnrollmentSteps == -1) {
setHeaderText(R.string.security_settings_fingerprint_enroll_start_title);
mStartMessage.setVisibility(View.VISIBLE);
mRepeatMessage.setVisibility(View.INVISIBLE);
} else {
setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
mStartMessage.setVisibility(View.INVISIBLE);
mRepeatMessage.setVisibility(View.VISIBLE);
}
}
private final Animator.AnimatorListener mProgressAnimationListener
= new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { }
@Override
public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
mProgressBar.postDelayed(mDelayedFinishRunnable, FINISH_DELAY);
}
}
@Override
public void onAnimationCancel(Animator animation) { }
};
private FingerprintManager.EnrollmentCallback mEnrollmentCallback
= new FingerprintManager.EnrollmentCallback() {
@Override
public void onEnrollmentProgress(int remaining) {
if (mEnrollmentSteps == -1) {
mEnrollmentSteps = remaining;
updateDescription();
}
if (remaining >= 0) {
int progress = Math.max(0, mEnrollmentSteps + 1 - remaining);
updateProgress(PROGRESS_BAR_MAX * progress / (mEnrollmentSteps + 1));
// Treat fingerprint like a touch event
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS);
}
mErrorText.setText("");
}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
mErrorText.setText(helpString);
}
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
mErrorText.setText(errString);
}
};
// Give the user a chance to see progress completed before jumping to the next stage.
Runnable mDelayedFinishRunnable = new Runnable() {
@Override
public void run() {
launchFinish(mToken);
}
};
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2015 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.fingerprint;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import com.android.settings.ChooseLockSettingsHelper;
import com.android.settings.R;
/**
* Activity explaining the fingerprint sensor location for fingerprint enrollment.
*/
public class FingerprintEnrollFindSensor extends FingerprintEnrollBase {
private static final int CONFIRM_REQUEST = 1;
private static final int ENROLLING = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fingerprint_enroll_find_sensor);
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
if (mToken == null) {
launchConfirmLock();
}
}
@Override
protected void onNextButtonClick() {
startActivityForResult(getEnrollingIntent(), ENROLLING);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CONFIRM_REQUEST) {
if (resultCode == RESULT_OK) {
mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
} else {
finish();
}
} else if (requestCode == ENROLLING) {
if (resultCode == RESULT_FINISHED) {
finish();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void launchConfirmLock() {
long challenge = getSystemService(FingerprintManager.class).preEnroll();
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
if (!helper.launchConfirmationActivity(CONFIRM_REQUEST,
getString(R.string.security_settings_fingerprint_preference_title),
null, null, challenge)) {
// This shouldn't happen, as we should only end up at this step if a lock thingy is
// already set.
finish();
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2015 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.fingerprint;
import android.os.Bundle;
import android.view.View;
import com.android.settings.R;
/**
* Activity which concludes fingerprint enrollment.
*/
public class FingerprintEnrollFinish extends FingerprintEnrollBase {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fingerprint_enroll_finish);
setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title);
findViewById(R.id.add_another_button).setOnClickListener(this);
}
@Override
protected void onNextButtonClick() {
finish();
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.add_another_button) {
finish();
startActivity(getEnrollingIntent());
}
super.onClick(v);
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2015 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.fingerprint;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import com.android.settings.ChooseLockGeneric;
import com.android.settings.ChooseLockSettingsHelper;
import com.android.settings.R;
/**
* Onboarding activity for fingerprint enrollment.
*/
public class FingerprintEnrollOnboard extends FingerprintEnrollBase {
private static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fingerprint_enroll_onboard);
setHeaderText(R.string.security_settings_fingerprint_enroll_onboard_title);
}
@Override
protected void onNextButtonClick() {
launchChooseLock();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST && resultCode == RESULT_FINISHED) {
byte[] token = data.getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
launchFindSensor(token);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void launchChooseLock() {
Intent intent = new Intent();
long challenge = getSystemService(FingerprintManager.class).preEnroll();
intent.setClassName("com.android.settings", ChooseLockGeneric.class.getName());
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
}
private void launchFindSensor(byte[] token) {
Intent intent = new Intent();
intent.setClassName("com.android.settings", FingerprintEnrollFindSensor.class.getName());
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
startActivity(intent);
finish();
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.settings;
package com.android.settings.fingerprint;
import android.app.Activity;
@@ -43,6 +43,12 @@ import android.widget.EditText;
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.settings.ChooseLockGeneric;
import com.android.settings.ChooseLockSettingsHelper;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.search.Indexable;
import java.util.List;
@@ -323,7 +329,8 @@ public class FingerprintSettings extends SettingsActivity {
final String key = pref.getKey();
if (KEY_FINGERPRINT_ADD.equals(key)) {
Intent intent = new Intent();
intent.setClassName("com.android.settings", FingerprintEnroll.class.getName());
intent.setClassName("com.android.settings",
FingerprintEnrollEnrolling.class.getName());
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
stopFingerprint();
startActivityForResult(intent, ADD_FINGERPRINT_REQUEST);
@@ -410,9 +417,9 @@ public class FingerprintSettings extends SettingsActivity {
Intent intent = new Intent();
long challenge = mFingerprintManager.preEnroll();
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);
// TODO: update text or remove params from method
if (!helper.launchConfirmationActivity(CONFIRM_REQUEST, null,
null, null, challenge)) {
if (!helper.launchConfirmationActivity(CONFIRM_REQUEST,
getString(R.string.security_settings_fingerprint_preference_title),
null, null, challenge)) {
intent.setClassName("com.android.settings", ChooseLockGeneric.class.getName());
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);

View File

@@ -14,7 +14,7 @@
* limitations under the License
*/
package com.android.settings;
package com.android.settings.fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
@@ -23,6 +23,8 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settings.R;
/**
* Small helper class to manage text/icon around fingerprint authentication UI.
*/