Home | History | Annotate | Download | only in menu
      1 /*
      2  * Copyright (C) 2010 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 android.content.Context;
     20 import android.content.res.Resources;
     21 import android.content.res.TypedArray;
     22 import android.graphics.Rect;
     23 import android.graphics.drawable.Drawable;
     24 import android.text.TextUtils;
     25 import android.util.AttributeSet;
     26 import android.view.Gravity;
     27 import android.view.MotionEvent;
     28 import android.view.View;
     29 import android.view.accessibility.AccessibilityEvent;
     30 import android.widget.Button;
     31 import android.widget.ImageButton;
     32 import android.widget.LinearLayout;
     33 import android.widget.Toast;
     34 
     35 /**
     36  * @hide
     37  */
     38 public class ActionMenuItemView extends LinearLayout
     39         implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
     40         ActionMenuView.ActionMenuChildView {
     41     private static final String TAG = "ActionMenuItemView";
     42 
     43     private MenuItemImpl mItemData;
     44     private CharSequence mTitle;
     45     private MenuBuilder.ItemInvoker mItemInvoker;
     46 
     47     private ImageButton mImageButton;
     48     private Button mTextButton;
     49     private boolean mAllowTextWithIcon;
     50     private boolean mExpandedFormat;
     51     private int mMinWidth;
     52 
     53     public ActionMenuItemView(Context context) {
     54         this(context, null);
     55     }
     56 
     57     public ActionMenuItemView(Context context, AttributeSet attrs) {
     58         this(context, attrs, 0);
     59     }
     60 
     61     public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
     62         super(context, attrs, defStyle);
     63         final Resources res = context.getResources();
     64         mAllowTextWithIcon = res.getBoolean(
     65                 com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
     66         TypedArray a = context.obtainStyledAttributes(attrs,
     67                 com.android.internal.R.styleable.ActionMenuItemView, 0, 0);
     68         mMinWidth = a.getDimensionPixelSize(
     69                 com.android.internal.R.styleable.ActionMenuItemView_minWidth, 0);
     70         a.recycle();
     71     }
     72 
     73     @Override
     74     public void onFinishInflate() {
     75         mImageButton = (ImageButton) findViewById(com.android.internal.R.id.imageButton);
     76         mTextButton = (Button) findViewById(com.android.internal.R.id.textButton);
     77         mImageButton.setOnClickListener(this);
     78         mTextButton.setOnClickListener(this);
     79         mImageButton.setOnLongClickListener(this);
     80         setOnClickListener(this);
     81         setOnLongClickListener(this);
     82     }
     83 
     84     public MenuItemImpl getItemData() {
     85         return mItemData;
     86     }
     87 
     88     public void initialize(MenuItemImpl itemData, int menuType) {
     89         mItemData = itemData;
     90 
     91         setIcon(itemData.getIcon());
     92         setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
     93         setId(itemData.getItemId());
     94 
     95         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
     96         setEnabled(itemData.isEnabled());
     97     }
     98 
     99     @Override
    100     public void setEnabled(boolean enabled) {
    101         super.setEnabled(enabled);
    102         mImageButton.setEnabled(enabled);
    103         mTextButton.setEnabled(enabled);
    104     }
    105 
    106     public void onClick(View v) {
    107         if (mItemInvoker != null) {
    108             mItemInvoker.invokeItem(mItemData);
    109         }
    110     }
    111 
    112     public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
    113         mItemInvoker = invoker;
    114     }
    115 
    116     public boolean prefersCondensedTitle() {
    117         return true;
    118     }
    119 
    120     public void setCheckable(boolean checkable) {
    121         // TODO Support checkable action items
    122     }
    123 
    124     public void setChecked(boolean checked) {
    125         // TODO Support checkable action items
    126     }
    127 
    128     public void setExpandedFormat(boolean expandedFormat) {
    129         if (mExpandedFormat != expandedFormat) {
    130             mExpandedFormat = expandedFormat;
    131             if (mItemData != null) {
    132                 mItemData.actionFormatChanged();
    133             }
    134         }
    135     }
    136 
    137     private void updateTextButtonVisibility() {
    138         boolean visible = !TextUtils.isEmpty(mTextButton.getText());
    139         visible &= mImageButton.getDrawable() == null ||
    140                 (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
    141 
    142         mTextButton.setVisibility(visible ? VISIBLE : GONE);
    143     }
    144 
    145     public void setIcon(Drawable icon) {
    146         mImageButton.setImageDrawable(icon);
    147         if (icon != null) {
    148             mImageButton.setVisibility(VISIBLE);
    149         } else {
    150             mImageButton.setVisibility(GONE);
    151         }
    152 
    153         updateTextButtonVisibility();
    154     }
    155 
    156     public boolean hasText() {
    157         return mTextButton.getVisibility() != GONE;
    158     }
    159 
    160     public void setShortcut(boolean showShortcut, char shortcutKey) {
    161         // Action buttons don't show text for shortcut keys.
    162     }
    163 
    164     public void setTitle(CharSequence title) {
    165         mTitle = title;
    166 
    167         mTextButton.setText(mTitle);
    168 
    169         setContentDescription(mTitle);
    170         updateTextButtonVisibility();
    171     }
    172 
    173     @Override
    174     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
    175         onPopulateAccessibilityEvent(event);
    176         return true;
    177     }
    178 
    179     @Override
    180     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
    181         super.onPopulateAccessibilityEvent(event);
    182         final CharSequence cdesc = getContentDescription();
    183         if (!TextUtils.isEmpty(cdesc)) {
    184             event.getText().add(cdesc);
    185         }
    186     }
    187 
    188     @Override
    189     public boolean dispatchHoverEvent(MotionEvent event) {
    190         // Don't allow children to hover; we want this to be treated as a single component.
    191         return onHoverEvent(event);
    192     }
    193 
    194     public boolean showsIcon() {
    195         return true;
    196     }
    197 
    198     public boolean needsDividerBefore() {
    199         return hasText() && mItemData.getIcon() == null;
    200     }
    201 
    202     public boolean needsDividerAfter() {
    203         return hasText();
    204     }
    205 
    206     @Override
    207     public boolean onLongClick(View v) {
    208         if (hasText()) {
    209             // Don't show the cheat sheet for items that already show text.
    210             return false;
    211         }
    212 
    213         final int[] screenPos = new int[2];
    214         final Rect displayFrame = new Rect();
    215         getLocationOnScreen(screenPos);
    216         getWindowVisibleDisplayFrame(displayFrame);
    217 
    218         final Context context = getContext();
    219         final int width = getWidth();
    220         final int height = getHeight();
    221         final int midy = screenPos[1] + height / 2;
    222         final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
    223 
    224         Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
    225         if (midy < displayFrame.height()) {
    226             // Show along the top; follow action buttons
    227             cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT,
    228                     screenWidth - screenPos[0] - width / 2, height);
    229         } else {
    230             // Show along the bottom center
    231             cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
    232         }
    233         cheatSheet.show();
    234         return true;
    235     }
    236 
    237     @Override
    238     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    239         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    240 
    241         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    242         final int specSize = MeasureSpec.getSize(widthMeasureSpec);
    243         final int oldMeasuredWidth = getMeasuredWidth();
    244         final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth)
    245                 : mMinWidth;
    246 
    247         if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
    248             // Remeasure at exactly the minimum width.
    249             super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
    250                     heightMeasureSpec);
    251         }
    252     }
    253 }
    254