blob: 1101701942943b4069bc763ee9aec9509eed227c [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;
Joe Onorato6c01a112010-10-04 17:38:47 -040030import android.graphics.Paint;
31import android.graphics.Rect;
John Spurlockde84f0e2013-06-12 12:41:00 -040032import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040033import android.graphics.drawable.Icon;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070034import android.os.Parcelable;
Jeff Sharkeyded653b2012-09-27 19:09:24 -070035import android.os.UserHandle;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070036import android.text.TextUtils;
Daniel Sandler05e24142011-11-10 11:56:49 -050037import android.util.AttributeSet;
Selim Cinek49014f82016-11-04 14:55:30 -070038import android.util.FloatProperty;
Joe Onoratof9ec03c2010-09-23 15:09:18 -070039import android.util.Log;
Selim Cinek49014f82016-11-04 14:55:30 -070040import android.util.Property;
Anthony Chen55e8e1e2016-01-08 10:31:46 -080041import android.util.TypedValue;
Joe Onorato0cbda992010-05-02 16:28:15 -070042import android.view.ViewDebug;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070043import android.view.accessibility.AccessibilityEvent;
Selim Cinek49014f82016-11-04 14:55:30 -070044import android.view.animation.Interpolator;
Winsonc0d70582016-01-29 10:24:39 -080045
Joe Onorato0cbda992010-05-02 16:28:15 -070046import com.android.internal.statusbar.StatusBarIcon;
Selim Cinek49014f82016-11-04 14:55:30 -070047import com.android.systemui.Interpolators;
Joe Onorato6c01a112010-10-04 17:38:47 -040048import com.android.systemui.R;
Selim Cinek49014f82016-11-04 14:55:30 -070049import com.android.systemui.statusbar.notification.NotificationUtils;
Joe Onorato6c01a112010-10-04 17:38:47 -040050
John Spurlockde84f0e2013-06-12 12:41:00 -040051import java.text.NumberFormat;
52
Joe Onorato0cbda992010-05-02 16:28:15 -070053public class StatusBarIconView extends AnimatedImageView {
Selim Cinek875ba9b2017-02-13 16:20:17 -080054 public static final int NO_COLOR = 0;
55 private final int ANIMATION_DURATION_FAST = 100;
56
Selim Cinek49014f82016-11-04 14:55:30 -070057 public static final int STATE_ICON = 0;
58 public static final int STATE_DOT = 1;
59 public static final int STATE_HIDDEN = 2;
Joe Onorato0cbda992010-05-02 16:28:15 -070060
Selim Cinek49014f82016-11-04 14:55:30 -070061 private static final String TAG = "StatusBarIconView";
62 private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
63 = new FloatProperty<StatusBarIconView>("iconAppearAmount") {
64
65 @Override
66 public void setValue(StatusBarIconView object, float value) {
67 object.setIconAppearAmount(value);
68 }
69
70 @Override
71 public Float get(StatusBarIconView object) {
72 return object.getIconAppearAmount();
73 }
74 };
Selim Cinek5b5beb012016-11-08 18:11:58 -080075 private static final Property<StatusBarIconView, Float> DOT_APPEAR_AMOUNT
Selim Cinek49014f82016-11-04 14:55:30 -070076 = new FloatProperty<StatusBarIconView>("dot_appear_amount") {
77
78 @Override
79 public void setValue(StatusBarIconView object, float value) {
80 object.setDotAppearAmount(value);
81 }
82
83 @Override
84 public Float get(StatusBarIconView object) {
85 return object.getDotAppearAmount();
86 }
87 };
88
89 private boolean mAlwaysScaleIcon;
Joe Onorato0cbda992010-05-02 16:28:15 -070090 private StatusBarIcon mIcon;
91 @ViewDebug.ExportedProperty private String mSlot;
Joe Onorato6c01a112010-10-04 17:38:47 -040092 private Drawable mNumberBackground;
93 private Paint mNumberPain;
94 private int mNumberX;
95 private int mNumberY;
96 private String mNumberText;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070097 private Notification mNotification;
Jason Monk3b230072015-05-29 11:11:29 -040098 private final boolean mBlocked;
Selim Cinek3e7592d2016-04-11 09:35:54 +080099 private int mDensity;
Selim Cinek281c2022016-10-13 19:14:43 -0700100 private float mIconScale = 1.0f;
Selim Cinek49014f82016-11-04 14:55:30 -0700101 private final Paint mDotPaint = new Paint();
102 private boolean mDotVisible;
103 private float mDotRadius;
104 private int mStaticDotRadius;
105 private int mVisibleState = STATE_ICON;
106 private float mIconAppearAmount = 1.0f;
107 private ObjectAnimator mIconAppearAnimator;
108 private ObjectAnimator mDotAnimator;
109 private float mDotAppearAmount;
Selim Cinek2b549f42016-11-22 16:38:51 -0800110 private OnVisibilityChangedListener mOnVisibilityChangedListener;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800111 private int mDrawableColor;
112 private int mIconColor;
113 private ValueAnimator mColorAnimator;
114 private int mCurrentSetColor = NO_COLOR;
115 private int mAnimationStartColor = NO_COLOR;
116 private final ValueAnimator.AnimatorUpdateListener mColorUpdater
117 = animation -> {
118 int newColor = NotificationUtils.interpolateColors(mAnimationStartColor, mIconColor,
119 animation.getAnimatedFraction());
120 setColorInternal(newColor);
121 };
Joe Onorato0cbda992010-05-02 16:28:15 -0700122
John Spurlocke6f0a712013-09-03 16:23:49 -0400123 public StatusBarIconView(Context context, String slot, Notification notification) {
Jason Monk3b230072015-05-29 11:11:29 -0400124 this(context, slot, notification, false);
125 }
126
127 public StatusBarIconView(Context context, String slot, Notification notification,
128 boolean blocked) {
Joe Onorato0cbda992010-05-02 16:28:15 -0700129 super(context);
Jason Monk3b230072015-05-29 11:11:29 -0400130 mBlocked = blocked;
Joe Onorato0cbda992010-05-02 16:28:15 -0700131 mSlot = slot;
Joe Onorato6c01a112010-10-04 17:38:47 -0400132 mNumberPain = new Paint();
133 mNumberPain.setTextAlign(Paint.Align.CENTER);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700134 mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
Joe Onorato6c01a112010-10-04 17:38:47 -0400135 mNumberPain.setAntiAlias(true);
Christoph Studera0506e72014-07-31 20:27:39 +0200136 setNotification(notification);
Selim Cinek3e7592d2016-04-11 09:35:54 +0800137 maybeUpdateIconScale();
138 setScaleType(ScaleType.CENTER);
139 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Selim Cinek49014f82016-11-04 14:55:30 -0700140 if (mNotification != null) {
Selim Cinek875ba9b2017-02-13 16:20:17 -0800141 setDecorColor(getContext().getColor(
Selim Cinek49014f82016-11-04 14:55:30 -0700142 com.android.internal.R.color.notification_icon_default_color));
143 }
144 reloadDimens();
Selim Cinek3e7592d2016-04-11 09:35:54 +0800145 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400146
Selim Cinek3e7592d2016-04-11 09:35:54 +0800147 private void maybeUpdateIconScale() {
Daniel Sandler7579bca2011-08-18 15:47:26 -0400148 // We do not resize and scale system icons (on the right), only notification icons (on the
149 // left).
Selim Cinek3e7592d2016-04-11 09:35:54 +0800150 if (mNotification != null || mAlwaysScaleIcon) {
151 updateIconScale();
Daniel Sandler7579bca2011-08-18 15:47:26 -0400152 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800153 }
Daniel Sandlerabff0322011-09-27 11:19:34 -0400154
Selim Cinek3e7592d2016-04-11 09:35:54 +0800155 private void updateIconScale() {
156 Resources res = mContext.getResources();
157 final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
158 final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
Selim Cinek281c2022016-10-13 19:14:43 -0700159 mIconScale = (float)imageBounds / (float)outerBounds;
Selim Cinek281c2022016-10-13 19:14:43 -0700160 }
161
162 public float getIconScale() {
163 return mIconScale;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800164 }
165
166 @Override
167 protected void onConfigurationChanged(Configuration newConfig) {
168 super.onConfigurationChanged(newConfig);
169 int density = newConfig.densityDpi;
170 if (density != mDensity) {
171 mDensity = density;
172 maybeUpdateIconScale();
173 updateDrawable();
Selim Cinek49014f82016-11-04 14:55:30 -0700174 reloadDimens();
175 }
176 }
177
178 private void reloadDimens() {
179 boolean applyRadius = mDotRadius == mStaticDotRadius;
180 mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
181 if (applyRadius) {
182 mDotRadius = mStaticDotRadius;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800183 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700184 }
185
Christoph Studera0506e72014-07-31 20:27:39 +0200186 public void setNotification(Notification notification) {
187 mNotification = notification;
188 setContentDescription(notification);
189 }
190
Daniel Sandler05e24142011-11-10 11:56:49 -0500191 public StatusBarIconView(Context context, AttributeSet attrs) {
192 super(context, attrs);
Jason Monk3b230072015-05-29 11:11:29 -0400193 mBlocked = false;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800194 mAlwaysScaleIcon = true;
195 updateIconScale();
196 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Daniel Sandler05e24142011-11-10 11:56:49 -0500197 }
198
Joe Onorato0cbda992010-05-02 16:28:15 -0700199 private static boolean streq(String a, String b) {
Joe Onorato66d7d012010-05-14 10:05:10 -0700200 if (a == b) {
201 return true;
202 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700203 if (a == null && b != null) {
204 return false;
205 }
206 if (a != null && b == null) {
207 return false;
208 }
209 return a.equals(b);
210 }
211
Dan Sandlerd63f9322015-05-06 15:18:49 -0400212 public boolean equalIcons(Icon a, Icon b) {
213 if (a == b) return true;
214 if (a.getType() != b.getType()) return false;
215 switch (a.getType()) {
216 case Icon.TYPE_RESOURCE:
217 return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId();
218 case Icon.TYPE_URI:
219 return a.getUriString().equals(b.getUriString());
220 default:
221 return false;
222 }
223 }
Joe Onorato005847b2010-06-04 16:08:02 -0400224 /**
225 * Returns whether the set succeeded.
226 */
227 public boolean set(StatusBarIcon icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -0400228 final boolean iconEquals = mIcon != null && equalIcons(mIcon.icon, icon.icon);
Joe Onorato005847b2010-06-04 16:08:02 -0400229 final boolean levelEquals = iconEquals
230 && mIcon.iconLevel == icon.iconLevel;
231 final boolean visibilityEquals = mIcon != null
232 && mIcon.visible == icon.visible;
Joe Onorato6c01a112010-10-04 17:38:47 -0400233 final boolean numberEquals = mIcon != null
234 && mIcon.number == icon.number;
235 mIcon = icon.clone();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700236 setContentDescription(icon.contentDescription);
Joe Onorato005847b2010-06-04 16:08:02 -0400237 if (!iconEquals) {
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800238 if (!updateDrawable(false /* no clear */)) return false;
Selim Cinek2b6eb8032016-12-29 14:22:21 +0100239 // we have to clear the grayscale tag since it may have changed
240 setTag(R.id.icon_is_grayscale, null);
Joe Onorato0cbda992010-05-02 16:28:15 -0700241 }
Joe Onorato005847b2010-06-04 16:08:02 -0400242 if (!levelEquals) {
243 setImageLevel(icon.iconLevel);
Joe Onorato0cbda992010-05-02 16:28:15 -0700244 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400245
Joe Onorato6c01a112010-10-04 17:38:47 -0400246 if (!numberEquals) {
John Spurlock01534782014-01-13 11:59:22 -0500247 if (icon.number > 0 && getContext().getResources().getBoolean(
Joe Onorato8595a3d2010-11-19 18:12:07 -0800248 R.bool.config_statusBarShowNumber)) {
Joe Onorato6c01a112010-10-04 17:38:47 -0400249 if (mNumberBackground == null) {
250 mNumberBackground = getContext().getResources().getDrawable(
251 R.drawable.ic_notification_overlay);
252 }
253 placeNumber();
254 } else {
255 mNumberBackground = null;
256 mNumberText = null;
257 }
258 invalidate();
259 }
Joe Onorato005847b2010-06-04 16:08:02 -0400260 if (!visibilityEquals) {
Jason Monk3b230072015-05-29 11:11:29 -0400261 setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
Joe Onorato005847b2010-06-04 16:08:02 -0400262 }
Joe Onorato005847b2010-06-04 16:08:02 -0400263 return true;
Joe Onorato0cbda992010-05-02 16:28:15 -0700264 }
265
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800266 public void updateDrawable() {
267 updateDrawable(true /* with clear */);
268 }
269
270 private boolean updateDrawable(boolean withClear) {
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100271 if (mIcon == null) {
272 return false;
273 }
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800274 Drawable drawable;
275 try {
276 drawable = getIcon(mIcon);
277 } catch (OutOfMemoryError e) {
278 Log.w(TAG, "OOM while inflating " + mIcon.icon + " for slot " + mSlot);
279 return false;
280 }
281
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800282 if (drawable == null) {
Adrian Roosfb2d0cc2017-01-31 15:10:00 -0800283 Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800284 return false;
285 }
286 if (withClear) {
287 setImageDrawable(null);
288 }
289 setImageDrawable(drawable);
290 return true;
291 }
292
Joe Onoratof5510542010-06-01 07:46:41 -0700293 private Drawable getIcon(StatusBarIcon icon) {
294 return getIcon(getContext(), icon);
295 }
296
Joe Onorato0cbda992010-05-02 16:28:15 -0700297 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -0400298 * Returns the right icon to use for this item
John Spurlock209bede2013-07-17 12:23:27 -0400299 *
Dan Sandlerd63f9322015-05-06 15:18:49 -0400300 * @param context Context to use to get resources
Joe Onorato0cbda992010-05-02 16:28:15 -0700301 * @return Drawable for this item, or null if the package or item could not
302 * be found
303 */
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800304 public static Drawable getIcon(Context context, StatusBarIcon statusBarIcon) {
305 int userId = statusBarIcon.user.getIdentifier();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400306 if (userId == UserHandle.USER_ALL) {
Xiaohui Chen87d0e252015-07-30 15:38:16 -0700307 userId = UserHandle.USER_SYSTEM;
Joe Onorato0cbda992010-05-02 16:28:15 -0700308 }
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800309
310 Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
311
312 TypedValue typedValue = new TypedValue();
313 context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
314 float scaleFactor = typedValue.getFloat();
315
316 // No need to scale the icon, so return it as is.
317 if (scaleFactor == 1.f) {
318 return icon;
319 }
320
321 return new ScalingDrawableWrapper(icon, scaleFactor);
Joe Onorato0cbda992010-05-02 16:28:15 -0700322 }
Joe Onoratob77f53b2010-05-28 19:59:51 -0400323
324 public StatusBarIcon getStatusBarIcon() {
325 return mIcon;
326 }
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700327
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700328 @Override
329 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
330 super.onInitializeAccessibilityEvent(event);
331 if (mNotification != null) {
332 event.setParcelableData(mNotification);
333 }
334 }
335
336 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400337 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
338 super.onSizeChanged(w, h, oldw, oldh);
339 if (mNumberBackground != null) {
340 placeNumber();
341 }
342 }
343
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700344 @Override
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100345 public void onRtlPropertiesChanged(int layoutDirection) {
346 super.onRtlPropertiesChanged(layoutDirection);
347 updateDrawable();
348 }
349
350 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400351 protected void onDraw(Canvas canvas) {
Selim Cinek49014f82016-11-04 14:55:30 -0700352 if (mIconAppearAmount > 0.0f) {
353 canvas.save();
354 canvas.scale(mIconScale * mIconAppearAmount, mIconScale * mIconAppearAmount,
355 getWidth() / 2, getHeight() / 2);
356 super.onDraw(canvas);
357 canvas.restore();
358 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400359
360 if (mNumberBackground != null) {
361 mNumberBackground.draw(canvas);
362 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain);
363 }
Selim Cinek49014f82016-11-04 14:55:30 -0700364 if (mDotAppearAmount != 0.0f) {
365 float radius;
366 float alpha;
367 if (mDotAppearAmount <= 1.0f) {
368 radius = mDotRadius * mDotAppearAmount;
369 alpha = 1.0f;
370 } else {
371 float fadeOutAmount = mDotAppearAmount - 1.0f;
372 alpha = 1.0f - fadeOutAmount;
373 radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount);
374 }
375 mDotPaint.setAlpha((int) (alpha * 255));
376 canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mDotPaint);
377 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400378 }
379
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700380 @Override
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700381 protected void debug(int depth) {
382 super.debug(depth);
383 Log.d("View", debugIndent(depth) + "slot=" + mSlot);
384 Log.d("View", debugIndent(depth) + "icon=" + mIcon);
385 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400386
387 void placeNumber() {
Daniel Sandlerebce0112011-06-16 16:44:51 -0400388 final String str;
John Spurlock01534782014-01-13 11:59:22 -0500389 final int tooBig = getContext().getResources().getInteger(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400390 android.R.integer.status_bar_notification_info_maxnum);
391 if (mIcon.number > tooBig) {
John Spurlock01534782014-01-13 11:59:22 -0500392 str = getContext().getResources().getString(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400393 android.R.string.status_bar_notification_info_overflow);
394 } else {
395 NumberFormat f = NumberFormat.getIntegerInstance();
396 str = f.format(mIcon.number);
397 }
398 mNumberText = str;
399
Joe Onorato6c01a112010-10-04 17:38:47 -0400400 final int w = getWidth();
401 final int h = getHeight();
402 final Rect r = new Rect();
403 mNumberPain.getTextBounds(str, 0, str.length(), r);
404 final int tw = r.right - r.left;
405 final int th = r.bottom - r.top;
406 mNumberBackground.getPadding(r);
407 int dw = r.left + tw + r.right;
408 if (dw < mNumberBackground.getMinimumWidth()) {
409 dw = mNumberBackground.getMinimumWidth();
410 }
411 mNumberX = w-r.right-((dw-r.right-r.left)/2);
412 int dh = r.top + th + r.bottom;
413 if (dh < mNumberBackground.getMinimumWidth()) {
414 dh = mNumberBackground.getMinimumWidth();
415 }
416 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2);
417 mNumberBackground.setBounds(w-dw, h-dh, w, h);
418 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700419
420 private void setContentDescription(Notification notification) {
421 if (notification != null) {
Adrian Rooseba05822016-04-22 17:09:27 -0700422 String d = contentDescForNotification(mContext, notification);
423 if (!TextUtils.isEmpty(d)) {
424 setContentDescription(d);
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700425 }
426 }
427 }
Daniel Sandler7579bca2011-08-18 15:47:26 -0400428
429 public String toString() {
John Spurlock209bede2013-07-17 12:23:27 -0400430 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
Daniel Sandler7579bca2011-08-18 15:47:26 -0400431 + " notification=" + mNotification + ")";
432 }
Jason Monk3b230072015-05-29 11:11:29 -0400433
434 public String getSlot() {
435 return mSlot;
436 }
Adrian Rooseba05822016-04-22 17:09:27 -0700437
438
439 public static String contentDescForNotification(Context c, Notification n) {
Adrian Roosa8e18ef2016-05-26 17:17:02 -0700440 String appName = "";
441 try {
442 Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
443 appName = builder.loadHeaderAppName();
444 } catch (RuntimeException e) {
445 Log.e(TAG, "Unable to recover builder", e);
446 // Trying to get the app name from the app info instead.
447 Parcelable appInfo = n.extras.getParcelable(
448 Notification.EXTRA_BUILDER_APPLICATION_INFO);
449 if (appInfo instanceof ApplicationInfo) {
450 appName = String.valueOf(((ApplicationInfo) appInfo).loadLabel(
451 c.getPackageManager()));
452 }
453 }
Adrian Roos51548e62016-04-28 11:20:26 -0700454
Julia Reynolds7f9ce782016-05-17 15:34:34 -0400455 CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
Adrian Rooseba05822016-04-22 17:09:27 -0700456 CharSequence ticker = n.tickerText;
Adrian Roos51548e62016-04-28 11:20:26 -0700457
458 CharSequence desc = !TextUtils.isEmpty(ticker) ? ticker
459 : !TextUtils.isEmpty(title) ? title : "";
460
461 return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
Adrian Rooseba05822016-04-22 17:09:27 -0700462 }
Adrian Roos51548e62016-04-28 11:20:26 -0700463
Selim Cinek875ba9b2017-02-13 16:20:17 -0800464 /**
465 * Set the color that is used to draw decoration like the overflow dot. This will not be applied
466 * to the drawable.
467 */
468 public void setDecorColor(int iconTint) {
Selim Cinek49014f82016-11-04 14:55:30 -0700469 mDotPaint.setColor(iconTint);
470 }
471
Selim Cinek875ba9b2017-02-13 16:20:17 -0800472 /**
473 * Set the static color that should be used for the drawable of this icon if it's not
474 * transitioning this also immediately sets the color.
475 */
476 public void setStaticDrawableColor(int color) {
477 mDrawableColor = color;
478 setColorInternal(color);
479 mIconColor = color;
480 }
481
482 private void setColorInternal(int color) {
483 if (color != NO_COLOR) {
484 setImageTintList(ColorStateList.valueOf(color));
485 } else {
486 setImageTintList(null);
487 }
488 mCurrentSetColor = color;
489 }
490
491 public void setIconColor(int iconColor, boolean animate) {
492 if (mIconColor != iconColor) {
493 mIconColor = iconColor;
494 if (mColorAnimator != null) {
495 mColorAnimator.cancel();
496 }
497 if (mCurrentSetColor == iconColor) {
498 return;
499 }
500 if (animate && mCurrentSetColor != NO_COLOR) {
501 mAnimationStartColor = mCurrentSetColor;
502 mColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
503 mColorAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
504 mColorAnimator.setDuration(ANIMATION_DURATION_FAST);
505 mColorAnimator.addUpdateListener(mColorUpdater);
506 mColorAnimator.addListener(new AnimatorListenerAdapter() {
507 @Override
508 public void onAnimationEnd(Animator animation) {
509 mColorAnimator = null;
510 mAnimationStartColor = NO_COLOR;
511 }
512 });
513 mColorAnimator.start();
514 } else {
515 setColorInternal(iconColor);
516 }
517 }
518 }
519
520 public int getStaticDrawableColor() {
521 return mDrawableColor;
522 }
523
Selim Cinek5b5beb012016-11-08 18:11:58 -0800524 public void setVisibleState(int state) {
525 setVisibleState(state, true /* animate */, null /* endRunnable */);
526 }
527
528 public void setVisibleState(int state, boolean animate) {
529 setVisibleState(state, animate, null);
530 }
531
532 @Override
533 public boolean hasOverlappingRendering() {
534 return false;
535 }
536
537 public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800538 boolean runnableAdded = false;
Selim Cinek49014f82016-11-04 14:55:30 -0700539 if (visibleState != mVisibleState) {
540 mVisibleState = visibleState;
Selim Cinek5ece5a72017-02-15 11:47:42 -0800541 if (mIconAppearAnimator != null) {
542 mIconAppearAnimator.cancel();
543 }
544 if (mDotAnimator != null) {
545 mDotAnimator.cancel();
546 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800547 if (animate) {
Selim Cinek5b5beb012016-11-08 18:11:58 -0800548 float targetAmount = 0.0f;
549 Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN;
550 if (visibleState == STATE_ICON) {
551 targetAmount = 1.0f;
552 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
553 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800554 float currentAmount = getIconAppearAmount();
555 if (targetAmount != currentAmount) {
556 mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
557 currentAmount, targetAmount);
558 mIconAppearAnimator.setInterpolator(interpolator);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800559 mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST);
Selim Cinek65d418e2016-11-29 15:42:34 -0800560 mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
561 @Override
562 public void onAnimationEnd(Animator animation) {
563 mIconAppearAnimator = null;
564 runRunnable(endRunnable);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800565 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800566 });
567 mIconAppearAnimator.start();
568 runnableAdded = true;
569 }
Selim Cinek49014f82016-11-04 14:55:30 -0700570
Selim Cinek5b5beb012016-11-08 18:11:58 -0800571 targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f;
572 interpolator = Interpolators.FAST_OUT_LINEAR_IN;
573 if (visibleState == STATE_DOT) {
574 targetAmount = 1.0f;
575 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
576 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800577 currentAmount = getDotAppearAmount();
578 if (targetAmount != currentAmount) {
579 mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
580 currentAmount, targetAmount);
581 mDotAnimator.setInterpolator(interpolator);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800582 mDotAnimator.setDuration(ANIMATION_DURATION_FAST);
Selim Cinek65d418e2016-11-29 15:42:34 -0800583 final boolean runRunnable = !runnableAdded;
584 mDotAnimator.addListener(new AnimatorListenerAdapter() {
585 @Override
586 public void onAnimationEnd(Animator animation) {
587 mDotAnimator = null;
588 if (runRunnable) {
589 runRunnable(endRunnable);
590 }
591 }
592 });
593 mDotAnimator.start();
594 runnableAdded = true;
595 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800596 } else {
597 setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f);
Selim Cinek01a73f92016-12-06 16:13:42 -0800598 setDotAppearAmount(visibleState == STATE_DOT ? 1.0f
599 : visibleState == STATE_ICON ? 2.0f
600 : 0.0f);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800601 }
Selim Cinek49014f82016-11-04 14:55:30 -0700602 }
Selim Cinek65d418e2016-11-29 15:42:34 -0800603 if (!runnableAdded) {
604 runRunnable(endRunnable);
605 }
606 }
607
608 private void runRunnable(Runnable runnable) {
609 if (runnable != null) {
610 runnable.run();
611 }
Selim Cinek49014f82016-11-04 14:55:30 -0700612 }
613
Selim Cinek5b5beb012016-11-08 18:11:58 -0800614 public void setIconAppearAmount(float iconAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800615 if (mIconAppearAmount != iconAppearAmount) {
616 mIconAppearAmount = iconAppearAmount;
617 invalidate();
618 }
Selim Cinek49014f82016-11-04 14:55:30 -0700619 }
620
621 public float getIconAppearAmount() {
622 return mIconAppearAmount;
623 }
624
625 public int getVisibleState() {
626 return mVisibleState;
627 }
628
629 public void setDotAppearAmount(float dotAppearAmount) {
Selim Cinek9ef119c2017-03-01 15:13:36 -0800630 if (mDotAppearAmount != dotAppearAmount) {
631 mDotAppearAmount = dotAppearAmount;
632 invalidate();
633 }
Selim Cinek49014f82016-11-04 14:55:30 -0700634 }
635
Selim Cinek2b549f42016-11-22 16:38:51 -0800636 @Override
637 public void setVisibility(int visibility) {
638 super.setVisibility(visibility);
639 if (mOnVisibilityChangedListener != null) {
640 mOnVisibilityChangedListener.onVisibilityChanged(visibility);
641 }
642 }
643
Selim Cinek49014f82016-11-04 14:55:30 -0700644 public float getDotAppearAmount() {
645 return mDotAppearAmount;
646 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800647
648 public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
649 mOnVisibilityChangedListener = listener;
650 }
651
652 public interface OnVisibilityChangedListener {
653 void onVisibilityChanged(int newVisibility);
654 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700655}