blob: 9a30ffafb75d069d5a080865387c2957b2fcc289 [file] [log] [blame]
package com.android.internal.view.menu;
import android.content.Context;
import android.content.res.Resources;
import android.os.Parcelable;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnKeyListener;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
import android.widget.PopupWindow;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.PopupWindow.OnDismissListener;
import com.android.internal.util.Preconditions;
/**
* A standard menu popup in which when a submenu is opened, it replaces its parent menu in the
* viewport.
*/
final class StandardMenuPopup extends MenuPopup implements OnDismissListener, OnItemClickListener,
MenuPresenter, OnKeyListener {
private final Context mContext;
private final LayoutInflater mInflater;
private final MenuBuilder mMenu;
private final MenuAdapter mAdapter;
private final boolean mOverflowOnly;
private final int mPopupMaxWidth;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
private PopupWindow.OnDismissListener mOnDismissListener;
private View mAnchorView;
private MenuPopupWindow mPopup;
private Callback mPresenterCallback;
private ViewGroup mMeasureParent;
/** Whether the cached content width value is valid. */
private boolean mHasContentWidth;
/** Cached content width. */
private int mContentWidth;
private int mDropDownGravity = Gravity.NO_GRAVITY;
public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
mInflater = LayoutInflater.from(context);
mMenu = menu;
mOverflowOnly = overflowOnly;
mAdapter = new MenuAdapter(menu, mInflater, mOverflowOnly);
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
final Resources res = context.getResources();
mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
mAnchorView = anchorView;
mPopup = new MenuPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes);
// Present the menu using our context, not the menu builder's context.
menu.addMenuPresenter(this, context);
}
@Override
public void setForceShowIcon(boolean forceShow) {
mAdapter.setForceShowIcon(forceShow);
}
@Override
public void setGravity(int gravity) {
mDropDownGravity = gravity;
}
private boolean tryShow() {
if (isShowing()) {
return true;
}
mPopup.setOnDismissListener(this);
mPopup.setOnItemClickListener(this);
mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
final View anchor = mAnchorView;
if (anchor != null) {
mPopup.setAnchorView(anchor);
mPopup.setDropDownGravity(mDropDownGravity);
} else {
return false;
}
if (!mHasContentWidth) {
mContentWidth = measureIndividualMenuWidth(
mAdapter, mMeasureParent, mContext, mPopupMaxWidth);
mHasContentWidth = true;
}
mPopup.setContentWidth(mContentWidth);
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
mPopup.show();
mPopup.getListView().setOnKeyListener(this);
return true;
}
@Override
public void show() {
if (!tryShow()) {
throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
}
}
@Override
public void dismiss() {
if (isShowing()) {
mPopup.dismiss();
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MenuAdapter adapter = mAdapter;
adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
}
@Override
public void addMenu(MenuBuilder menu) {
// No-op: standard implementation has only one menu which is set in the constructor.
}
@Override
public boolean isShowing() {
return mPopup != null && mPopup.isShowing();
}
@Override
public void onDismiss() {
mPopup = null;
mMenu.close();
mOnDismissListener.onDismiss();
}
@Override
public void updateMenuView(boolean cleared) {
mHasContentWidth = false;
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
@Override
public void setCallback(Callback cb) {
mPresenterCallback = cb;
}
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
if (subMenu.hasVisibleItems()) {
MenuPopupHelper subPopup = new MenuPopupHelper(
mContext, subMenu, mAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
subPopup.setCallback(mPresenterCallback);
boolean preserveIconSpacing = false;
final int count = subMenu.size();
for (int i = 0; i < count; i++) {
MenuItem childItem = subMenu.getItem(i);
if (childItem.isVisible() && childItem.getIcon() != null) {
preserveIconSpacing = true;
break;
}
}
subPopup.setForceShowIcon(preserveIconSpacing);
if (subPopup.tryShow()) {
if (mPresenterCallback != null) {
mPresenterCallback.onOpenSubMenu(subMenu);
}
return true;
}
}
return false;
}
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
// Only care about the (sub)menu we're presenting.
if (menu != mMenu) return;
dismiss();
if (mPresenterCallback != null) {
mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
}
}
@Override
public boolean flagActionItems() {
return false;
}
@Override
public Parcelable onSaveInstanceState() {
return null;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
}
@Override
public void setAnchorView(View anchor) {
mAnchorView = anchor;
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
dismiss();
return true;
}
return false;
}
@Override
public void setOnDismissListener(OnDismissListener listener) {
mOnDismissListener = listener;
}
@Override
public ListView getListView() {
return mPopup.getListView();
}
}