blob: 3c59f69fdee6ce41d88b683f15bbca9345568038 [file] [log] [blame]
Jason Monkdc35dcb2015-12-04 16:36:15 -05001/*
Jason Monk702e2eb2017-03-03 16:53:44 -05002 * Copyright (C) 2017 The Android Open Source Project
Jason Monkdc35dcb2015-12-04 16:36:15 -05003 *
Jason Monk702e2eb2017-03-03 16:53:44 -05004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
Jason Monkdc35dcb2015-12-04 16:36:15 -05006 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
Jason Monk702e2eb2017-03-03 16:53:44 -05009 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
Jason Monkdc35dcb2015-12-04 16:36:15 -050013 */
14
Jason Monk702e2eb2017-03-03 16:53:44 -050015package com.android.systemui.qs.tileimpl;
Jason Monkdc35dcb2015-12-04 16:36:15 -050016
Jason Monk702e2eb2017-03-03 16:53:44 -050017import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
Jason Monk32508852017-01-18 09:17:13 -050018
Amin Shaikha5902ea2018-03-29 14:23:29 -040019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Jason Monk32508852017-01-18 09:17:13 -050021import android.animation.ValueAnimator;
Jason Monkdc35dcb2015-12-04 16:36:15 -050022import android.content.Context;
Jason Monk32508852017-01-18 09:17:13 -050023import android.content.res.ColorStateList;
Jason Monkdc35dcb2015-12-04 16:36:15 -050024import android.content.res.Resources;
Jason Monk32508852017-01-18 09:17:13 -050025import android.graphics.Color;
Jason Monke645aee2017-03-31 13:19:26 -040026import android.graphics.drawable.Animatable2;
27import android.graphics.drawable.Animatable2.AnimationCallback;
Jason Monkdc35dcb2015-12-04 16:36:15 -050028import android.graphics.drawable.Drawable;
29import android.view.View;
Jason Monkdc35dcb2015-12-04 16:36:15 -050030import android.widget.ImageView;
Jason Monkeae7c312016-02-04 13:00:48 -050031import android.widget.ImageView.ScaleType;
Jason Monk32508852017-01-18 09:17:13 -050032
Jason Monkdc35dcb2015-12-04 16:36:15 -050033import com.android.systemui.R;
Jason Monk702e2eb2017-03-03 16:53:44 -050034import com.android.systemui.plugins.qs.QSIconView;
35import com.android.systemui.plugins.qs.QSTile;
Jason Monke5b770e2017-03-03 21:49:29 -050036import com.android.systemui.plugins.qs.QSTile.State;
Evan Laird6026b052017-09-21 14:11:00 -040037import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
Gus Prevasab336792018-11-14 13:52:20 -050038
Jason Monkdc35dcb2015-12-04 16:36:15 -050039import java.util.Objects;
40
Jason Monk702e2eb2017-03-03 16:53:44 -050041public class QSIconViewImpl extends QSIconView {
Jason Monkdc35dcb2015-12-04 16:36:15 -050042
Evan Laird8d2e5402017-06-08 10:36:57 -040043 public static final long QS_ANIM_LENGTH = 350;
44
Xiaohui Chen2f3551b2016-04-07 10:37:25 -070045 protected final View mIcon;
46 protected final int mIconSizePx;
Jason Monk1aec93f2016-03-01 09:39:30 -050047 private boolean mAnimationEnabled = true;
Jason Monk32508852017-01-18 09:17:13 -050048 private int mState = -1;
49 private int mTint;
Jason Monkdc35dcb2015-12-04 16:36:15 -050050
Jason Monk702e2eb2017-03-03 16:53:44 -050051 public QSIconViewImpl(Context context) {
Jason Monkdc35dcb2015-12-04 16:36:15 -050052 super(context);
53
54 final Resources res = context.getResources();
55 mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
Jason Monkdc35dcb2015-12-04 16:36:15 -050056
57 mIcon = createIcon();
58 addView(mIcon);
59 }
60
Jason Monk1aec93f2016-03-01 09:39:30 -050061 public void disableAnimation() {
62 mAnimationEnabled = false;
63 }
64
Xiaohui Chen2f3551b2016-04-07 10:37:25 -070065 public View getIconView() {
66 return mIcon;
67 }
68
Jason Monkdc35dcb2015-12-04 16:36:15 -050069 @Override
70 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
71 final int w = MeasureSpec.getSize(widthMeasureSpec);
72 final int iconSpec = exactly(mIconSizePx);
73 mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
Fabian Kozynskibca09b72019-04-04 15:57:42 -040074 setMeasuredDimension(w, mIcon.getMeasuredHeight());
Jason Monkdc35dcb2015-12-04 16:36:15 -050075 }
76
77 @Override
78 protected void onLayout(boolean changed, int l, int t, int r, int b) {
79 final int w = getMeasuredWidth();
Jason Monkdc35dcb2015-12-04 16:36:15 -050080 int top = 0;
81 final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
82 layout(mIcon, iconLeft, top);
83 }
84
Amin Shaikh32d786f2018-08-01 17:08:26 -040085 public void setIcon(State state, boolean allowAnimations) {
86 setIcon((ImageView) mIcon, state, allowAnimations);
Jason Monkdc35dcb2015-12-04 16:36:15 -050087 }
88
Amin Shaikh32d786f2018-08-01 17:08:26 -040089 protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
Evan Lairdb3daf2b2017-10-17 16:00:29 -040090 final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
91 if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
Jason Monk395617f2017-05-05 14:07:58 -040092 || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
Amin Shaikh32d786f2018-08-01 17:08:26 -040093 boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
Evan Lairdb3daf2b2017-10-17 16:00:29 -040094 Drawable d = icon != null
95 ? shouldAnimate ? icon.getDrawable(mContext)
96 : icon.getInvisibleDrawable(mContext) : null;
97 int padding = icon != null ? icon.getPadding() : 0;
Jason Monk702e2eb2017-03-03 16:53:44 -050098 if (d != null) {
Jason Monkd686e9c2017-03-27 15:45:32 -040099 d.setAutoMirrored(false);
Evan Laird3367c332017-07-07 13:24:56 -0400100 d.setLayoutDirection(getLayoutDirection());
Jason Monkdc35dcb2015-12-04 16:36:15 -0500101 }
Evan Laird929bd542017-07-12 16:36:06 -0400102
103 if (iv instanceof SlashImageView) {
104 ((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
Jason Monkcb2307c2018-01-15 14:19:45 -0500105 ((SlashImageView) iv).setState(null, d);
Evan Laird929bd542017-07-12 16:36:06 -0400106 } else {
107 iv.setImageDrawable(d);
Jason Monk395617f2017-05-05 14:07:58 -0400108 }
Evan Laird929bd542017-07-12 16:36:06 -0400109
Evan Lairdb3daf2b2017-10-17 16:00:29 -0400110 iv.setTag(R.id.qs_icon_tag, icon);
Jason Monk395617f2017-05-05 14:07:58 -0400111 iv.setTag(R.id.qs_slash_tag, state.slash);
Jason Monkb53b6c52016-02-24 17:25:49 -0500112 iv.setPadding(0, padding, 0, padding);
Jason Monke645aee2017-03-31 13:19:26 -0400113 if (d instanceof Animatable2) {
114 Animatable2 a = (Animatable2) d;
Jason Monk66239fb2015-12-21 14:27:00 -0500115 a.start();
Jason Monke645aee2017-03-31 13:19:26 -0400116 if (state.isTransient) {
117 a.registerAnimationCallback(new AnimationCallback() {
118 @Override
119 public void onAnimationEnd(Drawable drawable) {
120 a.start();
121 }
122 });
Jason Monkdc35dcb2015-12-04 16:36:15 -0500123 }
124 }
125 }
Jason Monke5b770e2017-03-03 21:49:29 -0500126 }
127
Amin Shaikh32d786f2018-08-01 17:08:26 -0400128 private boolean shouldAnimate(ImageView iv) {
129 return mAnimationEnabled && iv.isShown() && iv.getDrawable() != null;
130 }
131
132 protected void setIcon(ImageView iv, QSTile.State state, boolean allowAnimations) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000133 if (state.disabledByPolicy) {
134 iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
135 } else {
136 iv.clearColorFilter();
137 }
Jason Monk32508852017-01-18 09:17:13 -0500138 if (state.state != mState) {
Jason Monke5b770e2017-03-03 21:49:29 -0500139 int color = getColor(state.state);
Jason Monk32508852017-01-18 09:17:13 -0500140 mState = state.state;
Amin Shaikh32d786f2018-08-01 17:08:26 -0400141 if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
142 animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
Jason Monk32508852017-01-18 09:17:13 -0500143 mTint = color;
144 } else {
Evan Laird6026b052017-09-21 14:11:00 -0400145 if (iv instanceof AlphaControlledSlashImageView) {
146 ((AlphaControlledSlashImageView)iv)
147 .setFinalImageTintList(ColorStateList.valueOf(color));
148 } else {
149 setTint(iv, color);
150 }
Jason Monk32508852017-01-18 09:17:13 -0500151 mTint = color;
Amin Shaikh32d786f2018-08-01 17:08:26 -0400152 updateIcon(iv, state, allowAnimations);
Jason Monk32508852017-01-18 09:17:13 -0500153 }
Amin Shaikha5902ea2018-03-29 14:23:29 -0400154 } else {
Amin Shaikh32d786f2018-08-01 17:08:26 -0400155 updateIcon(iv, state, allowAnimations);
Jason Monk32508852017-01-18 09:17:13 -0500156 }
Jason Monkdc35dcb2015-12-04 16:36:15 -0500157 }
158
Jason Monke5b770e2017-03-03 21:49:29 -0500159 protected int getColor(int state) {
160 return getColorForState(getContext(), state);
161 }
162
Amin Shaikh5df1a752018-04-03 13:39:53 -0400163 private void animateGrayScale(int fromColor, int toColor, ImageView iv,
164 final Runnable endRunnable) {
Evan Laird6026b052017-09-21 14:11:00 -0400165 if (iv instanceof AlphaControlledSlashImageView) {
166 ((AlphaControlledSlashImageView)iv)
167 .setFinalImageTintList(ColorStateList.valueOf(toColor));
168 }
Amin Shaikh5df1a752018-04-03 13:39:53 -0400169 if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400170 final float fromAlpha = Color.alpha(fromColor);
171 final float toAlpha = Color.alpha(toColor);
172 final float fromChannel = Color.red(fromColor);
173 final float toChannel = Color.red(toColor);
Jason Monk32508852017-01-18 09:17:13 -0500174
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400175 ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
176 anim.setDuration(QS_ANIM_LENGTH);
177 anim.addUpdateListener(animation -> {
178 float fraction = animation.getAnimatedFraction();
179 int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
180 int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
Jason Monk32508852017-01-18 09:17:13 -0500181
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400182 setTint(iv, Color.argb(alpha, channel, channel, channel));
183 });
Amin Shaikha5902ea2018-03-29 14:23:29 -0400184 anim.addListener(new AnimatorListenerAdapter() {
185 @Override
186 public void onAnimationEnd(Animator animation) {
187 endRunnable.run();
188 }
189 });
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400190 anim.start();
191 } else {
192 setTint(iv, toColor);
Amin Shaikha5902ea2018-03-29 14:23:29 -0400193 endRunnable.run();
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400194 }
Jason Monk32508852017-01-18 09:17:13 -0500195 }
196
197 public static void setTint(ImageView iv, int color) {
198 iv.setImageTintList(ColorStateList.valueOf(color));
199 }
200
201
Jason Monkdc35dcb2015-12-04 16:36:15 -0500202 protected int getIconMeasureMode() {
203 return MeasureSpec.EXACTLY;
204 }
205
206 protected View createIcon() {
Jason Monk395617f2017-05-05 14:07:58 -0400207 final ImageView icon = new SlashImageView(mContext);
Jason Monkdc35dcb2015-12-04 16:36:15 -0500208 icon.setId(android.R.id.icon);
Jason Monkeae7c312016-02-04 13:00:48 -0500209 icon.setScaleType(ScaleType.FIT_CENTER);
Jason Monkdc35dcb2015-12-04 16:36:15 -0500210 return icon;
211 }
212
Xiaohui Chen2f3551b2016-04-07 10:37:25 -0700213 protected final int exactly(int size) {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500214 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
215 }
216
Xiaohui Chen2f3551b2016-04-07 10:37:25 -0700217 protected final void layout(View child, int left, int top) {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500218 child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
219 }
220}