Listen for changes to IME and show/hide nav bar

This feature is configurable and set to false as the default value. It
is overlayed as true for Automotive.

Bug: 138242566
Test: manual
Change-Id: I5dde8adc9d4ce749d895287ccf63017b284caec8
Merged-In: I5dde8adc9d4ce749d895287ccf63017b284caec8
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0513fee..356b344 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.app.Dialog;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.IBinder;
@@ -50,6 +51,7 @@
     final int mWindowType;
     final int mGravity;
     final boolean mTakesFocus;
+    final boolean mAutomotiveHideNavBarForKeyboard;
     private final Rect mBounds = new Rect();
 
     @Retention(SOURCE)
@@ -134,6 +136,8 @@
         mWindowType = windowType;
         mGravity = gravity;
         mTakesFocus = takesFocus;
+        mAutomotiveHideNavBarForKeyboard = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
         initDockWindow();
     }
 
@@ -247,6 +251,11 @@
             windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
         }
 
+        if (isAutomotive() && mAutomotiveHideNavBarForKeyboard) {
+            windowSetFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+            windowModFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+        }
+
         getWindow().setFlags(windowSetFlags, windowModFlags);
     }
 
@@ -338,6 +347,10 @@
         mWindowState = newState;
     }
 
+    private boolean isAutomotive() {
+        return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
     private static String stateToString(@SoftInputWindowState int state) {
         switch (state) {
             case SoftInputWindowState.TOKEN_PENDING:
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1577a22..bfcc8d4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4255,4 +4255,9 @@
     <!-- The list of packages to automatically opt out of refresh rates higher than 60hz because
          of known compatibility issues. -->
     <string-array name="config_highRefreshRateBlacklist"></string-array>
+
+    <!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
+         create additional screen real estate outside beyond the keyboard. Note that the user needs
+         to have a confirmed way to dismiss the keyboard when desired. -->
+    <bool name="config_automotiveHideNavBarForKeyboard">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0d9c4b3..f06b17e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3835,4 +3835,5 @@
   <java-symbol type="drawable" name="android_logotype" />
   <java-symbol type="layout" name="platlogo_layout" />
 
+  <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
 </resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 146bfe0..da42d4f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -30,7 +30,10 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
 import android.util.Log;
+import android.view.Display;
 import android.view.GestureDetector;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -85,8 +88,7 @@
 /**
  * A status bar (and navigation bar) tailored for the automotive use case.
  */
-public class CarStatusBar extends StatusBar implements
-        CarBatteryController.BatteryViewHandler {
+public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
     private static final String TAG = "CarStatusBar";
     // used to calculate how fast to open or close the window
     private static final float DEFAULT_FLING_VELOCITY = 0;
@@ -167,6 +169,9 @@
     private boolean mIsSwipingVerticallyToClose;
     // Whether heads-up notifications should be shown when shade is open.
     private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+    // If the nav bar should be hidden when the soft keyboard is visible.
+    private boolean mHideNavBarForKeyboard;
+    private boolean mBottomNavBarVisible;
 
     private final CarPowerStateListener mCarPowerStateListener =
             (int state) -> {
@@ -188,6 +193,12 @@
         // builds the nav bar
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+
+        // Keyboard related setup, before nav bars are created.
+        mHideNavBarForKeyboard = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+        mBottomNavBarVisible = false;
+
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -718,6 +729,13 @@
         buildNavBarContent();
         attachNavBarWindows();
 
+        // Try setting up the initial state of the nav bar if applicable.
+        if (result != null) {
+            setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
+                    result.mImeWindowVis, result.mImeBackDisposition,
+                    result.mShowImeSwitcher);
+        }
+
         // There has been a car customized nav bar on the default display, so just create nav bars
         // on external displays.
         mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
@@ -756,22 +774,33 @@
 
     }
 
-    private void attachNavBarWindows() {
-
-        if (mShowBottom) {
-            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                    PixelFormat.TRANSLUCENT);
-            lp.setTitle("CarNavigationBar");
-            lp.windowAnimations = 0;
-            mWindowManager.addView(mNavigationBarWindow, lp);
+    /**
+     * We register for soft keyboard visibility events such that we can hide the navigation bar
+     * giving more screen space to the IME. Note: this is optional and controlled by
+     * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
+     */
+    @Override
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher) {
+        if (!mHideNavBarForKeyboard) {
+            return;
         }
 
+        if (mContext.getDisplay().getDisplayId() != displayId) {
+            return;
+        }
+
+        boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+        if (!isKeyboardVisible) {
+            attachBottomNavBarWindow();
+        } else {
+            detachBottomNavBarWindow();
+        }
+    }
+
+    private void attachNavBarWindows() {
+        attachBottomNavBarWindow();
+
         if (mShowLeft) {
             int width = mContext.getResources().getDimensionPixelSize(
                     R.dimen.car_left_navigation_bar_width);
@@ -806,7 +835,49 @@
             rightlp.gravity = Gravity.RIGHT;
             mWindowManager.addView(mRightNavigationBarWindow, rightlp);
         }
+    }
 
+    /**
+     * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
+     * attaching the bottom nav bar.
+     */
+    protected void attachBottomNavBarWindow() {
+        if (!mShowBottom) {
+            return;
+        }
+
+        if (mBottomNavBarVisible) {
+            return;
+        }
+        mBottomNavBarVisible = true;
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        lp.setTitle("CarNavigationBar");
+        lp.windowAnimations = 0;
+        mWindowManager.addView(mNavigationBarWindow, lp);
+    }
+
+    /**
+     * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
+     * detaching the bottom nav bar.
+     */
+    protected void detachBottomNavBarWindow() {
+        if (!mShowBottom) {
+            return;
+        }
+
+        if (!mBottomNavBarVisible) {
+            return;
+        }
+        mBottomNavBarVisible = false;
+        mWindowManager.removeView(mNavigationBarWindow);
     }
 
     private void buildBottomBar(int layout) {