Merge "Move IME & shelf visibility/height into PipBoundsState"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
index 16c0566..ca16cfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
@@ -59,11 +59,6 @@
     private int mDefaultMinSize;
     private Point mScreenEdgeInsets;
 
-    private boolean mIsImeShowing;
-    private int mImeHeight;
-    private boolean mIsShelfShowing;
-    private int mShelfHeight;
-
     public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) {
         mPipBoundsState = pipBoundsState;
         mSnapAlgorithm = new PipSnapAlgorithm(context);
@@ -101,29 +96,6 @@
     }
 
     /**
-     * Sets both shelf visibility and its height if applicable.
-     * @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
-     */
-    public boolean setShelfHeight(boolean shelfVisible, int shelfHeight) {
-        final boolean shelfShowing = shelfVisible && shelfHeight > 0;
-        if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) {
-            return false;
-        }
-
-        mIsShelfShowing = shelfVisible;
-        mShelfHeight = shelfHeight;
-        return true;
-    }
-
-    /**
-     * Responds to IPinnedStackListener on IME visibility change.
-     */
-    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-        mIsImeShowing = imeVisible;
-        mImeHeight = imeHeight;
-    }
-
-    /**
      * Responds to IPinnedStackListener on movement bounds change.
      * Note that both inset and normal bounds will be calculated here rather than in the caller.
      */
@@ -360,8 +332,10 @@
                         mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
             }
             Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(),
-                    insetBounds, 0, Math.max(mIsImeShowing ? mImeHeight : 0,
-                            mIsShelfShowing ? mShelfHeight : 0), defaultBounds);
+                    insetBounds, 0, Math.max(
+                            mPipBoundsState.isImeShowing() ? mPipBoundsState.getImeHeight() : 0,
+                            mPipBoundsState.isShelfShowing()
+                                    ? mPipBoundsState.getShelfHeight() : 0), defaultBounds);
         }
         return defaultBounds;
     }
@@ -396,7 +370,8 @@
 
         // Apply the movement bounds adjustments based on the current state.
         mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
-                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+                (adjustForIme && mPipBoundsState.isImeShowing())
+                        ? mPipBoundsState.getImeHeight() : 0);
         return movementBounds;
     }
 
@@ -437,10 +412,6 @@
         pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
         pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
         pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
-        pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
-        pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
-        pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
-        pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         pw.println(innerPrefix + "mSnapAlgorithm" + mSnapAlgorithm);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 8d229d3..78f7e25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -33,6 +33,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
+import java.util.function.BiConsumer;
 
 /**
  * Singleton source of truth for the current state of PIP bounds.
@@ -66,8 +67,13 @@
     /** The preferred minimum (and default) size specified by apps. */
     private Size mOverrideMinSize;
     private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();
+    private boolean mIsImeShowing;
+    private int mImeHeight;
+    private boolean mIsShelfShowing;
+    private int mShelfHeight;
 
     private Runnable mOnMinimalSizeChangeCallback;
+    private BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback;
 
     public PipBoundsState(Context context) {
         mContext = context;
@@ -245,6 +251,46 @@
         return mAnimatingBoundsState;
     }
 
+    /** Set whether the IME is currently showing and its height. */
+    public void setImeVisibility(boolean imeShowing, int imeHeight) {
+        mIsImeShowing = imeShowing;
+        mImeHeight = imeHeight;
+    }
+
+    /** Returns whether the IME is currently showing. */
+    public boolean isImeShowing() {
+        return mIsImeShowing;
+    }
+
+    /** Returns the IME height. */
+    public int getImeHeight() {
+        return mImeHeight;
+    }
+
+    /** Set whether the shelf is showing and its height. */
+    public void setShelfVisibility(boolean showing, int height) {
+        final boolean shelfShowing = showing && height > 0;
+        if (shelfShowing == mIsShelfShowing && height == mShelfHeight) {
+            return;
+        }
+
+        mIsShelfShowing = showing;
+        mShelfHeight = height;
+        if (mOnShelfVisibilityChangeCallback != null) {
+            mOnShelfVisibilityChangeCallback.accept(mIsShelfShowing, mShelfHeight);
+        }
+    }
+
+    /** Returns whether the shelf is currently showing. */
+    public boolean isShelfShowing() {
+        return mIsShelfShowing;
+    }
+
+    /** Returns the shelf height. */
+    public int getShelfHeight() {
+        return mShelfHeight;
+    }
+
     /**
      * Registers a callback when the minimal size of PIP that is set by the app changes.
      */
@@ -252,6 +298,12 @@
         mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback;
     }
 
+    /** Set a callback to be notified when the shelf visibility changes. */
+    public void setOnShelfVisibilityChangeCallback(
+            BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) {
+        mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback;
+    }
+
     /** Source of truth for the current animation bounds of PIP. */
     public static class AnimatingBoundsState {
         /** The bounds used when PIP is being dragged or animated. */
@@ -345,6 +397,10 @@
         pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
         pw.println(innerPrefix + "mMinEdgeSize=" + mMinEdgeSize);
         pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
+        pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
+        pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
+        pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
+        pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         if (mPipReentryState == null) {
             pw.println(innerPrefix + "mPipReentryState=null");
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 6f16359..8e48229 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -125,8 +125,8 @@
             // not during the fixed rotation. In fixed rotation case, app is about to enter PiP
             // and we need the offsets preserved to calculate the destination bounds.
             if (!mIsInFixedRotation) {
-                mPipBoundsHandler.setShelfHeight(false, 0);
-                mPipBoundsHandler.onImeVisibilityChanged(false, 0);
+                mPipBoundsState.setShelfVisibility(false /* showing */, 0 /* height */);
+                mPipBoundsState.setImeVisibility(false /* showing */, 0 /* height */);
                 mTouchHandler.onShelfVisibilityChanged(false, 0);
                 mTouchHandler.onImeVisibilityChanged(false, 0);
             }
@@ -168,7 +168,7 @@
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             mMainExecutor.execute(() -> {
-                mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+                mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
                 mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
             });
         }
@@ -247,6 +247,12 @@
         mPipBoundsState = pipBoundsState;
         mPipTaskOrganizer = pipTaskOrganizer;
         mMainExecutor = mainExecutor;
+        mMediaController = pipMediaController;
+        mMenuController = pipMenuActivityController;
+        mTouchHandler = pipTouchHandler;
+        mAppOpsListener = pipAppOpsListener;
+        mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_PIP);
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
             final DisplayInfo newDisplayInfo = new DisplayInfo();
@@ -263,17 +269,17 @@
                             false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
                             null /* wct */);
                 });
-        mMediaController = pipMediaController;
-        mMenuController = pipMenuActivityController;
-        mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
-                INPUT_CONSUMER_PIP);
-        mTouchHandler = pipTouchHandler;
+        mPipBoundsState.setOnShelfVisibilityChangeCallback((isShowing, height) -> {
+            mTouchHandler.onShelfVisibilityChanged(isShowing, height);
+            updateMovementBounds(mPipBoundsState.getBounds(),
+                    false /* fromRotation */, false /* fromImeAdjustment */,
+                    true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
+        });
         if (mTouchHandler != null) {
             // Register the listener for input consumer touch events. Only for Phone
             mPipInputConsumer.setInputListener(mTouchHandler::handleTouchEvent);
             mPipInputConsumer.setRegistrationListener(mTouchHandler::onRegistrationChanged);
         }
-        mAppOpsListener = pipAppOpsListener;
         displayController.addDisplayChangingController(mRotationController);
         displayController.addDisplayWindowListener(mFixedRotationListener);
 
@@ -413,13 +419,7 @@
 
     private void setShelfHeightLocked(boolean visible, int height) {
         final int shelfHeight = visible ? height : 0;
-        final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
-        if (changed) {
-            mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
-            updateMovementBounds(mPipBoundsState.getBounds(),
-                    false /* fromRotation */, false /* fromImeAdjustment */,
-                    true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
-        }
+        mPipBoundsState.setShelfVisibility(visible, shelfHeight);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 402d79c..fd055d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -184,7 +184,7 @@
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             mHandler.post(() -> {
-                mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+                mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
                 if (mState == STATE_PIP) {
                     if (mImeVisible != imeVisible) {
                         if (imeVisible) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index 440e23c..ba60d3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -242,7 +242,7 @@
         mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
         final Rect oldPosition = mPipBoundsHandler.getEntryDestinationBounds();
 
-        mPipBoundsHandler.setShelfHeight(true, shelfHeight);
+        mPipBoundsState.setShelfVisibility(true, shelfHeight);
         final Rect newPosition = mPipBoundsHandler.getEntryDestinationBounds();
 
         oldPosition.offset(0, -shelfHeight);
@@ -255,7 +255,7 @@
         mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
         final Rect oldPosition = mPipBoundsHandler.getEntryDestinationBounds();
 
-        mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
+        mPipBoundsState.setImeVisibility(true, imeHeight);
         final Rect newPosition = mPipBoundsHandler.getEntryDestinationBounds();
 
         oldPosition.offset(0, -imeHeight);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 5e11de7..59e10c1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -19,6 +19,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.content.ComponentName;
 import android.graphics.Rect;
@@ -34,6 +37,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.function.BiConsumer;
+
 /**
  * Tests for {@link PipBoundsState}.
  */
@@ -109,4 +114,25 @@
 
         assertNull(mPipBoundsState.getReentryState());
     }
+
+    @Test
+    public void testSetShelfVisibility_changed_callbackInvoked() {
+        final BiConsumer<Boolean, Integer> callback = mock(BiConsumer.class);
+        mPipBoundsState.setOnShelfVisibilityChangeCallback(callback);
+
+        mPipBoundsState.setShelfVisibility(true, 100);
+
+        verify(callback).accept(true, 100);
+    }
+
+    @Test
+    public void testSetShelfVisibility_notChanged_callbackNotInvoked() {
+        final BiConsumer<Boolean, Integer> callback = mock(BiConsumer.class);
+        mPipBoundsState.setShelfVisibility(true, 100);
+        mPipBoundsState.setOnShelfVisibilityChangeCallback(callback);
+
+        mPipBoundsState.setShelfVisibility(true, 100);
+
+        verify(callback, never()).accept(true, 100);
+    }
 }