• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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