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 static com.android.systemui.util.ColorUtilKt.getPrivateAttrColorIfUnset; 19 20 import android.animation.AnimatorSet; 21 import android.animation.ArgbEvaluator; 22 import android.animation.ValueAnimator; 23 import android.annotation.Nullable; 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.animation.Interpolators; 34 35 /** 36 * Provides background color and radius animations for key pad buttons. 37 */ 38 class NumPadAnimator { 39 private ValueAnimator mExpandAnimator; 40 private AnimatorSet mExpandAnimatorSet; 41 private ValueAnimator mContractAnimator; 42 private AnimatorSet mContractAnimatorSet; 43 private GradientDrawable mBackground; 44 private Drawable mImageButton; 45 private TextView mDigitTextView; 46 private int mNormalBackgroundColor; 47 private int mPressedBackgroundColor; 48 private int mTextColorPrimary; 49 private int mTextColorPressed; 50 private int mStyle; 51 private float mStartRadius; 52 private float mEndRadius; 53 private int mHeight; 54 55 private static final int EXPAND_ANIMATION_MS = 100; 56 private static final int EXPAND_COLOR_ANIMATION_MS = 50; 57 private static final int CONTRACT_ANIMATION_DELAY_MS = 33; 58 private static final int CONTRACT_ANIMATION_MS = 417; 59 NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style, Drawable buttonImage)60 NumPadAnimator(Context context, final Drawable drawable, 61 @StyleRes int style, Drawable buttonImage) { 62 this(context, drawable, style, null, buttonImage); 63 } 64 NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style, @Nullable TextView digitTextView, @Nullable Drawable buttonImage)65 NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style, 66 @Nullable TextView digitTextView, @Nullable Drawable buttonImage) { 67 mStyle = style; 68 mBackground = (GradientDrawable) drawable; 69 mDigitTextView = digitTextView; 70 mImageButton = buttonImage; 71 72 reloadColors(context); 73 } 74 expand()75 public void expand() { 76 mExpandAnimatorSet.cancel(); 77 mContractAnimatorSet.cancel(); 78 mExpandAnimatorSet.start(); 79 } 80 contract()81 public void contract() { 82 mExpandAnimatorSet.cancel(); 83 mContractAnimatorSet.cancel(); 84 mContractAnimatorSet.start(); 85 } 86 setProgress(float progress)87 public void setProgress(float progress) { 88 mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress); 89 int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress); 90 int difference = mHeight - height; 91 mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2); 92 } 93 onLayout(int height)94 void onLayout(int height) { 95 boolean shouldUpdateHeight = height != mHeight; 96 mHeight = height; 97 mStartRadius = height / 2f; 98 mEndRadius = height / 4f; 99 mExpandAnimator.setFloatValues(mStartRadius, mEndRadius); 100 mContractAnimator.setFloatValues(mEndRadius, mStartRadius); 101 // Set initial corner radius. 102 if (shouldUpdateHeight) { 103 mBackground.setCornerRadius(mStartRadius); 104 } 105 } 106 107 /** 108 * Reload colors from resources. 109 **/ reloadColors(Context context)110 void reloadColors(Context context) { 111 boolean isNumPadKey = mImageButton == null; 112 113 int[] customAttrs = {android.R.attr.colorControlNormal}; 114 ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle); 115 TypedArray a = ctw.obtainStyledAttributes(customAttrs); 116 mNormalBackgroundColor = getPrivateAttrColorIfUnset(ctw, a, 0, 0, 117 com.android.internal.R.attr.colorSurface); 118 a.recycle(); 119 mBackground.setColor(mNormalBackgroundColor); 120 121 mPressedBackgroundColor = context.getColor(android.R.color.system_accent1_200); 122 mTextColorPrimary = isNumPadKey 123 ? com.android.settingslib.Utils 124 .getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) 125 : com.android.settingslib.Utils 126 .getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse); 127 mTextColorPressed = com.android.settingslib.Utils 128 .getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorOnAccent); 129 createAnimators(); 130 } 131 createAnimators()132 private void createAnimators() { 133 // Actual values will be updated later, usually during an onLayout() call 134 mExpandAnimator = ValueAnimator.ofFloat(0f, 1f); 135 mExpandAnimator.setDuration(EXPAND_ANIMATION_MS); 136 mExpandAnimator.setInterpolator(Interpolators.LINEAR); 137 mExpandAnimator.addUpdateListener( 138 anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue())); 139 140 ValueAnimator expandBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), 141 mNormalBackgroundColor, mPressedBackgroundColor); 142 expandBackgroundColorAnimator.setDuration(EXPAND_COLOR_ANIMATION_MS); 143 expandBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR); 144 expandBackgroundColorAnimator.addUpdateListener( 145 animator -> mBackground.setColor((int) animator.getAnimatedValue())); 146 147 ValueAnimator expandTextColorAnimator = 148 ValueAnimator.ofObject(new ArgbEvaluator(), 149 mTextColorPrimary, mTextColorPressed); 150 expandTextColorAnimator.setInterpolator(Interpolators.LINEAR); 151 expandTextColorAnimator.setDuration(EXPAND_COLOR_ANIMATION_MS); 152 expandTextColorAnimator.addUpdateListener(valueAnimator -> { 153 if (mDigitTextView != null) { 154 mDigitTextView.setTextColor((int) valueAnimator.getAnimatedValue()); 155 } 156 if (mImageButton != null) { 157 mImageButton.setTint((int) valueAnimator.getAnimatedValue()); 158 } 159 }); 160 161 mExpandAnimatorSet = new AnimatorSet(); 162 mExpandAnimatorSet.playTogether(mExpandAnimator, 163 expandBackgroundColorAnimator, expandTextColorAnimator); 164 165 mContractAnimator = ValueAnimator.ofFloat(1f, 0f); 166 mContractAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS); 167 mContractAnimator.setDuration(CONTRACT_ANIMATION_MS); 168 mContractAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 169 mContractAnimator.addUpdateListener( 170 anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue())); 171 ValueAnimator contractBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), 172 mPressedBackgroundColor, mNormalBackgroundColor); 173 contractBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR); 174 contractBackgroundColorAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS); 175 contractBackgroundColorAnimator.setDuration(CONTRACT_ANIMATION_MS); 176 contractBackgroundColorAnimator.addUpdateListener( 177 animator -> mBackground.setColor((int) animator.getAnimatedValue())); 178 179 ValueAnimator contractTextColorAnimator = 180 ValueAnimator.ofObject(new ArgbEvaluator(), mTextColorPressed, 181 mTextColorPrimary); 182 contractTextColorAnimator.setInterpolator(Interpolators.LINEAR); 183 contractTextColorAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS); 184 contractTextColorAnimator.setDuration(CONTRACT_ANIMATION_MS); 185 contractTextColorAnimator.addUpdateListener(valueAnimator -> { 186 if (mDigitTextView != null) { 187 mDigitTextView.setTextColor((int) valueAnimator.getAnimatedValue()); 188 } 189 if (mImageButton != null) { 190 mImageButton.setTint((int) valueAnimator.getAnimatedValue()); 191 } 192 }); 193 194 mContractAnimatorSet = new AnimatorSet(); 195 mContractAnimatorSet.playTogether(mContractAnimator, 196 contractBackgroundColorAnimator, contractTextColorAnimator); 197 } 198 } 199 200