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

Commit 021627eb authored by Alan Viverette's avatar Alan Viverette
Browse files

Ensure only one context menu is shown at a time

Refactors the menu helper classes. Both classes now implement a common
MenuHelper interface, which eliminates the need to keep separate helpers
on PhoneWindow and unifies the DecorView showContextMenuForChild()
implementations.

We now explicitly dismiss any previously shown context menu before showing
a new context menu. Previously we relied on the modal nature of the dialog
context menu to prevent multiple menus from being opened at once, but this
is no longer reliable with popup context menus.

Bug: 25656520
Change-Id: Idab3daa6d6888f803f2e33660fe1dd488e4c28d1
parent e38bdf6b
Loading
Loading
Loading
Loading
+12 −6
Original line number Original line Diff line number Diff line
@@ -5505,20 +5505,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    }
    /**
    /**
     * Bring up the context menu for this view.
     * Shows the context menu for this view.
     *
     *
     * @return Whether a context menu was displayed.
     * @return {@code true} if the context menu was shown, {@code false}
     *         otherwise
     * @see #showContextMenu(float, float)
     */
     */
    public boolean showContextMenu() {
    public boolean showContextMenu() {
        return getParent().showContextMenuForChild(this);
        return getParent().showContextMenuForChild(this);
    }
    }
    /**
    /**
     * Bring up the context menu for this view, referring to the item under the specified point.
     * Shows the context menu for this view anchored to the specified
     * view-relative coordinate.
     *
     *
     * @param x The referenced x coordinate.
     * @param x the X coordinate in pixels relative to the view to which the
     * @param y The referenced y coordinate.
     *          menu should be anchored
     * @return Whether a context menu was displayed.
     * @param y the Y coordinate in pixels relative to the view to which the
     *          menu should be anchored
     * @return {@code true} if the context menu was shown, {@code false}
     *         otherwise
     */
     */
    public boolean showContextMenu(float x, float y) {
    public boolean showContextMenu(float x, float y) {
        return getParent().showContextMenuForChild(this, x, y);
        return getParent().showContextMenuForChild(this, x, y);
+26 −14
Original line number Original line Diff line number Diff line
@@ -174,26 +174,38 @@ public interface ViewParent {
    public void focusableViewAvailable(View v);
    public void focusableViewAvailable(View v);


    /**
    /**
     * Bring up a context menu for the specified view or its ancestors.
     * Shows the context menu for the specified view or its ancestors.
     *
     * <p>
     * <p>In most cases, a subclass does not need to override this.  However, if
     * In most cases, a subclass does not need to override this. However, if
     * the subclass is added directly to the window manager (for example,
     * the subclass is added directly to the window manager (for example,
     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
     * then it should override this and show the context menu.</p>
     * then it should override this and show the context menu.
     *
     *
     * @param originalView The source view where the context menu was first invoked
     * @param originalView the source view where the context menu was first
     * @return true if a context menu was displayed
     *                     invoked
     * @return {@code true} if the context menu was shown, {@code false}
     *         otherwise
     * @see #showContextMenuForChild(View, float, float)
     */
     */
    public boolean showContextMenuForChild(View originalView);
    public boolean showContextMenuForChild(View originalView);


    /**
    /**
     * Bring up a context menu for the specified view at the given x/y offset from
     * Shows the context menu for the specified view or its ancestors anchored
     * the top left corner.
     * to the specified view-relative coordinate.
     *
     * <p>
     * @param originalView
     * In most cases, a subclass does not need to override this. However, if
     * @param x The x offset at which to open the menu
     * the subclass is added directly to the window manager (for example,
     * @param y The y offset at which to open the menu
     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
     * @return true if a context menu was displayed
     * then it should override this and show the context menu.
     *
     * @param originalView the source view where the context menu was first
     *                     invoked
     * @param x the X coordinate in pixels relative to the original view to
     *          which the menu should be anchored
     * @param y the Y coordinate in pixels relative to the original view to
     *          which the menu should be anchored
     * @return {@code true} if the context menu was shown, {@code false}
     *         otherwise
     */
     */
    public boolean showContextMenuForChild(View originalView, float x, float y);
    public boolean showContextMenuForChild(View originalView, float x, float y);


+2 −2
Original line number Original line Diff line number Diff line
@@ -933,7 +933,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter
            super(context, menu, anchorView, overflowOnly,
            super(context, menu, anchorView, overflowOnly,
                    com.android.internal.R.attr.actionOverflowMenuStyle);
                    com.android.internal.R.attr.actionOverflowMenuStyle);
            setGravity(Gravity.END);
            setGravity(Gravity.END);
            setCallback(mPopupPresenterCallback);
            setPresenterCallback(mPopupPresenterCallback);
        }
        }


        @Override
        @Override
@@ -956,7 +956,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter
                setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
                setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
            }
            }


            setCallback(mPopupPresenterCallback);
            setPresenterCallback(mPopupPresenterCallback);
        }
        }


        @Override
        @Override
+24 −30
Original line number Original line Diff line number Diff line
@@ -21,8 +21,7 @@ import com.android.internal.view.FloatingActionMode;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
import com.android.internal.view.menu.MenuHelper;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.DecorCaptionView;
@@ -659,30 +658,23 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind


    @Override
    @Override
    public boolean showContextMenuForChild(View originalView) {
    public boolean showContextMenuForChild(View originalView) {
        // Reuse the context menu builder
        return showContextMenuForChildInternal(originalView, 0, 0, false);
        if (mWindow.mContextMenu == null) {
            mWindow.mContextMenu = new ContextMenuBuilder(getContext());
            mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
        } else {
            mWindow.mContextMenu.clearAll();
    }
    }


        final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
    @Override
                originalView.getWindowToken());
    public boolean showContextMenuForChild(View originalView, float x, float y) {
        if (helper != null) {
        return showContextMenuForChildInternal(originalView, x, y, true);
            helper.setPresenterCallback(mWindow.mContextMenuCallback);
        } else if (mWindow.mContextMenuHelper != null) {
            // No menu to show, but if we have a menu currently showing it just became blank.
            // Close it.
            mWindow.mContextMenuHelper.dismiss();
    }
    }
        mWindow.mContextMenuHelper = helper;

        return helper != null;
    private boolean showContextMenuForChildInternal(View originalView,
            float x, float y, boolean isPopup) {
        // Only allow one context menu at a time.
        if (mWindow.mContextMenuHelper != null) {
            mWindow.mContextMenuHelper.dismiss();
            mWindow.mContextMenuHelper = null;
        }
        }


    @Override
        // Reuse the context menu builder.
    public boolean showContextMenuForChild(View originalView, float x, float y) {
        // Reuse the context menu builder
        if (mWindow.mContextMenu == null) {
        if (mWindow.mContextMenu == null) {
            mWindow.mContextMenu = new ContextMenuBuilder(getContext());
            mWindow.mContextMenu = new ContextMenuBuilder(getContext());
            mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
            mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
@@ -690,16 +682,18 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
            mWindow.mContextMenu.clearAll();
            mWindow.mContextMenu.clearAll();
        }
        }


        final MenuPopupHelper helper = mWindow.mContextMenu.showPopup(
        final MenuHelper helper;
                getContext(), originalView, x, y);
        if (isPopup) {
            helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
        } else {
            helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
        }

        if (helper != null) {
        if (helper != null) {
            helper.setCallback(mWindow.mContextMenuCallback);
            helper.setPresenterCallback(mWindow.mContextMenuCallback);
        } else if (mWindow.mContextMenuPopupHelper != null) {
            // No menu to show, but if we have a menu currently showing it just became blank.
            // Close it.
            mWindow.mContextMenuPopupHelper.dismiss();
        }
        }
        mWindow.mContextMenuPopupHelper = helper;

        mWindow.mContextMenuHelper = helper;
        return helper != null;
        return helper != null;
    }
    }


+2 −7
Original line number Original line Diff line number Diff line
@@ -53,7 +53,7 @@ import com.android.internal.view.menu.IconMenuPresenter;
import com.android.internal.view.menu.ListMenuPresenter;
import com.android.internal.view.menu.ListMenuPresenter;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
import com.android.internal.view.menu.MenuDialogHelper;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.DecorContentParent;
import com.android.internal.widget.DecorContentParent;
@@ -232,8 +232,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
    private boolean mAlwaysReadCloseOnTouchAttr = false;
    private boolean mAlwaysReadCloseOnTouchAttr = false;


    ContextMenuBuilder mContextMenu;
    ContextMenuBuilder mContextMenu;
    MenuDialogHelper mContextMenuHelper;
    MenuHelper mContextMenuHelper;
    MenuPopupHelper mContextMenuPopupHelper;
    private boolean mClosingActionMenu;
    private boolean mClosingActionMenu;


    private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
    private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -1103,10 +1102,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            mContextMenuHelper.dismiss();
            mContextMenuHelper.dismiss();
            mContextMenuHelper = null;
            mContextMenuHelper = null;
        }
        }
        if (mContextMenuPopupHelper != null) {
            mContextMenuPopupHelper.dismiss();
            mContextMenuPopupHelper = null;
        }
    }
    }


    @Override
    @Override
Loading