blob: 31526bf8f5e12b6d8b77a50b2274dede777196b2 [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;
Fabian Kozynski00d494d2019-04-04 09:53:50 -040050 private QSTile.Icon mLastIcon;
Jason Monkdc35dcb2015-12-04 16:36:15 -050051
Jason Monk702e2eb2017-03-03 16:53:44 -050052 public QSIconViewImpl(Context context) {
Jason Monkdc35dcb2015-12-04 16:36:15 -050053 super(context);
54
55 final Resources res = context.getResources();
56 mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
Jason Monkdc35dcb2015-12-04 16:36:15 -050057
58 mIcon = createIcon();
59 addView(mIcon);
60 }
61
Jason Monk1aec93f2016-03-01 09:39:30 -050062 public void disableAnimation() {
63 mAnimationEnabled = false;
64 }
65
Xiaohui Chen2f3551b2016-04-07 10:37:25 -070066 public View getIconView() {
67 return mIcon;
68 }
69
Jason Monkdc35dcb2015-12-04 16:36:15 -050070 @Override
71 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
72 final int w = MeasureSpec.getSize(widthMeasureSpec);
73 final int iconSpec = exactly(mIconSizePx);
74 mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
Fabian Kozynskibca09b72019-04-04 15:57:42 -040075 setMeasuredDimension(w, mIcon.getMeasuredHeight());
Jason Monkdc35dcb2015-12-04 16:36:15 -050076 }
77
78 @Override
Fabian Kozynski00d494d2019-04-04 09:53:50 -040079 public String toString() {
80 final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
81 sb.append("state=" + mState);
82 sb.append(", tint=" + mTint);
83 if (mLastIcon != null) sb.append(", lastIcon=" + mLastIcon.toString());
84 sb.append("]");
85 return sb.toString();
86 }
87
88 @Override
Jason Monkdc35dcb2015-12-04 16:36:15 -050089 protected void onLayout(boolean changed, int l, int t, int r, int b) {
90 final int w = getMeasuredWidth();
Jason Monkdc35dcb2015-12-04 16:36:15 -050091 int top = 0;
92 final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
93 layout(mIcon, iconLeft, top);
94 }
95
Amin Shaikh32d786f2018-08-01 17:08:26 -040096 public void setIcon(State state, boolean allowAnimations) {
97 setIcon((ImageView) mIcon, state, allowAnimations);
Jason Monkdc35dcb2015-12-04 16:36:15 -050098 }
99
Amin Shaikh32d786f2018-08-01 17:08:26 -0400100 protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
Evan Lairdb3daf2b2017-10-17 16:00:29 -0400101 final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
102 if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
Jason Monk395617f2017-05-05 14:07:58 -0400103 || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
Amin Shaikh32d786f2018-08-01 17:08:26 -0400104 boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400105 mLastIcon = icon;
Evan Lairdb3daf2b2017-10-17 16:00:29 -0400106 Drawable d = icon != null
107 ? shouldAnimate ? icon.getDrawable(mContext)
108 : icon.getInvisibleDrawable(mContext) : null;
109 int padding = icon != null ? icon.getPadding() : 0;
Jason Monk702e2eb2017-03-03 16:53:44 -0500110 if (d != null) {
Jason Monkd686e9c2017-03-27 15:45:32 -0400111 d.setAutoMirrored(false);
Evan Laird3367c332017-07-07 13:24:56 -0400112 d.setLayoutDirection(getLayoutDirection());
Jason Monkdc35dcb2015-12-04 16:36:15 -0500113 }
Evan Laird929bd542017-07-12 16:36:06 -0400114
115 if (iv instanceof SlashImageView) {
116 ((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
Jason Monkcb2307c2018-01-15 14:19:45 -0500117 ((SlashImageView) iv).setState(null, d);
Evan Laird929bd542017-07-12 16:36:06 -0400118 } else {
119 iv.setImageDrawable(d);
Jason Monk395617f2017-05-05 14:07:58 -0400120 }
Evan Laird929bd542017-07-12 16:36:06 -0400121
Evan Lairdb3daf2b2017-10-17 16:00:29 -0400122 iv.setTag(R.id.qs_icon_tag, icon);
Jason Monk395617f2017-05-05 14:07:58 -0400123 iv.setTag(R.id.qs_slash_tag, state.slash);
Jason Monkb53b6c52016-02-24 17:25:49 -0500124 iv.setPadding(0, padding, 0, padding);
Jason Monke645aee2017-03-31 13:19:26 -0400125 if (d instanceof Animatable2) {
126 Animatable2 a = (Animatable2) d;
Jason Monk66239fb2015-12-21 14:27:00 -0500127 a.start();
Jason Monke645aee2017-03-31 13:19:26 -0400128 if (state.isTransient) {
129 a.registerAnimationCallback(new AnimationCallback() {
130 @Override
131 public void onAnimationEnd(Drawable drawable) {
132 a.start();
133 }
134 });
Jason Monkdc35dcb2015-12-04 16:36:15 -0500135 }
136 }
137 }
Jason Monke5b770e2017-03-03 21:49:29 -0500138 }
139
Amin Shaikh32d786f2018-08-01 17:08:26 -0400140 private boolean shouldAnimate(ImageView iv) {
141 return mAnimationEnabled && iv.isShown() && iv.getDrawable() != null;
142 }
143
144 protected void setIcon(ImageView iv, QSTile.State state, boolean allowAnimations) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000145 if (state.disabledByPolicy) {
146 iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
147 } else {
148 iv.clearColorFilter();
149 }
Jason Monk32508852017-01-18 09:17:13 -0500150 if (state.state != mState) {
Jason Monke5b770e2017-03-03 21:49:29 -0500151 int color = getColor(state.state);
Jason Monk32508852017-01-18 09:17:13 -0500152 mState = state.state;
Amin Shaikh32d786f2018-08-01 17:08:26 -0400153 if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
154 animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
Jason Monk32508852017-01-18 09:17:13 -0500155 mTint = color;
156 } else {
Evan Laird6026b052017-09-21 14:11:00 -0400157 if (iv instanceof AlphaControlledSlashImageView) {
158 ((AlphaControlledSlashImageView)iv)
159 .setFinalImageTintList(ColorStateList.valueOf(color));
160 } else {
161 setTint(iv, color);
162 }
Jason Monk32508852017-01-18 09:17:13 -0500163 mTint = color;
Amin Shaikh32d786f2018-08-01 17:08:26 -0400164 updateIcon(iv, state, allowAnimations);
Jason Monk32508852017-01-18 09:17:13 -0500165 }
Amin Shaikha5902ea2018-03-29 14:23:29 -0400166 } else {
Amin Shaikh32d786f2018-08-01 17:08:26 -0400167 updateIcon(iv, state, allowAnimations);
Jason Monk32508852017-01-18 09:17:13 -0500168 }
Jason Monkdc35dcb2015-12-04 16:36:15 -0500169 }
170
Jason Monke5b770e2017-03-03 21:49:29 -0500171 protected int getColor(int state) {
172 return getColorForState(getContext(), state);
173 }
174
Amin Shaikh5df1a752018-04-03 13:39:53 -0400175 private void animateGrayScale(int fromColor, int toColor, ImageView iv,
176 final Runnable endRunnable) {
Evan Laird6026b052017-09-21 14:11:00 -0400177 if (iv instanceof AlphaControlledSlashImageView) {
178 ((AlphaControlledSlashImageView)iv)
179 .setFinalImageTintList(ColorStateList.valueOf(toColor));
180 }
Amin Shaikh5df1a752018-04-03 13:39:53 -0400181 if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400182 final float fromAlpha = Color.alpha(fromColor);
183 final float toAlpha = Color.alpha(toColor);
184 final float fromChannel = Color.red(fromColor);
185 final float toChannel = Color.red(toColor);
Jason Monk32508852017-01-18 09:17:13 -0500186
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400187 ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
188 anim.setDuration(QS_ANIM_LENGTH);
189 anim.addUpdateListener(animation -> {
190 float fraction = animation.getAnimatedFraction();
191 int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
192 int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
Jason Monk32508852017-01-18 09:17:13 -0500193
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400194 setTint(iv, Color.argb(alpha, channel, channel, channel));
195 });
Amin Shaikha5902ea2018-03-29 14:23:29 -0400196 anim.addListener(new AnimatorListenerAdapter() {
197 @Override
198 public void onAnimationEnd(Animator animation) {
199 endRunnable.run();
200 }
201 });
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400202 anim.start();
203 } else {
204 setTint(iv, toColor);
Amin Shaikha5902ea2018-03-29 14:23:29 -0400205 endRunnable.run();
Evan Lairdbdcd1cc2017-07-05 17:10:04 -0400206 }
Jason Monk32508852017-01-18 09:17:13 -0500207 }
208
209 public static void setTint(ImageView iv, int color) {
210 iv.setImageTintList(ColorStateList.valueOf(color));
211 }
212
213
Jason Monkdc35dcb2015-12-04 16:36:15 -0500214 protected int getIconMeasureMode() {
215 return MeasureSpec.EXACTLY;
216 }
217
218 protected View createIcon() {
Jason Monk395617f2017-05-05 14:07:58 -0400219 final ImageView icon = new SlashImageView(mContext);
Jason Monkdc35dcb2015-12-04 16:36:15 -0500220 icon.setId(android.R.id.icon);
Jason Monkeae7c312016-02-04 13:00:48 -0500221 icon.setScaleType(ScaleType.FIT_CENTER);
Jason Monkdc35dcb2015-12-04 16:36:15 -0500222 return icon;
223 }
224
Xiaohui Chen2f3551b2016-04-07 10:37:25 -0700225 protected final int exactly(int size) {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500226 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
227 }
228
Xiaohui Chen2f3551b2016-04-07 10:37:25 -0700229 protected final void layout(View child, int left, int top) {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500230 child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
231 }
232}