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

Commit 393b1c1e authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Fix issue #17789629: PopupWindow overlaps with navigation bar.

The Lollipop release introduced a feature that allowed
apps to extend under the navigation bar. This also means
any popup window that is anchored to the bottom of its
parent window will overlap with the navigation bar if the
parent window is extending underneath the navigation bar.

This change introduces a new window flag
(FLAG_LAYOUT_ATTACHED_IN_DECOR) that allows the app to
specify if the popup window should be attached to the decor
frame of the parent window thereby avoiding an overlap
with the screen decorations.

By default the flag is set on SDK version LOLLIPOP_MR1 or
greater and cleared on lesser SDK versions.

Also, replaced flags FLAG_NEEDS_MENU_KEY and
PRIVATE_FLAG_NEEDS_MENU_KEY_SET with needsMenuKey state
variable to make room for the new
FLAG_LAYOUT_ATTACHED_IN_DECOR flag.

Bug: 17789629
Change-Id: I2150e0c6ac688c966c0e8f7e54d42fd20285bea6
parent a9a550dd
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -34904,6 +34904,7 @@ package android.view {
    field public static final int FLAG_HARDWARE_ACCELERATED = 16777216; // 0x1000000
    field public static final int FLAG_HARDWARE_ACCELERATED = 16777216; // 0x1000000
    field public static final int FLAG_IGNORE_CHEEK_PRESSES = 32768; // 0x8000
    field public static final int FLAG_IGNORE_CHEEK_PRESSES = 32768; // 0x8000
    field public static final int FLAG_KEEP_SCREEN_ON = 128; // 0x80
    field public static final int FLAG_KEEP_SCREEN_ON = 128; // 0x80
    field public static final int FLAG_LAYOUT_ATTACHED_IN_DECOR = 1073741824; // 0x40000000
    field public static final int FLAG_LAYOUT_INSET_DECOR = 65536; // 0x10000
    field public static final int FLAG_LAYOUT_INSET_DECOR = 65536; // 0x10000
    field public static final int FLAG_LAYOUT_IN_OVERSCAN = 33554432; // 0x2000000
    field public static final int FLAG_LAYOUT_IN_OVERSCAN = 33554432; // 0x2000000
    field public static final int FLAG_LAYOUT_IN_SCREEN = 256; // 0x100
    field public static final int FLAG_LAYOUT_IN_SCREEN = 256; // 0x100
@@ -38167,6 +38168,7 @@ package android.widget {
    method public int getSoftInputMode();
    method public int getSoftInputMode();
    method public int getWidth();
    method public int getWidth();
    method public boolean isAboveAnchor();
    method public boolean isAboveAnchor();
    method public boolean isAttachedInDecor();
    method public boolean isClippingEnabled();
    method public boolean isClippingEnabled();
    method public boolean isFocusable();
    method public boolean isFocusable();
    method public boolean isOutsideTouchable();
    method public boolean isOutsideTouchable();
@@ -38174,6 +38176,7 @@ package android.widget {
    method public boolean isSplitTouchEnabled();
    method public boolean isSplitTouchEnabled();
    method public boolean isTouchable();
    method public boolean isTouchable();
    method public void setAnimationStyle(int);
    method public void setAnimationStyle(int);
    method public void setAttachedInDecor(boolean);
    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
    method public void setClippingEnabled(boolean);
    method public void setClippingEnabled(boolean);
    method public void setContentView(android.view.View);
    method public void setContentView(android.view.View);
+9 −4
Original line number Original line Diff line number Diff line
@@ -24,7 +24,6 @@ import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.media.session.MediaController;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder;
@@ -801,9 +800,6 @@ public abstract class Window {
    public void setFlags(int flags, int mask) {
    public void setFlags(int flags, int mask) {
        final WindowManager.LayoutParams attrs = getAttributes();
        final WindowManager.LayoutParams attrs = getAttributes();
        attrs.flags = (attrs.flags&~mask) | (flags&mask);
        attrs.flags = (attrs.flags&~mask) | (flags&mask);
        if ((mask&WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0) {
            attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
        }
        mForcedWindowFlags |= mask;
        mForcedWindowFlags |= mask;
        dispatchWindowAttributesChanged(attrs);
        dispatchWindowAttributesChanged(attrs);
    }
    }
@@ -814,6 +810,15 @@ public abstract class Window {
        dispatchWindowAttributesChanged(attrs);
        dispatchWindowAttributesChanged(attrs);
    }
    }


    /**
     * {@hide}
     */
    protected void setNeedsMenuKey(int value) {
        final WindowManager.LayoutParams attrs = getAttributes();
        attrs.needsMenuKey = value;
        dispatchWindowAttributesChanged(attrs);
    }

    /**
    /**
     * {@hide}
     * {@hide}
     */
     */
+65 −31
Original line number Original line Diff line number Diff line
@@ -878,9 +878,6 @@ public interface WindowManager extends ViewManager {
         */
         */
        public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
        public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;


        // ----- HIDDEN FLAGS.
        // These start at the high bit and go down.

        /**
        /**
         * Flag for a window in local focus mode.
         * Flag for a window in local focus mode.
         * Window in local focus mode can control focus independent of window manager using
         * Window in local focus mode can control focus independent of window manager using
@@ -903,17 +900,12 @@ public interface WindowManager extends ViewManager {
        public static final int FLAG_SLIPPERY = 0x20000000;
        public static final int FLAG_SLIPPERY = 0x20000000;


        /**
        /**
         * Flag for a window belonging to an activity that responds to {@link KeyEvent#KEYCODE_MENU}
         * Window flag: When requesting layout with an attached window, the attached window may
         * and therefore needs a Menu key. For devices where Menu is a physical button this flag is
         * overlap with the screen decorations of the parent window such as the navigation bar. By
         * ignored, but on devices where the Menu key is drawn in software it may be hidden unless
         * including this flag, the window manager will layout the attached window within the decor
         * this flag is set.
         * frame of the parent window such that it doesn't overlap with screen decorations.
         *
         * (Note that Action Bars, when available, are the preferred way to offer additional
         * functions otherwise accessed via an options menu.)
         *
         * {@hide}
         */
         */
        public static final int FLAG_NEEDS_MENU_KEY = 0x40000000;
        public static final int FLAG_LAYOUT_ATTACHED_IN_DECOR = 0x40000000;


        /**
        /**
         * Flag indicating that this Window is responsible for drawing the background for the
         * Flag indicating that this Window is responsible for drawing the background for the
@@ -1056,16 +1048,6 @@ public interface WindowManager extends ViewManager {
         */
         */
        public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;
        public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;


        /**
         * This is set for a window that has explicitly specified its
         * FLAG_NEEDS_MENU_KEY, so we know the value on this window is the
         * appropriate one to use.  If this is not set, we should look at
         * windows behind it to determine the appropriate value.
         *
         * @hide
         */
        public static final int PRIVATE_FLAG_SET_NEEDS_MENU_KEY = 0x00000008;

        /** In a multiuser system if this flag is set and the owner is a system process then this
        /** In a multiuser system if this flag is set and the owner is a system process then this
         * window will appear on all user screens. This overrides the default behavior of window
         * window will appear on all user screens. This overrides the default behavior of window
         * types that normally only appear on the owning user's screen. Refer to each window type
         * types that normally only appear on the owning user's screen. Refer to each window type
@@ -1112,6 +1094,45 @@ public interface WindowManager extends ViewManager {
         */
         */
        public int privateFlags;
        public int privateFlags;


        /**
         * Value for {@link #needsMenuKey} for a window that has not explicitly specified if it
         * needs {@link #NEEDS_MENU_SET_TRUE} or doesn't need {@link #NEEDS_MENU_SET_FALSE} a menu
         * key. For this case, we should look at windows behind it to determine the appropriate
         * value.
         *
         * @hide
         */
        public static final int NEEDS_MENU_UNSET = 0;

        /**
         * Value for {@link #needsMenuKey} for a window that has explicitly specified it needs a
         * menu key.
         *
         * @hide
         */
        public static final int NEEDS_MENU_SET_TRUE = 1;

        /**
         * Value for {@link #needsMenuKey} for a window that has explicitly specified it doesn't
         * needs a menu key.
         *
         * @hide
         */
        public static final int NEEDS_MENU_SET_FALSE = 2;

        /**
         * State variable for a window belonging to an activity that responds to
         * {@link KeyEvent#KEYCODE_MENU} and therefore needs a Menu key. For devices where Menu is a
         * physical button this variable is ignored, but on devices where the Menu key is drawn in
         * software it may be hidden unless this variable is set to {@link #NEEDS_MENU_SET_TRUE}.
         *
         *  (Note that Action Bars, when available, are the preferred way to offer additional
         * functions otherwise accessed via an options menu.)
         *
         * {@hide}
         */
        public int needsMenuKey = NEEDS_MENU_UNSET;

        /**
        /**
         * Given a particular set of window manager flags, determine whether
         * Given a particular set of window manager flags, determine whether
         * such a window may be a target for an input method when it has
         * such a window may be a target for an input method when it has
@@ -1587,6 +1608,7 @@ public interface WindowManager extends ViewManager {
            out.writeInt(surfaceInsets.top);
            out.writeInt(surfaceInsets.top);
            out.writeInt(surfaceInsets.right);
            out.writeInt(surfaceInsets.right);
            out.writeInt(surfaceInsets.bottom);
            out.writeInt(surfaceInsets.bottom);
            out.writeInt(needsMenuKey);
        }
        }


        public static final Parcelable.Creator<LayoutParams> CREATOR
        public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1634,6 +1656,7 @@ public interface WindowManager extends ViewManager {
            surfaceInsets.top = in.readInt();
            surfaceInsets.top = in.readInt();
            surfaceInsets.right = in.readInt();
            surfaceInsets.right = in.readInt();
            surfaceInsets.bottom = in.readInt();
            surfaceInsets.bottom = in.readInt();
            needsMenuKey = in.readInt();
        }
        }


        @SuppressWarnings({"PointlessBitwiseExpression"})
        @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1669,6 +1692,8 @@ public interface WindowManager extends ViewManager {
        /** {@hide} */
        /** {@hide} */
        public static final int PREFERRED_REFRESH_RATE_CHANGED = 1 << 21;
        public static final int PREFERRED_REFRESH_RATE_CHANGED = 1 << 21;
        /** {@hide} */
        /** {@hide} */
        public static final int NEEDS_MENU_KEY_CHANGED = 1 << 22;
        /** {@hide} */
        public static final int EVERYTHING_CHANGED = 0xffffffff;
        public static final int EVERYTHING_CHANGED = 0xffffffff;


        // internal buffer to backup/restore parameters under compatibility mode.
        // internal buffer to backup/restore parameters under compatibility mode.
@@ -1813,6 +1838,11 @@ public interface WindowManager extends ViewManager {
                changes |= SURFACE_INSETS_CHANGED;
                changes |= SURFACE_INSETS_CHANGED;
            }
            }


            if (needsMenuKey != o.needsMenuKey) {
                needsMenuKey = o.needsMenuKey;
                changes |= NEEDS_MENU_KEY_CHANGED;
            }

            return changes;
            return changes;
        }
        }


@@ -1919,6 +1949,10 @@ public interface WindowManager extends ViewManager {
            if (!surfaceInsets.equals(Insets.NONE)) {
            if (!surfaceInsets.equals(Insets.NONE)) {
                sb.append(" surfaceInsets=").append(surfaceInsets);
                sb.append(" surfaceInsets=").append(surfaceInsets);
            }
            }
            if (needsMenuKey != NEEDS_MENU_UNSET) {
                sb.append(" needsMenuKey=");
                sb.append(needsMenuKey);
            }
            sb.append('}');
            sb.append('}');
            return sb.toString();
            return sb.toString();
        }
        }
+56 −7
Original line number Original line Diff line number Diff line
@@ -97,6 +97,8 @@ public class PopupWindow {
    private boolean mAllowScrollingAnchorParent = true;
    private boolean mAllowScrollingAnchorParent = true;
    private boolean mLayoutInsetDecor = false;
    private boolean mLayoutInsetDecor = false;
    private boolean mNotTouchModal;
    private boolean mNotTouchModal;
    private boolean mAttachedInDecor = true;
    private boolean mAttachedInDecorSet = false;


    private OnTouchListener mTouchInterceptor;
    private OnTouchListener mTouchInterceptor;


@@ -316,6 +318,7 @@ public class PopupWindow {
            mContext = contentView.getContext();
            mContext = contentView.getContext();
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        }

        setContentView(contentView);
        setContentView(contentView);
        setWidth(width);
        setWidth(width);
        setHeight(height);
        setHeight(height);
@@ -375,7 +378,7 @@ public class PopupWindow {
    }
    }


    /**
    /**
     * Set the flag on popup to ignore cheek press eventt; by default this flag
     * Set the flag on popup to ignore cheek press event; by default this flag
     * is set to false
     * is set to false
     * which means the pop wont ignore cheek press dispatch events.
     * which means the pop wont ignore cheek press dispatch events.
     *
     *
@@ -443,6 +446,19 @@ public class PopupWindow {
        if (mWindowManager == null && mContentView != null) {
        if (mWindowManager == null && mContentView != null) {
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        }

        // Setting the default for attachedInDecor based on SDK version here
        // instead of in the constructor since we might not have the context
        // object in the constructor. We only want to set default here if the
        // app hasn't already set the attachedInDecor.
        if (mContext != null && !mAttachedInDecorSet) {
            // Attach popup window in decor frame of parent window by default for
            // {@link Build.VERSION_CODES.LOLLIPOP_MR1} or greater. Keep current
            // behavior of not attaching to decor frame for older SDKs.
            setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
                    >= Build.VERSION_CODES.LOLLIPOP_MR1);
        }

    }
    }


    /**
    /**
@@ -701,6 +717,36 @@ public class PopupWindow {
        mLayoutInScreen = enabled;
        mLayoutInScreen = enabled;
    }
    }


    /**
     * <p>Indicates whether the popup window will be attached in the decor frame of its parent
     * window.
     *
     * @return true if the window will be attached to the decor frame of its parent window.
     *
     * @see #setAttachedInDecor(boolean)
     * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
     */
    public boolean isAttachedInDecor() {
        return mAttachedInDecor;
    }

    /**
     * <p>This will attach the popup window to the decor frame of the parent window to avoid
     * overlaping with screen decorations like the navigation bar. Overrides the default behavior of
     * the flag {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR}.
     *
     * <p>By default the flag is set on SDK version {@link Build.VERSION_CODES#LOLLIPOP_MR1} or
     * greater and cleared on lesser SDK versions.
     *
     * @param enabled true if the popup should be attached to the decor frame of its parent window.
     *
     * @see WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR
     */
    public void setAttachedInDecor(boolean enabled) {
        mAttachedInDecor = enabled;
        mAttachedInDecorSet = true;
    }

    /**
    /**
     * Allows the popup window to force the flag
     * Allows the popup window to force the flag
     * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
     * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
@@ -1140,6 +1186,9 @@ public class PopupWindow {
        if (mNotTouchModal) {
        if (mNotTouchModal) {
            curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
            curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        }
        }
        if (mAttachedInDecor) {
          curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
        }
        return curFlags;
        return curFlags;
    }
    }


+2 −2
Original line number Original line Diff line number Diff line
@@ -3342,9 +3342,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);


        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
        } else {
        } else {
            clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
        }
        }


        // Non-floating windows on high end devices must put up decor beneath the system bars and
        // Non-floating windows on high end devices must put up decor beneath the system bars and
Loading