1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.settings.fingerprint; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.annotation.ColorInt; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.graphics.Canvas; 26 import android.graphics.Paint; 27 import android.util.AttributeSet; 28 import android.view.View; 29 import android.view.animation.AnimationUtils; 30 import android.view.animation.Interpolator; 31 32 import com.android.settings.R; 33 import com.android.settings.Utils; 34 35 /** 36 * View which plays an animation to indicate where the sensor is on the device. 37 */ 38 public class FingerprintLocationAnimationView extends View implements 39 FingerprintFindSensorAnimation { 40 41 private static final float MAX_PULSE_ALPHA = 0.15f; 42 private static final long DELAY_BETWEEN_PHASE = 1000; 43 44 private final Interpolator mLinearOutSlowInInterpolator; 45 private final Interpolator mFastOutSlowInInterpolator; 46 47 private final int mDotRadius; 48 private final int mMaxPulseRadius; 49 private final float mFractionCenterX; 50 private final float mFractionCenterY; 51 private final Paint mDotPaint = new Paint(); 52 private final Paint mPulsePaint = new Paint(); 53 private float mPulseRadius; 54 private ValueAnimator mRadiusAnimator; 55 private ValueAnimator mAlphaAnimator; 56 FingerprintLocationAnimationView(Context context, @Nullable AttributeSet attrs)57 public FingerprintLocationAnimationView(Context context, @Nullable AttributeSet attrs) { 58 super(context, attrs); 59 mDotRadius = getResources().getDimensionPixelSize(R.dimen.fingerprint_dot_radius); 60 mMaxPulseRadius = getResources().getDimensionPixelSize(R.dimen.fingerprint_pulse_radius); 61 mFractionCenterX = getResources().getFraction( 62 R.fraction.fingerprint_sensor_location_fraction_x, 1, 1); 63 mFractionCenterY = getResources().getFraction( 64 R.fraction.fingerprint_sensor_location_fraction_y, 1, 1); 65 @ColorInt int colorAccent = Utils.getColorAccent(context); 66 mDotPaint.setAntiAlias(true); 67 mPulsePaint.setAntiAlias(true); 68 mDotPaint.setColor(colorAccent); 69 mPulsePaint.setColor(colorAccent); 70 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 71 android.R.interpolator.linear_out_slow_in); 72 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 73 android.R.interpolator.linear_out_slow_in); 74 } 75 76 @Override onDraw(Canvas canvas)77 protected void onDraw(Canvas canvas) { 78 drawPulse(canvas); 79 drawDot(canvas); 80 } 81 drawDot(Canvas canvas)82 private void drawDot(Canvas canvas) { 83 canvas.drawCircle(getCenterX(), getCenterY(), mDotRadius, mDotPaint); 84 } 85 drawPulse(Canvas canvas)86 private void drawPulse(Canvas canvas) { 87 canvas.drawCircle(getCenterX(), getCenterY(), mPulseRadius, mPulsePaint); 88 } 89 getCenterX()90 private float getCenterX() { 91 return getWidth() * mFractionCenterX; 92 } 93 getCenterY()94 private float getCenterY() { 95 return getHeight() * mFractionCenterY; 96 } 97 98 @Override startAnimation()99 public void startAnimation() { 100 startPhase(); 101 } 102 103 @Override stopAnimation()104 public void stopAnimation() { 105 removeCallbacks(mStartPhaseRunnable); 106 if (mRadiusAnimator != null) { 107 mRadiusAnimator.cancel(); 108 } 109 if (mAlphaAnimator != null) { 110 mAlphaAnimator.cancel(); 111 } 112 } 113 114 @Override pauseAnimation()115 public void pauseAnimation() { 116 stopAnimation(); 117 } 118 startPhase()119 private void startPhase() { 120 startRadiusAnimation(); 121 startAlphaAnimation(); 122 } 123 startRadiusAnimation()124 private void startRadiusAnimation() { 125 ValueAnimator animator = ValueAnimator.ofFloat(0, mMaxPulseRadius); 126 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 127 @Override 128 public void onAnimationUpdate(ValueAnimator animation) { 129 mPulseRadius = (float) animation.getAnimatedValue(); 130 invalidate(); 131 } 132 }); 133 animator.addListener(new AnimatorListenerAdapter() { 134 135 boolean mCancelled; 136 137 @Override 138 public void onAnimationCancel(Animator animation) { 139 mCancelled = true; 140 } 141 142 @Override 143 public void onAnimationEnd(Animator animation) { 144 mRadiusAnimator = null; 145 if (!mCancelled) { 146 postDelayed(mStartPhaseRunnable, DELAY_BETWEEN_PHASE); 147 } 148 } 149 }); 150 animator.setDuration(1000); 151 animator.setInterpolator(mLinearOutSlowInInterpolator); 152 animator.start(); 153 mRadiusAnimator = animator; 154 } 155 startAlphaAnimation()156 private void startAlphaAnimation() { 157 mPulsePaint.setAlpha((int) (255f * MAX_PULSE_ALPHA)); 158 ValueAnimator animator = ValueAnimator.ofFloat(MAX_PULSE_ALPHA, 0f); 159 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 160 @Override 161 public void onAnimationUpdate(ValueAnimator animation) { 162 mPulsePaint.setAlpha((int) (255f * (float) animation.getAnimatedValue())); 163 invalidate(); 164 } 165 }); 166 animator.addListener(new AnimatorListenerAdapter() { 167 @Override 168 public void onAnimationEnd(Animator animation) { 169 mAlphaAnimator = null; 170 } 171 }); 172 animator.setDuration(750); 173 animator.setInterpolator(mFastOutSlowInInterpolator); 174 animator.setStartDelay(250); 175 animator.start(); 176 mAlphaAnimator = animator; 177 } 178 179 private final Runnable mStartPhaseRunnable = new Runnable() { 180 @Override 181 public void run() { 182 startPhase(); 183 } 184 }; 185 } 186