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