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
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 75c9ebd..3f84c9b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -878,9 +878,6 @@
          */
         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.
          * Window in local focus mode can control focus independent of window manager using
@@ -903,17 +900,12 @@
         public static final int FLAG_SLIPPERY = 0x20000000;
 
         /**
-         * Flag 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 flag is
-         * ignored, but on devices where the Menu key is drawn in software it may be hidden unless
-         * this flag is set.
-         *
-         * (Note that Action Bars, when available, are the preferred way to offer additional
-         * functions otherwise accessed via an options menu.)
-         *
-         * {@hide}
+         * Window flag: When requesting layout with an attached window, the attached window may
+         * overlap with the screen decorations of the parent window such as the navigation bar. By
+         * including this flag, the window manager will layout the attached window within the decor
+         * frame of the parent window such that it doesn't overlap with screen decorations.
          */
-        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
@@ -1056,16 +1048,6 @@
          */
         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
          * 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
@@ -1113,6 +1095,45 @@
         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
          * such a window may be a target for an input method when it has
          * focus.  In particular, this checks the
@@ -1120,9 +1141,9 @@
          * flags and returns true if the combination of the two corresponds
          * to a window that needs to be behind the input method so that the
          * user can type into it.
-         * 
+         *
          * @param flags The current window manager flags.
-         * 
+         *
          * @return Returns true if such a window should be behind/interact
          * with an input method, false if not.
          */
@@ -1587,14 +1608,15 @@
             out.writeInt(surfaceInsets.top);
             out.writeInt(surfaceInsets.right);
             out.writeInt(surfaceInsets.bottom);
+            out.writeInt(needsMenuKey);
         }
-        
+
         public static final Parcelable.Creator<LayoutParams> CREATOR
                     = new Parcelable.Creator<LayoutParams>() {
             public LayoutParams createFromParcel(Parcel in) {
                 return new LayoutParams(in);
             }
-    
+
             public LayoutParams[] newArray(int size) {
                 return new LayoutParams[size];
             }
@@ -1634,8 +1656,9 @@
             surfaceInsets.top = in.readInt();
             surfaceInsets.right = in.readInt();
             surfaceInsets.bottom = in.readInt();
+            needsMenuKey = in.readInt();
         }
-    
+
         @SuppressWarnings({"PointlessBitwiseExpression"})
         public static final int LAYOUT_CHANGED = 1<<0;
         public static final int TYPE_CHANGED = 1<<1;
@@ -1669,14 +1692,16 @@
         /** {@hide} */
         public static final int PREFERRED_REFRESH_RATE_CHANGED = 1 << 21;
         /** {@hide} */
+        public static final int NEEDS_MENU_KEY_CHANGED = 1 << 22;
+        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
         private int[] mCompatibilityParamsBackup = null;
-        
+
         public final int copyFrom(LayoutParams o) {
             int changes = 0;
-    
+
             if (width != o.width) {
                 width = o.width;
                 changes |= LAYOUT_CHANGED;
@@ -1813,9 +1838,14 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
+            if (needsMenuKey != o.needsMenuKey) {
+                needsMenuKey = o.needsMenuKey;
+                changes |= NEEDS_MENU_KEY_CHANGED;
+            }
+
             return changes;
         }
-    
+
         @Override
         public String debug(String output) {
             output += "Contents of " + this + ":";
@@ -1919,6 +1949,10 @@
             if (!surfaceInsets.equals(Insets.NONE)) {
                 sb.append(" surfaceInsets=").append(surfaceInsets);
             }
+            if (needsMenuKey != NEEDS_MENU_UNSET) {
+                sb.append(" needsMenuKey=");
+                sb.append(needsMenuKey);
+            }
             sb.append('}');
             return sb.toString();
         }