Home | History | Annotate | Download | only in statusbar
      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         setNotification(notification);
     61 
     62         // We do not resize and scale system icons (on the right), only notification icons (on the
     63         // left).
     64         if (notification != null) {
     65             final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
     66             final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
     67             final float scale = (float)imageBounds / (float)outerBounds;
     68             setScaleX(scale);
     69             setScaleY(scale);
     70         }
     71 
     72         setScaleType(ImageView.ScaleType.CENTER);
     73     }
     74 
     75     public void setNotification(Notification notification) {
     76         mNotification = notification;
     77         setContentDescription(notification);
     78     }
     79 
     80     public StatusBarIconView(Context context, AttributeSet attrs) {
     81         super(context, attrs);
     82         final Resources res = context.getResources();
     83         final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
     84         final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
     85         final float scale = (float)imageBounds / (float)outerBounds;
     86         setScaleX(scale);
     87         setScaleY(scale);
     88     }
     89 
     90     private static boolean streq(String a, String b) {
     91         if (a == b) {
     92             return true;
     93         }
     94         if (a == null && b != null) {
     95             return false;
     96         }
     97         if (a != null && b == null) {
     98             return false;
     99         }
    100         return a.equals(b);
    101     }
    102 
    103     /**
    104      * Returns whether the set succeeded.
    105      */
    106     public boolean set(StatusBarIcon icon) {
    107         final boolean iconEquals = mIcon != null
    108                 && streq(mIcon.iconPackage, icon.iconPackage)
    109                 && mIcon.iconId == icon.iconId;
    110         final boolean levelEquals = iconEquals
    111                 && mIcon.iconLevel == icon.iconLevel;
    112         final boolean visibilityEquals = mIcon != null
    113                 && mIcon.visible == icon.visible;
    114         final boolean numberEquals = mIcon != null
    115                 && mIcon.number == icon.number;
    116         mIcon = icon.clone();
    117         setContentDescription(icon.contentDescription);
    118         if (!iconEquals) {
    119             if (!updateDrawable(false /* no clear */)) return false;
    120         }
    121         if (!levelEquals) {
    122             setImageLevel(icon.iconLevel);
    123         }
    124 
    125         if (!numberEquals) {
    126             if (icon.number > 0 && getContext().getResources().getBoolean(
    127                         R.bool.config_statusBarShowNumber)) {
    128                 if (mNumberBackground == null) {
    129                     mNumberBackground = getContext().getResources().getDrawable(
    130                             R.drawable.ic_notification_overlay);
    131                 }
    132                 placeNumber();
    133             } else {
    134                 mNumberBackground = null;
    135                 mNumberText = null;
    136             }
    137             invalidate();
    138         }
    139         if (!visibilityEquals) {
    140             setVisibility(icon.visible ? VISIBLE : GONE);
    141         }
    142         return true;
    143     }
    144 
    145     public void updateDrawable() {
    146         updateDrawable(true /* with clear */);
    147     }
    148 
    149     private boolean updateDrawable(boolean withClear) {
    150         Drawable drawable = getIcon(mIcon);
    151         if (drawable == null) {
    152             Log.w(TAG, "No icon for slot " + mSlot);
    153             return false;
    154         }
    155         if (withClear) {
    156             setImageDrawable(null);
    157         }
    158         setImageDrawable(drawable);
    159         return true;
    160     }
    161 
    162     private Drawable getIcon(StatusBarIcon icon) {
    163         return getIcon(getContext(), icon);
    164     }
    165 
    166     /**
    167      * Returns the right icon to use for this item, respecting the iconId and
    168      * iconPackage (if set)
    169      *
    170      * @param context Context to use to get resources if iconPackage is not set
    171      * @return Drawable for this item, or null if the package or item could not
    172      *         be found
    173      */
    174     public static Drawable getIcon(Context context, StatusBarIcon icon) {
    175         Resources r = null;
    176 
    177         if (icon.iconPackage != null) {
    178             try {
    179                 int userId = icon.user.getIdentifier();
    180                 if (userId == UserHandle.USER_ALL) {
    181                     userId = UserHandle.USER_OWNER;
    182                 }
    183                 r = context.getPackageManager()
    184                         .getResourcesForApplicationAsUser(icon.iconPackage, userId);
    185             } catch (PackageManager.NameNotFoundException ex) {
    186                 Log.e(TAG, "Icon package not found: " + icon.iconPackage);
    187                 return null;
    188             }
    189         } else {
    190             r = context.getResources();
    191         }
    192 
    193         if (icon.iconId == 0) {
    194             return null;
    195         }
    196 
    197         try {
    198             return r.getDrawable(icon.iconId);
    199         } catch (RuntimeException e) {
    200             Log.w(TAG, "Icon not found in "
    201                   + (icon.iconPackage != null ? icon.iconId : "<system>")
    202                   + ": " + Integer.toHexString(icon.iconId));
    203         }
    204 
    205         return null;
    206     }
    207 
    208     public StatusBarIcon getStatusBarIcon() {
    209         return mIcon;
    210     }
    211 
    212     @Override
    213     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    214         super.onInitializeAccessibilityEvent(event);
    215         if (mNotification != null) {
    216             event.setParcelableData(mNotification);
    217         }
    218     }
    219 
    220     @Override
    221     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    222         super.onSizeChanged(w, h, oldw, oldh);
    223         if (mNumberBackground != null) {
    224             placeNumber();
    225         }
    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