Home | History | Annotate | Download | only in menu
      1 /*
      2  * Copyright (C) 2006 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.internal.view.menu;
     18 
     19 import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
     20 
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Rect;
     24 import android.graphics.drawable.Drawable;
     25 import android.text.TextUtils;
     26 import android.util.AttributeSet;
     27 import android.view.Gravity;
     28 import android.view.SoundEffectConstants;
     29 import android.view.View;
     30 import android.view.ViewDebug;
     31 import android.widget.TextView;
     32 import android.text.Layout;
     33 
     34 /**
     35  * The item view for each item in the {@link IconMenuView}.
     36  */
     37 public final class IconMenuItemView extends TextView implements MenuView.ItemView {
     38 
     39     private static final int NO_ALPHA = 0xFF;
     40 
     41     private IconMenuView mIconMenuView;
     42 
     43     private ItemInvoker mItemInvoker;
     44     private MenuItemImpl mItemData;
     45 
     46     private Drawable mIcon;
     47 
     48     private int mTextAppearance;
     49     private Context mTextAppearanceContext;
     50 
     51     private float mDisabledAlpha;
     52 
     53     private Rect mPositionIconAvailable = new Rect();
     54     private Rect mPositionIconOutput = new Rect();
     55 
     56     private boolean mShortcutCaptionMode;
     57     private String mShortcutCaption;
     58 
     59     private static String sPrependShortcutLabel;
     60 
     61     public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     62         super(context, attrs, defStyleAttr, defStyleRes);
     63 
     64         if (sPrependShortcutLabel == null) {
     65             /*
     66              * Views should only be constructed from the UI thread, so no
     67              * synchronization needed
     68              */
     69             sPrependShortcutLabel = getResources().getString(
     70                     com.android.internal.R.string.prepend_shortcut_label);
     71         }
     72 
     73         final TypedArray a = context.obtainStyledAttributes(
     74                 attrs, com.android.internal.R.styleable.MenuView, defStyleAttr, defStyleRes);
     75 
     76         mDisabledAlpha = a.getFloat(
     77                 com.android.internal.R.styleable.MenuView_itemIconDisabledAlpha, 0.8f);
     78         mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
     79                                           MenuView_itemTextAppearance, -1);
     80         mTextAppearanceContext = context;
     81 
     82         a.recycle();
     83     }
     84 
     85     public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
     86         this(context, attrs, defStyleAttr, 0);
     87     }
     88 
     89     public IconMenuItemView(Context context, AttributeSet attrs) {
     90         this(context, attrs, 0);
     91     }
     92 
     93     /**
     94      * Initializes with the provided title and icon
     95      * @param title The title of this item
     96      * @param icon The icon of this item
     97      */
     98     void initialize(CharSequence title, Drawable icon) {
     99         setClickable(true);
    100         setFocusable(true);
    101 
    102         if (mTextAppearance != -1) {
    103             setTextAppearance(mTextAppearanceContext, mTextAppearance);
    104         }
    105 
    106         setTitle(title);
    107         setIcon(icon);
    108 
    109         if (mItemData != null) {
    110             final CharSequence contentDescription = mItemData.getContentDescription();
    111             if (TextUtils.isEmpty(contentDescription)) {
    112                 setContentDescription(title);
    113             } else {
    114                 setContentDescription(contentDescription);
    115             }
    116             setTooltipText(mItemData.getTooltipText());
    117         }
    118     }
    119 
    120     public void initialize(MenuItemImpl itemData, int menuType) {
    121         mItemData = itemData;
    122 
    123         initialize(itemData.getTitleForItemView(this), itemData.getIcon());
    124 
    125         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
    126         setEnabled(itemData.isEnabled());
    127     }
    128 
    129     public void setItemData(MenuItemImpl data) {
    130         mItemData = data;
    131     }
    132 
    133     @Override
    134     public boolean performClick() {
    135         // Let the view's click listener have top priority (the More button relies on this)
    136         if (super.performClick()) {
    137             return true;
    138         }
    139 
    140         if ((mItemInvoker != null) && (mItemInvoker.invokeItem(mItemData))) {
    141             playSoundEffect(SoundEffectConstants.CLICK);
    142             return true;
    143         } else {
    144             return false;
    145         }
    146     }
    147 
    148     public void setTitle(CharSequence title) {
    149 
    150         if (mShortcutCaptionMode) {
    151             /*
    152              * Don't set the title directly since it will replace the
    153              * shortcut+title being shown. Instead, re-set the shortcut caption
    154              * mode so the new title is shown.
    155              */
    156             setCaptionMode(true);
    157 
    158         } else if (title != null) {
    159             setText(title);
    160         }
    161     }
    162 
    163     void setCaptionMode(boolean shortcut) {
    164         /*
    165          * If there is no item model, don't do any of the below (for example,
    166          * the 'More' item doesn't have a model)
    167          */
    168         if (mItemData == null) {
    169             return;
    170         }
    171 
    172         mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut());
    173 
    174         CharSequence text = mItemData.getTitleForItemView(this);
    175 
    176         if (mShortcutCaptionMode) {
    177 
    178             if (mShortcutCaption == null) {
    179                 mShortcutCaption = mItemData.getShortcutLabel();
    180             }
    181 
    182             text = mShortcutCaption;
    183         }
    184 
    185         setText(text);
    186     }
    187 
    188     public void setIcon(Drawable icon) {
    189         mIcon = icon;
    190 
    191         if (icon != null) {
    192 
    193             /* Set the bounds of the icon since setCompoundDrawables needs it. */
    194             icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
    195 
    196             // Set the compound drawables
    197             setCompoundDrawables(null, icon, null, null);
    198 
    199             // When there is an icon, make sure the text is at the bottom
    200             setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
    201 
    202             /*
    203              * Request a layout to reposition the icon. The positioning of icon
    204              * depends on this TextView's line bounds, which is only available
    205              * after a layout.
    206              */
    207             requestLayout();
    208         } else {
    209             setCompoundDrawables(null, null, null, null);
    210 
    211             // When there is no icon, make sure the text is centered vertically
    212             setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
    213         }
    214     }
    215 
    216     public void setItemInvoker(ItemInvoker itemInvoker) {
    217         mItemInvoker = itemInvoker;
    218     }
    219 
    220     @ViewDebug.CapturedViewProperty(retrieveReturn = true)
    221     public MenuItemImpl getItemData() {
    222         return mItemData;
    223     }
    224 
    225     @Override
    226     public void setVisibility(int v) {
    227         super.setVisibility(v);
    228 
    229         if (mIconMenuView != null) {
    230             // On visibility change, mark the IconMenuView to refresh itself eventually
    231             mIconMenuView.markStaleChildren();
    232         }
    233     }
    234 
    235     void setIconMenuView(IconMenuView iconMenuView) {
    236         mIconMenuView = iconMenuView;
    237     }
    238 
    239     @Override
    240     protected void drawableStateChanged() {
    241         super.drawableStateChanged();
    242 
    243         if (mItemData != null && mIcon != null) {
    244             // When disabled, the not-focused state and the pressed state should
    245             // drop alpha on the icon
    246             final boolean isInAlphaState = !mItemData.isEnabled() && (isPressed() || !isFocused());
    247             mIcon.setAlpha(isInAlphaState ? (int) (mDisabledAlpha * NO_ALPHA) : NO_ALPHA);
    248         }
    249     }
    250 
    251     @Override
    252     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    253         super.onLayout(changed, left, top, right, bottom);
    254 
    255         positionIcon();
    256     }
    257 
    258     @Override
    259     protected void onTextChanged(CharSequence text, int start, int before, int after) {
    260         super.onTextChanged(text, start, before, after);
    261 
    262         // our layout params depend on the length of the text
    263         setLayoutParams(getTextAppropriateLayoutParams());
    264     }
    265 
    266     /**
    267      * @return layout params appropriate for this view.  If layout params already exist, it will
    268      *         augment them to be appropriate to the current text size.
    269      */
    270     IconMenuView.LayoutParams getTextAppropriateLayoutParams() {
    271         IconMenuView.LayoutParams lp = (IconMenuView.LayoutParams) getLayoutParams();
    272         if (lp == null) {
    273             // Default layout parameters
    274             lp = new IconMenuView.LayoutParams(
    275                     IconMenuView.LayoutParams.MATCH_PARENT, IconMenuView.LayoutParams.MATCH_PARENT);
    276         }
    277 
    278         // Set the desired width of item
    279         lp.desiredWidth = (int) Layout.getDesiredWidth(getText(), 0, getText().length(),
    280                 getPaint(), getTextDirectionHeuristic());
    281 
    282         return lp;
    283     }
    284 
    285     /**
    286      * Positions the icon vertically (horizontal centering is taken care of by
    287      * the TextView's gravity).
    288      */
    289     private void positionIcon() {
    290 
    291         if (mIcon == null) {
    292             return;
    293         }
    294 
    295         // We reuse the output rectangle as a temp rect
    296         Rect tmpRect = mPositionIconOutput;
    297         getLineBounds(0, tmpRect);
    298         mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top);
    299         final int layoutDirection = getLayoutDirection();
    300         Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.START, mIcon.getIntrinsicWidth(), mIcon
    301                 .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput,
    302                 layoutDirection);
    303         mIcon.setBounds(mPositionIconOutput);
    304     }
    305 
    306     public void setCheckable(boolean checkable) {
    307     }
    308 
    309     public void setChecked(boolean checked) {
    310     }
    311 
    312     public void setShortcut(boolean showShortcut, char shortcutKey) {
    313 
    314         if (mShortcutCaptionMode) {
    315             /*
    316              * Shortcut has changed and we're showing it right now, need to
    317              * update (clear the old one first).
    318              */
    319             mShortcutCaption = null;
    320             setCaptionMode(true);
    321         }
    322     }
    323 
    324     public boolean prefersCondensedTitle() {
    325         return true;
    326     }
    327 
    328     public boolean showsIcon() {
    329         return true;
    330     }
    331 
    332 }
    333