Refine fingerprint enrollment experience

- Flash fingerprint graphic when enrollment progresses
- Show hint message when not progressing for a few seconds
- Make sure animation is always working

Bug: 21617091
Bug: 21644138
Change-Id: Ic54c10a655e6da914f960cee20f0066b46d87325
This commit is contained in:
Jorim Jaggi
2015-06-10 15:05:30 -07:00
parent 3f94f562c1
commit 436e02c3d5
10 changed files with 153 additions and 12 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,36 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88.0dp"
android:height="88.0dp"
android:viewportWidth="88.0"
android:viewportHeight="88.0">
<path
android:fillColor="#FF000000"
android:pathData="M67.74,11.59c-0.41,0.0 -0.82,-0.1 -1.2,-0.31c-7.44,-4.06 -15.0,-6.04 -23.11,-6.04c-7.92,0.0 -14.67,1.85 -21.88,6.01c-1.2,0.69 -2.73,0.28 -3.42,-0.92s-0.28,-2.72 0.92,-3.41c7.9,-4.55 15.65,-6.68 24.37,-6.68c8.97,0.0 17.32,2.17 25.51,6.65c1.21,0.66 1.66,2.18 1.0,3.39C69.48,11.12 68.62,11.59 67.74,11.59z"/>
<path
android:fillColor="#FF000000"
android:pathData="M9.25,34.74c-0.48,0.0 -0.96,-0.14 -1.39,-0.42c-1.15,-0.77 -1.45,-2.32 -0.68,-3.47c4.09,-6.09 9.3,-10.89 15.49,-14.27c6.52,-3.55 13.91,-5.43 21.38,-5.43c7.44,0.0 14.8,1.86 21.3,5.39c6.17,3.35 11.38,8.12 15.47,14.16c0.77,1.14 0.47,2.7 -0.67,3.47c-1.14,0.77 -2.7,0.47 -3.47,-0.67c-3.64,-5.38 -8.25,-9.61 -13.71,-12.57c-5.77,-3.13 -12.31,-4.78 -18.92,-4.78c-6.63,0.0 -13.2,1.67 -18.98,4.82c-5.48,2.99 -10.1,7.25 -13.73,12.66C10.85,34.35 10.06,34.74 9.25,34.74z"/>
<path
android:fillColor="#FF000000"
android:pathData="M34.76,86.82c-0.67,0.0 -1.33,-0.27 -1.82,-0.79c-3.49,-3.72 -5.51,-6.25 -8.26,-11.45c-2.84,-5.35 -4.34,-11.88 -4.34,-18.86c0.0,-13.02 10.59,-23.61 23.61,-23.61c13.02,0.0 23.61,10.59 23.61,23.61c0.0,1.38 -1.12,2.5 -2.5,2.5s-2.5,-1.12 -2.5,-2.5c0.0,-10.26 -8.35,-18.61 -18.61,-18.61c-10.26,0.0 -18.61,8.35 -18.61,18.61c0.0,6.17 1.3,11.89 3.76,16.52c2.62,4.94 4.37,7.04 7.49,10.37c0.94,1.01 0.89,2.59 -0.11,3.53C35.99,86.6 35.38,86.82 34.76,86.82z"/>
<path
android:fillColor="#FF000000"
android:pathData="M64.28,78.84c-4.99,0.0 -9.35,-1.32 -12.98,-3.92c-6.17,-4.43 -9.86,-11.6 -9.86,-19.19c0.0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5c0.0,5.98 2.91,11.64 7.77,15.13c2.8,2.01 6.09,2.98 10.06,2.98c0.97,0.0 2.57,-0.11 4.17,-0.4c1.36,-0.25 2.66,0.64 2.92,2.0c0.25,1.36 -0.64,2.66 -2.0,2.92C66.93,78.8 64.86,78.84 64.28,78.84z"/>
<path
android:fillColor="#FF000000"
android:pathData="M55.92,87.75c-0.23,0.0 -0.46,-0.03 -0.7,-0.1c-6.6,-1.91 -10.92,-4.49 -15.4,-9.2c-5.76,-6.06 -8.94,-14.13 -8.94,-22.72c0.0,-7.2 5.86,-13.05 13.05,-13.05c7.2,0.0 13.05,5.86 13.05,13.05c0.0,4.44 3.61,8.05 8.05,8.05s8.05,-3.61 8.05,-8.05c0.0,-16.08 -13.08,-29.16 -29.16,-29.16c-11.43,0.0 -21.86,6.73 -26.58,17.15c-1.57,3.48 -2.37,7.52 -2.37,12.01c0.0,3.36 0.28,8.62 2.71,15.49c0.46,1.3 -0.22,2.73 -1.52,3.19c-1.3,0.46 -2.73,-0.22 -3.19,-1.52c-2.02,-5.7 -3.0,-11.31 -3.0,-17.16c0.0,-5.21 0.95,-9.94 2.82,-14.07c5.52,-12.2 17.74,-20.09 31.13,-20.09c18.83,0.0 34.16,15.32 34.16,34.16c0.0,7.2 -5.86,13.05 -13.05,13.05S52.0,62.92 52.0,55.73c0.0,-4.44 -3.61,-8.05 -8.05,-8.05s-8.05,3.61 -8.05,8.05c0.0,7.3 2.69,14.15 7.56,19.28c3.86,4.06 7.43,6.18 13.17,7.84c1.33,0.38 2.09,1.77 1.71,3.1C58.01,87.04 57.01,87.75 55.92,87.75z"/>
</vector>

View File

@@ -69,7 +69,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_gravity="center_horizontal|bottom"/>
android:layout_gravity="center_horizontal|bottom"
android:visibility="invisible"/>
</FrameLayout>

View File

@@ -66,7 +66,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_gravity="center_horizontal"/>
android:layout_gravity="center_horizontal"
android:visibility="invisible"/>
</LinearLayout>

View File

@@ -22,18 +22,14 @@
android:layout_marginTop="36dp"
android:layout_gravity="center_horizontal">
<ImageView
android:layout_width="88dp"
android:layout_height="88dp"
android:layout_centerInParent="true"
android:src="@drawable/fingerprint_indicator" />
<ImageView
android:id="@+id/fingerprint_animator"
android:layout_width="88dp"
android:layout_height="88dp"
android:layout_centerInParent="true"
android:src="@drawable/enrollment_fingerprint_isolated_animation" />
android:background="@drawable/fp_illustration_enrollment"
android:backgroundTint="@color/fingerprint_indicator_background_resting"
android:src="@drawable/enrollment_fingerprint_isolated_animation"/>
<ProgressBar
android:id="@+id/fingerprint_progress_bar"

View File

@@ -63,7 +63,6 @@
android:id="@+id/add_another_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/fingerprint_enroll_button_add" />
<Button

View File

@@ -66,6 +66,8 @@
<color name="fingerprint_message_color">#de000000</color>
<color name="fingerprint_progress_ring">?android:attr/colorAccent</color>
<color name="fingerprint_progress_ring_bg">#20000000</color>
<color name="fingerprint_indicator_background_resting">#12000000</color>
<color name="fingerprint_indicator_background_activated">#80009688</color>
<color name="running_processes_system_ram">#ff384248</color>
<color name="running_processes_apps_ram">#ff009587</color>

View File

@@ -231,6 +231,8 @@
<dimen name="fingerprint_find_sensor_graphic_size">200dp</dimen>
<item name="fingerprint_illustration_aspect_ratio" format="float" type="dimen">2.6</item>
<dimen name="fingerprint_decor_padding_top">0dp</dimen>
<dimen name="fingerprint_error_text_appear_distance">16dp</dimen>
<dimen name="fingerprint_error_text_disappear_distance">-8dp</dimen>
<dimen name="confirm_credentials_security_method_margin">48dp</dimen>
<dimen name="fab_size">56dp</dimen>

View File

@@ -806,6 +806,8 @@
<string name="security_settings_fingerprint_enroll_disclaimer">In addition to unlocking your phone, you can also use your fingerprint to authorize purchases and app access. <annotation id="url">Learn more</annotation></string>
<!-- Text shown in fingerprint settings explaining what the fingerprint can be used for in the case unlocking is disabled [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled">Screen lock option disabled. You can still use your fingerprint to authorize purchases and app access. <annotation id="url">Learn more</annotation></string>
<!-- Text shown in fingerprint enroll when we didn't observe progress for a few seconds. [CHAR LIMIT=100] -->
<string name="security_settings_fingerprint_enroll_lift_touch_again">Lift finger, then touch sensor again</string>
<!-- Title of the preferences category for preference items to control encryption -->
<string name="crypt_keeper_settings_title">Encryption</string>

View File

@@ -17,12 +17,15 @@
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;
@@ -49,6 +52,12 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
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.
*/
@@ -67,10 +76,15 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
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) {
@@ -86,6 +100,10 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
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) {
@@ -104,6 +122,11 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
return true;
}
});
mIndicatorBackgroundRestingColor
= getColor(R.color.fingerprint_indicator_background_resting);
mIndicatorBackgroundActivatedColor
= getColor(R.color.fingerprint_indicator_background_activated);
mRestoring = savedInstanceState != null;
}
@Override
@@ -117,6 +140,9 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
mSidecar.setListener(this);
updateProgress(false /* animate */);
updateDescription();
if (mRestoring) {
startIconAnimation();
}
}
@Override
@@ -158,6 +184,34 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
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 = new Intent();
intent.setClassName("com.android.settings", FingerprintEnrollFinish.class.getName());
@@ -187,7 +241,7 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
@Override
public void onEnrollmentError(CharSequence errString) {
mErrorText.setText(errString);
showError(errString);
stopIconAnimation();
}
@@ -195,7 +249,10 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
public void onEnrollmentProgressChange(int steps, int remaining) {
updateProgress(true /* animate */);
updateDescription();
mErrorText.setText("");
clearError();
animateFlash();
mErrorText.removeCallbacks(mTouchAgainRunnable);
mErrorText.postDelayed(mTouchAgainRunnable, HINT_TIMEOUT_DURATION);
}
private void updateProgress(boolean animate) {
@@ -221,6 +278,44 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
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() {
@@ -274,6 +369,13 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
}
};
private final Runnable mTouchAgainRunnable = new Runnable() {
@Override
public void run() {
showError(getString(R.string.security_settings_fingerprint_enroll_lift_touch_again));
}
};
private static class IconTouchDialog extends DialogFragment {
@Override