Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96b22a98 authored by Oren Blasberg's avatar Oren Blasberg Committed by Android (Google) Code Review
Browse files

Merge "Add Cascading submenus implementation."

parents 64173641 8e12f8df
Loading
Loading
Loading
Loading
+26 −20
Original line number Diff line number Diff line
@@ -85,6 +85,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter
    private OpenOverflowRunnable mPostedOpenRunnable;
    private ActionMenuPopupCallback mPopupCallback;

    private final boolean mShowCascadingMenus;

    final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
    int mOpenSubMenuId;

@@ -126,6 +128,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter
    public ActionMenuPresenter(Context context) {
        super(context, com.android.internal.R.layout.action_menu_layout,
                com.android.internal.R.layout.action_menu_item_layout);

        mShowCascadingMenus = context.getResources().getBoolean(
                com.android.internal.R.bool.config_enableCascadingSubmenus);
    }

    @Override
@@ -500,14 +505,29 @@ public class ActionMenuPresenter extends BaseMenuPresenter
        }
        View anchor = findViewForItem(topSubMenu.getItem());
        if (anchor == null) {
            if (mOverflowButton == null) return false;
            anchor = mOverflowButton;
            // This means the submenu was opened from an overflow menu item, indicating the
            // MenuPopupHelper will handle opening the submenu via its MenuPopup. Return false to
            // ensure that the MenuPopup acts as presenter for the submenu, and acts on its
            // responsibility to display the new submenu.
            return false;
        }

        mOpenSubMenuId = subMenu.getItem().getItemId();
        mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
        mActionButtonPopup.setAnchorView(anchor);

        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;
            }
        }

        mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu, anchor);
        mActionButtonPopup.setForceShowIcon(preserveIconSpacing);
        mActionButtonPopup.show();

        super.onSubMenuSelected(subMenu);
        return true;
    }
@@ -926,12 +946,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter
    }

    private class ActionButtonSubmenu extends MenuPopupHelper {
        private SubMenuBuilder mSubMenu;

        public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
            super(context, subMenu, null, false,
        public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu, View anchorView) {
            super(context, subMenu, anchorView, false,
                    com.android.internal.R.attr.actionOverflowMenuStyle);
            mSubMenu = subMenu;

            MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
            if (!item.isActionButton()) {
@@ -940,17 +957,6 @@ public class ActionMenuPresenter extends BaseMenuPresenter
            }

            setCallback(mPopupPresenterCallback);

            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;
                }
            }
            setForceShowIcon(preserveIconSpacing);
        }

        @Override
+55 −2
Original line number Diff line number Diff line
@@ -25,10 +25,8 @@ import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.IntProperty;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.TextView;
import android.widget.ListView;
@@ -128,6 +126,61 @@ public class DropDownListView extends ListView {
        setCacheColorHint(0); // Transparent, since the background drawable could be anything.
    }

    @Override
    protected boolean shouldShowSelector() {
        View selectedView = getSelectedView();
        return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector();
    }

    protected void clearSelection() {
        setSelectedPositionInt(-1);
        setNextSelectedPositionInt(-1);
    }

    @Override
    public boolean onHoverEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
        if (action == MotionEvent.ACTION_HOVER_ENTER
                || action == MotionEvent.ACTION_HOVER_MOVE) {
            final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
            if (position != INVALID_POSITION && position != mSelectedPosition) {
                final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
                if (hoveredItem.isEnabled()) {
                    // Force a focus so that the proper selector state gets used when we update.
                    requestFocus();

                    positionSelector(position, hoveredItem);
                    setSelectedPositionInt(position);
                    setNextSelectedPositionInt(position);
                }
                updateSelectorState();
            }
        } else {
            // Do not cancel the selected position if the selection is visible by other reasons.
            if (!super.shouldShowSelector()) {
                setSelectedPositionInt(INVALID_POSITION);
            }
        }
        return super.onHoverEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int x = (int) event.getX();
        final int y = (int) event.getY();
        final int position = pointToPosition(x, y);
        if (position == INVALID_POSITION) {
            return super.onTouchEvent(event);
        }

        if (position != mSelectedPosition) {
            setSelectedPositionInt(position);
            setNextSelectedPositionInt(position);
        }

        return super.onTouchEvent(event);
    }

    /**
     * Handles forwarded events.
     *
+2 −1
Original line number Diff line number Diff line
@@ -69,7 +69,6 @@ public class ListPopupWindow implements ShowableListMenu {
    private static final int EXPAND_LIST_TIMEOUT = 250;

    private Context mContext;
    private PopupWindow mPopup;
    private ListAdapter mAdapter;
    private DropDownListView mDropDownList;

@@ -112,6 +111,8 @@ public class ListPopupWindow implements ShowableListMenu {

    private int mLayoutDirection;

    PopupWindow mPopup;

    /**
     * The provided prompt view should appear above list content.
     * 
+54 −41
Original line number Diff line number Diff line
@@ -17,10 +17,17 @@
package android.widget;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.transition.Transition;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.KeyEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.ViewGroup;
import android.view.ViewParent;

import com.android.internal.view.menu.ListMenuItemView;
import com.android.internal.view.menu.MenuAdapter;

/**
 * A MenuPopupWindow represents the popup window for menu.
@@ -40,52 +47,58 @@ public class MenuPopupWindow extends ListPopupWindow {
        return new MenuDropDownListView(context, hijackFocus);
    }

    static class MenuDropDownListView extends DropDownListView {
        private boolean mHoveredOnDisabledItem = false;
        private AccessibilityManager mAccessibilityManager;

        MenuDropDownListView(Context context, boolean hijackFocus) {
            super(context, hijackFocus);
            mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(
                    Context.ACCESSIBILITY_SERVICE);
    public void setEnterTransition(Transition enterTransition) {
        mPopup.setEnterTransition(enterTransition);
    }

        @Override
        protected boolean shouldShowSelector() {
            return (isHovered() && !mHoveredOnDisabledItem) || super.shouldShowSelector();
    /**
     * Set whether this window is touch modal or if outside touches will be sent to
     * other windows behind it.
     */
    public void setTouchModal(boolean touchModal) {
        mPopup.setTouchModal(touchModal);
    }

        @Override
        public boolean onHoverEvent(MotionEvent ev) {
            mHoveredOnDisabledItem = false;
    private static class MenuDropDownListView extends DropDownListView {
        final int mAdvanceKey;
        final int mRetreatKey;

            // Accessibility system should already handle hover events and selections, menu does
            // not have to handle it by itself.
            if (mAccessibilityManager.isTouchExplorationEnabled()) {
                return super.onHoverEvent(ev);
            }
        public MenuDropDownListView(Context context, boolean hijackFocus) {
            super(context, hijackFocus);

            final int action = ev.getActionMasked();
            if (action == MotionEvent.ACTION_HOVER_ENTER
                    || action == MotionEvent.ACTION_HOVER_MOVE) {
                final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
                if (position != INVALID_POSITION && position != mSelectedPosition) {
                    final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
                    if (hoveredItem.isEnabled()) {
                        positionSelector(position, hoveredItem);
                        setSelectedPositionInt(position);
            final Resources res = context.getResources();
            final Configuration config = res.getConfiguration();
            if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
                mAdvanceKey = KeyEvent.KEYCODE_DPAD_LEFT;
                mRetreatKey = KeyEvent.KEYCODE_DPAD_RIGHT;
            } else {
                        mHoveredOnDisabledItem = true;
                mAdvanceKey = KeyEvent.KEYCODE_DPAD_RIGHT;
                mRetreatKey = KeyEvent.KEYCODE_DPAD_LEFT;
            }
                    updateSelectorState();
        }
            } else {
                // Do not cancel the selected position if the selection is visible by other reasons.
                if (!super.shouldShowSelector()) {
                    setSelectedPositionInt(INVALID_POSITION);

        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
            if (selectedItem != null && keyCode == mAdvanceKey) {
                if (selectedItem.isEnabled() &&
                        ((ListMenuItemView) selectedItem).getItemData().hasSubMenu()) {
                    performItemClick(
                            selectedItem,
                            getSelectedItemPosition(),
                            getSelectedItemId());
                }
                return true;
            } else if (selectedItem != null && keyCode == mRetreatKey) {
                setSelectedPositionInt(-1);
                setNextSelectedPositionInt(-1);

                ((MenuAdapter) getAdapter()).getAdapterMenu().close();
                return true;
            }
            return super.onHoverEvent(ev);
            return super.onKeyDown(keyCode, event);
        }

    }

}
 No newline at end of file
+9 −2
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
    private final MenuBuilder mMenu;
    private final View mAnchor;
    private final MenuPopupHelper mPopup;
    private final boolean mShowCascadingMenus;

    private OnMenuItemClickListener mMenuItemClickListener;
    private OnDismissListener mDismissListener;
@@ -107,6 +108,8 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
    public PopupMenu(Context context, View anchor, int gravity, int popupStyleAttr,
            int popupStyleRes) {
        mContext = context;
        mShowCascadingMenus = context.getResources().getBoolean(
                com.android.internal.R.bool.config_enableCascadingSubmenus);
        mMenu = new MenuBuilder(context);
        mMenu.setCallback(this);
        mAnchor = anchor;
@@ -273,8 +276,12 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
            return true;
        }

        // Current menu will be dismissed by the normal helper, submenu will be shown in its place.
        if (!mShowCascadingMenus) {
            // Current menu will be dismissed by the normal helper, submenu will be shown in its
            // place. (If cascading menus are enabled, the cascading implementation will show the
            // submenu itself).
            new MenuPopupHelper(mContext, subMenu, mAnchor).show();
        }
        return true;
    }

Loading