Merge "Verify the pinned stack boundary when adjusting offset."
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index db6ad3d..2b765b2 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -247,8 +247,20 @@
             boolean preserveWindows, boolean animate, int animationDuration);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
             boolean animate, in Rect initialBounds, boolean showRecents);
-
-
+    /**
+     * Use the offset to adjust the stack boundary with animation.
+     *
+     * @param stackId Id of the stack to adjust.
+     * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to
+     *                      the compareBounds.
+     * @param xOffset The horizontal offset.
+     * @param yOffset The vertical offset.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     * @throws RemoteException
+     */
+    void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset,
+            int animationDuration);
     /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 82aa473..9c65994 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -79,6 +79,7 @@
 
     private static final int MSG_RESIZE_IMMEDIATE = 1;
     private static final int MSG_RESIZE_ANIMATE = 2;
+    private static final int MSG_OFFSET_ANIMATE = 3;
 
     private Context mContext;
     private IActivityManager mActivityManager;
@@ -360,9 +361,20 @@
     /**
      * Animates the PiP to offset it from the IME or shelf.
      */
-    void animateToOffset(Rect toBounds) {
+    void animateToOffset(Rect originalBounds, int offset) {
         cancelAnimations();
-        resizeAndAnimatePipUnchecked(toBounds, SHIFT_DURATION);
+        adjustAndAnimatePipOffset(originalBounds, offset, SHIFT_DURATION);
+    }
+
+    private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) {
+        if (offset == 0) {
+            return;
+        }
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = originalBounds;
+        args.argi1 = offset;
+        args.argi2 = duration;
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
     }
 
     /**
@@ -549,6 +561,31 @@
                 return true;
             }
 
+            case MSG_OFFSET_ANIMATE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                Rect originalBounds = (Rect) args.arg1;
+                final int offset = args.argi1;
+                final int duration = args.argi2;
+                try {
+                    StackInfo stackInfo = mActivityTaskManager.getStackInfo(
+                            WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+                    if (stackInfo == null) {
+                        // In the case where we've already re-expanded or dismissed the PiP, then
+                        // just skip the resize
+                        return true;
+                    }
+
+                    mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds,
+                            0/* xOffset */, offset, duration);
+                    Rect toBounds = new Rect(originalBounds);
+                    toBounds.offset(0, offset);
+                    mBounds.set(toBounds);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not animate offset pinned stack with offset: " + offset, e);
+                }
+                return true;
+            }
+
             default:
                 return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 6a9f24c..cef1b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -346,12 +346,11 @@
     }
 
     private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
-        final Rect bounds = new Rect(animatingBounds);
-        bounds.offset(0, toAdjustedBounds.bottom - bounds.top);
+        int offset = toAdjustedBounds.bottom - animatingBounds.top;
         // In landscape mode, PIP window can go offset while launching IME. We want to align the
         // the top of the PIP window with the top of the movement bounds in that case.
-        bounds.offset(0, Math.max(0, mMovementBounds.top - bounds.top));
-        mMotionHelper.animateToOffset(bounds);
+        offset += Math.max(0, mMovementBounds.top - animatingBounds.top);
+        mMotionHelper.animateToOffset(animatingBounds, offset);
     }
 
     private void onRegistrationChanged(boolean isRegistered) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index cc78588..932cfd3 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -5631,6 +5631,19 @@
         }
     }
 
+    /**
+     * Get current bounds of this stack, return empty when it is unavailable.
+     * @see TaskStack#getAnimationOrCurrentBounds(Rect)
+     */
+    void getAnimationOrCurrentBounds(Rect outBounds) {
+        final TaskStack stack = getTaskStack();
+        if (stack == null) {
+            outBounds.setEmpty();
+            return;
+        }
+        stack.getAnimationOrCurrentBounds(outBounds);
+    }
+
     private boolean skipResizeAnimation(boolean toFullscreen) {
         if (!toFullscreen) {
             return false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 875fc4e..7e9979d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2452,6 +2452,40 @@
         }
     }
 
+    @Override
+    public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset,
+            int animationDuration) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()");
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (xOffset == 0 && yOffset == 0) {
+                    return;
+                }
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found.");
+                    return;
+                }
+                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    throw new IllegalArgumentException("Stack: " + stackId
+                            + " doesn't support animated resize.");
+                }
+                final Rect destBounds = new Rect();
+                stack.getAnimationOrCurrentBounds(destBounds);
+                if (!destBounds.isEmpty() || !destBounds.equals(compareBounds)) {
+                    Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete.");
+                    return;
+                }
+                destBounds.offset(xOffset, yOffset);
+                stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+                        animationDuration, false /* fromFullscreen */);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
     /**
      * Moves the specified task to the primary-split-screen stack.
      *