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