blob: c0e7ac4810d9eb28f300edf16b613bcc2f0496da [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
Evan Lairde1d13c92018-03-20 16:58:01 -040019import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
20
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;
Lucas Dupin83519da2017-06-21 11:58:31 -070041import android.support.v4.graphics.ColorUtils;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070042import android.text.TextUtils;
Daniel Sandler05e24142011-11-10 11:56:49 -050043import android.util.AttributeSet;
Selim Cinek49014f82016-11-04 14:55:30 -070044import android.util.FloatProperty;
Joe Onoratof9ec03c2010-09-23 15:09:18 -070045import android.util.Log;
Selim Cinek49014f82016-11-04 14:55:30 -070046import android.util.Property;
Anthony Chen55e8e1e2016-01-08 10:31:46 -080047import android.util.TypedValue;
Joe Onorato0cbda992010-05-02 16:28:15 -070048import android.view.ViewDebug;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070049import android.view.accessibility.AccessibilityEvent;
Selim Cinek49014f82016-11-04 14:55:30 -070050import android.view.animation.Interpolator;
Winsonc0d70582016-01-29 10:24:39 -080051
Joe Onorato0cbda992010-05-02 16:28:15 -070052import com.android.internal.statusbar.StatusBarIcon;
Lucas Dupin83519da2017-06-21 11:58:31 -070053import com.android.internal.util.NotificationColorUtil;
Selim Cinek49014f82016-11-04 14:55:30 -070054import com.android.systemui.Interpolators;
Joe Onorato6c01a112010-10-04 17:38:47 -040055import com.android.systemui.R;
Adrian Roos456e0052017-04-04 16:44:29 -070056import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
Selim Cinek49014f82016-11-04 14:55:30 -070057import com.android.systemui.statusbar.notification.NotificationUtils;
Joe Onorato6c01a112010-10-04 17:38:47 -040058
John Spurlockde84f0e2013-06-12 12:41:00 -040059import java.text.NumberFormat;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +020060import java.util.Arrays;
John Spurlockde84f0e2013-06-12 12:41:00 -040061
Evan Lairde1d13c92018-03-20 16:58:01 -040062public class StatusBarIconView extends AnimatedImageView implements StatusIconDisplayable {
Selim Cinek875ba9b2017-02-13 16:20:17 -080063 public static final int NO_COLOR = 0;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +020064
65 /**
66 * Multiply alpha values with (1+DARK_ALPHA_BOOST) when dozing. The chosen value boosts
67 * everything above 30% to 50%, making it appear on 1bit color depths.
68 */
69 private static final float DARK_ALPHA_BOOST = 0.67f;
Evan Laird39ea8102018-05-18 19:49:07 -040070 /**
71 * Status icons are currently drawn with the intention of being 17dp tall, but we
72 * want to scale them (in a way that doesn't require an asset dump) down 2dp. So
73 * 17dp * (15 / 17) = 15dp, the new height.
74 */
75 private static final float SYSTEM_ICON_SCALE = 15.f / 17.f;
Selim Cinek875ba9b2017-02-13 16:20:17 -080076 private final int ANIMATION_DURATION_FAST = 100;
77
Selim Cinek49014f82016-11-04 14:55:30 -070078 public static final int STATE_ICON = 0;
79 public static final int STATE_DOT = 1;
80 public static final int STATE_HIDDEN = 2;
Joe Onorato0cbda992010-05-02 16:28:15 -070081
Selim Cinek49014f82016-11-04 14:55:30 -070082 private static final String TAG = "StatusBarIconView";
83 private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
84 = new FloatProperty<StatusBarIconView>("iconAppearAmount") {
85
86 @Override
87 public void setValue(StatusBarIconView object, float value) {
88 object.setIconAppearAmount(value);
89 }
90
91 @Override
92 public Float get(StatusBarIconView object) {
93 return object.getIconAppearAmount();
94 }
95 };
Selim Cinek5b5beb012016-11-08 18:11:58 -080096 private static final Property<StatusBarIconView, Float> DOT_APPEAR_AMOUNT
Selim Cinek49014f82016-11-04 14:55:30 -070097 = new FloatProperty<StatusBarIconView>("dot_appear_amount") {
98
99 @Override
100 public void setValue(StatusBarIconView object, float value) {
101 object.setDotAppearAmount(value);
102 }
103
104 @Override
105 public Float get(StatusBarIconView object) {
106 return object.getDotAppearAmount();
107 }
108 };
109
110 private boolean mAlwaysScaleIcon;
Adrian Roos138f0342017-04-28 09:11:28 -0700111 private int mStatusBarIconDrawingSizeDark = 1;
112 private int mStatusBarIconDrawingSize = 1;
113 private int mStatusBarIconSize = 1;
Joe Onorato0cbda992010-05-02 16:28:15 -0700114 private StatusBarIcon mIcon;
115 @ViewDebug.ExportedProperty private String mSlot;
Joe Onorato6c01a112010-10-04 17:38:47 -0400116 private Drawable mNumberBackground;
117 private Paint mNumberPain;
118 private int mNumberX;
119 private int mNumberY;
120 private String mNumberText;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700121 private StatusBarNotification mNotification;
Jason Monk3b230072015-05-29 11:11:29 -0400122 private final boolean mBlocked;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800123 private int mDensity;
Selim Cinek281c2022016-10-13 19:14:43 -0700124 private float mIconScale = 1.0f;
Evan Lairdbaccb462018-06-01 16:29:02 -0400125 private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Selim Cinek49014f82016-11-04 14:55:30 -0700126 private float mDotRadius;
127 private int mStaticDotRadius;
128 private int mVisibleState = STATE_ICON;
129 private float mIconAppearAmount = 1.0f;
130 private ObjectAnimator mIconAppearAnimator;
131 private ObjectAnimator mDotAnimator;
132 private float mDotAppearAmount;
Selim Cinek2b549f42016-11-22 16:38:51 -0800133 private OnVisibilityChangedListener mOnVisibilityChangedListener;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800134 private int mDrawableColor;
135 private int mIconColor;
Adrian Roos456e0052017-04-04 16:44:29 -0700136 private int mDecorColor;
137 private float mDarkAmount;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800138 private ValueAnimator mColorAnimator;
139 private int mCurrentSetColor = NO_COLOR;
140 private int mAnimationStartColor = NO_COLOR;
141 private final ValueAnimator.AnimatorUpdateListener mColorUpdater
142 = animation -> {
143 int newColor = NotificationUtils.interpolateColors(mAnimationStartColor, mIconColor,
144 animation.getAnimatedFraction());
145 setColorInternal(newColor);
146 };
Adrian Roos456e0052017-04-04 16:44:29 -0700147 private final NotificationIconDozeHelper mDozer;
Lucas Dupin83519da2017-06-21 11:58:31 -0700148 private int mContrastedDrawableColor;
149 private int mCachedContrastBackgroundColor = NO_COLOR;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200150 private float[] mMatrix;
151 private ColorMatrixColorFilter mMatrixColorFilter;
Selim Cinekd95ca7c2017-07-26 12:20:38 -0700152 private boolean mIsInShelf;
Selim Cinek887da3c2017-10-06 13:37:32 -0700153 private Runnable mLayoutRunnable;
Selim Cinekd03518c2018-03-15 12:13:51 -0700154 private boolean mDismissed;
155 private Runnable mOnDismissListener;
Joe Onorato0cbda992010-05-02 16:28:15 -0700156
Selim Cinek72fc8db2017-06-06 18:07:47 -0700157 public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
158 this(context, slot, sbn, false);
Jason Monk3b230072015-05-29 11:11:29 -0400159 }
160
Selim Cinek72fc8db2017-06-06 18:07:47 -0700161 public StatusBarIconView(Context context, String slot, StatusBarNotification sbn,
Jason Monk3b230072015-05-29 11:11:29 -0400162 boolean blocked) {
Joe Onorato0cbda992010-05-02 16:28:15 -0700163 super(context);
Adrian Roos456e0052017-04-04 16:44:29 -0700164 mDozer = new NotificationIconDozeHelper(context);
Jason Monk3b230072015-05-29 11:11:29 -0400165 mBlocked = blocked;
Joe Onorato0cbda992010-05-02 16:28:15 -0700166 mSlot = slot;
Joe Onorato6c01a112010-10-04 17:38:47 -0400167 mNumberPain = new Paint();
168 mNumberPain.setTextAlign(Paint.Align.CENTER);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700169 mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
Joe Onorato6c01a112010-10-04 17:38:47 -0400170 mNumberPain.setAntiAlias(true);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700171 setNotification(sbn);
Selim Cinek3e7592d2016-04-11 09:35:54 +0800172 setScaleType(ScaleType.CENTER);
173 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Selim Cinek49014f82016-11-04 14:55:30 -0700174 if (mNotification != null) {
Selim Cinek875ba9b2017-02-13 16:20:17 -0800175 setDecorColor(getContext().getColor(
Selim Cinekc7f5a822018-03-20 19:32:06 -0700176 com.android.internal.R.color.notification_default_color_light));
Selim Cinek49014f82016-11-04 14:55:30 -0700177 }
178 reloadDimens();
Evan Laird20b87bf2018-04-12 09:54:11 -0400179 maybeUpdateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800180 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400181
Evan Laird20b87bf2018-04-12 09:54:11 -0400182 /** Should always be preceded by {@link #reloadDimens()} */
Adrian Roos138f0342017-04-28 09:11:28 -0700183 private void maybeUpdateIconScaleDimens() {
Daniel Sandler7579bca2011-08-18 15:47:26 -0400184 // We do not resize and scale system icons (on the right), only notification icons (on the
185 // left).
Selim Cinek3e7592d2016-04-11 09:35:54 +0800186 if (mNotification != null || mAlwaysScaleIcon) {
Evan Laird39ea8102018-05-18 19:49:07 -0400187 updateIconScaleForNotifications();
188 } else {
189 updateIconScaleForSystemIcons();
Daniel Sandler7579bca2011-08-18 15:47:26 -0400190 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800191 }
Daniel Sandlerabff0322011-09-27 11:19:34 -0400192
Evan Laird39ea8102018-05-18 19:49:07 -0400193 private void updateIconScaleForNotifications() {
Adrian Roos138f0342017-04-28 09:11:28 -0700194 final float imageBounds = NotificationUtils.interpolate(
195 mStatusBarIconDrawingSize,
196 mStatusBarIconDrawingSizeDark,
197 mDarkAmount);
198 final int outerBounds = mStatusBarIconSize;
Selim Cinek281c2022016-10-13 19:14:43 -0700199 mIconScale = (float)imageBounds / (float)outerBounds;
Selim Cinek3ddda6d2018-06-08 00:26:48 -0700200 updatePivot();
Selim Cinek281c2022016-10-13 19:14:43 -0700201 }
202
Evan Laird39ea8102018-05-18 19:49:07 -0400203 private void updateIconScaleForSystemIcons() {
204 mIconScale = SYSTEM_ICON_SCALE;
205 }
206
Adrian Roosceac4a02017-05-30 20:25:52 -0700207 public float getIconScaleFullyDark() {
208 return (float) mStatusBarIconDrawingSizeDark / mStatusBarIconDrawingSize;
209 }
210
Selim Cinek281c2022016-10-13 19:14:43 -0700211 public float getIconScale() {
212 return mIconScale;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800213 }
214
215 @Override
216 protected void onConfigurationChanged(Configuration newConfig) {
217 super.onConfigurationChanged(newConfig);
218 int density = newConfig.densityDpi;
219 if (density != mDensity) {
220 mDensity = density;
Evan Laird20b87bf2018-04-12 09:54:11 -0400221 reloadDimens();
Adrian Roos138f0342017-04-28 09:11:28 -0700222 maybeUpdateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800223 updateDrawable();
Selim Cinek49014f82016-11-04 14:55:30 -0700224 }
225 }
226
227 private void reloadDimens() {
228 boolean applyRadius = mDotRadius == mStaticDotRadius;
Evan Laird20b87bf2018-04-12 09:54:11 -0400229 Resources res = getResources();
230 mStaticDotRadius = res.getDimensionPixelSize(R.dimen.overflow_dot_radius);
231 mStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
232 mStatusBarIconDrawingSizeDark =
233 res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
234 mStatusBarIconDrawingSize =
235 res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
Selim Cinek49014f82016-11-04 14:55:30 -0700236 if (applyRadius) {
237 mDotRadius = mStaticDotRadius;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800238 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700239 }
240
Selim Cinek72fc8db2017-06-06 18:07:47 -0700241 public void setNotification(StatusBarNotification notification) {
Christoph Studera0506e72014-07-31 20:27:39 +0200242 mNotification = notification;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700243 if (notification != null) {
244 setContentDescription(notification.getNotification());
245 }
Christoph Studera0506e72014-07-31 20:27:39 +0200246 }
247
Daniel Sandler05e24142011-11-10 11:56:49 -0500248 public StatusBarIconView(Context context, AttributeSet attrs) {
249 super(context, attrs);
Adrian Roos456e0052017-04-04 16:44:29 -0700250 mDozer = new NotificationIconDozeHelper(context);
Jason Monk3b230072015-05-29 11:11:29 -0400251 mBlocked = false;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800252 mAlwaysScaleIcon = true;
Evan Laird20b87bf2018-04-12 09:54:11 -0400253 reloadDimens();
Evan Laird39ea8102018-05-18 19:49:07 -0400254 updateIconScaleForNotifications();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800255 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Daniel Sandler05e24142011-11-10 11:56:49 -0500256 }
257
Joe Onorato0cbda992010-05-02 16:28:15 -0700258 private static boolean streq(String a, String b) {
Joe Onorato66d7d012010-05-14 10:05:10 -0700259 if (a == b) {
260 return true;
261 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700262 if (a == null && b != null) {
263 return false;
264 }
265 if (a != null && b == null) {
266 return false;
267 }
268 return a.equals(b);
269 }
270
Dan Sandlerd63f9322015-05-06 15:18:49 -0400271 public boolean equalIcons(Icon a, Icon b) {
272 if (a == b) return true;
273 if (a.getType() != b.getType()) return false;
274 switch (a.getType()) {
275 case Icon.TYPE_RESOURCE:
276 return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId();
277 case Icon.TYPE_URI:
278 return a.getUriString().equals(b.getUriString());
279 default:
280 return false;
281 }
282 }
Joe Onorato005847b2010-06-04 16:08:02 -0400283 /**
284 * Returns whether the set succeeded.
285 */
286 public boolean set(StatusBarIcon icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -0400287 final boolean iconEquals = mIcon != null && equalIcons(mIcon.icon, icon.icon);
Joe Onorato005847b2010-06-04 16:08:02 -0400288 final boolean levelEquals = iconEquals
289 && mIcon.iconLevel == icon.iconLevel;
290 final boolean visibilityEquals = mIcon != null
291 && mIcon.visible == icon.visible;
Joe Onorato6c01a112010-10-04 17:38:47 -0400292 final boolean numberEquals = mIcon != null
293 && mIcon.number == icon.number;
294 mIcon = icon.clone();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700295 setContentDescription(icon.contentDescription);
Joe Onorato005847b2010-06-04 16:08:02 -0400296 if (!iconEquals) {
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800297 if (!updateDrawable(false /* no clear */)) return false;
Selim Cinek2b6eb8032016-12-29 14:22:21 +0100298 // we have to clear the grayscale tag since it may have changed
299 setTag(R.id.icon_is_grayscale, null);
Joe Onorato0cbda992010-05-02 16:28:15 -0700300 }
Joe Onorato005847b2010-06-04 16:08:02 -0400301 if (!levelEquals) {
302 setImageLevel(icon.iconLevel);
Joe Onorato0cbda992010-05-02 16:28:15 -0700303 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400304
Joe Onorato6c01a112010-10-04 17:38:47 -0400305 if (!numberEquals) {
John Spurlock01534782014-01-13 11:59:22 -0500306 if (icon.number > 0 && getContext().getResources().getBoolean(
Joe Onorato8595a3d2010-11-19 18:12:07 -0800307 R.bool.config_statusBarShowNumber)) {
Joe Onorato6c01a112010-10-04 17:38:47 -0400308 if (mNumberBackground == null) {
309 mNumberBackground = getContext().getResources().getDrawable(
310 R.drawable.ic_notification_overlay);
311 }
312 placeNumber();
313 } else {
314 mNumberBackground = null;
315 mNumberText = null;
316 }
317 invalidate();
318 }
Joe Onorato005847b2010-06-04 16:08:02 -0400319 if (!visibilityEquals) {
Jason Monk3b230072015-05-29 11:11:29 -0400320 setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
Joe Onorato005847b2010-06-04 16:08:02 -0400321 }
Joe Onorato005847b2010-06-04 16:08:02 -0400322 return true;
Joe Onorato0cbda992010-05-02 16:28:15 -0700323 }
324
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800325 public void updateDrawable() {
326 updateDrawable(true /* with clear */);
327 }
328
329 private boolean updateDrawable(boolean withClear) {
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100330 if (mIcon == null) {
331 return false;
332 }
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800333 Drawable drawable;
334 try {
335 drawable = getIcon(mIcon);
336 } catch (OutOfMemoryError e) {
337 Log.w(TAG, "OOM while inflating " + mIcon.icon + " for slot " + mSlot);
338 return false;
339 }
340
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800341 if (drawable == null) {
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800342 Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800343 return false;
344 }
345 if (withClear) {
346 setImageDrawable(null);
347 }
348 setImageDrawable(drawable);
349 return true;
350 }
351
Selim Cinek72fc8db2017-06-06 18:07:47 -0700352 public Icon getSourceIcon() {
353 return mIcon.icon;
354 }
355
Joe Onoratof5510542010-06-01 07:46:41 -0700356 private Drawable getIcon(StatusBarIcon icon) {
357 return getIcon(getContext(), icon);
358 }
359
Joe Onorato0cbda992010-05-02 16:28:15 -0700360 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -0400361 * Returns the right icon to use for this item
John Spurlock209bede2013-07-17 12:23:27 -0400362 *
Dan Sandlerd63f9322015-05-06 15:18:49 -0400363 * @param context Context to use to get resources
Joe Onorato0cbda992010-05-02 16:28:15 -0700364 * @return Drawable for this item, or null if the package or item could not
365 * be found
366 */
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800367 public static Drawable getIcon(Context context, StatusBarIcon statusBarIcon) {
368 int userId = statusBarIcon.user.getIdentifier();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400369 if (userId == UserHandle.USER_ALL) {
Xiaohui Chen87d0e252015-07-30 15:38:16 -0700370 userId = UserHandle.USER_SYSTEM;
Joe Onorato0cbda992010-05-02 16:28:15 -0700371 }
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800372
373 Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
374
375 TypedValue typedValue = new TypedValue();
376 context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
377 float scaleFactor = typedValue.getFloat();
378
379 // No need to scale the icon, so return it as is.
380 if (scaleFactor == 1.f) {
381 return icon;
382 }
383
384 return new ScalingDrawableWrapper(icon, scaleFactor);
Joe Onorato0cbda992010-05-02 16:28:15 -0700385 }
Joe Onoratob77f53b2010-05-28 19:59:51 -0400386
387 public StatusBarIcon getStatusBarIcon() {
388 return mIcon;
389 }
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700390
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700391 @Override
392 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
393 super.onInitializeAccessibilityEvent(event);
394 if (mNotification != null) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700395 event.setParcelableData(mNotification.getNotification());
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700396 }
397 }
398
399 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400400 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
401 super.onSizeChanged(w, h, oldw, oldh);
402 if (mNumberBackground != null) {
403 placeNumber();
404 }
405 }
406
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700407 @Override
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100408 public void onRtlPropertiesChanged(int layoutDirection) {
409 super.onRtlPropertiesChanged(layoutDirection);
410 updateDrawable();
411 }
412
413 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400414 protected void onDraw(Canvas canvas) {
Selim Cinek49014f82016-11-04 14:55:30 -0700415 if (mIconAppearAmount > 0.0f) {
416 canvas.save();
417 canvas.scale(mIconScale * mIconAppearAmount, mIconScale * mIconAppearAmount,
418 getWidth() / 2, getHeight() / 2);
419 super.onDraw(canvas);
420 canvas.restore();
421 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400422
423 if (mNumberBackground != null) {
424 mNumberBackground.draw(canvas);
425 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain);
426 }
Selim Cinek49014f82016-11-04 14:55:30 -0700427 if (mDotAppearAmount != 0.0f) {
428 float radius;
Evan Lairdbaccb462018-06-01 16:29:02 -0400429 float alpha = Color.alpha(mDecorColor) / 255.f;
Selim Cinek49014f82016-11-04 14:55:30 -0700430 if (mDotAppearAmount <= 1.0f) {
431 radius = mDotRadius * mDotAppearAmount;
Selim Cinek49014f82016-11-04 14:55:30 -0700432 } else {
433 float fadeOutAmount = mDotAppearAmount - 1.0f;
Evan Lairdbaccb462018-06-01 16:29:02 -0400434 alpha = alpha * (1.0f - fadeOutAmount);
Selim Cinek49014f82016-11-04 14:55:30 -0700435 radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount);
436 }
437 mDotPaint.setAlpha((int) (alpha * 255));
Evan Laird20b87bf2018-04-12 09:54:11 -0400438 canvas.drawCircle(mStatusBarIconSize / 2, getHeight() / 2, radius, mDotPaint);
Selim Cinek49014f82016-11-04 14:55:30 -0700439 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400440 }
441
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700442 @Override
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700443 protected void debug(int depth) {
444 super.debug(depth);
445 Log.d("View", debugIndent(depth) + "slot=" + mSlot);
446 Log.d("View", debugIndent(depth) + "icon=" + mIcon);
447 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400448
449 void placeNumber() {
Daniel Sandlerebce0112011-06-16 16:44:51 -0400450 final String str;
John Spurlock01534782014-01-13 11:59:22 -0500451 final int tooBig = getContext().getResources().getInteger(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400452 android.R.integer.status_bar_notification_info_maxnum);
453 if (mIcon.number > tooBig) {
John Spurlock01534782014-01-13 11:59:22 -0500454 str = getContext().getResources().getString(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400455 android.R.string.status_bar_notification_info_overflow);
456 } else {
457 NumberFormat f = NumberFormat.getIntegerInstance();
458 str = f.format(mIcon.number);
459 }
460 mNumberText = str;
461
Joe Onorato6c01a112010-10-04 17:38:47 -0400462 final int w = getWidth();
463 final int h = getHeight();
464 final Rect r = new Rect();
465 mNumberPain.getTextBounds(str, 0, str.length(), r);
466 final int tw = r.right - r.left;
467 final int th = r.bottom - r.top;
468 mNumberBackground.getPadding(r);
469 int dw = r.left + tw + r.right;
470 if (dw < mNumberBackground.getMinimumWidth()) {
471 dw = mNumberBackground.getMinimumWidth();
472 }
473 mNumberX = w-r.right-((dw-r.right-r.left)/2);
474 int dh = r.top + th + r.bottom;
475 if (dh < mNumberBackground.getMinimumWidth()) {
476 dh = mNumberBackground.getMinimumWidth();
477 }
478 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2);
479 mNumberBackground.setBounds(w-dw, h-dh, w, h);
480 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700481
482 private void setContentDescription(Notification notification) {
483 if (notification != null) {
Adrian Rooseba05822016-04-22 17:09:27 -0700484 String d = contentDescForNotification(mContext, notification);
485 if (!TextUtils.isEmpty(d)) {
486 setContentDescription(d);
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700487 }
488 }
489 }
Daniel Sandler7579bca2011-08-18 15:47:26 -0400490
491 public String toString() {
John Spurlock209bede2013-07-17 12:23:27 -0400492 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
Daniel Sandler7579bca2011-08-18 15:47:26 -0400493 + " notification=" + mNotification + ")";
494 }
Jason Monk3b230072015-05-29 11:11:29 -0400495
Selim Cinek72fc8db2017-06-06 18:07:47 -0700496 public StatusBarNotification getNotification() {
497 return mNotification;
498 }
499
Jason Monk3b230072015-05-29 11:11:29 -0400500 public String getSlot() {
501 return mSlot;
502 }
Adrian Rooseba05822016-04-22 17:09:27 -0700503
504
505 public static String contentDescForNotification(Context c, Notification n) {
Adrian Roosa8e18ef2016-05-26 17:17:02 -0700506 String appName = "";
507 try {
508 Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
509 appName = builder.loadHeaderAppName();
510 } catch (RuntimeException e) {
511 Log.e(TAG, "Unable to recover builder", e);
512 // Trying to get the app name from the app info instead.
513 Parcelable appInfo = n.extras.getParcelable(
514 Notification.EXTRA_BUILDER_APPLICATION_INFO);
515 if (appInfo instanceof ApplicationInfo) {
516 appName = String.valueOf(((ApplicationInfo) appInfo).loadLabel(
517 c.getPackageManager()));
518 }
519 }
Adrian Roos51548e62016-04-28 11:20:26 -0700520
Julia Reynolds7f9ce782016-05-17 15:34:34 -0400521 CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
Adrian Roosf76600e2017-04-19 15:32:43 -0700522 CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
Adrian Rooseba05822016-04-22 17:09:27 -0700523 CharSequence ticker = n.tickerText;
Adrian Roos51548e62016-04-28 11:20:26 -0700524
Adrian Roosf76600e2017-04-19 15:32:43 -0700525 // Some apps just put the app name into the title
526 CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title;
527
528 CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText
Adrian Roos6e782a52017-03-30 18:34:10 -0700529 : !TextUtils.isEmpty(ticker) ? ticker : "";
Adrian Roos51548e62016-04-28 11:20:26 -0700530
531 return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
Adrian Rooseba05822016-04-22 17:09:27 -0700532 }
Adrian Roos51548e62016-04-28 11:20:26 -0700533
Selim Cinek875ba9b2017-02-13 16:20:17 -0800534 /**
535 * Set the color that is used to draw decoration like the overflow dot. This will not be applied
536 * to the drawable.
537 */
538 public void setDecorColor(int iconTint) {
Adrian Roos456e0052017-04-04 16:44:29 -0700539 mDecorColor = iconTint;
540 updateDecorColor();
541 }
542
543 private void updateDecorColor() {
544 int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount);
545 if (mDotPaint.getColor() != color) {
546 mDotPaint.setColor(color);
547
548 if (mDotAppearAmount != 0) {
549 invalidate();
550 }
551 }
Selim Cinek49014f82016-11-04 14:55:30 -0700552 }
553
Selim Cinek875ba9b2017-02-13 16:20:17 -0800554 /**
555 * Set the static color that should be used for the drawable of this icon if it's not
556 * transitioning this also immediately sets the color.
557 */
558 public void setStaticDrawableColor(int color) {
559 mDrawableColor = color;
560 setColorInternal(color);
Lucas Dupin83519da2017-06-21 11:58:31 -0700561 updateContrastedStaticColor();
Selim Cinek875ba9b2017-02-13 16:20:17 -0800562 mIconColor = color;
Adrian Roos456e0052017-04-04 16:44:29 -0700563 mDozer.setColor(color);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800564 }
565
566 private void setColorInternal(int color) {
Adrian Roosf3eacd32017-04-14 16:35:58 -0700567 mCurrentSetColor = color;
568 updateIconColor();
569 }
570
571 private void updateIconColor() {
572 if (mCurrentSetColor != NO_COLOR) {
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200573 if (mMatrixColorFilter == null) {
574 mMatrix = new float[4 * 5];
575 mMatrixColorFilter = new ColorMatrixColorFilter(mMatrix);
576 }
577 int color = NotificationUtils.interpolateColors(
578 mCurrentSetColor, Color.WHITE, mDarkAmount);
579 updateTintMatrix(mMatrix, color, DARK_ALPHA_BOOST * mDarkAmount);
580 mMatrixColorFilter.setColorMatrixArray(mMatrix);
581 setColorFilter(mMatrixColorFilter);
582 invalidate(); // setColorFilter only invalidates if the filter instance changed.
Selim Cinek875ba9b2017-02-13 16:20:17 -0800583 } else {
Adrian Roosf3eacd32017-04-14 16:35:58 -0700584 mDozer.updateGrayscale(this, mDarkAmount);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800585 }
Selim Cinek875ba9b2017-02-13 16:20:17 -0800586 }
587
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200588 /**
589 * Updates {@param array} such that it represents a matrix that changes RGB to {@param color}
Adrian Roos24dbff32017-08-02 16:57:11 +0200590 * and multiplies the alpha channel with the color's alpha+{@param alphaBoost}.
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200591 */
592 private static void updateTintMatrix(float[] array, int color, float alphaBoost) {
593 Arrays.fill(array, 0);
594 array[4] = Color.red(color);
595 array[9] = Color.green(color);
596 array[14] = Color.blue(color);
Adrian Roos24dbff32017-08-02 16:57:11 +0200597 array[18] = Color.alpha(color) / 255f + alphaBoost;
Adrian Roosc3e8cfc2017-07-17 17:09:39 +0200598 }
599
Selim Cinek875ba9b2017-02-13 16:20:17 -0800600 public void setIconColor(int iconColor, boolean animate) {
601 if (mIconColor != iconColor) {
602 mIconColor = iconColor;
603 if (mColorAnimator != null) {
604 mColorAnimator.cancel();
605 }
606 if (mCurrentSetColor == iconColor) {
607 return;
608 }
609 if (animate && mCurrentSetColor != NO_COLOR) {
610 mAnimationStartColor = mCurrentSetColor;
611 mColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
612 mColorAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
613 mColorAnimator.setDuration(ANIMATION_DURATION_FAST);
614 mColorAnimator.addUpdateListener(mColorUpdater);
615 mColorAnimator.addListener(new AnimatorListenerAdapter() {
616 @Override
617 public void onAnimationEnd(Animator animation) {
618 mColorAnimator = null;
619 mAnimationStartColor = NO_COLOR;
620 }
621 });
622 mColorAnimator.start();
623 } else {
624 setColorInternal(iconColor);
625 }
626 }
627 }
628
629 public int getStaticDrawableColor() {
630 return mDrawableColor;
631 }
632
Lucas Dupin83519da2017-06-21 11:58:31 -0700633 /**
634 * A drawable color that passes GAR on a specific background.
635 * This value is cached.
636 *
637 * @param backgroundColor Background to test against.
638 * @return GAR safe version of {@link StatusBarIconView#getStaticDrawableColor()}.
639 */
640 int getContrastedStaticDrawableColor(int backgroundColor) {
641 if (mCachedContrastBackgroundColor != backgroundColor) {
642 mCachedContrastBackgroundColor = backgroundColor;
643 updateContrastedStaticColor();
644 }
645 return mContrastedDrawableColor;
646 }
647
648 private void updateContrastedStaticColor() {
Lucas Dupinddbaf842017-06-23 09:30:49 -0700649 if (Color.alpha(mCachedContrastBackgroundColor) != 255) {
650 mContrastedDrawableColor = mDrawableColor;
Lucas Dupin83519da2017-06-21 11:58:31 -0700651 return;
652 }
653 // We'll modify the color if it doesn't pass GAR
654 int contrastedColor = mDrawableColor;
655 if (!NotificationColorUtil.satisfiesTextContrast(mCachedContrastBackgroundColor,
656 contrastedColor)) {
657 float[] hsl = new float[3];
658 ColorUtils.colorToHSL(mDrawableColor, hsl);
659 // This is basically a light grey, pushing the color will only distort it.
660 // Best thing to do in here is to fallback to the default color.
661 if (hsl[1] < 0.2f) {
662 contrastedColor = Notification.COLOR_DEFAULT;
663 }
664 contrastedColor = NotificationColorUtil.resolveContrastColor(mContext,
665 contrastedColor, mCachedContrastBackgroundColor);
666 }
667 mContrastedDrawableColor = contrastedColor;
668 }
669
Evan Laird20b87bf2018-04-12 09:54:11 -0400670 @Override
Selim Cinek5b5beb012016-11-08 18:11:58 -0800671 public void setVisibleState(int state) {
672 setVisibleState(state, true /* animate */, null /* endRunnable */);
673 }
674
675 public void setVisibleState(int state, boolean animate) {
676 setVisibleState(state, animate, null);
677 }
678
679 @Override
680 public boolean hasOverlappingRendering() {
681 return false;
682 }
683
684 public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
Selim Cinekd03518c2018-03-15 12:13:51 -0700685 setVisibleState(visibleState, animate, endRunnable, 0);
686 }
687
688 /**
689 * Set the visibleState of this view.
690 *
691 * @param visibleState The new state.
692 * @param animate Should we animate?
693 * @param endRunnable The runnable to run at the end.
694 * @param duration The duration of an animation or 0 if the default should be taken.
695 */
696 public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable,
697 long duration) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800698 boolean runnableAdded = false;
Selim Cinek49014f82016-11-04 14:55:30 -0700699 if (visibleState != mVisibleState) {
700 mVisibleState = visibleState;
Selim Cinek5ece5a72017-02-15 11:47:42 -0800701 if (mIconAppearAnimator != null) {
702 mIconAppearAnimator.cancel();
703 }
704 if (mDotAnimator != null) {
705 mDotAnimator.cancel();
706 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800707 if (animate) {
Selim Cinek5b5beb012016-11-08 18:11:58 -0800708 float targetAmount = 0.0f;
709 Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN;
710 if (visibleState == STATE_ICON) {
711 targetAmount = 1.0f;
712 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
713 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800714 float currentAmount = getIconAppearAmount();
715 if (targetAmount != currentAmount) {
716 mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
717 currentAmount, targetAmount);
718 mIconAppearAnimator.setInterpolator(interpolator);
Selim Cinekd03518c2018-03-15 12:13:51 -0700719 mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
720 : duration);
Selim Cinek65d418e2016-11-29 15:42:34 -0800721 mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
722 @Override
723 public void onAnimationEnd(Animator animation) {
724 mIconAppearAnimator = null;
725 runRunnable(endRunnable);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800726 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800727 });
728 mIconAppearAnimator.start();
729 runnableAdded = true;
730 }
Selim Cinek49014f82016-11-04 14:55:30 -0700731
Selim Cinek5b5beb012016-11-08 18:11:58 -0800732 targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f;
733 interpolator = Interpolators.FAST_OUT_LINEAR_IN;
734 if (visibleState == STATE_DOT) {
735 targetAmount = 1.0f;
736 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
737 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800738 currentAmount = getDotAppearAmount();
739 if (targetAmount != currentAmount) {
740 mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
741 currentAmount, targetAmount);
Selim Cinekd03518c2018-03-15 12:13:51 -0700742 mDotAnimator.setInterpolator(interpolator);;
743 mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
744 : duration);
Selim Cinek65d418e2016-11-29 15:42:34 -0800745 final boolean runRunnable = !runnableAdded;
746 mDotAnimator.addListener(new AnimatorListenerAdapter() {
747 @Override
748 public void onAnimationEnd(Animator animation) {
749 mDotAnimator = null;
750 if (runRunnable) {
751 runRunnable(endRunnable);
752 }
753 }
754 });
755 mDotAnimator.start();
756 runnableAdded = true;
757 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800758 } else {
759 setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f);
Selim Cinek01a73f92016-12-06 16:13:42 -0800760 setDotAppearAmount(visibleState == STATE_DOT ? 1.0f
761 : visibleState == STATE_ICON ? 2.0f
762 : 0.0f);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800763 }
Selim Cinek49014f82016-11-04 14:55:30 -0700764 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800765 if (!runnableAdded) {
766 runRunnable(endRunnable);
767 }
768 }
769
770 private void runRunnable(Runnable runnable) {
771 if (runnable != null) {
772 runnable.run();
773 }
Selim Cinek49014f82016-11-04 14:55:30 -0700774 }
775
Selim Cinek5b5beb012016-11-08 18:11:58 -0800776 public void setIconAppearAmount(float iconAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800777 if (mIconAppearAmount != iconAppearAmount) {
778 mIconAppearAmount = iconAppearAmount;
779 invalidate();
780 }
Selim Cinek49014f82016-11-04 14:55:30 -0700781 }
782
783 public float getIconAppearAmount() {
784 return mIconAppearAmount;
785 }
786
787 public int getVisibleState() {
788 return mVisibleState;
789 }
790
791 public void setDotAppearAmount(float dotAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800792 if (mDotAppearAmount != dotAppearAmount) {
793 mDotAppearAmount = dotAppearAmount;
794 invalidate();
795 }
Selim Cinek49014f82016-11-04 14:55:30 -0700796 }
797
Selim Cinek2b549f42016-11-22 16:38:51 -0800798 @Override
799 public void setVisibility(int visibility) {
800 super.setVisibility(visibility);
801 if (mOnVisibilityChangedListener != null) {
802 mOnVisibilityChangedListener.onVisibilityChanged(visibility);
803 }
804 }
805
Selim Cinek49014f82016-11-04 14:55:30 -0700806 public float getDotAppearAmount() {
807 return mDotAppearAmount;
808 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800809
810 public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
811 mOnVisibilityChangedListener = listener;
812 }
813
Adrian Roos456e0052017-04-04 16:44:29 -0700814 public void setDark(boolean dark, boolean fade, long delay) {
Adrian Roos456e0052017-04-04 16:44:29 -0700815 mDozer.setIntensityDark(f -> {
816 mDarkAmount = f;
Evan Laird39ea8102018-05-18 19:49:07 -0400817 updateIconScaleForNotifications();
Adrian Roos456e0052017-04-04 16:44:29 -0700818 updateDecorColor();
Adrian Roosf3eacd32017-04-14 16:35:58 -0700819 updateIconColor();
Adrian Roosfe0071f2017-07-06 18:45:10 +0200820 updateAllowAnimation();
Selim Cinek653c1392018-05-15 13:02:42 -0700821 }, dark, fade, delay, this);
Adrian Roos456e0052017-04-04 16:44:29 -0700822 }
823
Adrian Roosfe0071f2017-07-06 18:45:10 +0200824 private void updateAllowAnimation() {
825 if (mDarkAmount == 0 || mDarkAmount == 1) {
826 setAllowAnimation(mDarkAmount == 0);
827 }
828 }
829
Selim Cinek13859052017-10-05 17:12:18 -0700830 /**
831 * This method returns the drawing rect for the view which is different from the regular
832 * drawing rect, since we layout all children at position 0 and usually the translation is
833 * neglected. The standard implementation doesn't account for translation.
834 *
835 * @param outRect The (scrolled) drawing bounds of the view.
836 */
837 @Override
838 public void getDrawingRect(Rect outRect) {
839 super.getDrawingRect(outRect);
840 float translationX = getTranslationX();
841 float translationY = getTranslationY();
842 outRect.left += translationX;
843 outRect.right += translationX;
844 outRect.top += translationY;
845 outRect.bottom += translationY;
846 }
847
Selim Cinekd95ca7c2017-07-26 12:20:38 -0700848 public void setIsInShelf(boolean isInShelf) {
849 mIsInShelf = isInShelf;
850 }
851
852 public boolean isInShelf() {
853 return mIsInShelf;
854 }
855
Selim Cinek887da3c2017-10-06 13:37:32 -0700856 @Override
857 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
858 super.onLayout(changed, left, top, right, bottom);
859 if (mLayoutRunnable != null) {
860 mLayoutRunnable.run();
861 mLayoutRunnable = null;
862 }
Selim Cinek3ddda6d2018-06-08 00:26:48 -0700863 updatePivot();
864 }
865
866 private void updatePivot() {
867 setPivotX((1 - mIconScale) / 2.0f * getWidth());
868 setPivotY((getHeight() - mIconScale * getWidth()) / 2.0f);
Selim Cinek887da3c2017-10-06 13:37:32 -0700869 }
870
871 public void executeOnLayout(Runnable runnable) {
872 mLayoutRunnable = runnable;
873 }
874
Selim Cinekd03518c2018-03-15 12:13:51 -0700875 public void setDismissed() {
876 mDismissed = true;
877 if (mOnDismissListener != null) {
878 mOnDismissListener.run();
879 }
880 }
881
882 public boolean isDismissed() {
883 return mDismissed;
884 }
885
886 public void setOnDismissListener(Runnable onDismissListener) {
887 mOnDismissListener = onDismissListener;
888 }
889
Evan Lairde1d13c92018-03-20 16:58:01 -0400890 @Override
891 public void onDarkChanged(Rect area, float darkIntensity, int tint) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400892 int areaTint = getTint(area, this, tint);
893 ColorStateList color = ColorStateList.valueOf(areaTint);
894 setImageTintList(color);
895 setDecorColor(areaTint);
Evan Lairde1d13c92018-03-20 16:58:01 -0400896 }
897
898 @Override
899 public boolean isIconVisible() {
900 return mIcon != null && mIcon.visible;
901 }
902
903 @Override
904 public boolean isIconBlocked() {
905 return mBlocked;
906 }
907
Selim Cinek2b549f42016-11-22 16:38:51 -0800908 public interface OnVisibilityChangedListener {
909 void onVisibilityChanged(int newVisibility);
910 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700911}