Merge "Add the IME insets to virtual display for bubble" into rvc-dev
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 1098fa1..635ed13 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -29,6 +29,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.VirtualDisplay;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.AttributeSet;
@@ -444,6 +445,14 @@
     }
 
     /**
+     * @hide
+     * @return virtual display.
+     */
+    public VirtualDisplay getVirtualDisplay() {
+        return mTaskEmbedder.getVirtualDisplay();
+    }
+
+    /**
      * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
      * virtual display.
      */
diff --git a/core/java/android/window/TaskEmbedder.java b/core/java/android/window/TaskEmbedder.java
index 2ead37a..4257ce0 100644
--- a/core/java/android/window/TaskEmbedder.java
+++ b/core/java/android/window/TaskEmbedder.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
-import android.app.ActivityView;
 import android.app.IActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -36,9 +35,8 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.RemoteException;
+import android.hardware.display.VirtualDisplay;
 import android.os.UserHandle;
-import android.util.Log;
 import android.view.IWindow;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
@@ -256,6 +254,10 @@
         return INVALID_DISPLAY;
     }
 
+    public VirtualDisplay getVirtualDisplay() {
+        return null;
+    }
+
     /**
      * Set forwarded insets on the task content.
      *
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index 1c0598b..6f85dc2 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -251,6 +251,14 @@
         return INVALID_DISPLAY;
     }
 
+    @Override
+    public VirtualDisplay getVirtualDisplay() {
+        if (isInitialized()) {
+            return mVirtualDisplay;
+        }
+        return null;
+    }
+
     /**
      * Check if container is ready to launch and create {@link ActivityOptions} to target the
      * virtual display.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index e367927..b3c2c6d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -19,9 +19,15 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.graphics.PixelFormat.TRANSPARENT;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InsetsState.ITYPE_IME;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.ViewRootImpl.sNewInsetsMode;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -42,9 +48,12 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
+import android.hardware.display.VirtualDisplay;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -62,6 +71,7 @@
  */
 public class BubbleExpandedView extends LinearLayout {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
+    private static final String WINDOW_TITLE = "ImeInsetsWindowWithoutContent";
 
     private enum ActivityViewStatus {
         // ActivityView is being initialized, cannot start an activity yet.
@@ -107,6 +117,9 @@
     private WindowManager mWindowManager;
 
     private BubbleStackView mStackView;
+    private View mVirtualImeView;
+    private WindowManager mVirtualDisplayWindowManager;
+    private boolean mImeShowing = false;
 
     private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
         @Override
@@ -317,11 +330,8 @@
         mKeyboardVisible = false;
         mNeedsNewHeight = false;
         if (mActivityView != null) {
-            // TODO: Temporary hack to offset the view until we can properly inset Bubbles again.
             if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
-                mStackView.animate()
-                        .setDuration(100)
-                        .translationY(0);
+                setImeWindowToDisplay(0, 0);
             } else {
                 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
             }
@@ -365,18 +375,61 @@
                             : 0);
             final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
 
-            // TODO: Temporary hack to offset the view until we can properly inset Bubbles again.
             if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
-                mStackView.animate()
-                        .setDuration(100)
-                        .translationY(-insetsBottom)
-                        .withEndAction(() -> mActivityView.onLocationChanged());
+                setImeWindowToDisplay(getWidth(), insetsBottom);
             } else {
                 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
             }
         }
     }
 
+    private void setImeWindowToDisplay(int w, int h) {
+        if (getVirtualDisplayId() == INVALID_DISPLAY) {
+            return;
+        }
+        if (h == 0 || w == 0) {
+            if (mImeShowing) {
+                mVirtualImeView.setVisibility(GONE);
+                mImeShowing = false;
+            }
+            return;
+        }
+        final Context virtualDisplayContext = mContext.createDisplayContext(
+                getVirtualDisplay().getDisplay());
+
+        if (mVirtualDisplayWindowManager == null) {
+            mVirtualDisplayWindowManager =
+                    (WindowManager) virtualDisplayContext.getSystemService(Context.WINDOW_SERVICE);
+        }
+        if (mVirtualImeView == null) {
+            mVirtualImeView = new View(virtualDisplayContext);
+            mVirtualImeView.setVisibility(VISIBLE);
+            mVirtualDisplayWindowManager.addView(mVirtualImeView,
+                    getVirtualImeViewAttrs(w, h));
+        } else {
+            mVirtualDisplayWindowManager.updateViewLayout(mVirtualImeView,
+                    getVirtualImeViewAttrs(w, h));
+            mVirtualImeView.setVisibility(VISIBLE);
+        }
+
+        mImeShowing = true;
+    }
+
+    private WindowManager.LayoutParams getVirtualImeViewAttrs(int w, int h) {
+        // To use TYPE_NAVIGATION_BAR_PANEL instead of TYPE_IME_BAR to bypass the IME window type
+        // token check when adding the window.
+        final WindowManager.LayoutParams attrs =
+                new WindowManager.LayoutParams(w, h, TYPE_NAVIGATION_BAR_PANEL,
+                        FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE,
+                        TRANSPARENT);
+        attrs.gravity = Gravity.BOTTOM;
+        attrs.setTitle(WINDOW_TITLE);
+        attrs.token = new Binder();
+        attrs.providesInsetsTypes = new int[]{ITYPE_IME};
+        attrs.alpha = 0.0f;
+        return attrs;
+    }
+
     void setStackView(BubbleStackView stackView) {
         mStackView = stackView;
     }
@@ -570,4 +623,11 @@
         }
         return INVALID_DISPLAY;
     }
+
+    private VirtualDisplay getVirtualDisplay() {
+        if (usingActivityView()) {
+            return mActivityView.getVirtualDisplay();
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e109ded..9868446 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5640,6 +5640,11 @@
                 Slog.w(TAG, "Failed to deliver showInsets", e);
             }
         }
+
+        @Override
+        public boolean isClientControlled() {
+            return false;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index bbc6c2b..42c1a07 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -61,4 +61,12 @@
     default boolean canShowTransient() {
         return false;
     }
+
+    /**
+     * Returns {@code true} if the object controlling the insets is on client.
+     */
+    default boolean isClientControlled() {
+        return true;
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 351743f..56986c2 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -38,6 +38,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.function.TriConsumer;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -288,6 +289,7 @@
             t.deferTransactionUntil(leash, barrier, frameNumber);
         }
         mControlTarget = target;
+        updateVisibility();
         mControl = new InsetsSourceControl(mSource.getType(), leash,
                 new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
     }
@@ -330,13 +332,16 @@
         updateVisibility();
     }
 
-    private void setServerVisible(boolean serverVisible) {
+    @VisibleForTesting
+    void setServerVisible(boolean serverVisible) {
         mServerVisible = serverVisible;
         updateVisibility();
     }
 
     private void updateVisibility() {
-        mSource.setVisible(mServerVisible && mClientVisible);
+        final boolean isClientControlled = mControlTarget != null
+                && mControlTarget.isClientControlled();
+        mSource.setVisible(mServerVisible && (!isClientControlled || mClientVisible));
     }
 
     InsetsSourceControl getControl(InsetsControlTarget target) {
@@ -408,10 +413,10 @@
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
                 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
-                setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
                 mControl = null;
                 mControlTarget = null;
                 mAdapter = null;
+                setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
             }
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index f831287..2444c24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -221,7 +221,7 @@
         addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
                 .getControllableInsetProvider().getSource().setVisible(false);
         addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
-                .getControllableInsetProvider().getSource().setVisible(true);
+                .getControllableInsetProvider().setServerVisible(true);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
         doNothing().when(policy).startAnimation(anyBoolean(), any(), any());