1 /* 2 * Copyright (C) 2021 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.systemui.biometrics; 18 19 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; 20 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset; 21 22 import android.animation.Animator; 23 import android.animation.AnimatorListenerAdapter; 24 import android.animation.AnimatorSet; 25 import android.animation.ObjectAnimator; 26 import android.content.Context; 27 import android.graphics.PorterDuff; 28 import android.graphics.PorterDuffColorFilter; 29 import android.util.AttributeSet; 30 import android.util.MathUtils; 31 import android.view.View; 32 import android.widget.ImageView; 33 34 import androidx.annotation.Nullable; 35 36 import com.android.settingslib.Utils; 37 import com.android.systemui.R; 38 import com.android.systemui.animation.Interpolators; 39 import com.android.systemui.statusbar.StatusBarState; 40 41 import com.airbnb.lottie.LottieAnimationView; 42 import com.airbnb.lottie.LottieProperty; 43 import com.airbnb.lottie.model.KeyPath; 44 45 /** 46 * View corresponding with udfps_keyguard_view.xml 47 */ 48 public class UdfpsKeyguardView extends UdfpsAnimationView { 49 private UdfpsDrawable mFingerprintDrawable; // placeholder 50 private LottieAnimationView mAodFp; 51 private LottieAnimationView mLockScreenFp; 52 private int mStatusBarState; 53 54 // used when highlighting fp icon: 55 private int mTextColorPrimary; 56 private ImageView mBgProtection; 57 boolean mUdfpsRequested; 58 59 private AnimatorSet mBackgroundInAnimator = new AnimatorSet(); 60 private int mAlpha; // 0-255 61 62 // AOD anti-burn-in offsets 63 private final int mMaxBurnInOffsetX; 64 private final int mMaxBurnInOffsetY; 65 private float mBurnInOffsetX; 66 private float mBurnInOffsetY; 67 private float mBurnInProgress; 68 private float mInterpolatedDarkAmount; 69 UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs)70 public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { 71 super(context, attrs); 72 mFingerprintDrawable = new UdfpsFpDrawable(context); 73 74 mMaxBurnInOffsetX = context.getResources() 75 .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); 76 mMaxBurnInOffsetY = context.getResources() 77 .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); 78 } 79 80 @Override onFinishInflate()81 protected void onFinishInflate() { 82 super.onFinishInflate(); 83 mAodFp = findViewById(R.id.udfps_aod_fp); 84 mLockScreenFp = findViewById(R.id.udfps_lockscreen_fp); 85 mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg); 86 87 updateColor(); 88 89 // requires call to invalidate to update the color 90 mLockScreenFp.addValueCallback( 91 new KeyPath("**"), LottieProperty.COLOR_FILTER, 92 frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, PorterDuff.Mode.SRC_ATOP) 93 ); 94 } 95 96 @Override getDrawable()97 public UdfpsDrawable getDrawable() { 98 return mFingerprintDrawable; 99 } 100 101 @Override onIlluminationStarting()102 void onIlluminationStarting() { 103 } 104 105 @Override onIlluminationStopped()106 void onIlluminationStopped() { 107 } 108 109 @Override dozeTimeTick()110 public boolean dozeTimeTick() { 111 updateBurnInOffsets(); 112 return true; 113 } 114 updateBurnInOffsets()115 private void updateBurnInOffsets() { 116 mBurnInOffsetX = MathUtils.lerp(0f, 117 getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) 118 - mMaxBurnInOffsetX, mInterpolatedDarkAmount); 119 mBurnInOffsetY = MathUtils.lerp(0f, 120 getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) 121 - mMaxBurnInOffsetY, mInterpolatedDarkAmount); 122 mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount); 123 124 mAodFp.setTranslationX(mBurnInOffsetX); 125 mAodFp.setTranslationY(mBurnInOffsetY); 126 mAodFp.setProgress(mBurnInProgress); 127 mAodFp.setAlpha(255 * mInterpolatedDarkAmount); 128 129 mLockScreenFp.setTranslationX(mBurnInOffsetX); 130 mLockScreenFp.setTranslationY(mBurnInOffsetY); 131 mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); 132 mLockScreenFp.setAlpha((1f - mInterpolatedDarkAmount) * 255); 133 } 134 requestUdfps(boolean request, int color)135 void requestUdfps(boolean request, int color) { 136 mUdfpsRequested = request; 137 } 138 setStatusBarState(int statusBarState)139 void setStatusBarState(int statusBarState) { 140 mStatusBarState = statusBarState; 141 } 142 updateColor()143 void updateColor() { 144 mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, 145 android.R.attr.textColorPrimary); 146 mBgProtection.setImageDrawable(getContext().getDrawable(R.drawable.fingerprint_bg)); 147 mLockScreenFp.invalidate(); // updated with a valueCallback 148 } 149 150 /** 151 * @param alpha between 0 and 255 152 */ setUnpausedAlpha(int alpha)153 void setUnpausedAlpha(int alpha) { 154 mAlpha = alpha; 155 updateAlpha(); 156 } 157 158 /** 159 * @return alpha between 0 and 255 160 */ getUnpausedAlpha()161 int getUnpausedAlpha() { 162 return mAlpha; 163 } 164 165 @Override updateAlpha()166 protected int updateAlpha() { 167 int alpha = super.updateAlpha(); 168 mLockScreenFp.setAlpha(alpha / 255f); 169 if (mInterpolatedDarkAmount != 0f) { 170 mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); 171 } else { 172 mBgProtection.setAlpha(alpha / 255f); 173 } 174 175 return alpha; 176 } 177 178 @Override calculateAlpha()179 int calculateAlpha() { 180 if (mPauseAuth) { 181 return 0; 182 } 183 return mAlpha; 184 } 185 onDozeAmountChanged(float linear, float eased)186 void onDozeAmountChanged(float linear, float eased) { 187 mInterpolatedDarkAmount = eased; 188 updateAlpha(); 189 updateBurnInOffsets(); 190 } 191 192 /** 193 * Animates in the bg protection circle behind the fp icon to highlight the icon. 194 */ animateInUdfpsBouncer(Runnable onEndAnimation)195 void animateInUdfpsBouncer(Runnable onEndAnimation) { 196 if (mBackgroundInAnimator.isRunning()) { 197 // already animating in 198 return; 199 } 200 201 // fade in and scale up 202 mBackgroundInAnimator = new AnimatorSet(); 203 mBackgroundInAnimator.playTogether( 204 ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), 205 ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), 206 ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f)); 207 mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 208 mBackgroundInAnimator.setDuration(500); 209 mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() { 210 @Override 211 public void onAnimationEnd(Animator animation) { 212 if (onEndAnimation != null) { 213 onEndAnimation.run(); 214 } 215 } 216 }); 217 mBackgroundInAnimator.start(); 218 } 219 isShadeLocked()220 private boolean isShadeLocked() { 221 return mStatusBarState == StatusBarState.SHADE_LOCKED; 222 } 223 } 224