1bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/* 2bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Copyright (C) 2010 The Android Open Source Project 3bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * 4bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 5bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * you may not use this file except in compliance with the License. 6bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * You may obtain a copy of the License at 7bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * 8bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * http://www.apache.org/licenses/LICENSE-2.0 9bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * 10bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Unless required by applicable law or agreed to in writing, software 11bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * distributed under the License is distributed on an "AS IS" BASIS, 12bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * See the License for the specific language governing permissions and 14bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * limitations under the License. 15bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */ 16bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banespackage android.support.v7.view.menu; 18bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 19bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.content.Context; 2049c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.content.res.Configuration; 21bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.content.res.Resources; 22bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.content.res.TypedArray; 23bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.graphics.Rect; 24bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.graphics.drawable.Drawable; 2549c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.os.Build; 261ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikasimport android.os.Parcelable; 270cbe64051d4241ad38cb4a232a86e6f26a3c8259Chris Banesimport android.support.v4.content.res.ConfigurationHelper; 2849c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.support.v4.view.GravityCompat; 294a201fef1a0116e987e82ee8fba1340ca9e6f1dbYigit Boyarimport android.support.v4.view.ViewCompat; 30da10fdd1400ecfd8d7f2e55651dd528d0614dfc5Jeff Brownimport android.support.v7.appcompat.R; 3149c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.support.v7.widget.ActionMenuView; 322cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banesimport android.support.v7.widget.AppCompatTextView; 336142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v7.widget.ForwardingListener; 3449c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.support.v7.widget.ListPopupWindow; 35bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.text.TextUtils; 36bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.util.AttributeSet; 37ed1096ebffac8f3cac5e5b98f54b96ae2ce8579eChris Banesimport android.util.DisplayMetrics; 38bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.view.Gravity; 3949c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.MotionEvent; 40bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.view.View; 41bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.widget.Toast; 42bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 43bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/** 44bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * @hide 45bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */ 462cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banespublic class ActionMenuItemView extends AppCompatTextView 4720ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener, 4820ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns ActionMenuView.ActionMenuChildView { 4920ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns 50ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private static final String TAG = "ActionMenuItemView"; 51ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 52ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private MenuItemImpl mItemData; 53ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private CharSequence mTitle; 54ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private Drawable mIcon; 55ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private MenuBuilder.ItemInvoker mItemInvoker; 566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private ForwardingListener mForwardingListener; 5749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private PopupCallback mPopupCallback; 58ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 59ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private boolean mAllowTextWithIcon; 60ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private boolean mExpandedFormat; 61ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private int mMinWidth; 62ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private int mSavedPaddingLeft; 63ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 6449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private static final int MAX_ICON_SIZE = 32; // dp 6549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private int mMaxIconSize; 6649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 67ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public ActionMenuItemView(Context context) { 68ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani this(context, null); 69ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 70ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 71ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public ActionMenuItemView(Context context, AttributeSet attrs) { 72ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani this(context, attrs, 0); 73ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 74ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 75ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) { 76ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani super(context, attrs, defStyle); 77ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final Resources res = context.getResources(); 788dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski mAllowTextWithIcon = shouldAllowTextWithIcon(); 79ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani TypedArray a = context.obtainStyledAttributes(attrs, 8049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes R.styleable.ActionMenuItemView, defStyle, 0); 81ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mMinWidth = a.getDimensionPixelSize( 8220ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns R.styleable.ActionMenuItemView_android_minWidth, 0); 83ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani a.recycle(); 84ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 8549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes final float density = res.getDisplayMetrics().density; 8649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f); 8749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 88ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setOnClickListener(this); 89ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setOnLongClickListener(this); 90ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 91ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mSavedPaddingLeft = -1; 921ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas setSaveEnabled(false); 93ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 94ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 9549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public void onConfigurationChanged(Configuration newConfig) { 9649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (Build.VERSION.SDK_INT >= 8) { 9749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes super.onConfigurationChanged(newConfig); 9849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 9949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1008dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski mAllowTextWithIcon = shouldAllowTextWithIcon(); 10149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes updateTextButtonVisibility(); 10249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 10349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1048dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski /** 1058dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski * Whether action menu items should obey the "withText" showAsAction flag. This may be set to 1068dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski * false for situations where space is extremely limited. --> 1078dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski */ 1088dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski private boolean shouldAllowTextWithIcon() { 109ed1096ebffac8f3cac5e5b98f54b96ae2ce8579eChris Banes final Configuration config = getContext().getResources().getConfiguration(); 1100cbe64051d4241ad38cb4a232a86e6f26a3c8259Chris Banes final int widthDp = ConfigurationHelper.getScreenWidthDp(getResources()); 1110cbe64051d4241ad38cb4a232a86e6f26a3c8259Chris Banes final int heightDp = ConfigurationHelper.getScreenHeightDp(getResources()); 112ed1096ebffac8f3cac5e5b98f54b96ae2ce8579eChris Banes 113ed1096ebffac8f3cac5e5b98f54b96ae2ce8579eChris Banes return widthDp >= 480 || (widthDp >= 640 && heightDp >= 480) 114ed1096ebffac8f3cac5e5b98f54b96ae2ce8579eChris Banes || config.orientation == Configuration.ORIENTATION_LANDSCAPE; 1158dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski } 1168dd9fe7e5a46645cc7a0ec950df7ff7d76b90abaFilip Gruszczynski 117ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani @Override 118ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setPadding(int l, int t, int r, int b) { 119ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mSavedPaddingLeft = l; 120ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani super.setPadding(l, t, r, b); 121ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 122ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 123ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public MenuItemImpl getItemData() { 124ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return mItemData; 125ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 126ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 127ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void initialize(MenuItemImpl itemData, int menuType) { 128ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mItemData = itemData; 129ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 130ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setIcon(itemData.getIcon()); 131ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon 132ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setId(itemData.getItemId()); 133ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 134ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE); 135ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setEnabled(itemData.isEnabled()); 13649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (itemData.hasSubMenu()) { 13749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (mForwardingListener == null) { 13849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mForwardingListener = new ActionMenuItemForwardingListener(); 13949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 14049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 141ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 142ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 14349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes @Override 14449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public boolean onTouchEvent(MotionEvent e) { 14549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (mItemData.hasSubMenu() && mForwardingListener != null 14649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes && mForwardingListener.onTouch(this, e)) { 14749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return true; 14849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 14949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return super.onTouchEvent(e); 15049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 15149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 15249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes @Override 153ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void onClick(View v) { 154ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (mItemInvoker != null) { 155ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mItemInvoker.invokeItem(mItemData); 156ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 157ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 158ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 159ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setItemInvoker(MenuBuilder.ItemInvoker invoker) { 16020ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns mItemInvoker = invoker; 161ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 162ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 16349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public void setPopupCallback(PopupCallback popupCallback) { 16449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mPopupCallback = popupCallback; 16549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 16649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 167ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean prefersCondensedTitle() { 168ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return true; 169ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 170ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 171ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setCheckable(boolean checkable) { 172ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // TODO Support checkable action items 173ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 174ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 175ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setChecked(boolean checked) { 176ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // TODO Support checkable action items 177ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 178ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 179ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setExpandedFormat(boolean expandedFormat) { 180ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (mExpandedFormat != expandedFormat) { 181ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mExpandedFormat = expandedFormat; 182ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (mItemData != null) { 183ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mItemData.actionFormatChanged(); 184ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 185ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 186ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 187ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 188ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private void updateTextButtonVisibility() { 189ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani boolean visible = !TextUtils.isEmpty(mTitle); 190ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani visible &= mIcon == null || 19120ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat)); 192ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 193ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setText(visible ? mTitle : null); 194ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 195ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 196ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setIcon(Drawable icon) { 197ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mIcon = icon; 19849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (icon != null) { 19949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes int width = icon.getIntrinsicWidth(); 20049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes int height = icon.getIntrinsicHeight(); 20149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (width > mMaxIconSize) { 20249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes final float scale = (float) mMaxIconSize / width; 20349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes width = mMaxIconSize; 20449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes height *= scale; 20549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 20649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (height > mMaxIconSize) { 20749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes final float scale = (float) mMaxIconSize / height; 20849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes height = mMaxIconSize; 20949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes width *= scale; 21049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 21149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes icon.setBounds(0, 0, width, height); 21249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 21349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes setCompoundDrawables(icon, null, null, null); 214ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 215ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani updateTextButtonVisibility(); 216ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 217ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 218ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean hasText() { 219ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return !TextUtils.isEmpty(getText()); 220ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 221ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 222ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setShortcut(boolean showShortcut, char shortcutKey) { 223ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // Action buttons don't show text for shortcut keys. 224ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 225ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 226ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void setTitle(CharSequence title) { 227ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mTitle = title; 228ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 229ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani setContentDescription(mTitle); 230ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani updateTextButtonVisibility(); 231ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 232ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 233ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean showsIcon() { 234ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return true; 235ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 236ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 237ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean needsDividerBefore() { 238ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return hasText() && mItemData.getIcon() == null; 239ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 240ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 241ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean needsDividerAfter() { 242ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return hasText(); 243ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 244ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 245ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani @Override 246ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean onLongClick(View v) { 247ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (hasText()) { 248ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // Don't show the cheat sheet for items that already show text. 249ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return false; 250ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 251ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 252ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int[] screenPos = new int[2]; 253ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final Rect displayFrame = new Rect(); 254ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani getLocationOnScreen(screenPos); 255ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani getWindowVisibleDisplayFrame(displayFrame); 256ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 257ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final Context context = getContext(); 258ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int width = getWidth(); 259ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int height = getHeight(); 260ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int midy = screenPos[1] + height / 2; 2614a201fef1a0116e987e82ee8fba1340ca9e6f1dbYigit Boyar int referenceX = screenPos[0] + width / 2; 2624a201fef1a0116e987e82ee8fba1340ca9e6f1dbYigit Boyar if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) { 2634a201fef1a0116e987e82ee8fba1340ca9e6f1dbYigit Boyar final int screenWidth = context.getResources().getDisplayMetrics().widthPixels; 2644a201fef1a0116e987e82ee8fba1340ca9e6f1dbYigit Boyar referenceX = screenWidth - referenceX; // mirror 2654a201fef1a0116e987e82ee8fba1340ca9e6f1dbYigit Boyar } 266ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT); 267ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (midy < displayFrame.height()) { 268ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // Show along the top; follow action buttons 2698b18135662bd0531adfd66d06cf203fc98f6188aChris Banes cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX, 2708b18135662bd0531adfd66d06cf203fc98f6188aChris Banes screenPos[1] + height - displayFrame.top); 271ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } else { 272ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // Show along the bottom center 273ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height); 274ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 275ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani cheatSheet.show(); 276ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return true; 277ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 278ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 279ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani @Override 280ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 281ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final boolean textVisible = hasText(); 282ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (textVisible && mSavedPaddingLeft >= 0) { 283ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani super.setPadding(mSavedPaddingLeft, getPaddingTop(), 28420ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns getPaddingRight(), getPaddingBottom()); 285ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 286ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 287ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani super.onMeasure(widthMeasureSpec, heightMeasureSpec); 288ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 289ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 290ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 291ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int oldMeasuredWidth = getMeasuredWidth(); 292ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth) 29320ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns : mMinWidth; 294ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 295ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) { 296ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // Remeasure at exactly the minimum width. 297ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), 29820ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns heightMeasureSpec); 299ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 300ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 301ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (!textVisible && mIcon != null) { 302ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // TextView won't center compound drawables in both dimensions without 303ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani // a little coercion. Pad in to center the icon after we've measured. 304ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani final int w = getMeasuredWidth(); 30549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes final int dw = mIcon.getBounds().width(); 306ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom()); 307ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 308ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 30930837f1095c803f332f4a1c3f0917c8afdd50156Adam Powell 3106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private class ActionMenuItemForwardingListener extends ForwardingListener { 31149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public ActionMenuItemForwardingListener() { 31249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes super(ActionMenuItemView.this); 31349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 31449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 31549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes @Override 3166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public ShowableListMenu getPopup() { 31749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (mPopupCallback != null) { 31849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return mPopupCallback.getPopup(); 31949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 32049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return null; 32149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 32249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 32349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes @Override 32449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes protected boolean onForwardingStarted() { 32549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes // Call the invoker, then check if the expected popup is showing. 32649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) { 3276142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final ShowableListMenu popup = getPopup(); 32849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return popup != null && popup.isShowing(); 32949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 33049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return false; 33149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 33249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 3337e3865a3c83a2c85af215bf1d9ec9c4f37438e19Chris Banes // Do not backport the framework impl here. 3347e3865a3c83a2c85af215bf1d9ec9c4f37438e19Chris Banes // The framework's ListPopupWindow uses an animation before performing the item click 3357e3865a3c83a2c85af215bf1d9ec9c4f37438e19Chris Banes // after selecting an item. As AppCompat doesn't use an animation, the popup is 3367e3865a3c83a2c85af215bf1d9ec9c4f37438e19Chris Banes // dismissed and thus null'ed out before onForwardingStopped() has been called. 3377e3865a3c83a2c85af215bf1d9ec9c4f37438e19Chris Banes // This messes up ActionMenuItemView's onForwardingStopped() impl since it will now 3387e3865a3c83a2c85af215bf1d9ec9c4f37438e19Chris Banes // return false and make ListPopupWindow think it's still forwarding. 33949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 34049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 3411ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas @Override 3421ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas public void onRestoreInstanceState(Parcelable state) { 3431ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas // This might get called with the state of ActionView since it shares the same ID with 3441ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas // ActionMenuItemView. Do not restore this state as ActionMenuItemView never saved it. 3451ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas super.onRestoreInstanceState(null); 3461ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas } 3471ebe1d4ab755d1a852413bb0845f45caa000fed4Aurimas Liutikas 34849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public static abstract class PopupCallback { 3496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public abstract ShowableListMenu getPopup(); 35049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 351bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell} 352