• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Configuration;
21 import android.content.res.Resources;
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.MotionEvent;
29 import android.view.View;
30 import android.view.accessibility.AccessibilityEvent;
31 import android.widget.TextView;
32 import android.widget.Toast;
33 
34 /**
35  * @hide
36  */
37 public class ActionMenuItemView extends TextView
38         implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
39         ActionMenuView.ActionMenuChildView {
40     private static final String TAG = "ActionMenuItemView";
41 
42     private MenuItemImpl mItemData;
43     private CharSequence mTitle;
44     private Drawable mIcon;
45     private MenuBuilder.ItemInvoker mItemInvoker;
46 
47     private boolean mAllowTextWithIcon;
48     private boolean mExpandedFormat;
49     private int mMinWidth;
50     private int mSavedPaddingLeft;
51 
52     private static final int MAX_ICON_SIZE = 32; // dp
53     private int mMaxIconSize;
54 
ActionMenuItemView(Context context)55     public ActionMenuItemView(Context context) {
56         this(context, null);
57     }
58 
ActionMenuItemView(Context context, AttributeSet attrs)59     public ActionMenuItemView(Context context, AttributeSet attrs) {
60         this(context, attrs, 0);
61     }
62 
ActionMenuItemView(Context context, AttributeSet attrs, int defStyle)63     public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
64         super(context, attrs, defStyle);
65         final Resources res = context.getResources();
66         mAllowTextWithIcon = res.getBoolean(
67                 com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
68         TypedArray a = context.obtainStyledAttributes(attrs,
69                 com.android.internal.R.styleable.ActionMenuItemView, 0, 0);
70         mMinWidth = a.getDimensionPixelSize(
71                 com.android.internal.R.styleable.ActionMenuItemView_minWidth, 0);
72         a.recycle();
73 
74         final float density = res.getDisplayMetrics().density;
75         mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f);
76 
77         setOnClickListener(this);
78         setOnLongClickListener(this);
79 
80         mSavedPaddingLeft = -1;
81     }
82 
83     @Override
onConfigurationChanged(Configuration newConfig)84     public void onConfigurationChanged(Configuration newConfig) {
85         super.onConfigurationChanged(newConfig);
86 
87         mAllowTextWithIcon = getContext().getResources().getBoolean(
88                 com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
89         updateTextButtonVisibility();
90     }
91 
92     @Override
setPadding(int l, int t, int r, int b)93     public void setPadding(int l, int t, int r, int b) {
94         mSavedPaddingLeft = l;
95         super.setPadding(l, t, r, b);
96     }
97 
getItemData()98     public MenuItemImpl getItemData() {
99         return mItemData;
100     }
101 
initialize(MenuItemImpl itemData, int menuType)102     public void initialize(MenuItemImpl itemData, int menuType) {
103         mItemData = itemData;
104 
105         setIcon(itemData.getIcon());
106         setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
107         setId(itemData.getItemId());
108 
109         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
110         setEnabled(itemData.isEnabled());
111     }
112 
onClick(View v)113     public void onClick(View v) {
114         if (mItemInvoker != null) {
115             mItemInvoker.invokeItem(mItemData);
116         }
117     }
118 
setItemInvoker(MenuBuilder.ItemInvoker invoker)119     public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
120         mItemInvoker = invoker;
121     }
122 
prefersCondensedTitle()123     public boolean prefersCondensedTitle() {
124         return true;
125     }
126 
setCheckable(boolean checkable)127     public void setCheckable(boolean checkable) {
128         // TODO Support checkable action items
129     }
130 
setChecked(boolean checked)131     public void setChecked(boolean checked) {
132         // TODO Support checkable action items
133     }
134 
setExpandedFormat(boolean expandedFormat)135     public void setExpandedFormat(boolean expandedFormat) {
136         if (mExpandedFormat != expandedFormat) {
137             mExpandedFormat = expandedFormat;
138             if (mItemData != null) {
139                 mItemData.actionFormatChanged();
140             }
141         }
142     }
143 
updateTextButtonVisibility()144     private void updateTextButtonVisibility() {
145         boolean visible = !TextUtils.isEmpty(mTitle);
146         visible &= mIcon == null ||
147                 (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
148 
149         setText(visible ? mTitle : null);
150     }
151 
setIcon(Drawable icon)152     public void setIcon(Drawable icon) {
153         mIcon = icon;
154         if (icon != null) {
155             int width = icon.getIntrinsicWidth();
156             int height = icon.getIntrinsicHeight();
157             if (width > mMaxIconSize) {
158                 final float scale = (float) mMaxIconSize / width;
159                 width = mMaxIconSize;
160                 height *= scale;
161             }
162             if (height > mMaxIconSize) {
163                 final float scale = (float) mMaxIconSize / height;
164                 height = mMaxIconSize;
165                 width *= scale;
166             }
167             icon.setBounds(0, 0, width, height);
168         }
169         setCompoundDrawables(icon, null, null, null);
170 
171         updateTextButtonVisibility();
172     }
173 
hasText()174     public boolean hasText() {
175         return !TextUtils.isEmpty(getText());
176     }
177 
setShortcut(boolean showShortcut, char shortcutKey)178     public void setShortcut(boolean showShortcut, char shortcutKey) {
179         // Action buttons don't show text for shortcut keys.
180     }
181 
setTitle(CharSequence title)182     public void setTitle(CharSequence title) {
183         mTitle = title;
184 
185         setContentDescription(mTitle);
186         updateTextButtonVisibility();
187     }
188 
189     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)190     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
191         onPopulateAccessibilityEvent(event);
192         return true;
193     }
194 
195     @Override
onPopulateAccessibilityEvent(AccessibilityEvent event)196     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
197         super.onPopulateAccessibilityEvent(event);
198         final CharSequence cdesc = getContentDescription();
199         if (!TextUtils.isEmpty(cdesc)) {
200             event.getText().add(cdesc);
201         }
202     }
203 
204     @Override
dispatchHoverEvent(MotionEvent event)205     public boolean dispatchHoverEvent(MotionEvent event) {
206         // Don't allow children to hover; we want this to be treated as a single component.
207         return onHoverEvent(event);
208     }
209 
showsIcon()210     public boolean showsIcon() {
211         return true;
212     }
213 
needsDividerBefore()214     public boolean needsDividerBefore() {
215         return hasText() && mItemData.getIcon() == null;
216     }
217 
needsDividerAfter()218     public boolean needsDividerAfter() {
219         return hasText();
220     }
221 
222     @Override
onLongClick(View v)223     public boolean onLongClick(View v) {
224         if (hasText()) {
225             // Don't show the cheat sheet for items that already show text.
226             return false;
227         }
228 
229         final int[] screenPos = new int[2];
230         final Rect displayFrame = new Rect();
231         getLocationOnScreen(screenPos);
232         getWindowVisibleDisplayFrame(displayFrame);
233 
234         final Context context = getContext();
235         final int width = getWidth();
236         final int height = getHeight();
237         final int midy = screenPos[1] + height / 2;
238         final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
239 
240         Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
241         if (midy < displayFrame.height()) {
242             // Show along the top; follow action buttons
243             cheatSheet.setGravity(Gravity.TOP | Gravity.END,
244                     screenWidth - screenPos[0] - width / 2, height);
245         } else {
246             // Show along the bottom center
247             cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
248         }
249         cheatSheet.show();
250         return true;
251     }
252 
253     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)254     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
255         if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
256             // Fill all available height.
257             heightMeasureSpec = MeasureSpec.makeMeasureSpec(
258                     MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
259         }
260         final boolean textVisible = hasText();
261         if (textVisible && mSavedPaddingLeft >= 0) {
262             super.setPadding(mSavedPaddingLeft, getPaddingTop(),
263                     getPaddingRight(), getPaddingBottom());
264         }
265 
266         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
267 
268         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
269         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
270         final int oldMeasuredWidth = getMeasuredWidth();
271         final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
272                 : mMinWidth;
273 
274         if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
275             // Remeasure at exactly the minimum width.
276             super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
277                     heightMeasureSpec);
278         }
279 
280         if (!textVisible && mIcon != null) {
281             // TextView won't center compound drawables in both dimensions without
282             // a little coercion. Pad in to center the icon after we've measured.
283             final int w = getMeasuredWidth();
284             final int dw = mIcon.getBounds().width();
285             super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
286         }
287     }
288 }
289