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 package com.android.keyguard; 17 18 import android.animation.Animator; 19 import android.animation.AnimatorSet; 20 import android.animation.ArgbEvaluator; 21 import android.animation.ValueAnimator; 22 import android.annotation.Nullable; 23 import android.annotation.SuppressLint; 24 import android.content.Context; 25 import android.content.res.TypedArray; 26 import android.graphics.drawable.Drawable; 27 import android.graphics.drawable.GradientDrawable; 28 import android.view.ContextThemeWrapper; 29 import android.widget.TextView; 30 31 import androidx.annotation.StyleRes; 32 33 import com.android.systemui.Flags; 34 import com.android.systemui.bouncer.shared.constants.PinBouncerConstants.Animation; 35 import com.android.systemui.bouncer.shared.constants.PinBouncerConstants.Color; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 40 /** 41 * Provides background color and radius animations for key pad buttons. 42 */ 43 class NumPadAnimator { 44 private ValueAnimator mExpandAnimator; 45 private AnimatorSet mExpandAnimatorSet; 46 private ValueAnimator mContractAnimator; 47 private AnimatorSet mContractAnimatorSet; 48 private GradientDrawable mBackground; 49 private Drawable mImageButton; 50 private TextView mDigitTextView; 51 private int mNormalBackgroundColor; 52 private int mPressedBackgroundColor; 53 private int mTextColorPrimary; 54 private int mTextColorPressed; 55 private int mStyle; 56 private float mStartRadius; 57 private float mEndRadius; 58 private int mHeight; 59 private int mWidth; 60 NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style, Drawable buttonImage)61 NumPadAnimator(Context context, final Drawable drawable, 62 @StyleRes int style, Drawable buttonImage) { 63 this(context, drawable, style, null, buttonImage); 64 } 65 NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style, @Nullable TextView digitTextView, @Nullable Drawable buttonImage)66 NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style, 67 @Nullable TextView digitTextView, @Nullable Drawable buttonImage) { 68 mStyle = style; 69 mBackground = (GradientDrawable) drawable; 70 mDigitTextView = digitTextView; 71 mImageButton = buttonImage; 72 73 reloadColors(context); 74 } 75 expand()76 public void expand() { 77 mExpandAnimatorSet.cancel(); 78 mContractAnimatorSet.cancel(); 79 mExpandAnimatorSet.start(); 80 } 81 contract()82 public void contract() { 83 mExpandAnimatorSet.cancel(); 84 mContractAnimatorSet.cancel(); 85 mContractAnimatorSet.start(); 86 } 87 setProgress(float progress)88 public void setProgress(float progress) { 89 mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress); 90 int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress); 91 int difference = mHeight - height; 92 93 int left = 0; 94 int top = difference / 2; 95 int right = mWidth; 96 int bottom = mHeight - difference / 2; 97 mBackground.setBounds(left, top, right, bottom); 98 } 99 onLayout(int width, int height)100 void onLayout(int width, int height) { 101 boolean shouldUpdateHeight = height != mHeight; 102 mWidth = width; 103 mHeight = height; 104 mStartRadius = height / 2f; 105 mEndRadius = height / 4f; 106 mExpandAnimator.setFloatValues(mStartRadius, mEndRadius); 107 mContractAnimator.setFloatValues(mEndRadius, mStartRadius); 108 // Set initial corner radius. 109 if (shouldUpdateHeight) { 110 mBackground.setCornerRadius(mStartRadius); 111 } 112 } 113 114 /** 115 * Reload colors from resources. 116 **/ reloadColors(Context context)117 void reloadColors(Context context) { 118 boolean isNumPadKey = mImageButton == null; 119 120 if (!Flags.bouncerUiRevamp2()) { 121 int[] customAttrs = {android.R.attr.colorControlNormal}; 122 ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle); 123 @SuppressLint("ResourceType") TypedArray a = ctw.obtainStyledAttributes(customAttrs); 124 125 mNormalBackgroundColor = a.getColor(0, context.getColor(Color.digitBg)); 126 127 a.recycle(); 128 } else { 129 mNormalBackgroundColor = context.getColor(isNumPadKey ? Color.digitBg : Color.actionBg); 130 } 131 132 mPressedBackgroundColor = context.getColor(Color.bgPressed); 133 mTextColorPressed = context.getColor(Color.digitPressed); 134 135 mBackground.setColor(mNormalBackgroundColor); 136 mTextColorPrimary = context.getColor(isNumPadKey ? Color.digit : Color.action); 137 createAnimators(); 138 } 139 createAnimators()140 private void createAnimators() { 141 // Actual values will be updated later, usually during an onLayout() call 142 mExpandAnimator = ValueAnimator.ofFloat(0f, 1f); 143 mExpandAnimator.setDuration(Animation.expansionDuration); 144 mExpandAnimator.setInterpolator(Animation.expansionInterpolator); 145 mExpandAnimator.addUpdateListener( 146 anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue())); 147 148 List<Animator> expandAnimators = new ArrayList<>(); 149 ValueAnimator expandBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), 150 mNormalBackgroundColor, mPressedBackgroundColor); 151 expandBackgroundColorAnimator.setDuration(Animation.expansionColorDuration); 152 expandBackgroundColorAnimator.setInterpolator(Animation.expansionInterpolator); 153 expandBackgroundColorAnimator.addUpdateListener( 154 animator -> mBackground.setColor((int) animator.getAnimatedValue())); 155 156 ValueAnimator expandTextColorAnimator = 157 ValueAnimator.ofObject(new ArgbEvaluator(), 158 mTextColorPrimary, mTextColorPressed); 159 expandTextColorAnimator.setInterpolator(Animation.expansionInterpolator); 160 expandTextColorAnimator.setDuration(Animation.expansionColorDuration); 161 expandTextColorAnimator.addUpdateListener(valueAnimator -> { 162 if (mDigitTextView != null) { 163 mDigitTextView.setTextColor((int) valueAnimator.getAnimatedValue()); 164 } 165 if (mImageButton != null) { 166 mImageButton.setTint((int) valueAnimator.getAnimatedValue()); 167 } 168 }); 169 170 expandAnimators.add(mExpandAnimator); 171 expandAnimators.add(expandBackgroundColorAnimator); 172 expandAnimators.add(expandTextColorAnimator); 173 174 if (Flags.bouncerUiRevamp2()) { 175 ValueAnimator expandTextScaleAnimator = ValueAnimator.ofFloat( 176 Animation.normalTextScaleX, Animation.pressedTextScaleX); 177 expandTextScaleAnimator.setInterpolator(Animation.expansionInterpolator); 178 expandTextScaleAnimator.setDuration(Animation.expansionDuration); 179 expandTextScaleAnimator.addUpdateListener(valueAnimator -> { 180 if (mDigitTextView != null) { 181 mDigitTextView.setTextScaleX((Float) valueAnimator.getAnimatedValue()); 182 } 183 }); 184 expandAnimators.add(expandTextScaleAnimator); 185 } 186 187 mExpandAnimatorSet = new AnimatorSet(); 188 mExpandAnimatorSet.playTogether(expandAnimators); 189 190 List<Animator> contractAnimators = new ArrayList<>(); 191 mContractAnimator = ValueAnimator.ofFloat(1f, 0f); 192 mContractAnimator.setStartDelay(Animation.contractionStartDelay); 193 mContractAnimator.setDuration(Animation.contractionDuration); 194 mContractAnimator.setInterpolator(Animation.contractionRadiusInterpolator); 195 mContractAnimator.addUpdateListener( 196 anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue())); 197 ValueAnimator contractBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), 198 mPressedBackgroundColor, mNormalBackgroundColor); 199 contractBackgroundColorAnimator.setInterpolator(Animation.contractionColorInterpolator); 200 contractBackgroundColorAnimator.setStartDelay(Animation.contractionStartDelay); 201 contractBackgroundColorAnimator.setDuration(Animation.contractionDuration); 202 contractBackgroundColorAnimator.addUpdateListener( 203 animator -> mBackground.setColor((int) animator.getAnimatedValue())); 204 205 ValueAnimator contractTextColorAnimator = 206 ValueAnimator.ofObject(new ArgbEvaluator(), mTextColorPressed, 207 mTextColorPrimary); 208 contractTextColorAnimator.setInterpolator(Animation.contractionColorInterpolator); 209 contractTextColorAnimator.setStartDelay(Animation.contractionStartDelay); 210 contractTextColorAnimator.setDuration(Animation.contractionDuration); 211 contractTextColorAnimator.addUpdateListener(valueAnimator -> { 212 if (mDigitTextView != null) { 213 mDigitTextView.setTextColor((int) valueAnimator.getAnimatedValue()); 214 } 215 if (mImageButton != null) { 216 mImageButton.setTint((int) valueAnimator.getAnimatedValue()); 217 } 218 }); 219 220 contractAnimators.add(mContractAnimator); 221 contractAnimators.add(contractBackgroundColorAnimator); 222 contractAnimators.add(contractTextColorAnimator); 223 224 if (Flags.bouncerUiRevamp2()) { 225 ValueAnimator contractTextScaleAnimator = ValueAnimator.ofFloat( 226 Animation.pressedTextScaleX, Animation.normalTextScaleX); 227 contractTextScaleAnimator.setInterpolator(Animation.contractionRadiusInterpolator); 228 contractTextScaleAnimator.setDuration(Animation.contractionDuration); 229 contractTextScaleAnimator.addUpdateListener(valueAnimator -> { 230 if (mDigitTextView != null) { 231 mDigitTextView.setTextScaleX((Float) valueAnimator.getAnimatedValue()); 232 } 233 }); 234 contractAnimators.add(contractTextScaleAnimator); 235 } 236 mContractAnimatorSet = new AnimatorSet(); 237 mContractAnimatorSet.playTogether(contractAnimators); 238 } 239 } 240 241