1 /* 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 17 package com.android.systemui.statusbar; 18 19 import android.app.Notification; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.Rect; 25 import android.graphics.drawable.Drawable; 26 import android.graphics.drawable.Icon; 27 import android.os.UserHandle; 28 import android.text.TextUtils; 29 import android.util.AttributeSet; 30 import android.util.Log; 31 import android.view.ViewDebug; 32 import android.view.accessibility.AccessibilityEvent; 33 import android.widget.ImageView; 34 35 import com.android.internal.statusbar.StatusBarIcon; 36 import com.android.systemui.R; 37 38 import java.text.NumberFormat; 39 40 public class StatusBarIconView extends AnimatedImageView { 41 private static final String TAG = "StatusBarIconView"; 42 43 private StatusBarIcon mIcon; 44 @ViewDebug.ExportedProperty private String mSlot; 45 private Drawable mNumberBackground; 46 private Paint mNumberPain; 47 private int mNumberX; 48 private int mNumberY; 49 private String mNumberText; 50 private Notification mNotification; 51 private final boolean mBlocked; 52 53 public StatusBarIconView(Context context, String slot, Notification notification) { 54 this(context, slot, notification, false); 55 } 56 57 public StatusBarIconView(Context context, String slot, Notification notification, 58 boolean blocked) { 59 super(context); 60 final Resources res = context.getResources(); 61 mBlocked = blocked; 62 mSlot = slot; 63 mNumberPain = new Paint(); 64 mNumberPain.setTextAlign(Paint.Align.CENTER); 65 mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color)); 66 mNumberPain.setAntiAlias(true); 67 setNotification(notification); 68 69 // We do not resize and scale system icons (on the right), only notification icons (on the 70 // left). 71 if (notification != null) { 72 final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); 73 final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size); 74 final float scale = (float)imageBounds / (float)outerBounds; 75 setScaleX(scale); 76 setScaleY(scale); 77 } 78 79 setScaleType(ImageView.ScaleType.CENTER); 80 } 81 82 public void setNotification(Notification notification) { 83 mNotification = notification; 84 setContentDescription(notification); 85 } 86 87 public StatusBarIconView(Context context, AttributeSet attrs) { 88 super(context, attrs); 89 mBlocked = false; 90 final Resources res = context.getResources(); 91 final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); 92 final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size); 93 final float scale = (float)imageBounds / (float)outerBounds; 94 setScaleX(scale); 95 setScaleY(scale); 96 } 97 98 private static boolean streq(String a, String b) { 99 if (a == b) { 100 return true; 101 } 102 if (a == null && b != null) { 103 return false; 104 } 105 if (a != null && b == null) { 106 return false; 107 } 108 return a.equals(b); 109 } 110 111 public boolean equalIcons(Icon a, Icon b) { 112 if (a == b) return true; 113 if (a.getType() != b.getType()) return false; 114 switch (a.getType()) { 115 case Icon.TYPE_RESOURCE: 116 return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId(); 117 case Icon.TYPE_URI: 118 return a.getUriString().equals(b.getUriString()); 119 default: 120 return false; 121 } 122 } 123 /** 124 * Returns whether the set succeeded. 125 */ 126 public boolean set(StatusBarIcon icon) { 127 final boolean iconEquals = mIcon != null && equalIcons(mIcon.icon, icon.icon); 128 final boolean levelEquals = iconEquals 129 && mIcon.iconLevel == icon.iconLevel; 130 final boolean visibilityEquals = mIcon != null 131 && mIcon.visible == icon.visible; 132 final boolean numberEquals = mIcon != null 133 && mIcon.number == icon.number; 134 mIcon = icon.clone(); 135 setContentDescription(icon.contentDescription); 136 if (!iconEquals) { 137 if (!updateDrawable(false /* no clear */)) return false; 138 } 139 if (!levelEquals) { 140 setImageLevel(icon.iconLevel); 141 } 142 143 if (!numberEquals) { 144 if (icon.number > 0 && getContext().getResources().getBoolean( 145 R.bool.config_statusBarShowNumber)) { 146 if (mNumberBackground == null) { 147 mNumberBackground = getContext().getResources().getDrawable( 148 R.drawable.ic_notification_overlay); 149 } 150 placeNumber(); 151 } else { 152 mNumberBackground = null; 153 mNumberText = null; 154 } 155 invalidate(); 156 } 157 if (!visibilityEquals) { 158 setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE); 159 } 160 return true; 161 } 162 163 public void updateDrawable() { 164 updateDrawable(true /* with clear */); 165 } 166 167 private boolean updateDrawable(boolean withClear) { 168 if (mIcon == null) { 169 return false; 170 } 171 Drawable drawable = getIcon(mIcon); 172 if (drawable == null) { 173 Log.w(TAG, "No icon for slot " + mSlot); 174 return false; 175 } 176 if (withClear) { 177 setImageDrawable(null); 178 } 179 setImageDrawable(drawable); 180 return true; 181 } 182 183 private Drawable getIcon(StatusBarIcon icon) { 184 return getIcon(getContext(), icon); 185 } 186 187 /** 188 * Returns the right icon to use for this item 189 * 190 * @param context Context to use to get resources 191 * @return Drawable for this item, or null if the package or item could not 192 * be found 193 */ 194 public static Drawable getIcon(Context context, StatusBarIcon icon) { 195 int userId = icon.user.getIdentifier(); 196 if (userId == UserHandle.USER_ALL) { 197 userId = UserHandle.USER_OWNER; 198 } 199 return icon.icon.loadDrawableAsUser(context, userId); 200 } 201 202 public StatusBarIcon getStatusBarIcon() { 203 return mIcon; 204 } 205 206 @Override 207 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 208 super.onInitializeAccessibilityEvent(event); 209 if (mNotification != null) { 210 event.setParcelableData(mNotification); 211 } 212 } 213 214 @Override 215 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 216 super.onSizeChanged(w, h, oldw, oldh); 217 if (mNumberBackground != null) { 218 placeNumber(); 219 } 220 } 221 222 @Override 223 public void onRtlPropertiesChanged(int layoutDirection) { 224 super.onRtlPropertiesChanged(layoutDirection); 225 updateDrawable(); 226 } 227 228 @Override 229 protected void onDraw(Canvas canvas) { 230 super.onDraw(canvas); 231 232 if (mNumberBackground != null) { 233 mNumberBackground.draw(canvas); 234 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain); 235 } 236 } 237 238 @Override 239 protected void debug(int depth) { 240 super.debug(depth); 241 Log.d("View", debugIndent(depth) + "slot=" + mSlot); 242 Log.d("View", debugIndent(depth) + "icon=" + mIcon); 243 } 244 245 void placeNumber() { 246 final String str; 247 final int tooBig = getContext().getResources().getInteger( 248 android.R.integer.status_bar_notification_info_maxnum); 249 if (mIcon.number > tooBig) { 250 str = getContext().getResources().getString( 251 android.R.string.status_bar_notification_info_overflow); 252 } else { 253 NumberFormat f = NumberFormat.getIntegerInstance(); 254 str = f.format(mIcon.number); 255 } 256 mNumberText = str; 257 258 final int w = getWidth(); 259 final int h = getHeight(); 260 final Rect r = new Rect(); 261 mNumberPain.getTextBounds(str, 0, str.length(), r); 262 final int tw = r.right - r.left; 263 final int th = r.bottom - r.top; 264 mNumberBackground.getPadding(r); 265 int dw = r.left + tw + r.right; 266 if (dw < mNumberBackground.getMinimumWidth()) { 267 dw = mNumberBackground.getMinimumWidth(); 268 } 269 mNumberX = w-r.right-((dw-r.right-r.left)/2); 270 int dh = r.top + th + r.bottom; 271 if (dh < mNumberBackground.getMinimumWidth()) { 272 dh = mNumberBackground.getMinimumWidth(); 273 } 274 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2); 275 mNumberBackground.setBounds(w-dw, h-dh, w, h); 276 } 277 278 private void setContentDescription(Notification notification) { 279 if (notification != null) { 280 CharSequence tickerText = notification.tickerText; 281 if (!TextUtils.isEmpty(tickerText)) { 282 setContentDescription(tickerText); 283 } 284 } 285 } 286 287 public String toString() { 288 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon 289 + " notification=" + mNotification + ")"; 290 } 291 292 public String getSlot() { 293 return mSlot; 294 } 295 } 296