blob: f4af9ae21b7544c4e1a6563a84e50d29f4011d35 [file] [log] [blame]
Joe Onorato0cbda992010-05-02 16:28:15 -07001/*
2 * Copyright (C) 2008 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
Joe Onorato79de0c52010-05-26 17:03:26 -040017package com.android.systemui.statusbar;
Joe Onorato0cbda992010-05-02 16:28:15 -070018
Beverly1be62f42018-12-19 17:17:48 -050019import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
Evan Lairde1d13c92018-03-20 16:58:01 -040020
Selim Cinek49014f82016-11-04 14:55:30 -070021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.ObjectAnimator;
Selim Cinek875ba9b2017-02-13 16:20:17 -080024import android.animation.ValueAnimator;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070025import android.app.Notification;
Joe Onorato0cbda992010-05-02 16:28:15 -070026import android.content.Context;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070027import android.content.pm.ApplicationInfo;
Evan Lairde1d13c92018-03-20 16:58:01 -040028import android.content.res.ColorStateList;
Selim Cinek3e7592d2016-04-11 09:35:54 +080029import android.content.res.Configuration;
Joe Onorato0cbda992010-05-02 16:28:15 -070030import android.content.res.Resources;
Joe Onorato0cbda992010-05-02 16:28:15 -070031import android.graphics.Canvas;
Adrian Roos456e0052017-04-04 16:44:29 -070032import android.graphics.Color;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +020033import android.graphics.ColorMatrixColorFilter;
Joe Onorato6c01a112010-10-04 17:38:47 -040034import android.graphics.Paint;
35import android.graphics.Rect;
John Spurlockde84f0e2013-06-12 12:41:00 -040036import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040037import android.graphics.drawable.Icon;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070038import android.os.Parcelable;
Jeff Sharkeyded653b2012-09-27 19:09:24 -070039import android.os.UserHandle;
Selim Cinek72fc8db2017-06-06 18:07:47 -070040import android.service.notification.StatusBarNotification;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070041import android.text.TextUtils;
Daniel Sandler05e24142011-11-10 11:56:49 -050042import android.util.AttributeSet;
Selim Cinek49014f82016-11-04 14:55:30 -070043import android.util.FloatProperty;
Joe Onoratof9ec03c2010-09-23 15:09:18 -070044import android.util.Log;
Selim Cinek49014f82016-11-04 14:55:30 -070045import android.util.Property;
Anthony Chen55e8e1e2016-01-08 10:31:46 -080046import android.util.TypedValue;
Joe Onorato0cbda992010-05-02 16:28:15 -070047import android.view.ViewDebug;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070048import android.view.accessibility.AccessibilityEvent;
Selim Cinek49014f82016-11-04 14:55:30 -070049import android.view.animation.Interpolator;
Winsonc0d70582016-01-29 10:24:39 -080050
Lucas Dupinde07d212018-10-23 17:47:03 -070051import androidx.core.graphics.ColorUtils;
52
Joe Onorato0cbda992010-05-02 16:28:15 -070053import com.android.internal.statusbar.StatusBarIcon;
Lucas Dupina291d192018-06-07 13:59:42 -070054import com.android.internal.util.ContrastColorUtil;
Selim Cinek49014f82016-11-04 14:55:30 -070055import com.android.systemui.Interpolators;
Joe Onorato6c01a112010-10-04 17:38:47 -040056import com.android.systemui.R;
Adrian Roos456e0052017-04-04 16:44:29 -070057import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
Selim Cinek49014f82016-11-04 14:55:30 -070058import com.android.systemui.statusbar.notification.NotificationUtils;
Joe Onorato6c01a112010-10-04 17:38:47 -040059
John Spurlockde84f0e2013-06-12 12:41:00 -040060import java.text.NumberFormat;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +020061import java.util.Arrays;
John Spurlockde84f0e2013-06-12 12:41:00 -040062
Evan Lairde1d13c92018-03-20 16:58:01 -040063public class StatusBarIconView extends AnimatedImageView implements StatusIconDisplayable {
Selim Cinek875ba9b2017-02-13 16:20:17 -080064 public static final int NO_COLOR = 0;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +020065
66 /**
67 * Multiply alpha values with (1+DARK_ALPHA_BOOST) when dozing. The chosen value boosts
68 * everything above 30% to 50%, making it appear on 1bit color depths.
69 */
70 private static final float DARK_ALPHA_BOOST = 0.67f;
Evan Laird39ea8102018-05-18 19:49:07 -040071 /**
72 * Status icons are currently drawn with the intention of being 17dp tall, but we
73 * want to scale them (in a way that doesn't require an asset dump) down 2dp. So
Fabian Kozynski61e1ba62019-05-15 10:04:39 -040074 * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all
75 * values will be in px.
Evan Laird39ea8102018-05-18 19:49:07 -040076 */
Fabian Kozynski61e1ba62019-05-15 10:04:39 -040077 private float mSystemIconDesiredHeight = 15f;
78 private float mSystemIconIntrinsicHeight = 17f;
79 private float mSystemIconDefaultScale = mSystemIconDesiredHeight / mSystemIconIntrinsicHeight;
Selim Cinek875ba9b2017-02-13 16:20:17 -080080 private final int ANIMATION_DURATION_FAST = 100;
81
Selim Cinek49014f82016-11-04 14:55:30 -070082 public static final int STATE_ICON = 0;
83 public static final int STATE_DOT = 1;
84 public static final int STATE_HIDDEN = 2;
Joe Onorato0cbda992010-05-02 16:28:15 -070085
Selim Cinek49014f82016-11-04 14:55:30 -070086 private static final String TAG = "StatusBarIconView";
87 private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
88 = new FloatProperty<StatusBarIconView>("iconAppearAmount") {
89
90 @Override
91 public void setValue(StatusBarIconView object, float value) {
92 object.setIconAppearAmount(value);
93 }
94
95 @Override
96 public Float get(StatusBarIconView object) {
97 return object.getIconAppearAmount();
98 }
99 };
Selim Cinek5b5beb012016-11-08 18:11:58 -0800100 private static final Property<StatusBarIconView, Float> DOT_APPEAR_AMOUNT
Selim Cinek49014f82016-11-04 14:55:30 -0700101 = new FloatProperty<StatusBarIconView>("dot_appear_amount") {
102
103 @Override
104 public void setValue(StatusBarIconView object, float value) {
105 object.setDotAppearAmount(value);
106 }
107
108 @Override
109 public Float get(StatusBarIconView object) {
110 return object.getDotAppearAmount();
111 }
112 };
113
114 private boolean mAlwaysScaleIcon;
Selim Cinek195dfc52019-05-30 19:35:05 -0700115 private int mStatusBarIconDrawingSizeIncreased = 1;
Adrian Roos138f0342017-04-28 09:11:28 -0700116 private int mStatusBarIconDrawingSize = 1;
117 private int mStatusBarIconSize = 1;
Joe Onorato0cbda992010-05-02 16:28:15 -0700118 private StatusBarIcon mIcon;
119 @ViewDebug.ExportedProperty private String mSlot;
Joe Onorato6c01a112010-10-04 17:38:47 -0400120 private Drawable mNumberBackground;
121 private Paint mNumberPain;
122 private int mNumberX;
123 private int mNumberY;
124 private String mNumberText;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700125 private StatusBarNotification mNotification;
Jason Monk3b230072015-05-29 11:11:29 -0400126 private final boolean mBlocked;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800127 private int mDensity;
Lucas Dupinde07d212018-10-23 17:47:03 -0700128 private boolean mNightMode;
Selim Cinek281c2022016-10-13 19:14:43 -0700129 private float mIconScale = 1.0f;
Evan Lairdbaccb462018-06-01 16:29:02 -0400130 private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Selim Cinek49014f82016-11-04 14:55:30 -0700131 private float mDotRadius;
132 private int mStaticDotRadius;
133 private int mVisibleState = STATE_ICON;
134 private float mIconAppearAmount = 1.0f;
135 private ObjectAnimator mIconAppearAnimator;
136 private ObjectAnimator mDotAnimator;
137 private float mDotAppearAmount;
Selim Cinek2b549f42016-11-22 16:38:51 -0800138 private OnVisibilityChangedListener mOnVisibilityChangedListener;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800139 private int mDrawableColor;
140 private int mIconColor;
Adrian Roos456e0052017-04-04 16:44:29 -0700141 private int mDecorColor;
Selim Cinek195dfc52019-05-30 19:35:05 -0700142 private float mDozeAmount;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800143 private ValueAnimator mColorAnimator;
144 private int mCurrentSetColor = NO_COLOR;
145 private int mAnimationStartColor = NO_COLOR;
146 private final ValueAnimator.AnimatorUpdateListener mColorUpdater
147 = animation -> {
148 int newColor = NotificationUtils.interpolateColors(mAnimationStartColor, mIconColor,
149 animation.getAnimatedFraction());
150 setColorInternal(newColor);
151 };
Adrian Roos456e0052017-04-04 16:44:29 -0700152 private final NotificationIconDozeHelper mDozer;
Lucas Dupin83519da2017-06-21 11:58:31 -0700153 private int mContrastedDrawableColor;
154 private int mCachedContrastBackgroundColor = NO_COLOR;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200155 private float[] mMatrix;
156 private ColorMatrixColorFilter mMatrixColorFilter;
Selim Cinekd95ca7c2017-07-26 12:20:38 -0700157 private boolean mIsInShelf;
Selim Cinek887da3c2017-10-06 13:37:32 -0700158 private Runnable mLayoutRunnable;
Selim Cinekd03518c2018-03-15 12:13:51 -0700159 private boolean mDismissed;
160 private Runnable mOnDismissListener;
Selim Cinek195dfc52019-05-30 19:35:05 -0700161 private boolean mIncreasedSize;
Joe Onorato0cbda992010-05-02 16:28:15 -0700162
Selim Cinek72fc8db2017-06-06 18:07:47 -0700163 public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
164 this(context, slot, sbn, false);
Jason Monk3b230072015-05-29 11:11:29 -0400165 }
166
Selim Cinek72fc8db2017-06-06 18:07:47 -0700167 public StatusBarIconView(Context context, String slot, StatusBarNotification sbn,
Jason Monk3b230072015-05-29 11:11:29 -0400168 boolean blocked) {
Joe Onorato0cbda992010-05-02 16:28:15 -0700169 super(context);
Adrian Roos456e0052017-04-04 16:44:29 -0700170 mDozer = new NotificationIconDozeHelper(context);
Jason Monk3b230072015-05-29 11:11:29 -0400171 mBlocked = blocked;
Joe Onorato0cbda992010-05-02 16:28:15 -0700172 mSlot = slot;
Joe Onorato6c01a112010-10-04 17:38:47 -0400173 mNumberPain = new Paint();
174 mNumberPain.setTextAlign(Paint.Align.CENTER);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700175 mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
Joe Onorato6c01a112010-10-04 17:38:47 -0400176 mNumberPain.setAntiAlias(true);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700177 setNotification(sbn);
Selim Cinek3e7592d2016-04-11 09:35:54 +0800178 setScaleType(ScaleType.CENTER);
179 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Lucas Dupinde07d212018-10-23 17:47:03 -0700180 Configuration configuration = context.getResources().getConfiguration();
181 mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
182 == Configuration.UI_MODE_NIGHT_YES;
183 initializeDecorColor();
Selim Cinek49014f82016-11-04 14:55:30 -0700184 reloadDimens();
Evan Laird20b87bf2018-04-12 09:54:11 -0400185 maybeUpdateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800186 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400187
Evan Laird20b87bf2018-04-12 09:54:11 -0400188 /** Should always be preceded by {@link #reloadDimens()} */
Adrian Roos138f0342017-04-28 09:11:28 -0700189 private void maybeUpdateIconScaleDimens() {
Daniel Sandler7579bca2011-08-18 15:47:26 -0400190 // We do not resize and scale system icons (on the right), only notification icons (on the
191 // left).
Selim Cinek3e7592d2016-04-11 09:35:54 +0800192 if (mNotification != null || mAlwaysScaleIcon) {
Evan Laird39ea8102018-05-18 19:49:07 -0400193 updateIconScaleForNotifications();
194 } else {
195 updateIconScaleForSystemIcons();
Daniel Sandler7579bca2011-08-18 15:47:26 -0400196 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800197 }
Daniel Sandlerabff0322011-09-27 11:19:34 -0400198
Evan Laird39ea8102018-05-18 19:49:07 -0400199 private void updateIconScaleForNotifications() {
Selim Cinek195dfc52019-05-30 19:35:05 -0700200 final float imageBounds = mIncreasedSize ?
201 mStatusBarIconDrawingSizeIncreased : mStatusBarIconDrawingSize;
Adrian Roos138f0342017-04-28 09:11:28 -0700202 final int outerBounds = mStatusBarIconSize;
Selim Cinek195dfc52019-05-30 19:35:05 -0700203 mIconScale = imageBounds / (float)outerBounds;
Selim Cinek3ddda6d2018-06-08 00:26:48 -0700204 updatePivot();
Selim Cinek281c2022016-10-13 19:14:43 -0700205 }
206
Fabian Kozynski718dab92019-05-02 17:28:02 -0400207 // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height
208 // for the icon, it uses the default SCALE (15f / 17f) which is the old behavior
Evan Laird39ea8102018-05-18 19:49:07 -0400209 private void updateIconScaleForSystemIcons() {
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400210 float iconHeight = getIconHeight();
Fabian Kozynski718dab92019-05-02 17:28:02 -0400211 if (iconHeight != 0) {
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400212 mIconScale = mSystemIconDesiredHeight / iconHeight;
Fabian Kozynski718dab92019-05-02 17:28:02 -0400213 } else {
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400214 mIconScale = mSystemIconDefaultScale;
Fabian Kozynski718dab92019-05-02 17:28:02 -0400215 }
216 }
217
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400218 private float getIconHeight() {
Fabian Kozynski718dab92019-05-02 17:28:02 -0400219 Drawable d = getDrawable();
220 if (d != null) {
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400221 return (float) getDrawable().getIntrinsicHeight();
Fabian Kozynski718dab92019-05-02 17:28:02 -0400222 } else {
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400223 return mSystemIconIntrinsicHeight;
Fabian Kozynski718dab92019-05-02 17:28:02 -0400224 }
Evan Laird39ea8102018-05-18 19:49:07 -0400225 }
226
Selim Cinek195dfc52019-05-30 19:35:05 -0700227 public float getIconScaleIncreased() {
228 return (float) mStatusBarIconDrawingSizeIncreased / mStatusBarIconDrawingSize;
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800229 }
230
Selim Cinek281c2022016-10-13 19:14:43 -0700231 public float getIconScale() {
232 return mIconScale;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800233 }
234
235 @Override
236 protected void onConfigurationChanged(Configuration newConfig) {
237 super.onConfigurationChanged(newConfig);
238 int density = newConfig.densityDpi;
239 if (density != mDensity) {
240 mDensity = density;
Evan Laird20b87bf2018-04-12 09:54:11 -0400241 reloadDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800242 updateDrawable();
Fabian Kozynski718dab92019-05-02 17:28:02 -0400243 maybeUpdateIconScaleDimens();
Selim Cinek49014f82016-11-04 14:55:30 -0700244 }
Lucas Dupinde07d212018-10-23 17:47:03 -0700245 boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
246 == Configuration.UI_MODE_NIGHT_YES;
247 if (nightMode != mNightMode) {
248 mNightMode = nightMode;
249 initializeDecorColor();
250 }
Selim Cinek49014f82016-11-04 14:55:30 -0700251 }
252
253 private void reloadDimens() {
254 boolean applyRadius = mDotRadius == mStaticDotRadius;
Evan Laird20b87bf2018-04-12 09:54:11 -0400255 Resources res = getResources();
256 mStaticDotRadius = res.getDimensionPixelSize(R.dimen.overflow_dot_radius);
257 mStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
Selim Cinek195dfc52019-05-30 19:35:05 -0700258 mStatusBarIconDrawingSizeIncreased =
Evan Laird20b87bf2018-04-12 09:54:11 -0400259 res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
260 mStatusBarIconDrawingSize =
261 res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
Selim Cinek49014f82016-11-04 14:55:30 -0700262 if (applyRadius) {
263 mDotRadius = mStaticDotRadius;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800264 }
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400265 mSystemIconDesiredHeight = res.getDimension(
266 com.android.internal.R.dimen.status_bar_system_icon_size);
267 mSystemIconIntrinsicHeight = res.getDimension(
268 com.android.internal.R.dimen.status_bar_system_icon_intrinsic_size);
269 mSystemIconDefaultScale = mSystemIconDesiredHeight / mSystemIconIntrinsicHeight;
Joe Onorato0cbda992010-05-02 16:28:15 -0700270 }
271
Selim Cinek72fc8db2017-06-06 18:07:47 -0700272 public void setNotification(StatusBarNotification notification) {
Christoph Studera0506e72014-07-31 20:27:39 +0200273 mNotification = notification;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700274 if (notification != null) {
275 setContentDescription(notification.getNotification());
276 }
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400277 maybeUpdateIconScaleDimens();
Christoph Studera0506e72014-07-31 20:27:39 +0200278 }
279
Daniel Sandler05e24142011-11-10 11:56:49 -0500280 public StatusBarIconView(Context context, AttributeSet attrs) {
281 super(context, attrs);
Adrian Roos456e0052017-04-04 16:44:29 -0700282 mDozer = new NotificationIconDozeHelper(context);
Jason Monk3b230072015-05-29 11:11:29 -0400283 mBlocked = false;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800284 mAlwaysScaleIcon = true;
Evan Laird20b87bf2018-04-12 09:54:11 -0400285 reloadDimens();
Fabian Kozynski61e1ba62019-05-15 10:04:39 -0400286 maybeUpdateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800287 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Daniel Sandler05e24142011-11-10 11:56:49 -0500288 }
289
Joe Onorato0cbda992010-05-02 16:28:15 -0700290 private static boolean streq(String a, String b) {
Joe Onorato66d7d012010-05-14 10:05:10 -0700291 if (a == b) {
292 return true;
293 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700294 if (a == null && b != null) {
295 return false;
296 }
297 if (a != null && b == null) {
298 return false;
299 }
300 return a.equals(b);
301 }
302
Dan Sandlerd63f9322015-05-06 15:18:49 -0400303 public boolean equalIcons(Icon a, Icon b) {
304 if (a == b) return true;
305 if (a.getType() != b.getType()) return false;
306 switch (a.getType()) {
307 case Icon.TYPE_RESOURCE:
308 return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId();
309 case Icon.TYPE_URI:
310 return a.getUriString().equals(b.getUriString());
311 default:
312 return false;
313 }
314 }
Joe Onorato005847b2010-06-04 16:08:02 -0400315 /**
316 * Returns whether the set succeeded.
317 */
318 public boolean set(StatusBarIcon icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -0400319 final boolean iconEquals = mIcon != null && equalIcons(mIcon.icon, icon.icon);
Joe Onorato005847b2010-06-04 16:08:02 -0400320 final boolean levelEquals = iconEquals
321 && mIcon.iconLevel == icon.iconLevel;
322 final boolean visibilityEquals = mIcon != null
323 && mIcon.visible == icon.visible;
Joe Onorato6c01a112010-10-04 17:38:47 -0400324 final boolean numberEquals = mIcon != null
325 && mIcon.number == icon.number;
326 mIcon = icon.clone();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700327 setContentDescription(icon.contentDescription);
Joe Onorato005847b2010-06-04 16:08:02 -0400328 if (!iconEquals) {
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800329 if (!updateDrawable(false /* no clear */)) return false;
Selim Cinek2b6eb8032016-12-29 14:22:21 +0100330 // we have to clear the grayscale tag since it may have changed
331 setTag(R.id.icon_is_grayscale, null);
Fabian Kozynski718dab92019-05-02 17:28:02 -0400332 // Maybe set scale based on icon height
333 maybeUpdateIconScaleDimens();
Joe Onorato0cbda992010-05-02 16:28:15 -0700334 }
Joe Onorato005847b2010-06-04 16:08:02 -0400335 if (!levelEquals) {
336 setImageLevel(icon.iconLevel);
Joe Onorato0cbda992010-05-02 16:28:15 -0700337 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400338
Joe Onorato6c01a112010-10-04 17:38:47 -0400339 if (!numberEquals) {
John Spurlock01534782014-01-13 11:59:22 -0500340 if (icon.number > 0 && getContext().getResources().getBoolean(
Joe Onorato8595a3d2010-11-19 18:12:07 -0800341 R.bool.config_statusBarShowNumber)) {
Joe Onorato6c01a112010-10-04 17:38:47 -0400342 if (mNumberBackground == null) {
343 mNumberBackground = getContext().getResources().getDrawable(
344 R.drawable.ic_notification_overlay);
345 }
346 placeNumber();
347 } else {
348 mNumberBackground = null;
349 mNumberText = null;
350 }
351 invalidate();
352 }
Joe Onorato005847b2010-06-04 16:08:02 -0400353 if (!visibilityEquals) {
Jason Monk3b230072015-05-29 11:11:29 -0400354 setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
Joe Onorato005847b2010-06-04 16:08:02 -0400355 }
Joe Onorato005847b2010-06-04 16:08:02 -0400356 return true;
Joe Onorato0cbda992010-05-02 16:28:15 -0700357 }
358
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800359 public void updateDrawable() {
360 updateDrawable(true /* with clear */);
361 }
362
363 private boolean updateDrawable(boolean withClear) {
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100364 if (mIcon == null) {
365 return false;
366 }
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800367 Drawable drawable;
368 try {
369 drawable = getIcon(mIcon);
370 } catch (OutOfMemoryError e) {
371 Log.w(TAG, "OOM while inflating " + mIcon.icon + " for slot " + mSlot);
372 return false;
373 }
374
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800375 if (drawable == null) {
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800376 Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800377 return false;
378 }
379 if (withClear) {
380 setImageDrawable(null);
381 }
382 setImageDrawable(drawable);
383 return true;
384 }
385
Selim Cinek72fc8db2017-06-06 18:07:47 -0700386 public Icon getSourceIcon() {
387 return mIcon.icon;
388 }
389
Joe Onoratof5510542010-06-01 07:46:41 -0700390 private Drawable getIcon(StatusBarIcon icon) {
391 return getIcon(getContext(), icon);
392 }
393
Joe Onorato0cbda992010-05-02 16:28:15 -0700394 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -0400395 * Returns the right icon to use for this item
John Spurlock209bede2013-07-17 12:23:27 -0400396 *
Dan Sandlerd63f9322015-05-06 15:18:49 -0400397 * @param context Context to use to get resources
Joe Onorato0cbda992010-05-02 16:28:15 -0700398 * @return Drawable for this item, or null if the package or item could not
399 * be found
400 */
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800401 public static Drawable getIcon(Context context, StatusBarIcon statusBarIcon) {
402 int userId = statusBarIcon.user.getIdentifier();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400403 if (userId == UserHandle.USER_ALL) {
Xiaohui Chen87d0e252015-07-30 15:38:16 -0700404 userId = UserHandle.USER_SYSTEM;
Joe Onorato0cbda992010-05-02 16:28:15 -0700405 }
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800406
407 Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
408
409 TypedValue typedValue = new TypedValue();
410 context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
411 float scaleFactor = typedValue.getFloat();
412
413 // No need to scale the icon, so return it as is.
414 if (scaleFactor == 1.f) {
415 return icon;
416 }
417
418 return new ScalingDrawableWrapper(icon, scaleFactor);
Joe Onorato0cbda992010-05-02 16:28:15 -0700419 }
Joe Onoratob77f53b2010-05-28 19:59:51 -0400420
421 public StatusBarIcon getStatusBarIcon() {
422 return mIcon;
423 }
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700424
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700425 @Override
426 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
427 super.onInitializeAccessibilityEvent(event);
428 if (mNotification != null) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700429 event.setParcelableData(mNotification.getNotification());
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700430 }
431 }
432
433 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400434 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
435 super.onSizeChanged(w, h, oldw, oldh);
436 if (mNumberBackground != null) {
437 placeNumber();
438 }
439 }
440
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700441 @Override
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100442 public void onRtlPropertiesChanged(int layoutDirection) {
443 super.onRtlPropertiesChanged(layoutDirection);
444 updateDrawable();
445 }
446
447 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400448 protected void onDraw(Canvas canvas) {
Selim Cinek49014f82016-11-04 14:55:30 -0700449 if (mIconAppearAmount > 0.0f) {
450 canvas.save();
451 canvas.scale(mIconScale * mIconAppearAmount, mIconScale * mIconAppearAmount,
452 getWidth() / 2, getHeight() / 2);
453 super.onDraw(canvas);
454 canvas.restore();
455 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400456
457 if (mNumberBackground != null) {
458 mNumberBackground.draw(canvas);
459 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain);
460 }
Selim Cinek49014f82016-11-04 14:55:30 -0700461 if (mDotAppearAmount != 0.0f) {
462 float radius;
Evan Lairdbaccb462018-06-01 16:29:02 -0400463 float alpha = Color.alpha(mDecorColor) / 255.f;
Selim Cinek49014f82016-11-04 14:55:30 -0700464 if (mDotAppearAmount <= 1.0f) {
465 radius = mDotRadius * mDotAppearAmount;
Selim Cinek49014f82016-11-04 14:55:30 -0700466 } else {
467 float fadeOutAmount = mDotAppearAmount - 1.0f;
Evan Lairdbaccb462018-06-01 16:29:02 -0400468 alpha = alpha * (1.0f - fadeOutAmount);
Selim Cinek49014f82016-11-04 14:55:30 -0700469 radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount);
470 }
471 mDotPaint.setAlpha((int) (alpha * 255));
Evan Laird20b87bf2018-04-12 09:54:11 -0400472 canvas.drawCircle(mStatusBarIconSize / 2, getHeight() / 2, radius, mDotPaint);
Selim Cinek49014f82016-11-04 14:55:30 -0700473 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400474 }
475
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700476 @Override
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700477 protected void debug(int depth) {
478 super.debug(depth);
479 Log.d("View", debugIndent(depth) + "slot=" + mSlot);
480 Log.d("View", debugIndent(depth) + "icon=" + mIcon);
481 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400482
483 void placeNumber() {
Daniel Sandlerebce0112011-06-16 16:44:51 -0400484 final String str;
John Spurlock01534782014-01-13 11:59:22 -0500485 final int tooBig = getContext().getResources().getInteger(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400486 android.R.integer.status_bar_notification_info_maxnum);
487 if (mIcon.number > tooBig) {
John Spurlock01534782014-01-13 11:59:22 -0500488 str = getContext().getResources().getString(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400489 android.R.string.status_bar_notification_info_overflow);
490 } else {
491 NumberFormat f = NumberFormat.getIntegerInstance();
492 str = f.format(mIcon.number);
493 }
494 mNumberText = str;
495
Joe Onorato6c01a112010-10-04 17:38:47 -0400496 final int w = getWidth();
497 final int h = getHeight();
498 final Rect r = new Rect();
499 mNumberPain.getTextBounds(str, 0, str.length(), r);
500 final int tw = r.right - r.left;
501 final int th = r.bottom - r.top;
502 mNumberBackground.getPadding(r);
503 int dw = r.left + tw + r.right;
504 if (dw < mNumberBackground.getMinimumWidth()) {
505 dw = mNumberBackground.getMinimumWidth();
506 }
507 mNumberX = w-r.right-((dw-r.right-r.left)/2);
508 int dh = r.top + th + r.bottom;
509 if (dh < mNumberBackground.getMinimumWidth()) {
510 dh = mNumberBackground.getMinimumWidth();
511 }
512 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2);
513 mNumberBackground.setBounds(w-dw, h-dh, w, h);
514 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700515
516 private void setContentDescription(Notification notification) {
517 if (notification != null) {
Adrian Rooseba05822016-04-22 17:09:27 -0700518 String d = contentDescForNotification(mContext, notification);
519 if (!TextUtils.isEmpty(d)) {
520 setContentDescription(d);
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700521 }
522 }
523 }
Daniel Sandler7579bca2011-08-18 15:47:26 -0400524
525 public String toString() {
John Spurlock209bede2013-07-17 12:23:27 -0400526 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
Daniel Sandler7579bca2011-08-18 15:47:26 -0400527 + " notification=" + mNotification + ")";
528 }
Jason Monk3b230072015-05-29 11:11:29 -0400529
Selim Cinek72fc8db2017-06-06 18:07:47 -0700530 public StatusBarNotification getNotification() {
531 return mNotification;
532 }
533
Jason Monk3b230072015-05-29 11:11:29 -0400534 public String getSlot() {
535 return mSlot;
536 }
Adrian Rooseba05822016-04-22 17:09:27 -0700537
538
539 public static String contentDescForNotification(Context c, Notification n) {
Adrian Roosa8e18ef2016-05-26 17:17:02 -0700540 String appName = "";
541 try {
542 Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
543 appName = builder.loadHeaderAppName();
544 } catch (RuntimeException e) {
545 Log.e(TAG, "Unable to recover builder", e);
546 // Trying to get the app name from the app info instead.
547 Parcelable appInfo = n.extras.getParcelable(
548 Notification.EXTRA_BUILDER_APPLICATION_INFO);
549 if (appInfo instanceof ApplicationInfo) {
550 appName = String.valueOf(((ApplicationInfo) appInfo).loadLabel(
551 c.getPackageManager()));
552 }
553 }
Adrian Roos51548e62016-04-28 11:20:26 -0700554
Julia Reynolds7f9ce782016-05-17 15:34:34 -0400555 CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
Adrian Roosf76600e2017-04-19 15:32:43 -0700556 CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
Adrian Rooseba05822016-04-22 17:09:27 -0700557 CharSequence ticker = n.tickerText;
Adrian Roos51548e62016-04-28 11:20:26 -0700558
Adrian Roosf76600e2017-04-19 15:32:43 -0700559 // Some apps just put the app name into the title
560 CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title;
561
562 CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText
Adrian Roos6e782a52017-03-30 18:34:10 -0700563 : !TextUtils.isEmpty(ticker) ? ticker : "";
Adrian Roos51548e62016-04-28 11:20:26 -0700564
565 return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
Adrian Rooseba05822016-04-22 17:09:27 -0700566 }
Adrian Roos51548e62016-04-28 11:20:26 -0700567
Selim Cinek875ba9b2017-02-13 16:20:17 -0800568 /**
569 * Set the color that is used to draw decoration like the overflow dot. This will not be applied
570 * to the drawable.
571 */
572 public void setDecorColor(int iconTint) {
Adrian Roos456e0052017-04-04 16:44:29 -0700573 mDecorColor = iconTint;
574 updateDecorColor();
575 }
576
Lucas Dupinde07d212018-10-23 17:47:03 -0700577 private void initializeDecorColor() {
578 if (mNotification != null) {
579 setDecorColor(getContext().getColor(mNightMode
580 ? com.android.internal.R.color.notification_default_color_dark
581 : com.android.internal.R.color.notification_default_color_light));
582 }
583 }
584
Adrian Roos456e0052017-04-04 16:44:29 -0700585 private void updateDecorColor() {
Selim Cinek195dfc52019-05-30 19:35:05 -0700586 int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDozeAmount);
Adrian Roos456e0052017-04-04 16:44:29 -0700587 if (mDotPaint.getColor() != color) {
588 mDotPaint.setColor(color);
589
590 if (mDotAppearAmount != 0) {
591 invalidate();
592 }
593 }
Selim Cinek49014f82016-11-04 14:55:30 -0700594 }
595
Selim Cinek875ba9b2017-02-13 16:20:17 -0800596 /**
597 * Set the static color that should be used for the drawable of this icon if it's not
598 * transitioning this also immediately sets the color.
599 */
600 public void setStaticDrawableColor(int color) {
601 mDrawableColor = color;
602 setColorInternal(color);
Lucas Dupin83519da2017-06-21 11:58:31 -0700603 updateContrastedStaticColor();
Selim Cinek875ba9b2017-02-13 16:20:17 -0800604 mIconColor = color;
Adrian Roos456e0052017-04-04 16:44:29 -0700605 mDozer.setColor(color);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800606 }
607
608 private void setColorInternal(int color) {
Adrian Roosf3eacd32017-04-14 16:35:58 -0700609 mCurrentSetColor = color;
610 updateIconColor();
611 }
612
613 private void updateIconColor() {
614 if (mCurrentSetColor != NO_COLOR) {
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200615 if (mMatrixColorFilter == null) {
616 mMatrix = new float[4 * 5];
617 mMatrixColorFilter = new ColorMatrixColorFilter(mMatrix);
618 }
619 int color = NotificationUtils.interpolateColors(
Selim Cinek195dfc52019-05-30 19:35:05 -0700620 mCurrentSetColor, Color.WHITE, mDozeAmount);
621 updateTintMatrix(mMatrix, color, DARK_ALPHA_BOOST * mDozeAmount);
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200622 mMatrixColorFilter.setColorMatrixArray(mMatrix);
Selim Cinek1280a722019-01-29 13:08:23 -0800623 setColorFilter(null); // setColorFilter only invalidates if the instance changed.
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200624 setColorFilter(mMatrixColorFilter);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800625 } else {
Selim Cinek195dfc52019-05-30 19:35:05 -0700626 mDozer.updateGrayscale(this, mDozeAmount);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800627 }
Selim Cinek875ba9b2017-02-13 16:20:17 -0800628 }
629
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200630 /**
631 * Updates {@param array} such that it represents a matrix that changes RGB to {@param color}
Adrian Roos24dbff32017-08-02 16:57:11 +0200632 * and multiplies the alpha channel with the color's alpha+{@param alphaBoost}.
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200633 */
634 private static void updateTintMatrix(float[] array, int color, float alphaBoost) {
635 Arrays.fill(array, 0);
636 array[4] = Color.red(color);
637 array[9] = Color.green(color);
638 array[14] = Color.blue(color);
Adrian Roos24dbff32017-08-02 16:57:11 +0200639 array[18] = Color.alpha(color) / 255f + alphaBoost;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200640 }
641
Selim Cinek875ba9b2017-02-13 16:20:17 -0800642 public void setIconColor(int iconColor, boolean animate) {
643 if (mIconColor != iconColor) {
644 mIconColor = iconColor;
645 if (mColorAnimator != null) {
646 mColorAnimator.cancel();
647 }
648 if (mCurrentSetColor == iconColor) {
649 return;
650 }
651 if (animate && mCurrentSetColor != NO_COLOR) {
652 mAnimationStartColor = mCurrentSetColor;
653 mColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
654 mColorAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
655 mColorAnimator.setDuration(ANIMATION_DURATION_FAST);
656 mColorAnimator.addUpdateListener(mColorUpdater);
657 mColorAnimator.addListener(new AnimatorListenerAdapter() {
658 @Override
659 public void onAnimationEnd(Animator animation) {
660 mColorAnimator = null;
661 mAnimationStartColor = NO_COLOR;
662 }
663 });
664 mColorAnimator.start();
665 } else {
666 setColorInternal(iconColor);
667 }
668 }
669 }
670
671 public int getStaticDrawableColor() {
672 return mDrawableColor;
673 }
674
Lucas Dupin83519da2017-06-21 11:58:31 -0700675 /**
676 * A drawable color that passes GAR on a specific background.
677 * This value is cached.
678 *
679 * @param backgroundColor Background to test against.
680 * @return GAR safe version of {@link StatusBarIconView#getStaticDrawableColor()}.
681 */
682 int getContrastedStaticDrawableColor(int backgroundColor) {
683 if (mCachedContrastBackgroundColor != backgroundColor) {
684 mCachedContrastBackgroundColor = backgroundColor;
685 updateContrastedStaticColor();
686 }
687 return mContrastedDrawableColor;
688 }
689
690 private void updateContrastedStaticColor() {
Lucas Dupinddbaf842017-06-23 09:30:49 -0700691 if (Color.alpha(mCachedContrastBackgroundColor) != 255) {
692 mContrastedDrawableColor = mDrawableColor;
Lucas Dupin83519da2017-06-21 11:58:31 -0700693 return;
694 }
695 // We'll modify the color if it doesn't pass GAR
696 int contrastedColor = mDrawableColor;
Lucas Dupina291d192018-06-07 13:59:42 -0700697 if (!ContrastColorUtil.satisfiesTextContrast(mCachedContrastBackgroundColor,
Lucas Dupin83519da2017-06-21 11:58:31 -0700698 contrastedColor)) {
699 float[] hsl = new float[3];
700 ColorUtils.colorToHSL(mDrawableColor, hsl);
701 // This is basically a light grey, pushing the color will only distort it.
702 // Best thing to do in here is to fallback to the default color.
703 if (hsl[1] < 0.2f) {
704 contrastedColor = Notification.COLOR_DEFAULT;
705 }
Lucas Dupinf03e7522018-06-25 16:21:13 -0700706 boolean isDark = !ContrastColorUtil.isColorLight(mCachedContrastBackgroundColor);
Lucas Dupina291d192018-06-07 13:59:42 -0700707 contrastedColor = ContrastColorUtil.resolveContrastColor(mContext,
Lucas Dupinf03e7522018-06-25 16:21:13 -0700708 contrastedColor, mCachedContrastBackgroundColor, isDark);
Lucas Dupin83519da2017-06-21 11:58:31 -0700709 }
710 mContrastedDrawableColor = contrastedColor;
711 }
712
Evan Laird20b87bf2018-04-12 09:54:11 -0400713 @Override
Selim Cinek5b5beb012016-11-08 18:11:58 -0800714 public void setVisibleState(int state) {
715 setVisibleState(state, true /* animate */, null /* endRunnable */);
716 }
717
718 public void setVisibleState(int state, boolean animate) {
719 setVisibleState(state, animate, null);
720 }
721
722 @Override
723 public boolean hasOverlappingRendering() {
724 return false;
725 }
726
727 public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
Selim Cinekd03518c2018-03-15 12:13:51 -0700728 setVisibleState(visibleState, animate, endRunnable, 0);
729 }
730
731 /**
732 * Set the visibleState of this view.
733 *
734 * @param visibleState The new state.
735 * @param animate Should we animate?
736 * @param endRunnable The runnable to run at the end.
737 * @param duration The duration of an animation or 0 if the default should be taken.
738 */
739 public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable,
740 long duration) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800741 boolean runnableAdded = false;
Selim Cinek49014f82016-11-04 14:55:30 -0700742 if (visibleState != mVisibleState) {
743 mVisibleState = visibleState;
Selim Cinek5ece5a72017-02-15 11:47:42 -0800744 if (mIconAppearAnimator != null) {
745 mIconAppearAnimator.cancel();
746 }
747 if (mDotAnimator != null) {
748 mDotAnimator.cancel();
749 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800750 if (animate) {
Selim Cinek5b5beb012016-11-08 18:11:58 -0800751 float targetAmount = 0.0f;
752 Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN;
753 if (visibleState == STATE_ICON) {
754 targetAmount = 1.0f;
755 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
756 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800757 float currentAmount = getIconAppearAmount();
758 if (targetAmount != currentAmount) {
759 mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
760 currentAmount, targetAmount);
761 mIconAppearAnimator.setInterpolator(interpolator);
Selim Cinekd03518c2018-03-15 12:13:51 -0700762 mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
763 : duration);
Selim Cinek65d418e2016-11-29 15:42:34 -0800764 mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
765 @Override
766 public void onAnimationEnd(Animator animation) {
767 mIconAppearAnimator = null;
768 runRunnable(endRunnable);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800769 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800770 });
771 mIconAppearAnimator.start();
772 runnableAdded = true;
773 }
Selim Cinek49014f82016-11-04 14:55:30 -0700774
Selim Cinek5b5beb012016-11-08 18:11:58 -0800775 targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f;
776 interpolator = Interpolators.FAST_OUT_LINEAR_IN;
777 if (visibleState == STATE_DOT) {
778 targetAmount = 1.0f;
779 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
780 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800781 currentAmount = getDotAppearAmount();
782 if (targetAmount != currentAmount) {
783 mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
784 currentAmount, targetAmount);
Selim Cinekd03518c2018-03-15 12:13:51 -0700785 mDotAnimator.setInterpolator(interpolator);;
786 mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
787 : duration);
Selim Cinek65d418e2016-11-29 15:42:34 -0800788 final boolean runRunnable = !runnableAdded;
789 mDotAnimator.addListener(new AnimatorListenerAdapter() {
790 @Override
791 public void onAnimationEnd(Animator animation) {
792 mDotAnimator = null;
793 if (runRunnable) {
794 runRunnable(endRunnable);
795 }
796 }
797 });
798 mDotAnimator.start();
799 runnableAdded = true;
800 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800801 } else {
802 setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f);
Selim Cinek01a73f92016-12-06 16:13:42 -0800803 setDotAppearAmount(visibleState == STATE_DOT ? 1.0f
804 : visibleState == STATE_ICON ? 2.0f
805 : 0.0f);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800806 }
Selim Cinek49014f82016-11-04 14:55:30 -0700807 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800808 if (!runnableAdded) {
809 runRunnable(endRunnable);
810 }
811 }
812
813 private void runRunnable(Runnable runnable) {
814 if (runnable != null) {
815 runnable.run();
816 }
Selim Cinek49014f82016-11-04 14:55:30 -0700817 }
818
Selim Cinek5b5beb012016-11-08 18:11:58 -0800819 public void setIconAppearAmount(float iconAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800820 if (mIconAppearAmount != iconAppearAmount) {
821 mIconAppearAmount = iconAppearAmount;
822 invalidate();
823 }
Selim Cinek49014f82016-11-04 14:55:30 -0700824 }
825
826 public float getIconAppearAmount() {
827 return mIconAppearAmount;
828 }
829
830 public int getVisibleState() {
831 return mVisibleState;
832 }
833
834 public void setDotAppearAmount(float dotAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800835 if (mDotAppearAmount != dotAppearAmount) {
836 mDotAppearAmount = dotAppearAmount;
837 invalidate();
838 }
Selim Cinek49014f82016-11-04 14:55:30 -0700839 }
840
Selim Cinek2b549f42016-11-22 16:38:51 -0800841 @Override
842 public void setVisibility(int visibility) {
843 super.setVisibility(visibility);
844 if (mOnVisibilityChangedListener != null) {
845 mOnVisibilityChangedListener.onVisibilityChanged(visibility);
846 }
847 }
848
Selim Cinek49014f82016-11-04 14:55:30 -0700849 public float getDotAppearAmount() {
850 return mDotAppearAmount;
851 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800852
853 public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
854 mOnVisibilityChangedListener = listener;
855 }
856
Selim Cinek195dfc52019-05-30 19:35:05 -0700857 public void setDozing(boolean dozing, boolean fade, long delay) {
858 mDozer.setDozing(f -> {
859 mDozeAmount = f;
Adrian Roos456e0052017-04-04 16:44:29 -0700860 updateDecorColor();
Adrian Roosf3eacd32017-04-14 16:35:58 -0700861 updateIconColor();
Adrian Roosfe0071f2017-07-06 18:45:10 +0200862 updateAllowAnimation();
Selim Cinek195dfc52019-05-30 19:35:05 -0700863 }, dozing, fade, delay, this);
Adrian Roos456e0052017-04-04 16:44:29 -0700864 }
865
Adrian Roosfe0071f2017-07-06 18:45:10 +0200866 private void updateAllowAnimation() {
Selim Cinek195dfc52019-05-30 19:35:05 -0700867 if (mDozeAmount == 0 || mDozeAmount == 1) {
868 setAllowAnimation(mDozeAmount == 0);
Adrian Roosfe0071f2017-07-06 18:45:10 +0200869 }
870 }
871
Selim Cinek13859052017-10-05 17:12:18 -0700872 /**
873 * This method returns the drawing rect for the view which is different from the regular
874 * drawing rect, since we layout all children at position 0 and usually the translation is
875 * neglected. The standard implementation doesn't account for translation.
876 *
877 * @param outRect The (scrolled) drawing bounds of the view.
878 */
879 @Override
880 public void getDrawingRect(Rect outRect) {
881 super.getDrawingRect(outRect);
882 float translationX = getTranslationX();
883 float translationY = getTranslationY();
884 outRect.left += translationX;
885 outRect.right += translationX;
886 outRect.top += translationY;
887 outRect.bottom += translationY;
888 }
889
Selim Cinekd95ca7c2017-07-26 12:20:38 -0700890 public void setIsInShelf(boolean isInShelf) {
891 mIsInShelf = isInShelf;
892 }
893
894 public boolean isInShelf() {
895 return mIsInShelf;
896 }
897
Selim Cinek887da3c2017-10-06 13:37:32 -0700898 @Override
899 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
900 super.onLayout(changed, left, top, right, bottom);
901 if (mLayoutRunnable != null) {
902 mLayoutRunnable.run();
903 mLayoutRunnable = null;
904 }
Selim Cinek3ddda6d2018-06-08 00:26:48 -0700905 updatePivot();
906 }
907
908 private void updatePivot() {
909 setPivotX((1 - mIconScale) / 2.0f * getWidth());
910 setPivotY((getHeight() - mIconScale * getWidth()) / 2.0f);
Selim Cinek887da3c2017-10-06 13:37:32 -0700911 }
912
913 public void executeOnLayout(Runnable runnable) {
914 mLayoutRunnable = runnable;
915 }
916
Selim Cinekd03518c2018-03-15 12:13:51 -0700917 public void setDismissed() {
918 mDismissed = true;
919 if (mOnDismissListener != null) {
920 mOnDismissListener.run();
921 }
922 }
923
924 public boolean isDismissed() {
925 return mDismissed;
926 }
927
928 public void setOnDismissListener(Runnable onDismissListener) {
929 mOnDismissListener = onDismissListener;
930 }
931
Evan Lairde1d13c92018-03-20 16:58:01 -0400932 @Override
933 public void onDarkChanged(Rect area, float darkIntensity, int tint) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400934 int areaTint = getTint(area, this, tint);
935 ColorStateList color = ColorStateList.valueOf(areaTint);
936 setImageTintList(color);
937 setDecorColor(areaTint);
Evan Lairde1d13c92018-03-20 16:58:01 -0400938 }
939
940 @Override
941 public boolean isIconVisible() {
942 return mIcon != null && mIcon.visible;
943 }
944
945 @Override
946 public boolean isIconBlocked() {
947 return mBlocked;
948 }
949
Selim Cinek195dfc52019-05-30 19:35:05 -0700950 public void setIncreasedSize(boolean increasedSize) {
951 mIncreasedSize = increasedSize;
952 maybeUpdateIconScaleDimens();
953 }
954
Selim Cinek2b549f42016-11-22 16:38:51 -0800955 public interface OnVisibilityChangedListener {
956 void onVisibilityChanged(int newVisibility);
957 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700958}