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 @Override updateAlpha()159 protected int updateAlpha() { 160 int alpha = super.updateAlpha(); 161 mLockScreenFp.setAlpha(alpha / 255f); 162 if (mInterpolatedDarkAmount != 0f) { 163 mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); 164 } else { 165 mBgProtection.setAlpha(alpha / 255f); 166 } 167 168 return alpha; 169 } 170 171 @Override calculateAlpha()172 int calculateAlpha() { 173 if (mPauseAuth) { 174 return 0; 175 } 176 return mAlpha; 177 } 178 onDozeAmountChanged(float linear, float eased)179 void onDozeAmountChanged(float linear, float eased) { 180 mInterpolatedDarkAmount = eased; 181 updateAlpha(); 182 updateBurnInOffsets(); 183 } 184 185 /** 186 * Animates in the bg protection circle behind the fp icon to highlight the icon. 187 */ animateInUdfpsBouncer(Runnable onEndAnimation)188 void animateInUdfpsBouncer(Runnable onEndAnimation) { 189 if (mBackgroundInAnimator.isRunning()) { 190 // already animating in 191 return; 192 } 193 194 // fade in and scale up 195 mBackgroundInAnimator = new AnimatorSet(); 196 mBackgroundInAnimator.playTogether( 197 ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), 198 ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), 199 ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f)); 200 mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 201 mBackgroundInAnimator.setDuration(500); 202 mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() { 203 @Override 204 public void onAnimationEnd(Animator animation) { 205 if (onEndAnimation != null) { 206 onEndAnimation.run(); 207 } 208 } 209 }); 210 mBackgroundInAnimator.start(); 211 } 212 isShadeLocked()213 private boolean isShadeLocked() { 214 return mStatusBarState == StatusBarState.SHADE_LOCKED; 215 } 216 } 217