Merge "Allow bubbles to stash at the side of the screen after some time" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 9fabd42..69bf5fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -145,6 +145,15 @@
      */
     private static final int ANIMATE_TEMPORARILY_INVISIBLE_DELAY = 1000;
 
+    /**
+     * Percent of the bubble that is hidden while stashed.
+     */
+    private static final float PERCENT_HIDDEN_WHEN_STASHED = 0.55f;
+    /**
+     * How long to wait to animate the stack for stashing.
+     */
+    private static final int ANIMATE_STASH_DELAY = 700;
+
     private static final PhysicsAnimator.SpringConfig FLYOUT_IME_ANIMATION_SPRING_CONFIG =
             new PhysicsAnimator.SpringConfig(
                     StackAnimationController.IME_ANIMATION_STIFFNESS,
@@ -725,6 +734,13 @@
 
             // Hide the stack after a delay, if needed.
             updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
+            animateStashedState(false /* stashImmediately */);
+        }
+
+        @Override
+        public void onCancel(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+                float viewInitialY) {
+            animateStashedState(false /* stashImmediately */);
         }
     };
 
@@ -1216,6 +1232,46 @@
         }
     };
 
+    /**
+     * Animates the bubble stack to stash along the edge of the screen.
+     *
+     * @param stashImmediately whether the stash should happen immediately or without delay.
+     */
+    private void animateStashedState(boolean stashImmediately) {
+        if (!Flags.enableBubbleStashing()) return;
+
+        removeCallbacks(mAnimateStashedState);
+
+        postDelayed(mAnimateStashedState, stashImmediately ? 0 : ANIMATE_STASH_DELAY);
+    }
+
+    private final Runnable mAnimateStashedState = () -> {
+        if (mFlyout.getVisibility() != View.VISIBLE
+                && !mIsDraggingStack
+                && !isExpansionAnimating()
+                && !isExpanded()
+                && !isStackEduVisible()) {
+            // To calculate a distance, bubble stack needs to be moved to become stashed,
+            // we need to take into account that the bubble stack is positioned on the edge
+            // of the available screen rect, which can be offset by system bars and cutouts.
+            final float amountOffscreen = mBubbleSize - (mBubbleSize * PERCENT_HIDDEN_WHEN_STASHED);
+            if (mStackAnimationController.isStackOnLeftSide()) {
+                int availableRectOffsetX =
+                        mPositioner.getAvailableRect().left - mPositioner.getScreenRect().left;
+                mBubbleContainer
+                        .animate()
+                        .translationX(-(amountOffscreen + availableRectOffsetX))
+                        .start();
+            } else {
+                int availableRectOffsetX =
+                        mPositioner.getAvailableRect().right - mPositioner.getScreenRect().right;
+                mBubbleContainer.animate()
+                        .translationX(amountOffscreen - availableRectOffsetX)
+                        .start();
+            }
+        }
+    };
+
     private void setUpOverflow() {
         resetOverflowView();
         mBubbleContainer.addView(mBubbleOverflow.getIconView(),
@@ -2385,6 +2441,9 @@
         updateBadges(false /* setBadgeForCollapsedStack */);
         updateOverflowVisibility();
         updatePointerPosition(false /* forIme */);
+        if (Flags.enableBubbleStashing()) {
+            mBubbleContainer.animate().translationX(0).start();
+        }
         mExpandedAnimationController.expandFromStack(() -> {
             if (mIsExpanded && mExpandedBubble != null
                     && mExpandedBubble.getExpandedView() != null) {
@@ -2550,6 +2609,7 @@
                 previouslySelected.setTaskViewVisibility(false);
             }
             mExpandedViewAnimationController.reset();
+            animateStashedState(false /* stashImmediately */);
         };
         mExpandedViewAnimationController.animateCollapse(collapseBackToStack, after,
                 collapsePosition);
@@ -2952,6 +3012,7 @@
             }
             // Hide the stack after a delay, if needed.
             updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
+            animateStashedState(true /* stashImmediately */);
         };
 
         // Suppress the dot when we are animating the flyout.
@@ -3044,6 +3105,13 @@
                 outRect.left -= mBubbleTouchPadding;
                 outRect.right += mBubbleTouchPadding;
                 outRect.bottom += mBubbleTouchPadding;
+                if (Flags.enableBubbleStashing()) {
+                    if (mStackOnLeftOrWillBe) {
+                        outRect.right += mBubbleTouchPadding;
+                    } else {
+                        outRect.left -= mBubbleTouchPadding;
+                    }
+                }
             }
         } else {
             mBubbleContainer.getBoundsOnScreen(outRect);