blob: c7fbbf956c68ce0e86efe5fa6d545b514a1c063a [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
Selim Cinek49014f82016-11-04 14:55:30 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
Selim Cinek875ba9b2017-02-13 16:20:17 -080022import android.animation.ValueAnimator;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070023import android.app.Notification;
Joe Onorato0cbda992010-05-02 16:28:15 -070024import android.content.Context;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070025import android.content.pm.ApplicationInfo;
Selim Cinek875ba9b2017-02-13 16:20:17 -080026import android.content.res.ColorStateList;
Selim Cinek3e7592d2016-04-11 09:35:54 +080027import android.content.res.Configuration;
Joe Onorato0cbda992010-05-02 16:28:15 -070028import android.content.res.Resources;
Joe Onorato0cbda992010-05-02 16:28:15 -070029import android.graphics.Canvas;
Adrian Roos456e0052017-04-04 16:44:29 -070030import android.graphics.Color;
Joe Onorato6c01a112010-10-04 17:38:47 -040031import android.graphics.Paint;
32import android.graphics.Rect;
John Spurlockde84f0e2013-06-12 12:41:00 -040033import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040034import android.graphics.drawable.Icon;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070035import android.os.Parcelable;
Jeff Sharkeyded653b2012-09-27 19:09:24 -070036import android.os.UserHandle;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070037import android.text.TextUtils;
Daniel Sandler05e24142011-11-10 11:56:49 -050038import android.util.AttributeSet;
Selim Cinek49014f82016-11-04 14:55:30 -070039import android.util.FloatProperty;
Joe Onoratof9ec03c2010-09-23 15:09:18 -070040import android.util.Log;
Selim Cinek49014f82016-11-04 14:55:30 -070041import android.util.Property;
Anthony Chen55e8e1e2016-01-08 10:31:46 -080042import android.util.TypedValue;
Joe Onorato0cbda992010-05-02 16:28:15 -070043import android.view.ViewDebug;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070044import android.view.accessibility.AccessibilityEvent;
Selim Cinek49014f82016-11-04 14:55:30 -070045import android.view.animation.Interpolator;
Winsonc0d70582016-01-29 10:24:39 -080046
Joe Onorato0cbda992010-05-02 16:28:15 -070047import com.android.internal.statusbar.StatusBarIcon;
Selim Cinek49014f82016-11-04 14:55:30 -070048import com.android.systemui.Interpolators;
Joe Onorato6c01a112010-10-04 17:38:47 -040049import com.android.systemui.R;
Adrian Roos456e0052017-04-04 16:44:29 -070050import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
Selim Cinek49014f82016-11-04 14:55:30 -070051import com.android.systemui.statusbar.notification.NotificationUtils;
Joe Onorato6c01a112010-10-04 17:38:47 -040052
John Spurlockde84f0e2013-06-12 12:41:00 -040053import java.text.NumberFormat;
54
Joe Onorato0cbda992010-05-02 16:28:15 -070055public class StatusBarIconView extends AnimatedImageView {
Selim Cinek875ba9b2017-02-13 16:20:17 -080056 public static final int NO_COLOR = 0;
57 private final int ANIMATION_DURATION_FAST = 100;
58
Selim Cinek49014f82016-11-04 14:55:30 -070059 public static final int STATE_ICON = 0;
60 public static final int STATE_DOT = 1;
61 public static final int STATE_HIDDEN = 2;
Joe Onorato0cbda992010-05-02 16:28:15 -070062
Selim Cinek49014f82016-11-04 14:55:30 -070063 private static final String TAG = "StatusBarIconView";
64 private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
65 = new FloatProperty<StatusBarIconView>("iconAppearAmount") {
66
67 @Override
68 public void setValue(StatusBarIconView object, float value) {
69 object.setIconAppearAmount(value);
70 }
71
72 @Override
73 public Float get(StatusBarIconView object) {
74 return object.getIconAppearAmount();
75 }
76 };
Selim Cinek5b5beb012016-11-08 18:11:58 -080077 private static final Property<StatusBarIconView, Float> DOT_APPEAR_AMOUNT
Selim Cinek49014f82016-11-04 14:55:30 -070078 = new FloatProperty<StatusBarIconView>("dot_appear_amount") {
79
80 @Override
81 public void setValue(StatusBarIconView object, float value) {
82 object.setDotAppearAmount(value);
83 }
84
85 @Override
86 public Float get(StatusBarIconView object) {
87 return object.getDotAppearAmount();
88 }
89 };
90
91 private boolean mAlwaysScaleIcon;
Adrian Roos138f0342017-04-28 09:11:28 -070092 private int mStatusBarIconDrawingSizeDark = 1;
93 private int mStatusBarIconDrawingSize = 1;
94 private int mStatusBarIconSize = 1;
Joe Onorato0cbda992010-05-02 16:28:15 -070095 private StatusBarIcon mIcon;
96 @ViewDebug.ExportedProperty private String mSlot;
Joe Onorato6c01a112010-10-04 17:38:47 -040097 private Drawable mNumberBackground;
98 private Paint mNumberPain;
99 private int mNumberX;
100 private int mNumberY;
101 private String mNumberText;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700102 private Notification mNotification;
Jason Monk3b230072015-05-29 11:11:29 -0400103 private final boolean mBlocked;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800104 private int mDensity;
Selim Cinek281c2022016-10-13 19:14:43 -0700105 private float mIconScale = 1.0f;
Selim Cinek49014f82016-11-04 14:55:30 -0700106 private final Paint mDotPaint = new Paint();
Selim Cinek49014f82016-11-04 14:55:30 -0700107 private float mDotRadius;
108 private int mStaticDotRadius;
109 private int mVisibleState = STATE_ICON;
110 private float mIconAppearAmount = 1.0f;
111 private ObjectAnimator mIconAppearAnimator;
112 private ObjectAnimator mDotAnimator;
113 private float mDotAppearAmount;
Selim Cinek2b549f42016-11-22 16:38:51 -0800114 private OnVisibilityChangedListener mOnVisibilityChangedListener;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800115 private int mDrawableColor;
116 private int mIconColor;
Adrian Roos456e0052017-04-04 16:44:29 -0700117 private int mDecorColor;
118 private float mDarkAmount;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800119 private ValueAnimator mColorAnimator;
120 private int mCurrentSetColor = NO_COLOR;
121 private int mAnimationStartColor = NO_COLOR;
122 private final ValueAnimator.AnimatorUpdateListener mColorUpdater
123 = animation -> {
124 int newColor = NotificationUtils.interpolateColors(mAnimationStartColor, mIconColor,
125 animation.getAnimatedFraction());
126 setColorInternal(newColor);
127 };
Adrian Roos456e0052017-04-04 16:44:29 -0700128 private final NotificationIconDozeHelper mDozer;
Joe Onorato0cbda992010-05-02 16:28:15 -0700129
John Spurlocke6f0a712013-09-03 16:23:49 -0400130 public StatusBarIconView(Context context, String slot, Notification notification) {
Jason Monk3b230072015-05-29 11:11:29 -0400131 this(context, slot, notification, false);
132 }
133
134 public StatusBarIconView(Context context, String slot, Notification notification,
135 boolean blocked) {
Joe Onorato0cbda992010-05-02 16:28:15 -0700136 super(context);
Adrian Roos456e0052017-04-04 16:44:29 -0700137 mDozer = new NotificationIconDozeHelper(context);
Jason Monk3b230072015-05-29 11:11:29 -0400138 mBlocked = blocked;
Joe Onorato0cbda992010-05-02 16:28:15 -0700139 mSlot = slot;
Joe Onorato6c01a112010-10-04 17:38:47 -0400140 mNumberPain = new Paint();
141 mNumberPain.setTextAlign(Paint.Align.CENTER);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700142 mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
Joe Onorato6c01a112010-10-04 17:38:47 -0400143 mNumberPain.setAntiAlias(true);
Christoph Studera0506e72014-07-31 20:27:39 +0200144 setNotification(notification);
Adrian Roos138f0342017-04-28 09:11:28 -0700145 maybeUpdateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800146 setScaleType(ScaleType.CENTER);
147 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Selim Cinek49014f82016-11-04 14:55:30 -0700148 if (mNotification != null) {
Selim Cinek875ba9b2017-02-13 16:20:17 -0800149 setDecorColor(getContext().getColor(
Selim Cinek49014f82016-11-04 14:55:30 -0700150 com.android.internal.R.color.notification_icon_default_color));
151 }
152 reloadDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800153 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400154
Adrian Roos138f0342017-04-28 09:11:28 -0700155 private void maybeUpdateIconScaleDimens() {
Daniel Sandler7579bca2011-08-18 15:47:26 -0400156 // We do not resize and scale system icons (on the right), only notification icons (on the
157 // left).
Selim Cinek3e7592d2016-04-11 09:35:54 +0800158 if (mNotification != null || mAlwaysScaleIcon) {
Adrian Roos138f0342017-04-28 09:11:28 -0700159 updateIconScaleDimens();
Daniel Sandler7579bca2011-08-18 15:47:26 -0400160 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800161 }
Daniel Sandlerabff0322011-09-27 11:19:34 -0400162
Adrian Roos138f0342017-04-28 09:11:28 -0700163 private void updateIconScaleDimens() {
Selim Cinek3e7592d2016-04-11 09:35:54 +0800164 Resources res = mContext.getResources();
Adrian Roos138f0342017-04-28 09:11:28 -0700165 mStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
166 mStatusBarIconDrawingSizeDark =
167 res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
168 mStatusBarIconDrawingSize =
169 res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
170 updateIconScale();
171 }
172
173 private void updateIconScale() {
174 final float imageBounds = NotificationUtils.interpolate(
175 mStatusBarIconDrawingSize,
176 mStatusBarIconDrawingSizeDark,
177 mDarkAmount);
178 final int outerBounds = mStatusBarIconSize;
Selim Cinek281c2022016-10-13 19:14:43 -0700179 mIconScale = (float)imageBounds / (float)outerBounds;
Selim Cinek281c2022016-10-13 19:14:43 -0700180 }
181
182 public float getIconScale() {
183 return mIconScale;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800184 }
185
186 @Override
187 protected void onConfigurationChanged(Configuration newConfig) {
188 super.onConfigurationChanged(newConfig);
189 int density = newConfig.densityDpi;
190 if (density != mDensity) {
191 mDensity = density;
Adrian Roos138f0342017-04-28 09:11:28 -0700192 maybeUpdateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800193 updateDrawable();
Selim Cinek49014f82016-11-04 14:55:30 -0700194 reloadDimens();
195 }
196 }
197
198 private void reloadDimens() {
199 boolean applyRadius = mDotRadius == mStaticDotRadius;
200 mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
201 if (applyRadius) {
202 mDotRadius = mStaticDotRadius;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800203 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700204 }
205
Christoph Studera0506e72014-07-31 20:27:39 +0200206 public void setNotification(Notification notification) {
207 mNotification = notification;
208 setContentDescription(notification);
209 }
210
Daniel Sandler05e24142011-11-10 11:56:49 -0500211 public StatusBarIconView(Context context, AttributeSet attrs) {
212 super(context, attrs);
Adrian Roos456e0052017-04-04 16:44:29 -0700213 mDozer = new NotificationIconDozeHelper(context);
Jason Monk3b230072015-05-29 11:11:29 -0400214 mBlocked = false;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800215 mAlwaysScaleIcon = true;
Adrian Roos138f0342017-04-28 09:11:28 -0700216 updateIconScaleDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800217 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Daniel Sandler05e24142011-11-10 11:56:49 -0500218 }
219
Joe Onorato0cbda992010-05-02 16:28:15 -0700220 private static boolean streq(String a, String b) {
Joe Onorato66d7d012010-05-14 10:05:10 -0700221 if (a == b) {
222 return true;
223 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700224 if (a == null && b != null) {
225 return false;
226 }
227 if (a != null && b == null) {
228 return false;
229 }
230 return a.equals(b);
231 }
232
Dan Sandlerd63f9322015-05-06 15:18:49 -0400233 public boolean equalIcons(Icon a, Icon b) {
234 if (a == b) return true;
235 if (a.getType() != b.getType()) return false;
236 switch (a.getType()) {
237 case Icon.TYPE_RESOURCE:
238 return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId();
239 case Icon.TYPE_URI:
240 return a.getUriString().equals(b.getUriString());
241 default:
242 return false;
243 }
244 }
Joe Onorato005847b2010-06-04 16:08:02 -0400245 /**
246 * Returns whether the set succeeded.
247 */
248 public boolean set(StatusBarIcon icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -0400249 final boolean iconEquals = mIcon != null && equalIcons(mIcon.icon, icon.icon);
Joe Onorato005847b2010-06-04 16:08:02 -0400250 final boolean levelEquals = iconEquals
251 && mIcon.iconLevel == icon.iconLevel;
252 final boolean visibilityEquals = mIcon != null
253 && mIcon.visible == icon.visible;
Joe Onorato6c01a112010-10-04 17:38:47 -0400254 final boolean numberEquals = mIcon != null
255 && mIcon.number == icon.number;
256 mIcon = icon.clone();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700257 setContentDescription(icon.contentDescription);
Joe Onorato005847b2010-06-04 16:08:02 -0400258 if (!iconEquals) {
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800259 if (!updateDrawable(false /* no clear */)) return false;
Selim Cinek2b6eb8032016-12-29 14:22:21 +0100260 // we have to clear the grayscale tag since it may have changed
261 setTag(R.id.icon_is_grayscale, null);
Joe Onorato0cbda992010-05-02 16:28:15 -0700262 }
Joe Onorato005847b2010-06-04 16:08:02 -0400263 if (!levelEquals) {
264 setImageLevel(icon.iconLevel);
Joe Onorato0cbda992010-05-02 16:28:15 -0700265 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400266
Joe Onorato6c01a112010-10-04 17:38:47 -0400267 if (!numberEquals) {
John Spurlock01534782014-01-13 11:59:22 -0500268 if (icon.number > 0 && getContext().getResources().getBoolean(
Joe Onorato8595a3d2010-11-19 18:12:07 -0800269 R.bool.config_statusBarShowNumber)) {
Joe Onorato6c01a112010-10-04 17:38:47 -0400270 if (mNumberBackground == null) {
271 mNumberBackground = getContext().getResources().getDrawable(
272 R.drawable.ic_notification_overlay);
273 }
274 placeNumber();
275 } else {
276 mNumberBackground = null;
277 mNumberText = null;
278 }
279 invalidate();
280 }
Joe Onorato005847b2010-06-04 16:08:02 -0400281 if (!visibilityEquals) {
Jason Monk3b230072015-05-29 11:11:29 -0400282 setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
Joe Onorato005847b2010-06-04 16:08:02 -0400283 }
Joe Onorato005847b2010-06-04 16:08:02 -0400284 return true;
Joe Onorato0cbda992010-05-02 16:28:15 -0700285 }
286
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800287 public void updateDrawable() {
288 updateDrawable(true /* with clear */);
289 }
290
291 private boolean updateDrawable(boolean withClear) {
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100292 if (mIcon == null) {
293 return false;
294 }
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800295 Drawable drawable;
296 try {
297 drawable = getIcon(mIcon);
298 } catch (OutOfMemoryError e) {
299 Log.w(TAG, "OOM while inflating " + mIcon.icon + " for slot " + mSlot);
300 return false;
301 }
302
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800303 if (drawable == null) {
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800304 Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800305 return false;
306 }
307 if (withClear) {
308 setImageDrawable(null);
309 }
310 setImageDrawable(drawable);
311 return true;
312 }
313
Joe Onoratof5510542010-06-01 07:46:41 -0700314 private Drawable getIcon(StatusBarIcon icon) {
315 return getIcon(getContext(), icon);
316 }
317
Joe Onorato0cbda992010-05-02 16:28:15 -0700318 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -0400319 * Returns the right icon to use for this item
John Spurlock209bede2013-07-17 12:23:27 -0400320 *
Dan Sandlerd63f9322015-05-06 15:18:49 -0400321 * @param context Context to use to get resources
Joe Onorato0cbda992010-05-02 16:28:15 -0700322 * @return Drawable for this item, or null if the package or item could not
323 * be found
324 */
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800325 public static Drawable getIcon(Context context, StatusBarIcon statusBarIcon) {
326 int userId = statusBarIcon.user.getIdentifier();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400327 if (userId == UserHandle.USER_ALL) {
Xiaohui Chen87d0e252015-07-30 15:38:16 -0700328 userId = UserHandle.USER_SYSTEM;
Joe Onorato0cbda992010-05-02 16:28:15 -0700329 }
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800330
331 Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
332
333 TypedValue typedValue = new TypedValue();
334 context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
335 float scaleFactor = typedValue.getFloat();
336
337 // No need to scale the icon, so return it as is.
338 if (scaleFactor == 1.f) {
339 return icon;
340 }
341
342 return new ScalingDrawableWrapper(icon, scaleFactor);
Joe Onorato0cbda992010-05-02 16:28:15 -0700343 }
Joe Onoratob77f53b2010-05-28 19:59:51 -0400344
345 public StatusBarIcon getStatusBarIcon() {
346 return mIcon;
347 }
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700348
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700349 @Override
350 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
351 super.onInitializeAccessibilityEvent(event);
352 if (mNotification != null) {
353 event.setParcelableData(mNotification);
354 }
355 }
356
357 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400358 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
359 super.onSizeChanged(w, h, oldw, oldh);
360 if (mNumberBackground != null) {
361 placeNumber();
362 }
363 }
364
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700365 @Override
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100366 public void onRtlPropertiesChanged(int layoutDirection) {
367 super.onRtlPropertiesChanged(layoutDirection);
368 updateDrawable();
369 }
370
371 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400372 protected void onDraw(Canvas canvas) {
Selim Cinek49014f82016-11-04 14:55:30 -0700373 if (mIconAppearAmount > 0.0f) {
374 canvas.save();
375 canvas.scale(mIconScale * mIconAppearAmount, mIconScale * mIconAppearAmount,
376 getWidth() / 2, getHeight() / 2);
377 super.onDraw(canvas);
378 canvas.restore();
379 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400380
381 if (mNumberBackground != null) {
382 mNumberBackground.draw(canvas);
383 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain);
384 }
Selim Cinek49014f82016-11-04 14:55:30 -0700385 if (mDotAppearAmount != 0.0f) {
386 float radius;
387 float alpha;
388 if (mDotAppearAmount <= 1.0f) {
389 radius = mDotRadius * mDotAppearAmount;
390 alpha = 1.0f;
391 } else {
392 float fadeOutAmount = mDotAppearAmount - 1.0f;
393 alpha = 1.0f - fadeOutAmount;
394 radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount);
395 }
396 mDotPaint.setAlpha((int) (alpha * 255));
397 canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mDotPaint);
398 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400399 }
400
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700401 @Override
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700402 protected void debug(int depth) {
403 super.debug(depth);
404 Log.d("View", debugIndent(depth) + "slot=" + mSlot);
405 Log.d("View", debugIndent(depth) + "icon=" + mIcon);
406 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400407
408 void placeNumber() {
Daniel Sandlerebce0112011-06-16 16:44:51 -0400409 final String str;
John Spurlock01534782014-01-13 11:59:22 -0500410 final int tooBig = getContext().getResources().getInteger(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400411 android.R.integer.status_bar_notification_info_maxnum);
412 if (mIcon.number > tooBig) {
John Spurlock01534782014-01-13 11:59:22 -0500413 str = getContext().getResources().getString(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400414 android.R.string.status_bar_notification_info_overflow);
415 } else {
416 NumberFormat f = NumberFormat.getIntegerInstance();
417 str = f.format(mIcon.number);
418 }
419 mNumberText = str;
420
Joe Onorato6c01a112010-10-04 17:38:47 -0400421 final int w = getWidth();
422 final int h = getHeight();
423 final Rect r = new Rect();
424 mNumberPain.getTextBounds(str, 0, str.length(), r);
425 final int tw = r.right - r.left;
426 final int th = r.bottom - r.top;
427 mNumberBackground.getPadding(r);
428 int dw = r.left + tw + r.right;
429 if (dw < mNumberBackground.getMinimumWidth()) {
430 dw = mNumberBackground.getMinimumWidth();
431 }
432 mNumberX = w-r.right-((dw-r.right-r.left)/2);
433 int dh = r.top + th + r.bottom;
434 if (dh < mNumberBackground.getMinimumWidth()) {
435 dh = mNumberBackground.getMinimumWidth();
436 }
437 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2);
438 mNumberBackground.setBounds(w-dw, h-dh, w, h);
439 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700440
441 private void setContentDescription(Notification notification) {
442 if (notification != null) {
Adrian Rooseba05822016-04-22 17:09:27 -0700443 String d = contentDescForNotification(mContext, notification);
444 if (!TextUtils.isEmpty(d)) {
445 setContentDescription(d);
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700446 }
447 }
448 }
Daniel Sandler7579bca2011-08-18 15:47:26 -0400449
450 public String toString() {
John Spurlock209bede2013-07-17 12:23:27 -0400451 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
Daniel Sandler7579bca2011-08-18 15:47:26 -0400452 + " notification=" + mNotification + ")";
453 }
Jason Monk3b230072015-05-29 11:11:29 -0400454
455 public String getSlot() {
456 return mSlot;
457 }
Adrian Rooseba05822016-04-22 17:09:27 -0700458
459
460 public static String contentDescForNotification(Context c, Notification n) {
Adrian Roosa8e18ef2016-05-26 17:17:02 -0700461 String appName = "";
462 try {
463 Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
464 appName = builder.loadHeaderAppName();
465 } catch (RuntimeException e) {
466 Log.e(TAG, "Unable to recover builder", e);
467 // Trying to get the app name from the app info instead.
468 Parcelable appInfo = n.extras.getParcelable(
469 Notification.EXTRA_BUILDER_APPLICATION_INFO);
470 if (appInfo instanceof ApplicationInfo) {
471 appName = String.valueOf(((ApplicationInfo) appInfo).loadLabel(
472 c.getPackageManager()));
473 }
474 }
Adrian Roos51548e62016-04-28 11:20:26 -0700475
Julia Reynolds7f9ce782016-05-17 15:34:34 -0400476 CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
Adrian Roosf76600e2017-04-19 15:32:43 -0700477 CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
Adrian Rooseba05822016-04-22 17:09:27 -0700478 CharSequence ticker = n.tickerText;
Adrian Roos51548e62016-04-28 11:20:26 -0700479
Adrian Roosf76600e2017-04-19 15:32:43 -0700480 // Some apps just put the app name into the title
481 CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title;
482
483 CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText
Adrian Roos6e782a52017-03-30 18:34:10 -0700484 : !TextUtils.isEmpty(ticker) ? ticker : "";
Adrian Roos51548e62016-04-28 11:20:26 -0700485
486 return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
Adrian Rooseba05822016-04-22 17:09:27 -0700487 }
Adrian Roos51548e62016-04-28 11:20:26 -0700488
Selim Cinek875ba9b2017-02-13 16:20:17 -0800489 /**
490 * Set the color that is used to draw decoration like the overflow dot. This will not be applied
491 * to the drawable.
492 */
493 public void setDecorColor(int iconTint) {
Adrian Roos456e0052017-04-04 16:44:29 -0700494 mDecorColor = iconTint;
495 updateDecorColor();
496 }
497
498 private void updateDecorColor() {
499 int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount);
500 if (mDotPaint.getColor() != color) {
501 mDotPaint.setColor(color);
502
503 if (mDotAppearAmount != 0) {
504 invalidate();
505 }
506 }
Selim Cinek49014f82016-11-04 14:55:30 -0700507 }
508
Selim Cinek875ba9b2017-02-13 16:20:17 -0800509 /**
510 * Set the static color that should be used for the drawable of this icon if it's not
511 * transitioning this also immediately sets the color.
512 */
513 public void setStaticDrawableColor(int color) {
514 mDrawableColor = color;
515 setColorInternal(color);
516 mIconColor = color;
Adrian Roos456e0052017-04-04 16:44:29 -0700517 mDozer.setColor(color);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800518 }
519
520 private void setColorInternal(int color) {
Adrian Roosf3eacd32017-04-14 16:35:58 -0700521 mCurrentSetColor = color;
522 updateIconColor();
523 }
524
525 private void updateIconColor() {
526 if (mCurrentSetColor != NO_COLOR) {
527 setImageTintList(ColorStateList.valueOf(NotificationUtils.interpolateColors(
528 mCurrentSetColor, Color.WHITE, mDarkAmount)));
Selim Cinek875ba9b2017-02-13 16:20:17 -0800529 } else {
530 setImageTintList(null);
Adrian Roosf3eacd32017-04-14 16:35:58 -0700531 mDozer.updateGrayscale(this, mDarkAmount);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800532 }
Selim Cinek875ba9b2017-02-13 16:20:17 -0800533 }
534
535 public void setIconColor(int iconColor, boolean animate) {
536 if (mIconColor != iconColor) {
537 mIconColor = iconColor;
538 if (mColorAnimator != null) {
539 mColorAnimator.cancel();
540 }
541 if (mCurrentSetColor == iconColor) {
542 return;
543 }
544 if (animate && mCurrentSetColor != NO_COLOR) {
545 mAnimationStartColor = mCurrentSetColor;
546 mColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
547 mColorAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
548 mColorAnimator.setDuration(ANIMATION_DURATION_FAST);
549 mColorAnimator.addUpdateListener(mColorUpdater);
550 mColorAnimator.addListener(new AnimatorListenerAdapter() {
551 @Override
552 public void onAnimationEnd(Animator animation) {
553 mColorAnimator = null;
554 mAnimationStartColor = NO_COLOR;
555 }
556 });
557 mColorAnimator.start();
558 } else {
559 setColorInternal(iconColor);
560 }
561 }
562 }
563
564 public int getStaticDrawableColor() {
565 return mDrawableColor;
566 }
567
Selim Cinek5b5beb012016-11-08 18:11:58 -0800568 public void setVisibleState(int state) {
569 setVisibleState(state, true /* animate */, null /* endRunnable */);
570 }
571
572 public void setVisibleState(int state, boolean animate) {
573 setVisibleState(state, animate, null);
574 }
575
576 @Override
577 public boolean hasOverlappingRendering() {
578 return false;
579 }
580
581 public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800582 boolean runnableAdded = false;
Selim Cinek49014f82016-11-04 14:55:30 -0700583 if (visibleState != mVisibleState) {
584 mVisibleState = visibleState;
Selim Cinek5ece5a72017-02-15 11:47:42 -0800585 if (mIconAppearAnimator != null) {
586 mIconAppearAnimator.cancel();
587 }
588 if (mDotAnimator != null) {
589 mDotAnimator.cancel();
590 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800591 if (animate) {
Selim Cinek5b5beb012016-11-08 18:11:58 -0800592 float targetAmount = 0.0f;
593 Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN;
594 if (visibleState == STATE_ICON) {
595 targetAmount = 1.0f;
596 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
597 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800598 float currentAmount = getIconAppearAmount();
599 if (targetAmount != currentAmount) {
600 mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
601 currentAmount, targetAmount);
602 mIconAppearAnimator.setInterpolator(interpolator);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800603 mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST);
Selim Cinek65d418e2016-11-29 15:42:34 -0800604 mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
605 @Override
606 public void onAnimationEnd(Animator animation) {
607 mIconAppearAnimator = null;
608 runRunnable(endRunnable);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800609 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800610 });
611 mIconAppearAnimator.start();
612 runnableAdded = true;
613 }
Selim Cinek49014f82016-11-04 14:55:30 -0700614
Selim Cinek5b5beb012016-11-08 18:11:58 -0800615 targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f;
616 interpolator = Interpolators.FAST_OUT_LINEAR_IN;
617 if (visibleState == STATE_DOT) {
618 targetAmount = 1.0f;
619 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
620 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800621 currentAmount = getDotAppearAmount();
622 if (targetAmount != currentAmount) {
623 mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
624 currentAmount, targetAmount);
625 mDotAnimator.setInterpolator(interpolator);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800626 mDotAnimator.setDuration(ANIMATION_DURATION_FAST);
Selim Cinek65d418e2016-11-29 15:42:34 -0800627 final boolean runRunnable = !runnableAdded;
628 mDotAnimator.addListener(new AnimatorListenerAdapter() {
629 @Override
630 public void onAnimationEnd(Animator animation) {
631 mDotAnimator = null;
632 if (runRunnable) {
633 runRunnable(endRunnable);
634 }
635 }
636 });
637 mDotAnimator.start();
638 runnableAdded = true;
639 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800640 } else {
641 setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f);
Selim Cinek01a73f92016-12-06 16:13:42 -0800642 setDotAppearAmount(visibleState == STATE_DOT ? 1.0f
643 : visibleState == STATE_ICON ? 2.0f
644 : 0.0f);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800645 }
Selim Cinek49014f82016-11-04 14:55:30 -0700646 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800647 if (!runnableAdded) {
648 runRunnable(endRunnable);
649 }
650 }
651
652 private void runRunnable(Runnable runnable) {
653 if (runnable != null) {
654 runnable.run();
655 }
Selim Cinek49014f82016-11-04 14:55:30 -0700656 }
657
Selim Cinek5b5beb012016-11-08 18:11:58 -0800658 public void setIconAppearAmount(float iconAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800659 if (mIconAppearAmount != iconAppearAmount) {
660 mIconAppearAmount = iconAppearAmount;
661 invalidate();
662 }
Selim Cinek49014f82016-11-04 14:55:30 -0700663 }
664
665 public float getIconAppearAmount() {
666 return mIconAppearAmount;
667 }
668
669 public int getVisibleState() {
670 return mVisibleState;
671 }
672
673 public void setDotAppearAmount(float dotAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800674 if (mDotAppearAmount != dotAppearAmount) {
675 mDotAppearAmount = dotAppearAmount;
676 invalidate();
677 }
Selim Cinek49014f82016-11-04 14:55:30 -0700678 }
679
Selim Cinek2b549f42016-11-22 16:38:51 -0800680 @Override
681 public void setVisibility(int visibility) {
682 super.setVisibility(visibility);
683 if (mOnVisibilityChangedListener != null) {
684 mOnVisibilityChangedListener.onVisibilityChanged(visibility);
685 }
686 }
687
Selim Cinek49014f82016-11-04 14:55:30 -0700688 public float getDotAppearAmount() {
689 return mDotAppearAmount;
690 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800691
692 public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
693 mOnVisibilityChangedListener = listener;
694 }
695
Adrian Roos456e0052017-04-04 16:44:29 -0700696 public void setDark(boolean dark, boolean fade, long delay) {
Adrian Roos456e0052017-04-04 16:44:29 -0700697 mDozer.setIntensityDark(f -> {
698 mDarkAmount = f;
Adrian Roos138f0342017-04-28 09:11:28 -0700699 updateIconScale();
Adrian Roos456e0052017-04-04 16:44:29 -0700700 updateDecorColor();
Adrian Roosf3eacd32017-04-14 16:35:58 -0700701 updateIconColor();
Adrian Roos456e0052017-04-04 16:44:29 -0700702 }, dark, fade, delay);
703 }
704
Selim Cinek2b549f42016-11-22 16:38:51 -0800705 public interface OnVisibilityChangedListener {
706 void onVisibilityChanged(int newVisibility);
707 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700708}