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.drawable.Drawable; 24 import android.graphics.Canvas; 25 import android.graphics.Paint; 26 import android.graphics.Rect; 27 import android.text.TextUtils; 28 import android.util.Slog; 29 import android.util.Log; 30 import android.view.ViewDebug; 31 import android.view.accessibility.AccessibilityEvent; 32 import android.widget.ImageView; 33 34 import java.text.NumberFormat; 35 36 import com.android.internal.statusbar.StatusBarIcon; 37 38 import com.android.systemui.R; 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 StatusBarIconView(Context context, String slot, Notification notification)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 final float alpha = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1); 72 setAlpha(alpha); 73 } 74 75 setScaleType(ImageView.ScaleType.CENTER); 76 } 77 streq(String a, String b)78 private static boolean streq(String a, String b) { 79 if (a == b) { 80 return true; 81 } 82 if (a == null && b != null) { 83 return false; 84 } 85 if (a != null && b == null) { 86 return false; 87 } 88 return a.equals(b); 89 } 90 91 /** 92 * Returns whether the set succeeded. 93 */ set(StatusBarIcon icon)94 public boolean set(StatusBarIcon icon) { 95 final boolean iconEquals = mIcon != null 96 && streq(mIcon.iconPackage, icon.iconPackage) 97 && mIcon.iconId == icon.iconId; 98 final boolean levelEquals = iconEquals 99 && mIcon.iconLevel == icon.iconLevel; 100 final boolean visibilityEquals = mIcon != null 101 && mIcon.visible == icon.visible; 102 final boolean numberEquals = mIcon != null 103 && mIcon.number == icon.number; 104 mIcon = icon.clone(); 105 setContentDescription(icon.contentDescription); 106 if (!iconEquals) { 107 Drawable drawable = getIcon(icon); 108 if (drawable == null) { 109 Slog.w(StatusBar.TAG, "No icon for slot " + mSlot); 110 return false; 111 } 112 setImageDrawable(drawable); 113 } 114 if (!levelEquals) { 115 setImageLevel(icon.iconLevel); 116 } 117 118 if (!numberEquals) { 119 if (icon.number > 0 && mContext.getResources().getBoolean( 120 R.bool.config_statusBarShowNumber)) { 121 if (mNumberBackground == null) { 122 mNumberBackground = getContext().getResources().getDrawable( 123 R.drawable.ic_notification_overlay); 124 } 125 placeNumber(); 126 } else { 127 mNumberBackground = null; 128 mNumberText = null; 129 } 130 invalidate(); 131 } 132 if (!visibilityEquals) { 133 setVisibility(icon.visible ? VISIBLE : GONE); 134 } 135 return true; 136 } 137 getIcon(StatusBarIcon icon)138 private Drawable getIcon(StatusBarIcon icon) { 139 return getIcon(getContext(), icon); 140 } 141 142 /** 143 * Returns the right icon to use for this item, respecting the iconId and 144 * iconPackage (if set) 145 * 146 * @param context Context to use to get resources if iconPackage is not set 147 * @return Drawable for this item, or null if the package or item could not 148 * be found 149 */ getIcon(Context context, StatusBarIcon icon)150 public static Drawable getIcon(Context context, StatusBarIcon icon) { 151 Resources r = null; 152 153 if (icon.iconPackage != null) { 154 try { 155 r = context.getPackageManager().getResourcesForApplication(icon.iconPackage); 156 } catch (PackageManager.NameNotFoundException ex) { 157 Slog.e(StatusBar.TAG, "Icon package not found: " + icon.iconPackage); 158 return null; 159 } 160 } else { 161 r = context.getResources(); 162 } 163 164 if (icon.iconId == 0) { 165 return null; 166 } 167 168 try { 169 return r.getDrawable(icon.iconId); 170 } catch (RuntimeException e) { 171 Slog.w(StatusBar.TAG, "Icon not found in " 172 + (icon.iconPackage != null ? icon.iconId : "<system>") 173 + ": " + Integer.toHexString(icon.iconId)); 174 } 175 176 return null; 177 } 178 getStatusBarIcon()179 public StatusBarIcon getStatusBarIcon() { 180 return mIcon; 181 } 182 183 @Override onInitializeAccessibilityEvent(AccessibilityEvent event)184 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 185 super.onInitializeAccessibilityEvent(event); 186 if (mNotification != null) { 187 event.setParcelableData(mNotification); 188 } 189 } 190 191 @Override onSizeChanged(int w, int h, int oldw, int oldh)192 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 193 super.onSizeChanged(w, h, oldw, oldh); 194 if (mNumberBackground != null) { 195 placeNumber(); 196 } 197 } 198 199 @Override onDraw(Canvas canvas)200 protected void onDraw(Canvas canvas) { 201 super.onDraw(canvas); 202 203 if (mNumberBackground != null) { 204 mNumberBackground.draw(canvas); 205 canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain); 206 } 207 } 208 209 @Override debug(int depth)210 protected void debug(int depth) { 211 super.debug(depth); 212 Log.d("View", debugIndent(depth) + "slot=" + mSlot); 213 Log.d("View", debugIndent(depth) + "icon=" + mIcon); 214 } 215 placeNumber()216 void placeNumber() { 217 final String str; 218 final int tooBig = mContext.getResources().getInteger( 219 android.R.integer.status_bar_notification_info_maxnum); 220 if (mIcon.number > tooBig) { 221 str = mContext.getResources().getString( 222 android.R.string.status_bar_notification_info_overflow); 223 } else { 224 NumberFormat f = NumberFormat.getIntegerInstance(); 225 str = f.format(mIcon.number); 226 } 227 mNumberText = str; 228 229 final int w = getWidth(); 230 final int h = getHeight(); 231 final Rect r = new Rect(); 232 mNumberPain.getTextBounds(str, 0, str.length(), r); 233 final int tw = r.right - r.left; 234 final int th = r.bottom - r.top; 235 mNumberBackground.getPadding(r); 236 int dw = r.left + tw + r.right; 237 if (dw < mNumberBackground.getMinimumWidth()) { 238 dw = mNumberBackground.getMinimumWidth(); 239 } 240 mNumberX = w-r.right-((dw-r.right-r.left)/2); 241 int dh = r.top + th + r.bottom; 242 if (dh < mNumberBackground.getMinimumWidth()) { 243 dh = mNumberBackground.getMinimumWidth(); 244 } 245 mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2); 246 mNumberBackground.setBounds(w-dw, h-dh, w, h); 247 } 248 setContentDescription(Notification notification)249 private void setContentDescription(Notification notification) { 250 if (notification != null) { 251 CharSequence tickerText = notification.tickerText; 252 if (!TextUtils.isEmpty(tickerText)) { 253 setContentDescription(tickerText); 254 } 255 } 256 } 257 toString()258 public String toString() { 259 return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon 260 + " notification=" + mNotification + ")"; 261 } 262 } 263