Files
packages_apps_Settings/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
Maurice Lam da433902d4 [Fingerprint] Send RESULT_CANCELED when back from Add another
Forward the result in the enrolling screen from the finish screen,
so that if the user presses back in "Add another" after enrolling
the first fingerprint, it will return RESULT_CANCELED instead of
RESULT_FINISHED, and go back to find sensor screen.

This results in the following behavioral changes:
- RESULT_FINISHED will only be sent if the user presses "Done" in
  FingerprintEnrollFinish. If the user clicks back in
  FingerprintEnrollEnrolling, they will always go back to the
  previous screen with RESULT_CANCELED, which would either be
  FindSensor or FingerprintSettings.
- If the user presses back in FingerprintEnrollFinish, which is only
  possible outside of Setup Wizard, they will be back in Find Sensor,
  and pressing next will enroll an additional fingerprint.
  - Edge case: if the user enrolled the maximum number of
    fingerprints, and presses back, they will be bounced all the way
    back to Settings > Security.

Bug: 22552741
Change-Id: Ifc5e8a9150491b4303e01ebd0fc17b6d39dd372d
2015-07-21 15:23:11 -07:00

401 lines
15 KiB
Java

/*
* 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.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.MotionEvent;
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
implements FingerprintEnrollSidecar.Listener {
private static final String TAG_SIDECAR = "sidecar";
private static final int PROGRESS_BAR_MAX = 10000;
private static final int FINISH_DELAY = 250;
/**
* If we don't see progress during this time, we show an error message to remind the user that
* he needs to lift the finger and touch again.
*/
private static final int HINT_TIMEOUT_DURATION = 2500;
/**
* How long the user needs to touch the icon until we show the dialog.
*/
private static final long ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN = 500;
/**
* How many times the user needs to touch the icon until we show the dialog that this is not the
* fingerprint sensor.
*/
private static final int ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN = 3;
private ProgressBar mProgressBar;
private ImageView mFingerprintAnimator;
private ObjectAnimator mProgressAnim;
private TextView mStartMessage;
private TextView mRepeatMessage;
private TextView mErrorText;
private Interpolator mFastOutSlowInInterpolator;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
private int mIconTouchCount;
private FingerprintEnrollSidecar mSidecar;
private boolean mAnimationCancelled;
private AnimatedVectorDrawable mIconAnimationDrawable;
private int mIndicatorBackgroundRestingColor;
private int mIndicatorBackgroundActivatedColor;
private boolean mRestoring;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fingerprint_enroll_enrolling);
setHeaderText(R.string.security_settings_fingerprint_enroll_start_title);
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);
mIconAnimationDrawable = (AnimatedVectorDrawable) mFingerprintAnimator.getDrawable();
mIconAnimationDrawable.registerAnimationCallback(mIconAnimationCallback);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
this, android.R.interpolator.fast_out_slow_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
this, android.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
this, android.R.interpolator.fast_out_linear_in);
mFingerprintAnimator.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mIconTouchCount++;
if (mIconTouchCount == ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN) {
showIconTouchDialog();
} else {
mFingerprintAnimator.postDelayed(mShowDialogRunnable,
ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN);
}
} else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| event.getActionMasked() == MotionEvent.ACTION_UP) {
mFingerprintAnimator.removeCallbacks(mShowDialogRunnable);
}
return true;
}
});
mIndicatorBackgroundRestingColor
= getColor(R.color.fingerprint_indicator_background_resting);
mIndicatorBackgroundActivatedColor
= getColor(R.color.fingerprint_indicator_background_activated);
mRestoring = savedInstanceState != null;
}
@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) {
startIconAnimation();
}
}
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
mAnimationCancelled = false;
startIconAnimation();
}
private void startIconAnimation() {
mIconAnimationDrawable.start();
}
private void stopIconAnimation() {
mAnimationCancelled = true;
mIconAnimationDrawable.stop();
}
@Override
protected void onStop() {
super.onStop();
mSidecar.setListener(null);
stopIconAnimation();
if (!isChangingConfigurations()) {
finish();
}
}
private void animateProgress(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 animateFlash() {
ValueAnimator anim = ValueAnimator.ofArgb(mIndicatorBackgroundRestingColor,
mIndicatorBackgroundActivatedColor);
final ValueAnimator.AnimatorUpdateListener listener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mFingerprintAnimator.setBackgroundTintList(ColorStateList.valueOf(
(Integer) animation.getAnimatedValue()));
}
};
anim.addUpdateListener(listener);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
ValueAnimator anim = ValueAnimator.ofArgb(mIndicatorBackgroundActivatedColor,
mIndicatorBackgroundRestingColor);
anim.addUpdateListener(listener);
anim.setDuration(300);
anim.setInterpolator(mLinearOutSlowInInterpolator);
anim.start();
}
});
anim.setInterpolator(mFastOutSlowInInterpolator);
anim.setDuration(300);
anim.start();
}
private void launchFinish(byte[] token) {
Intent intent = getFinishIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
startActivity(intent);
finish();
}
protected Intent getFinishIntent() {
return new Intent(this, FingerprintEnrollFinish.class);
}
private void updateDescription() {
if (mSidecar.getEnrollmentSteps() == -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,
true /* force */);
mStartMessage.setVisibility(View.INVISIBLE);
mRepeatMessage.setVisibility(View.VISIBLE);
}
}
@Override
public void onEnrollmentHelp(CharSequence helpString) {
mErrorText.setText(helpString);
}
@Override
public void onEnrollmentError(CharSequence errString) {
showError(errString);
stopIconAnimation();
}
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
updateProgress(true /* animate */);
updateDescription();
clearError();
animateFlash();
mErrorText.removeCallbacks(mTouchAgainRunnable);
mErrorText.postDelayed(mTouchAgainRunnable, HINT_TIMEOUT_DURATION);
}
private void updateProgress(boolean animate) {
int progress = getProgress(
mSidecar.getEnrollmentSteps(), mSidecar.getEnrollmentRemaining());
if (animate) {
animateProgress(progress);
} else {
mProgressBar.setProgress(progress);
}
}
private int getProgress(int steps, int remaining) {
if (steps == -1) {
return 0;
}
int progress = Math.max(0, steps + 1 - remaining);
return PROGRESS_BAR_MAX * progress / (steps + 1);
}
private void showIconTouchDialog() {
mIconTouchCount = 0;
new IconTouchDialog().show(getFragmentManager(), null /* tag */);
}
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);
}
}
private void clearError() {
if (mErrorText.getVisibility() == View.VISIBLE) {
mErrorText.animate()
.alpha(0f)
.translationY(getResources().getDimensionPixelSize(
R.dimen.fingerprint_error_text_disappear_distance))
.setDuration(100)
.setInterpolator(mFastOutLinearInInterpolator)
.withEndAction(new Runnable() {
@Override
public void run() {
mErrorText.setVisibility(View.INVISIBLE);
}
})
.start();
}
}
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) { }
};
// Give the user a chance to see progress completed before jumping to the next stage.
private final Runnable mDelayedFinishRunnable = new Runnable() {
@Override
public void run() {
launchFinish(mToken);
}
};
private final Animatable2.AnimationCallback mIconAnimationCallback =
new Animatable2.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable d) {
if (mAnimationCancelled) {
return;
}
// Start animation after it has ended.
mFingerprintAnimator.post(new Runnable() {
@Override
public void run() {
startIconAnimation();
}
});
}
};
private final Runnable mShowDialogRunnable = new Runnable() {
@Override
public void run() {
showIconTouchDialog();
}
};
private final Runnable mTouchAgainRunnable = new Runnable() {
@Override
public void run() {
showError(getString(R.string.security_settings_fingerprint_enroll_lift_touch_again));
}
};
public static class IconTouchDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.security_settings_fingerprint_enroll_touch_dialog_title)
.setMessage(R.string.security_settings_fingerprint_enroll_touch_dialog_message)
.setPositiveButton(R.string.security_settings_fingerprint_enroll_dialog_ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}
}