blob: 238a9c033796ec0297dbc10be24efdf14abed014 [file] [log] [blame]
Adam Powell96675b12010-06-10 18:58:59 -07001/*
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
17package com.android.internal.view.menu;
18
19import android.content.Context;
Adam Powelld5c81db2012-08-02 14:35:26 -070020import android.content.res.Configuration;
Adam Powell35aecd52011-07-01 13:43:49 -070021import android.content.res.Resources;
Adam Powell24340af2011-10-31 22:07:44 -070022import android.content.res.TypedArray;
Adam Powell7b5e9e62011-08-15 19:04:08 -070023import android.graphics.Rect;
Adam Powell96675b12010-06-10 18:58:59 -070024import android.graphics.drawable.Drawable;
Gilles Debunnec619e742011-05-31 15:49:51 -070025import android.text.TextUtils;
Adam Powell96675b12010-06-10 18:58:59 -070026import android.util.AttributeSet;
Adam Powell7b5e9e62011-08-15 19:04:08 -070027import android.view.Gravity;
Adam Powell7bc3ca02011-08-26 18:29:58 -070028import android.view.MotionEvent;
Adam Powell96675b12010-06-10 18:58:59 -070029import android.view.View;
Adam Powell7bc3ca02011-08-26 18:29:58 -070030import android.view.accessibility.AccessibilityEvent;
Adam Powell75d022a2012-03-06 12:04:07 -080031import android.widget.TextView;
Adam Powell7b5e9e62011-08-15 19:04:08 -070032import android.widget.Toast;
Adam Powell96675b12010-06-10 18:58:59 -070033
34/**
35 * @hide
36 */
Adam Powell75d022a2012-03-06 12:04:07 -080037public class ActionMenuItemView extends TextView
Adam Powell7b5e9e62011-08-15 19:04:08 -070038 implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
39 ActionMenuView.ActionMenuChildView {
Adam Powell96675b12010-06-10 18:58:59 -070040 private static final String TAG = "ActionMenuItemView";
Jeff Sharkeya8a72c32010-07-31 18:27:13 -070041
Adam Powell96675b12010-06-10 18:58:59 -070042 private MenuItemImpl mItemData;
43 private CharSequence mTitle;
Adam Powell75d022a2012-03-06 12:04:07 -080044 private Drawable mIcon;
Adam Powell96675b12010-06-10 18:58:59 -070045 private MenuBuilder.ItemInvoker mItemInvoker;
Jeff Sharkeya8a72c32010-07-31 18:27:13 -070046
Adam Powell35aecd52011-07-01 13:43:49 -070047 private boolean mAllowTextWithIcon;
Adam Powell35aecd52011-07-01 13:43:49 -070048 private boolean mExpandedFormat;
Adam Powell24340af2011-10-31 22:07:44 -070049 private int mMinWidth;
Adam Powell75d022a2012-03-06 12:04:07 -080050 private int mSavedPaddingLeft;
Adam Powell1f9c7af2010-08-03 11:26:07 -070051
Adam Powellc0047d42012-07-30 12:18:15 -070052 private static final int MAX_ICON_SIZE = 32; // dp
53 private int mMaxIconSize;
54
Adam Powell96675b12010-06-10 18:58:59 -070055 public ActionMenuItemView(Context context) {
56 this(context, null);
57 }
Jeff Sharkeya8a72c32010-07-31 18:27:13 -070058
Adam Powell96675b12010-06-10 18:58:59 -070059 public ActionMenuItemView(Context context, AttributeSet attrs) {
Adam Powell35aecd52011-07-01 13:43:49 -070060 this(context, attrs, 0);
Adam Powell96675b12010-06-10 18:58:59 -070061 }
Jeff Sharkeya8a72c32010-07-31 18:27:13 -070062
Adam Powell96675b12010-06-10 18:58:59 -070063 public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
64 super(context, attrs, defStyle);
Adam Powell35aecd52011-07-01 13:43:49 -070065 final Resources res = context.getResources();
66 mAllowTextWithIcon = res.getBoolean(
67 com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
Adam Powell24340af2011-10-31 22:07:44 -070068 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();
Adam Powell96675b12010-06-10 18:58:59 -070073
Adam Powellc0047d42012-07-30 12:18:15 -070074 final float density = res.getDisplayMetrics().density;
75 mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f);
76
Adam Powell6bddd872011-03-24 15:34:54 -070077 setOnClickListener(this);
Adam Powell7b5e9e62011-08-15 19:04:08 -070078 setOnLongClickListener(this);
Adam Powell75d022a2012-03-06 12:04:07 -080079
Adam Powell367ee322012-05-06 18:32:33 -070080 mSavedPaddingLeft = -1;
81 }
82
83 @Override
Adam Powelld5c81db2012-08-02 14:35:26 -070084 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
Adam Powell367ee322012-05-06 18:32:33 -070093 public void setPadding(int l, int t, int r, int b) {
94 mSavedPaddingLeft = l;
95 super.setPadding(l, t, r, b);
Adam Powell1f9c7af2010-08-03 11:26:07 -070096 }
97
Adam Powell96675b12010-06-10 18:58:59 -070098 public MenuItemImpl getItemData() {
99 return mItemData;
100 }
101
102 public void initialize(MenuItemImpl itemData, int menuType) {
103 mItemData = itemData;
Jeff Sharkeya8a72c32010-07-31 18:27:13 -0700104
Adam Powell96675b12010-06-10 18:58:59 -0700105 setIcon(itemData.getIcon());
Adam Powelle7d46842011-01-13 21:36:09 -0800106 setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
Adam Powell96675b12010-06-10 18:58:59 -0700107 setId(itemData.getItemId());
Jeff Sharkeya8a72c32010-07-31 18:27:13 -0700108
Adam Powell96675b12010-06-10 18:58:59 -0700109 setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
110 setEnabled(itemData.isEnabled());
111 }
Jeff Sharkeya8a72c32010-07-31 18:27:13 -0700112
Adam Powell1f9c7af2010-08-03 11:26:07 -0700113 public void onClick(View v) {
114 if (mItemInvoker != null) {
115 mItemInvoker.invokeItem(mItemData);
Adam Powell96675b12010-06-10 18:58:59 -0700116 }
117 }
Jeff Sharkeya8a72c32010-07-31 18:27:13 -0700118
Adam Powell96675b12010-06-10 18:58:59 -0700119 public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
120 mItemInvoker = invoker;
121 }
122
123 public boolean prefersCondensedTitle() {
Adam Powelle7d46842011-01-13 21:36:09 -0800124 return true;
Adam Powell96675b12010-06-10 18:58:59 -0700125 }
126
127 public void setCheckable(boolean checkable) {
128 // TODO Support checkable action items
129 }
130
131 public void setChecked(boolean checked) {
132 // TODO Support checkable action items
133 }
134
Adam Powell35aecd52011-07-01 13:43:49 -0700135 public void setExpandedFormat(boolean expandedFormat) {
136 if (mExpandedFormat != expandedFormat) {
137 mExpandedFormat = expandedFormat;
138 if (mItemData != null) {
139 mItemData.actionFormatChanged();
140 }
141 }
142 }
143
Gilles Debunnec619e742011-05-31 15:49:51 -0700144 private void updateTextButtonVisibility() {
Adam Powell75d022a2012-03-06 12:04:07 -0800145 boolean visible = !TextUtils.isEmpty(mTitle);
146 visible &= mIcon == null ||
Adam Powell35aecd52011-07-01 13:43:49 -0700147 (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
148
Adam Powell75d022a2012-03-06 12:04:07 -0800149 setText(visible ? mTitle : null);
Gilles Debunnec619e742011-05-31 15:49:51 -0700150 }
151
Adam Powell96675b12010-06-10 18:58:59 -0700152 public void setIcon(Drawable icon) {
Adam Powell75d022a2012-03-06 12:04:07 -0800153 mIcon = icon;
Adam Powellc1eea132012-07-31 11:27:19 -0700154 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);
Adam Powellc0047d42012-07-30 12:18:15 -0700168 }
Adam Powellc0047d42012-07-30 12:18:15 -0700169 setCompoundDrawables(icon, null, null, null);
Adam Powell9f125d32011-01-14 15:36:56 -0800170
Gilles Debunnec619e742011-05-31 15:49:51 -0700171 updateTextButtonVisibility();
Adam Powell96675b12010-06-10 18:58:59 -0700172 }
Gilles Debunnec619e742011-05-31 15:49:51 -0700173
Adam Powellbe4d68e2010-10-08 18:16:34 -0700174 public boolean hasText() {
Adam Powell75d022a2012-03-06 12:04:07 -0800175 return !TextUtils.isEmpty(getText());
Adam Powellbe4d68e2010-10-08 18:16:34 -0700176 }
Adam Powell96675b12010-06-10 18:58:59 -0700177
178 public void setShortcut(boolean showShortcut, char shortcutKey) {
179 // Action buttons don't show text for shortcut keys.
180 }
181
182 public void setTitle(CharSequence title) {
183 mTitle = title;
Jeff Sharkeya8a72c32010-07-31 18:27:13 -0700184
Adam Powell7bc3ca02011-08-26 18:29:58 -0700185 setContentDescription(mTitle);
Gilles Debunnec619e742011-05-31 15:49:51 -0700186 updateTextButtonVisibility();
Adam Powell96675b12010-06-10 18:58:59 -0700187 }
188
Adam Powell7bc3ca02011-08-26 18:29:58 -0700189 @Override
190 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
191 onPopulateAccessibilityEvent(event);
192 return true;
193 }
194
195 @Override
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
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
Adam Powell96675b12010-06-10 18:58:59 -0700210 public boolean showsIcon() {
211 return true;
212 }
Adam Powell696cba52011-03-29 10:38:16 -0700213
214 public boolean needsDividerBefore() {
215 return hasText() && mItemData.getIcon() == null;
216 }
217
218 public boolean needsDividerAfter() {
219 return hasText();
220 }
Adam Powell7b5e9e62011-08-15 19:04:08 -0700221
222 @Override
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
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -0700243 cheatSheet.setGravity(Gravity.TOP | Gravity.END,
Adam Powell7b5e9e62011-08-15 19:04:08 -0700244 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 }
Adam Powell24340af2011-10-31 22:07:44 -0700252
253 @Override
254 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Adam Powelld5c81db2012-08-02 14:35:26 -0700255 if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
256 // Fill all available height.
257 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
258 MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
259 }
Adam Powell75d022a2012-03-06 12:04:07 -0800260 final boolean textVisible = hasText();
Adam Powell367ee322012-05-06 18:32:33 -0700261 if (textVisible && mSavedPaddingLeft >= 0) {
262 super.setPadding(mSavedPaddingLeft, getPaddingTop(),
263 getPaddingRight(), getPaddingBottom());
Adam Powell75d022a2012-03-06 12:04:07 -0800264 }
265
Adam Powell24340af2011-10-31 22:07:44 -0700266 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
267
268 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
Adam Powell75d022a2012-03-06 12:04:07 -0800269 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Adam Powell24340af2011-10-31 22:07:44 -0700270 final int oldMeasuredWidth = getMeasuredWidth();
Adam Powell75d022a2012-03-06 12:04:07 -0800271 final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
Adam Powell24340af2011-10-31 22:07:44 -0700272 : 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 }
Adam Powell75d022a2012-03-06 12:04:07 -0800279
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();
Adam Powellc0047d42012-07-30 12:18:15 -0700284 final int dw = mIcon.getBounds().width();
Adam Powell367ee322012-05-06 18:32:33 -0700285 super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
Adam Powell75d022a2012-03-06 12:04:07 -0800286 }
Adam Powell24340af2011-10-31 22:07:44 -0700287 }
Adam Powell96675b12010-06-10 18:58:59 -0700288}