On-screen navigation bar (separate from the status bar).

In Honeycomb we introduced navigation controls in the status
bar, for xlarge devices without physical buttons. What about
phones? The status bar is pretty cramped already, and
besides, it's at the top of the display most of the time,
not at the bottom where your thumb is likely to be.

Enter the navigation bar. It's a new window type that
appears atop almost everything (including the keyguard); the
window manager subtracts its rectangle from the default
visible rectangle of other windows (including the status bar
and notification shade).

However, it behaves (on phones) like the status bar in that
applications that request fullscreen windows can get access
to those pixels. Well, almost; they need cooperation from
the navigation bar implementation to make the navbar
disappear, just like the status bar.

The current SystemUI implementation of the navigation bar on
phones is still rough, but it has the basics:

 + back, home, and menu keys (NB: we're showing menu all the
   time right now because checking the api level of the
   package owning the top window is currently a poor
   indicator of whether the app requires the menu key)
 + it tries to stick to the same physical end of the device,
   regardless of device orientation (on a phone, this is
   the strip of land closest to the microphone)

Change-Id: Ic613a3351220af0bbfbdef63e1d99cbefd5ed1c2
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8a18aaf..9395d5c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -388,6 +388,12 @@
         public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
 
         /**
+         * Window type: Navigation bar (when distinct from status bar)
+         * @hide
+         */
+        public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
new file mode 100644
index 0000000..eba4480
--- /dev/null
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.phone.NavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    >
+
+    <LinearLayout android:id="@+id/rot0"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:paddingLeft="8dip"
+        android:paddingRight="8dip"
+        android:background="#FF000000"
+        android:orientation="horizontal"
+        >
+
+        <!-- navigation controls -->
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_sysbar_back"
+            systemui:keyCode="4"
+            android:layout_weight="1"
+            />
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_sysbar_home"
+            systemui:keyCode="3"
+            android:layout_weight="1"
+            />
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_sysbar_menu"
+            systemui:keyCode="82"
+            android:layout_weight="1"
+            />
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/rot90"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:background="#FF000000"
+        android:orientation="vertical"
+        android:visibility="gone"
+        >
+
+        <!-- navigation controls -->
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_sysbar_menu"
+            systemui:keyCode="82"
+            android:layout_weight="1"
+            />
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_sysbar_home"
+            systemui:keyCode="3"
+            android:layout_weight="1"
+            />
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_sysbar_back"
+            systemui:keyCode="4"
+            android:layout_weight="1"
+            />
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/rot270"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:background="#FF000000"
+        android:orientation="vertical"
+        android:visibility="gone"
+        >
+
+        <!-- navigation controls -->
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_sysbar_back"
+            systemui:keyCode="4"
+            android:layout_weight="1"
+            />
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_sysbar_home"
+            systemui:keyCode="3"
+            android:layout_weight="1"
+            />
+        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_sysbar_menu"
+            systemui:keyCode="82"
+            android:layout_weight="1"
+            />
+    </LinearLayout>
+</com.android.systemui.statusbar.phone.NavigationBarView>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
new file mode 100644
index 0000000..bcc8da1
--- /dev/null
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+    <!-- thickness (width) of the navigation bar on phones that require it -->
+    <dimen name="navigation_bar_size">42dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 88cd43c..a2577cb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -31,5 +31,7 @@
     <!-- Width of scrollable area in recents -->
     <dimen name="status_bar_recents_width">356dp</dimen>
 
+    <!-- thickness (height) of the navigation bar on phones that require it -->
+    <dimen name="navigation_bar_size">42dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
new file mode 100644
index 0000000..ec169e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Surface;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.content.res.Configuration;
+
+import com.android.systemui.R;
+
+public class NavigationBarView extends LinearLayout {
+    final Display mDisplay;
+    View[] mRotatedViews = new View[4];
+
+    public NavigationBarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mDisplay = ((WindowManager)context.getSystemService(
+                Context.WINDOW_SERVICE)).getDefaultDisplay();
+    }
+
+    public void onFinishInflate() {
+        mRotatedViews[Surface.ROTATION_0] = 
+        mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
+
+        mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
+        
+        mRotatedViews[Surface.ROTATION_270] = findViewById(R.id.rot270);
+    }
+
+    public void reorient() {
+        final int rot = mDisplay.getRotation();
+        for (int i=0; i<4; i++) {
+            mRotatedViews[i].setVisibility(View.GONE);
+        }
+        mRotatedViews[rot].setVisibility(View.VISIBLE);
+
+        android.util.Log.d("NavigationBarView", "reorient(): rot=" + mDisplay.getRotation());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1e46246..97c8aee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -45,6 +45,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup;
@@ -141,6 +142,9 @@
     // for immersive activities
     private View mIntruderAlertView;
 
+    // on-screen navigation buttons
+    private NavigationBarView mNavigationBarView;
+
     // the tracker view
     TrackingView mTrackingView;
     WindowManager.LayoutParams mTrackingParams;
@@ -199,7 +203,9 @@
 
         super.start();
 
-        addIntruderView();
+        addNavigationBar();
+
+        //addIntruderView();
 
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext);
@@ -223,6 +229,9 @@
         mIntruderAlertView.setVisibility(View.GONE);
         mIntruderAlertView.setClickable(true);
 
+        mNavigationBarView = 
+            (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
+
         PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
                 R.layout.status_bar, null);
         sb.mService = this;
@@ -292,6 +301,58 @@
         return res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
+    // For small-screen devices (read: phones) that lack hardware navigation buttons
+    private void addNavigationBar() {
+        mNavigationBarView.reorient();
+        WindowManagerImpl.getDefault().addView(
+                mNavigationBarView, getNavigationBarLayoutParams());
+    }
+
+    private void repositionNavigationBar() {
+        mNavigationBarView.reorient();
+        WindowManagerImpl.getDefault().updateViewLayout(
+                mNavigationBarView, getNavigationBarLayoutParams());
+    }
+
+    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
+        final int rotation = mDisplay.getRotation();
+        final boolean sideways = 
+            (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+
+        final Resources res = mContext.getResources();
+        final int size = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                sideways ? size : ViewGroup.LayoutParams.MATCH_PARENT,
+                sideways ? ViewGroup.LayoutParams.MATCH_PARENT : size,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                    0
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+
+        lp.setTitle("NavigationBar");
+        switch (rotation) {
+            case Surface.ROTATION_90:
+                // device has been turned 90deg counter-clockwise
+                lp.gravity = Gravity.RIGHT | Gravity.FILL_VERTICAL;
+                break;
+            case Surface.ROTATION_270:
+                // device has been turned 90deg clockwise
+                lp.gravity = Gravity.LEFT | Gravity.FILL_VERTICAL;
+                break;
+            default:
+                lp.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
+                break;
+        }
+        lp.windowAnimations = 0;
+
+        return lp;
+    }
+
     private void addIntruderView() {
         final int height = getStatusBarHeight();
 
@@ -1497,6 +1558,7 @@
                 animateCollapse();
             }
             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                repositionNavigationBar();
                 updateResources();
             }
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a37ccc7..8a29419 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -111,6 +111,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.KeyCharacterMap.FallbackAction;
@@ -175,14 +176,16 @@
     // responsible for power management when displayed.
     static final int KEYGUARD_LAYER = 15;
     static final int KEYGUARD_DIALOG_LAYER = 16;
+    // the navigation bar, if available, shows atop most things
+    static final int NAVIGATION_BAR_LAYER = 17;
     // the drag layer: input for drag-and-drop is associated with this window,
     // which sits above all other focusable windows
-    static final int DRAG_LAYER = 17;
+    static final int DRAG_LAYER = 18;
     // things in here CAN NOT take focus, but are shown on top of everything else.
-    static final int SYSTEM_OVERLAY_LAYER = 18;
-    static final int SECURE_SYSTEM_OVERLAY_LAYER = 19;
+    static final int SYSTEM_OVERLAY_LAYER = 19;
+    static final int SECURE_SYSTEM_OVERLAY_LAYER = 20;
     // the (mouse) pointer layer
-    static final int POINTER_LAYER = 20;
+    static final int POINTER_LAYER = 21;
 
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -228,6 +231,8 @@
     WindowState mStatusBar = null;
     boolean mStatusBarCanHide;
     final ArrayList<WindowState> mStatusBarPanels = new ArrayList<WindowState>();
+    WindowState mNavigationBar = null;
+
     WindowState mKeyguard = null;
     KeyguardViewMediator mKeyguardMediator;
     GlobalActions mGlobalActions;
@@ -1037,6 +1042,8 @@
             return DRAG_LAYER;
         case TYPE_POINTER:
             return POINTER_LAYER;
+        case TYPE_NAVIGATION_BAR:
+            return NAVIGATION_BAR_LAYER;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return APPLICATION_LAYER;
@@ -1214,6 +1221,13 @@
                         com.android.internal.R.bool.config_statusBarCanHide);
 
                 break;
+            case TYPE_NAVIGATION_BAR:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                        "PhoneWindowManager");
+                mNavigationBar = win;
+                if (DEBUG_LAYOUT) Log.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
+                break;
             case TYPE_STATUS_BAR_PANEL:
                 mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.STATUS_BAR_SERVICE,
@@ -1240,9 +1254,10 @@
     public void removeWindowLw(WindowState win) {
         if (mStatusBar == win) {
             mStatusBar = null;
-        }
-        else if (mKeyguard == win) {
+        } else if (mKeyguard == win) {
             mKeyguard = null;
+        } else if (mNavigationBar == win) {
+            mNavigationBar = null;
         } else {
             mStatusBarPanels.remove(win);
         }
@@ -1609,17 +1624,48 @@
         mDockBottom = mContentBottom = mCurBottom = displayHeight;
         mDockLayer = 0x10000000;
 
+        // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
+        final Rect pf = mTmpParentFrame;
+        final Rect df = mTmpDisplayFrame;
+        final Rect vf = mTmpVisibleFrame;
+        pf.left = df.left = vf.left = mDockLeft;
+        pf.top = df.top = vf.top = mDockTop;
+        pf.right = df.right = vf.right = mDockRight;
+        pf.bottom = df.bottom = vf.bottom = mDockBottom;
+
         // decide where the status bar goes ahead of time
         if (mStatusBar != null) {
-            final Rect pf = mTmpParentFrame;
-            final Rect df = mTmpDisplayFrame;
-            final Rect vf = mTmpVisibleFrame;
-            pf.left = df.left = vf.left = 0;
-            pf.top = df.top = vf.top = 0;
-            pf.right = df.right = vf.right = displayWidth;
-            pf.bottom = df.bottom = vf.bottom = displayHeight;
-            
+            Rect navr = null;
+            if (mNavigationBar != null) {
+                mNavigationBar.computeFrameLw(pf, df, vf, vf);
+                if (mNavigationBar.isVisibleLw()) {
+                    navr = mNavigationBar.getFrameLw();
+
+                    if (navr.top == 0) {
+                        // Navigation bar is vertical
+                        if (mDockLeft == navr.left) {
+                            mDockLeft = navr.right;
+                        } else if (mDockRight == navr.right) {
+                            mDockRight = navr.left;
+                        }
+                    } else {
+                        // Navigation bar horizontal, at bottom
+                        if (mDockBottom == navr.bottom) {
+                            mDockBottom = navr.top;
+                        }
+                    }
+                }
+            }
+            if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + navr);
+
+            // apply navigation bar insets
+            pf.left = df.left = vf.left = mDockLeft;
+            pf.top = df.top = vf.top = mDockTop;
+            pf.right = df.right = vf.right = mDockRight;
+            pf.bottom = df.bottom = vf.bottom = mDockBottom;
+
             mStatusBar.computeFrameLw(pf, df, vf, vf);
+
             if (mStatusBar.isVisibleLw()) {
                 // If the status bar is hidden, we don't want to cause
                 // windows behind it to scroll.
@@ -1630,14 +1676,18 @@
                     // status bar is visible.
                     if (mDockTop == r.top) mDockTop = r.bottom;
                     else if (mDockBottom == r.bottom) mDockBottom = r.top;
+                    
                     mContentTop = mCurTop = mDockTop;
                     mContentBottom = mCurBottom = mDockBottom;
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mDockTop=" + mDockTop
-                            + " mContentTop=" + mContentTop
-                            + " mCurTop=" + mCurTop
-                            + " mDockBottom=" + mDockBottom
-                            + " mContentBottom=" + mContentBottom
-                            + " mCurBottom=" + mCurBottom);
+                    mContentLeft = mCurLeft = mDockLeft;
+                    mContentRight = mCurRight = mDockRight;
+
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: " +
+                        String.format(
+                            "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
+                            mDockLeft, mDockTop, mDockRight, mDockBottom,
+                            mContentLeft, mContentTop, mContentRight, mContentBottom,
+                            mCurLeft, mCurTop, mCurRight, mCurBottom));
                 } else {
                     // Status bar can't go away; the part of the screen it
                     // covers does not exist for anything behind it.
@@ -1647,12 +1697,32 @@
                     } else if ((mRestrictedScreenHeight-mRestrictedScreenTop) == r.bottom) {
                         mRestrictedScreenHeight -= (r.bottom-r.top);
                     }
+
+                    if (navr != null) {
+                        if (navr.top == 0) {
+                            // Navigation bar is vertical
+                            if (mRestrictedScreenLeft == navr.left) {
+                                mRestrictedScreenLeft = navr.right;
+                                mRestrictedScreenWidth -= (navr.right - navr.left);
+                            } else if ((mRestrictedScreenLeft+mRestrictedScreenWidth) == navr.right) {
+                                mRestrictedScreenWidth -= (navr.right - navr.left);
+                            }
+                        } else {
+                            // Navigation bar horizontal, at bottom
+                            if ((mRestrictedScreenHeight-mRestrictedScreenTop) == r.bottom) {
+                                mRestrictedScreenHeight -= (navr.bottom-navr.top);
+                            }
+                        }
+                    }
+
                     mContentTop = mCurTop = mDockTop = mRestrictedScreenTop;
                     mContentBottom = mCurBottom = mDockBottom
                             = mRestrictedScreenTop + mRestrictedScreenHeight;
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mRestrictedScreenTop="
-                            + mRestrictedScreenTop
-                            + " mRestrictedScreenHeight=" + mRestrictedScreenHeight);
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: restricted screen area: ("
+                            + mRestrictedScreenLeft + ","
+                            + mRestrictedScreenTop + ","
+                            + (mRestrictedScreenLeft + mRestrictedScreenWidth) + ","
+                            + (mRestrictedScreenTop + mRestrictedScreenHeight) + ")");
                 }
             }
         }
@@ -1722,6 +1792,8 @@
         final Rect cf = mTmpContentFrame;
         final Rect vf = mTmpVisibleFrame;
         
+        final boolean hasNavBar = (mNavigationBar != null && mNavigationBar.isVisibleLw());
+
         if (attrs.type == TYPE_INPUT_METHOD) {
             pf.left = df.left = cf.left = vf.left = mDockLeft;
             pf.top = df.top = cf.top = vf.top = mDockTop;
@@ -1735,6 +1807,9 @@
 
             if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
                     == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+                if (DEBUG_LAYOUT)
+                    Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() 
+                            + "): IN_SCREEN, INSET_DECOR, !FULLSCREEN");
                 // This is the case for a normal activity window: we want it
                 // to cover all of the screen space, and it can take care of
                 // moving its contents to account for screen decorations that
@@ -1744,15 +1819,26 @@
                     // frame is the same as the one we are attached to.
                     setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
                 } else {
-                    if (attrs.type == TYPE_STATUS_BAR_PANEL) {
+                    if (attrs.type == TYPE_STATUS_BAR_PANEL
+                            || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
                         // Status bar panels are the only windows who can go on top of
                         // the status bar.  They are protected by the STATUS_BAR_SERVICE
                         // permission, so they have the same privileges as the status
                         // bar itself.
-                        pf.left = df.left = mUnrestrictedScreenLeft;
+                        //
+                        // However, they should still dodge the navigation bar if it exists. A
+                        // straightforward way to do this is to only allow the status bar panels to
+                        // extend to the extrema of the allowable region for the IME dock.
+
+                        pf.left = df.left = hasNavBar ? mDockLeft : mUnrestrictedScreenLeft;
                         pf.top = df.top = mUnrestrictedScreenTop;
-                        pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
-                        pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                        pf.right = df.right = hasNavBar
+                                            ? mDockRight
+                                            : mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                        pf.bottom = df.bottom = hasNavBar
+                                              ? mDockBottom
+                                              : mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+
                     } else {
                         pf.left = df.left = mRestrictedScreenLeft;
                         pf.top = df.top = mRestrictedScreenTop;
@@ -1780,15 +1866,21 @@
                     }
                 }
             } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) {
+                if (DEBUG_LAYOUT)
+                    Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN");
                 // A window that has requested to fill the entire screen just
                 // gets everything, period.
-                if (attrs.type == TYPE_STATUS_BAR_PANEL) {
-                    pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
+                if (attrs.type == TYPE_STATUS_BAR_PANEL
+                        || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
+                    pf.left = df.left = cf.left = hasNavBar ? mDockLeft : mUnrestrictedScreenLeft;
                     pf.top = df.top = cf.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = cf.right
-                            = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = cf.bottom
-                            = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                    pf.right = df.right = cf.right = hasNavBar
+                                        ? mDockRight
+                                        : mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                    pf.bottom = df.bottom = cf.bottom = hasNavBar
+                                          ? mDockBottom
+                                          : mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+
                 } else {
                     pf.left = df.left = cf.left = mRestrictedScreenLeft;
                     pf.top = df.top = cf.top = mRestrictedScreenTop;
@@ -1805,10 +1897,14 @@
                     vf.set(cf);
                 }
             } else if (attached != null) {
+                if (DEBUG_LAYOUT)
+                    Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): attached to " + attached);
                 // A child window should be placed inside of the same visible
                 // frame that its parent had.
                 setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, cf, vf);
             } else {
+                if (DEBUG_LAYOUT)
+                    Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): normal window");
                 // Otherwise, a normal window must be placed inside the content
                 // of all screen decorations.
                 pf.left = mContentLeft;
@@ -1844,6 +1940,8 @@
 
         if (DEBUG_LAYOUT) Log.v(TAG, "Compute frame " + attrs.getTitle()
                 + ": sim=#" + Integer.toHexString(sim)
+                + " attach=" + attached + " type=" + attrs.type 
+                + String.format(" flags=0x%08x", fl)
                 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                 + " cf=" + cf.toShortString() + " vf=" + vf.toShortString());
         
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index f04fb02..208353d 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -119,6 +119,7 @@
         TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16,
         TYPE_STATUS_BAR_SUB_PANEL  = FIRST_SYSTEM_WINDOW+17,
         TYPE_POINTER            = FIRST_SYSTEM_WINDOW+18,
+        TYPE_NAVIGATION_BAR     = FIRST_SYSTEM_WINDOW+19,
         LAST_SYSTEM_WINDOW      = 2999,
     };