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

Commit 9916282b authored by Oren Blasberg's avatar Oren Blasberg
Browse files

Fix broken popup menu in Shelf (andromeda).

This addresses a few oversights from an earlier CL:
1. In MenuPopupHelper#show() make sure to create a new MenuPopup in
   case the earlier one was dismissed.
2. Ensure the on-dismiss listener gets called even if the MenuPopupHelper's
   MenuPopup was previously closed and if a new one is opened.
3. Handle global layout changes properly by having the MenuPopup
   re-drawing/positioning itself.

Bug: 23973158
Change-Id: Iee866079770026f0fe17814892abc9825f9760a2
parent 96b22a98
Loading
Loading
Loading
Loading
+53 −6
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.View.OnKeyListener;
import android.widget.AdapterView;
import android.widget.DropDownListView;
@@ -31,7 +32,8 @@ import com.android.internal.util.Preconditions;
 * @hide
 */
final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemClickListener,
        MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener {
        MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener,
        ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener{
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
    public @interface HorizPosition {}
@@ -47,13 +49,15 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
    private final int mLayoutDirection;

    private int mDropDownGravity = Gravity.NO_GRAVITY;
    private View mAnchor;
    private View mAnchorView;
    private View mShownAnchorView;
    private List<DropDownListView> mListViews;
    private List<MenuPopupWindow> mPopupWindows;
    private List<int[]> mOffsets;
    private int mPreferredPosition;
    private boolean mForceShowIcon;
    private Callback mPresenterCallback;
    private ViewTreeObserver mTreeObserver;
    private PopupWindow.OnDismissListener mOnDismissListener;

    /**
@@ -64,7 +68,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
    public CascadingMenuPopup(Context context, View anchor, int popupStyleAttr,
            int popupStyleRes, boolean overflowOnly) {
        mContext = Preconditions.checkNotNull(context);
        mAnchor = Preconditions.checkNotNull(anchor);
        mAnchorView = Preconditions.checkNotNull(anchor);
        mPopupStyleAttr = popupStyleAttr;
        mPopupStyleRes = popupStyleRes;
        mOverflowOnly = overflowOnly;
@@ -94,7 +98,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
                mContext, null, mPopupStyleAttr, mPopupStyleRes);
        popupWindow.setOnItemClickListener(this);
        popupWindow.setOnDismissListener(this);
        popupWindow.setAnchorView(mAnchor);
        popupWindow.setAnchorView(mAnchorView);
        popupWindow.setDropDownGravity(mDropDownGravity);
        popupWindow.setModal(true);
        popupWindow.setTouchModal(false);
@@ -116,6 +120,14 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
            popupWindow.show();
            mListViews.add((DropDownListView) popupWindow.getListView());
        }

        mShownAnchorView = mAnchorView;
        if (mShownAnchorView != null) {
            final boolean addGlobalListener = mTreeObserver == null;
            mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest
            if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
            mShownAnchorView.addOnAttachStateChangeListener(this);
        }
    }

    @Override
@@ -160,7 +172,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
        lastListView.getLocationOnScreen(screenLocation);

        final Rect displayFrame = new Rect();
        mAnchor.getWindowVisibleDisplayFrame(displayFrame);
        mShownAnchorView.getWindowVisibleDisplayFrame(displayFrame);

        if (mPreferredPosition == HORIZ_POSITION_RIGHT) {
            final int right = screenLocation[0] + lastListView.getWidth() + nextMenuWidth;
@@ -352,6 +364,13 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
        }

        if (mPopupWindows.size() == 0) {
            if (mTreeObserver != null) {
                if (!mTreeObserver.isAlive()) mTreeObserver =
                        mShownAnchorView.getViewTreeObserver();
                mTreeObserver.removeGlobalOnLayoutListener(this);
                mTreeObserver = null;
            }
            mShownAnchorView.removeOnAttachStateChangeListener(this);
            // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify
            // the owner.
            mOnDismissListener.onDismiss();
@@ -379,7 +398,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl

    @Override
    public void setAnchorView(View anchor) {
        mAnchor = anchor;
        mAnchorView = anchor;
    }

    @Override
@@ -391,4 +410,32 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
    public ListView getListView() {
        return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null;
    }

    @Override
    public void onGlobalLayout() {
        if (isShowing()) {
            final View anchor = mShownAnchorView;
            if (anchor == null || !anchor.isShown()) {
                dismiss();
            } else if (isShowing()) {
                // Recompute window sizes and positions.
                for (MenuPopupWindow popup : mPopupWindows) {
                    popup.show();
                }
            }
        }
    }

    @Override
    public void onViewAttachedToWindow(View v) {
    }

    @Override
    public void onViewDetachedFromWindow(View v) {
        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
        }
        v.removeOnAttachStateChangeListener(this);
    }
}
 No newline at end of file
+11 −44
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@

package com.android.internal.view.menu;

import com.android.internal.view.menu.MenuPresenter.Callback;

import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.PopupWindow;

/**
@@ -27,8 +28,7 @@ import android.widget.PopupWindow;
 *
 * @hide
 */
public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
        PopupWindow.OnDismissListener, View.OnAttachStateChangeListener {
public class MenuPopupHelper implements PopupWindow.OnDismissListener {
    private final Context mContext;
    private final MenuBuilder mMenu;
    private final boolean mOverflowOnly;
@@ -37,9 +37,9 @@ public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,

    private View mAnchorView;
    private MenuPopup mPopup;
    private ViewTreeObserver mTreeObserver;

    private int mDropDownGravity = Gravity.NO_GRAVITY;
    private Callback mPresenterCallback;

    public MenuPopupHelper(Context context, MenuBuilder menu) {
        this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
@@ -114,18 +114,15 @@ public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
            return true;
        }

        final View anchor = mAnchorView;
        if (anchor != null) {
            final boolean addGlobalListener = mTreeObserver == null;
            mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
            if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
            anchor.addOnAttachStateChangeListener(this);
            mPopup.setAnchorView(anchor);
            mPopup.setGravity(mDropDownGravity);
        } else {
        if (mAnchorView == null) {
            return false;
        }

        mPopup = createMenuPopup();
        mPopup.setAnchorView(mAnchorView);
        mPopup.setGravity(mDropDownGravity);
        mPopup.setCallback(mPresenterCallback);

        // In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
        // we must set the listener to this outer Helper rather than to the inner MenuPopup.
        // Not to worry -- the inner MenuPopup will call our own #onDismiss method after it's done
@@ -146,45 +143,15 @@ public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
    @Override
    public void onDismiss() {
        mPopup = null;
        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
            mTreeObserver = null;
        }
        mAnchorView.removeOnAttachStateChangeListener(this);
    }

    public boolean isShowing() {
        return mPopup != null && mPopup.isShowing();
    }

    @Override
    public void onGlobalLayout() {
        if (isShowing()) {
            final View anchor = mAnchorView;
            if (anchor == null || !anchor.isShown()) {
                dismiss();
            } else if (isShowing()) {
                // Recompute window size and position
                mPopup.show();
            }
        }
    }

    @Override
    public void onViewAttachedToWindow(View v) {
    }

    @Override
    public void onViewDetachedFromWindow(View v) {
        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
        }
        v.removeOnAttachStateChangeListener(this);
    }

    public void setCallback(MenuPresenter.Callback cb) {
        mPresenterCallback = cb;
        mPopup.setCallback(cb);
    }
}
+61 −11
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
@@ -24,7 +25,8 @@ import com.android.internal.util.Preconditions;
 * viewport.
 */
final class StandardMenuPopup extends MenuPopup implements OnDismissListener, OnItemClickListener,
        MenuPresenter, OnKeyListener {
        MenuPresenter, OnKeyListener, ViewTreeObserver.OnGlobalLayoutListener,
        View.OnAttachStateChangeListener {

    private final Context mContext;
    private final LayoutInflater mInflater;
@@ -34,15 +36,22 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
    private final int mPopupMaxWidth;
    private final int mPopupStyleAttr;
    private final int mPopupStyleRes;
    // The popup window is final in order to couple its lifecycle to the lifecycle of the
    // StandardMenuPopup.
    private final MenuPopupWindow mPopup;

    private PopupWindow.OnDismissListener mOnDismissListener;

    private View mAnchorView;
    private MenuPopupWindow mPopup;
    private View mShownAnchorView;
    private Callback mPresenterCallback;
    private ViewTreeObserver mTreeObserver;

    private ViewGroup mMeasureParent;

    /** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
    private boolean wasDismissed;

    /** Whether the cached content width value is valid. */
    private boolean mHasContentWidth;

@@ -88,18 +97,26 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
            return true;
        }

        if (mAnchorView == null) {
            return false;
        }

        mShownAnchorView = mAnchorView;

        mPopup.setOnDismissListener(this);
        mPopup.setOnItemClickListener(this);
        mPopup.setAdapter(mAdapter);
        mPopup.setModal(true);

        final View anchor = mAnchorView;
        if (anchor != null) {
        final View anchor = mShownAnchorView;
        final boolean addGlobalListener = mTreeObserver == null;
        mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
        if (addGlobalListener) {
            mTreeObserver.addOnGlobalLayoutListener(this);
        }
        anchor.addOnAttachStateChangeListener(this);
        mPopup.setAnchorView(anchor);
        mPopup.setDropDownGravity(mDropDownGravity);
        } else {
            return false;
        }

        if (!mHasContentWidth) {
            mContentWidth = measureIndividualMenuWidth(
@@ -141,14 +158,20 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On

    @Override
    public boolean isShowing() {
        return mPopup != null && mPopup.isShowing();
        return !wasDismissed && mPopup.isShowing();
    }

    @Override
    public void onDismiss() {
        mPopup = null;
        wasDismissed = true;
        mMenu.close();

        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
            mTreeObserver = null;
        }
        mShownAnchorView.removeOnAttachStateChangeListener(this);
        mOnDismissListener.onDismiss();
    }

@@ -170,7 +193,8 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
        if (subMenu.hasVisibleItems()) {
            MenuPopupHelper subPopup = new MenuPopupHelper(
                    mContext, subMenu, mAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
                    mContext, subMenu, mShownAnchorView, mOverflowOnly, mPopupStyleAttr,
                    mPopupStyleRes);
            subPopup.setCallback(mPresenterCallback);

            boolean preserveIconSpacing = false;
@@ -243,4 +267,30 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
    public ListView getListView() {
        return mPopup.getListView();
    }

    @Override
    public void onGlobalLayout() {
        if (isShowing()) {
            final View anchor = mShownAnchorView;
            if (anchor == null || !anchor.isShown()) {
                dismiss();
            } else if (isShowing()) {
                // Recompute window size and position
                mPopup.show();
            }
        }
    }

    @Override
    public void onViewAttachedToWindow(View v) {
    }

    @Override
    public void onViewDetachedFromWindow(View v) {
        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
        }
        v.removeOnAttachStateChangeListener(this);
    }
}
 No newline at end of file