blob: 03e366283b4edaf387d5cae47f6336fd16ac1976 [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
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070019import android.app.Notification;
Joe Onorato0cbda992010-05-02 16:28:15 -070020import android.content.Context;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070021import android.content.pm.ApplicationInfo;
Selim Cinek3e7592d2016-04-11 09:35:54 +080022import android.content.res.Configuration;
Joe Onorato0cbda992010-05-02 16:28:15 -070023import android.content.res.Resources;
Joe Onorato0cbda992010-05-02 16:28:15 -070024import android.graphics.Canvas;
Joe Onorato6c01a112010-10-04 17:38:47 -040025import android.graphics.Paint;
26import android.graphics.Rect;
John Spurlockde84f0e2013-06-12 12:41:00 -040027import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040028import android.graphics.drawable.Icon;
Adrian Roosa8e18ef2016-05-26 17:17:02 -070029import android.os.Parcelable;
Jeff Sharkeyded653b2012-09-27 19:09:24 -070030import android.os.UserHandle;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070031import android.text.TextUtils;
Daniel Sandler05e24142011-11-10 11:56:49 -050032import android.util.AttributeSet;
Joe Onoratof9ec03c2010-09-23 15:09:18 -070033import android.util.Log;
Anthony Chen55e8e1e2016-01-08 10:31:46 -080034import android.util.TypedValue;
Joe Onorato0cbda992010-05-02 16:28:15 -070035import android.view.ViewDebug;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070036import android.view.accessibility.AccessibilityEvent;
Winsonc0d70582016-01-29 10:24:39 -080037
Joe Onorato0cbda992010-05-02 16:28:15 -070038import com.android.internal.statusbar.StatusBarIcon;
Joe Onorato6c01a112010-10-04 17:38:47 -040039import com.android.systemui.R;
40
John Spurlockde84f0e2013-06-12 12:41:00 -040041import java.text.NumberFormat;
42
Joe Onorato0cbda992010-05-02 16:28:15 -070043public class StatusBarIconView extends AnimatedImageView {
44 private static final String TAG = "StatusBarIconView";
Selim Cinek3e7592d2016-04-11 09:35:54 +080045 private boolean mAlwaysScaleIcon;
Joe Onorato0cbda992010-05-02 16:28:15 -070046
47 private StatusBarIcon mIcon;
48 @ViewDebug.ExportedProperty private String mSlot;
Joe Onorato6c01a112010-10-04 17:38:47 -040049 private Drawable mNumberBackground;
50 private Paint mNumberPain;
51 private int mNumberX;
52 private int mNumberY;
53 private String mNumberText;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070054 private Notification mNotification;
Jason Monk3b230072015-05-29 11:11:29 -040055 private final boolean mBlocked;
Selim Cinek3e7592d2016-04-11 09:35:54 +080056 private int mDensity;
Selim Cinek281c2022016-10-13 19:14:43 -070057 private float mIconScale = 1.0f;
Joe Onorato0cbda992010-05-02 16:28:15 -070058
John Spurlocke6f0a712013-09-03 16:23:49 -040059 public StatusBarIconView(Context context, String slot, Notification notification) {
Jason Monk3b230072015-05-29 11:11:29 -040060 this(context, slot, notification, false);
61 }
62
63 public StatusBarIconView(Context context, String slot, Notification notification,
64 boolean blocked) {
Joe Onorato0cbda992010-05-02 16:28:15 -070065 super(context);
Jason Monk3b230072015-05-29 11:11:29 -040066 mBlocked = blocked;
Joe Onorato0cbda992010-05-02 16:28:15 -070067 mSlot = slot;
Joe Onorato6c01a112010-10-04 17:38:47 -040068 mNumberPain = new Paint();
69 mNumberPain.setTextAlign(Paint.Align.CENTER);
Alan Viverette4a357cd2015-03-18 18:37:18 -070070 mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
Joe Onorato6c01a112010-10-04 17:38:47 -040071 mNumberPain.setAntiAlias(true);
Christoph Studera0506e72014-07-31 20:27:39 +020072 setNotification(notification);
Selim Cinek3e7592d2016-04-11 09:35:54 +080073 maybeUpdateIconScale();
74 setScaleType(ScaleType.CENTER);
75 mDensity = context.getResources().getDisplayMetrics().densityDpi;
76 }
Daniel Sandler26c84b12011-07-27 00:09:40 -040077
Selim Cinek3e7592d2016-04-11 09:35:54 +080078 private void maybeUpdateIconScale() {
Daniel Sandler7579bca2011-08-18 15:47:26 -040079 // We do not resize and scale system icons (on the right), only notification icons (on the
80 // left).
Selim Cinek3e7592d2016-04-11 09:35:54 +080081 if (mNotification != null || mAlwaysScaleIcon) {
82 updateIconScale();
Daniel Sandler7579bca2011-08-18 15:47:26 -040083 }
Selim Cinek3e7592d2016-04-11 09:35:54 +080084 }
Daniel Sandlerabff0322011-09-27 11:19:34 -040085
Selim Cinek3e7592d2016-04-11 09:35:54 +080086 private void updateIconScale() {
87 Resources res = mContext.getResources();
88 final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
89 final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
Selim Cinek281c2022016-10-13 19:14:43 -070090 mIconScale = (float)imageBounds / (float)outerBounds;
91 setScaleX(mIconScale);
92 setScaleY(mIconScale);
93 }
94
95 public float getIconScale() {
96 return mIconScale;
Selim Cinek3e7592d2016-04-11 09:35:54 +080097 }
98
99 @Override
100 protected void onConfigurationChanged(Configuration newConfig) {
101 super.onConfigurationChanged(newConfig);
102 int density = newConfig.densityDpi;
103 if (density != mDensity) {
104 mDensity = density;
105 maybeUpdateIconScale();
106 updateDrawable();
107 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700108 }
109
Christoph Studera0506e72014-07-31 20:27:39 +0200110 public void setNotification(Notification notification) {
111 mNotification = notification;
112 setContentDescription(notification);
113 }
114
Daniel Sandler05e24142011-11-10 11:56:49 -0500115 public StatusBarIconView(Context context, AttributeSet attrs) {
116 super(context, attrs);
Jason Monk3b230072015-05-29 11:11:29 -0400117 mBlocked = false;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800118 mAlwaysScaleIcon = true;
119 updateIconScale();
120 mDensity = context.getResources().getDisplayMetrics().densityDpi;
Daniel Sandler05e24142011-11-10 11:56:49 -0500121 }
122
Joe Onorato0cbda992010-05-02 16:28:15 -0700123 private static boolean streq(String a, String b) {
Joe Onorato66d7d012010-05-14 10:05:10 -0700124 if (a == b) {
125 return true;
126 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700127 if (a == null && b != null) {
128 return false;
129 }
130 if (a != null && b == null) {
131 return false;
132 }
133 return a.equals(b);
134 }
135
Dan Sandlerd63f9322015-05-06 15:18:49 -0400136 public boolean equalIcons(Icon a, Icon b) {
137 if (a == b) return true;
138 if (a.getType() != b.getType()) return false;
139 switch (a.getType()) {
140 case Icon.TYPE_RESOURCE:
141 return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId();
142 case Icon.TYPE_URI:
143 return a.getUriString().equals(b.getUriString());
144 default:
145 return false;
146 }
147 }
Joe Onorato005847b2010-06-04 16:08:02 -0400148 /**
149 * Returns whether the set succeeded.
150 */
151 public boolean set(StatusBarIcon icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -0400152 final boolean iconEquals = mIcon != null && equalIcons(mIcon.icon, icon.icon);
Joe Onorato005847b2010-06-04 16:08:02 -0400153 final boolean levelEquals = iconEquals
154 && mIcon.iconLevel == icon.iconLevel;
155 final boolean visibilityEquals = mIcon != null
156 && mIcon.visible == icon.visible;
Joe Onorato6c01a112010-10-04 17:38:47 -0400157 final boolean numberEquals = mIcon != null
158 && mIcon.number == icon.number;
159 mIcon = icon.clone();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700160 setContentDescription(icon.contentDescription);
Joe Onorato005847b2010-06-04 16:08:02 -0400161 if (!iconEquals) {
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800162 if (!updateDrawable(false /* no clear */)) return false;
Joe Onorato0cbda992010-05-02 16:28:15 -0700163 }
Joe Onorato005847b2010-06-04 16:08:02 -0400164 if (!levelEquals) {
165 setImageLevel(icon.iconLevel);
Joe Onorato0cbda992010-05-02 16:28:15 -0700166 }
Daniel Sandler26c84b12011-07-27 00:09:40 -0400167
Joe Onorato6c01a112010-10-04 17:38:47 -0400168 if (!numberEquals) {
John Spurlock01534782014-01-13 11:59:22 -0500169 if (icon.number > 0 && getContext().getResources().getBoolean(
Joe Onorato8595a3d2010-11-19 18:12:07 -0800170 R.bool.config_statusBarShowNumber)) {
Joe Onorato6c01a112010-10-04 17:38:47 -0400171 if (mNumberBackground == null) {
172 mNumberBackground = getContext().getResources().getDrawable(
173 R.drawable.ic_notification_overlay);
174 }
175 placeNumber();
176 } else {
177 mNumberBackground = null;
178 mNumberText = null;
179 }
180 invalidate();
181 }
Joe Onorato005847b2010-06-04 16:08:02 -0400182 if (!visibilityEquals) {
Jason Monk3b230072015-05-29 11:11:29 -0400183 setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
Joe Onorato005847b2010-06-04 16:08:02 -0400184 }
Joe Onorato005847b2010-06-04 16:08:02 -0400185 return true;
Joe Onorato0cbda992010-05-02 16:28:15 -0700186 }
187
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800188 public void updateDrawable() {
189 updateDrawable(true /* with clear */);
190 }
191
192 private boolean updateDrawable(boolean withClear) {
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100193 if (mIcon == null) {
194 return false;
195 }
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800196 Drawable drawable = getIcon(mIcon);
197 if (drawable == null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400198 Log.w(TAG, "No icon for slot " + mSlot);
Fabrice Di Meglio82fca5d2013-01-09 15:42:31 -0800199 return false;
200 }
201 if (withClear) {
202 setImageDrawable(null);
203 }
204 setImageDrawable(drawable);
205 return true;
206 }
207
Joe Onoratof5510542010-06-01 07:46:41 -0700208 private Drawable getIcon(StatusBarIcon icon) {
209 return getIcon(getContext(), icon);
210 }
211
Joe Onorato0cbda992010-05-02 16:28:15 -0700212 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -0400213 * Returns the right icon to use for this item
John Spurlock209bede2013-07-17 12:23:27 -0400214 *
Dan Sandlerd63f9322015-05-06 15:18:49 -0400215 * @param context Context to use to get resources
Joe Onorato0cbda992010-05-02 16:28:15 -0700216 * @return Drawable for this item, or null if the package or item could not
217 * be found
218 */
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800219 public static Drawable getIcon(Context context, StatusBarIcon statusBarIcon) {
220 int userId = statusBarIcon.user.getIdentifier();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400221 if (userId == UserHandle.USER_ALL) {
Xiaohui Chen87d0e252015-07-30 15:38:16 -0700222 userId = UserHandle.USER_SYSTEM;
Joe Onorato0cbda992010-05-02 16:28:15 -0700223 }
Anthony Chen55e8e1e2016-01-08 10:31:46 -0800224
225 Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
226
227 TypedValue typedValue = new TypedValue();
228 context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
229 float scaleFactor = typedValue.getFloat();
230
231 // No need to scale the icon, so return it as is.
232 if (scaleFactor == 1.f) {
233 return icon;
234 }
235
236 return new ScalingDrawableWrapper(icon, scaleFactor);
Joe Onorato0cbda992010-05-02 16:28:15 -0700237 }
Joe Onoratob77f53b2010-05-28 19:59:51 -0400238
239 public StatusBarIcon getStatusBarIcon() {
240 return mIcon;
241 }
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700242
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700243 @Override
244 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
245 super.onInitializeAccessibilityEvent(event);
246 if (mNotification != null) {
247 event.setParcelableData(mNotification);
248 }
249 }
250
251 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400252 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
253 super.onSizeChanged(w, h, oldw, oldh);
254 if (mNumberBackground != null) {
255 placeNumber();
256 }
257 }
258
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700259 @Override
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100260 public void onRtlPropertiesChanged(int layoutDirection) {
261 super.onRtlPropertiesChanged(layoutDirection);
262 updateDrawable();
263 }
264
265 @Override
Joe Onorato6c01a112010-10-04 17:38:47 -0400266 protected void onDraw(Canvas canvas) {
267 super.onDraw(canvas);
268
269 if (mNumberBackground != null) {
270 mNumberBackground.draw(canvas);
271 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain);
272 }
273 }
274
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700275 @Override
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700276 protected void debug(int depth) {
277 super.debug(depth);
278 Log.d("View", debugIndent(depth) + "slot=" + mSlot);
279 Log.d("View", debugIndent(depth) + "icon=" + mIcon);
280 }
Joe Onorato6c01a112010-10-04 17:38:47 -0400281
282 void placeNumber() {
Daniel Sandlerebce0112011-06-16 16:44:51 -0400283 final String str;
John Spurlock01534782014-01-13 11:59:22 -0500284 final int tooBig = getContext().getResources().getInteger(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400285 android.R.integer.status_bar_notification_info_maxnum);
286 if (mIcon.number > tooBig) {
John Spurlock01534782014-01-13 11:59:22 -0500287 str = getContext().getResources().getString(
Daniel Sandlerebce0112011-06-16 16:44:51 -0400288 android.R.string.status_bar_notification_info_overflow);
289 } else {
290 NumberFormat f = NumberFormat.getIntegerInstance();
291 str = f.format(mIcon.number);
292 }
293 mNumberText = str;
294
Joe Onorato6c01a112010-10-04 17:38:47 -0400295 final int w = getWidth();
296 final int h = getHeight();
297 final Rect r = new Rect();
298 mNumberPain.getTextBounds(str, 0, str.length(), r);
299 final int tw = r.right - r.left;
300 final int th = r.bottom - r.top;
301 mNumberBackground.getPadding(r);
302 int dw = r.left + tw + r.right;
303 if (dw < mNumberBackground.getMinimumWidth()) {
304 dw = mNumberBackground.getMinimumWidth();
305 }
306 mNumberX = w-r.right-((dw-r.right-r.left)/2);
307 int dh = r.top + th + r.bottom;
308 if (dh < mNumberBackground.getMinimumWidth()) {
309 dh = mNumberBackground.getMinimumWidth();
310 }
311 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2);
312 mNumberBackground.setBounds(w-dw, h-dh, w, h);
313 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700314
315 private void setContentDescription(Notification notification) {
316 if (notification != null) {
Adrian Rooseba05822016-04-22 17:09:27 -0700317 String d = contentDescForNotification(mContext, notification);
318 if (!TextUtils.isEmpty(d)) {
319 setContentDescription(d);
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700320 }
321 }
322 }
Daniel Sandler7579bca2011-08-18 15:47:26 -0400323
324 public String toString() {
John Spurlock209bede2013-07-17 12:23:27 -0400325 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
Daniel Sandler7579bca2011-08-18 15:47:26 -0400326 + " notification=" + mNotification + ")";
327 }
Jason Monk3b230072015-05-29 11:11:29 -0400328
329 public String getSlot() {
330 return mSlot;
331 }
Adrian Rooseba05822016-04-22 17:09:27 -0700332
333
334 public static String contentDescForNotification(Context c, Notification n) {
Adrian Roosa8e18ef2016-05-26 17:17:02 -0700335 String appName = "";
336 try {
337 Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
338 appName = builder.loadHeaderAppName();
339 } catch (RuntimeException e) {
340 Log.e(TAG, "Unable to recover builder", e);
341 // Trying to get the app name from the app info instead.
342 Parcelable appInfo = n.extras.getParcelable(
343 Notification.EXTRA_BUILDER_APPLICATION_INFO);
344 if (appInfo instanceof ApplicationInfo) {
345 appName = String.valueOf(((ApplicationInfo) appInfo).loadLabel(
346 c.getPackageManager()));
347 }
348 }
Adrian Roos51548e62016-04-28 11:20:26 -0700349
Julia Reynolds7f9ce782016-05-17 15:34:34 -0400350 CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
Adrian Rooseba05822016-04-22 17:09:27 -0700351 CharSequence ticker = n.tickerText;
Adrian Roos51548e62016-04-28 11:20:26 -0700352
353 CharSequence desc = !TextUtils.isEmpty(ticker) ? ticker
354 : !TextUtils.isEmpty(title) ? title : "";
355
356 return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
Adrian Rooseba05822016-04-22 17:09:27 -0700357 }
Adrian Roos51548e62016-04-28 11:20:26 -0700358
Joe Onorato0cbda992010-05-02 16:28:15 -0700359}