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