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         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