Modifies Bubbles to use the FloatingContentCoordinator.
Test: atest SystemUITests
Bug: 138115889
Change-Id: I599024a140f9c9c2e54e835ba3f32d180e07c39a
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1f94dbd..de707b9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -94,6 +94,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.FloatingContentCoordinator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -140,6 +141,7 @@
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
private final NotificationGroupManager mNotificationGroupManager;
private final ShadeController mShadeController;
+ private final FloatingContentCoordinator mFloatingContentCoordinator;
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
@@ -284,11 +286,12 @@
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
- DumpController dumpController) {
+ DumpController dumpController,
+ FloatingContentCoordinator floatingContentCoordinator) {
this(context, notificationShadeWindowController, statusBarStateController, shadeController,
data, null /* synchronizer */, configurationController, interruptionStateProvider,
zenModeController, notifUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpController);
+ notifPipeline, featureFlags, dumpController, floatingContentCoordinator);
}
/**
@@ -308,13 +311,15 @@
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
- DumpController dumpController) {
+ DumpController dumpController,
+ FloatingContentCoordinator floatingContentCoordinator) {
dumpController.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
mNotificationInterruptionStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager;
mZenModeController = zenModeController;
+ mFloatingContentCoordinator = floatingContentCoordinator;
mZenModeController.addCallback(new ZenModeController.Callback() {
@Override
public void onZenChanged(int zen) {
@@ -584,7 +589,8 @@
*/
private void ensureStackViewCreated() {
if (mStackView == null) {
- mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer);
+ mStackView = new BubbleStackView(
+ mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView();
int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble));
int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 9a6295a..3d9865c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -72,6 +72,7 @@
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.util.FloatingContentCoordinator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -319,7 +320,8 @@
private BubbleOverflow mBubbleOverflow;
public BubbleStackView(Context context, BubbleData data,
- @Nullable SurfaceSynchronizer synchronizer) {
+ @Nullable SurfaceSynchronizer synchronizer,
+ FloatingContentCoordinator floatingContentCoordinator) {
super(context);
mBubbleData = data;
@@ -353,7 +355,7 @@
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
- mStackAnimationController = new StackAnimationController();
+ mStackAnimationController = new StackAnimationController(floatingContentCoordinator);
mExpandedAnimationController = new ExpandedAnimationController(
mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation);
@@ -620,16 +622,16 @@
mBubbleData.setExpanded(true);
return true;
} else if (action == R.id.action_move_top_left) {
- mStackAnimationController.springStack(stackBounds.left, stackBounds.top);
+ mStackAnimationController.springStackAfterFling(stackBounds.left, stackBounds.top);
return true;
} else if (action == R.id.action_move_top_right) {
- mStackAnimationController.springStack(stackBounds.right, stackBounds.top);
+ mStackAnimationController.springStackAfterFling(stackBounds.right, stackBounds.top);
return true;
} else if (action == R.id.action_move_bottom_left) {
- mStackAnimationController.springStack(stackBounds.left, stackBounds.bottom);
+ mStackAnimationController.springStackAfterFling(stackBounds.left, stackBounds.bottom);
return true;
} else if (action == R.id.action_move_bottom_right) {
- mStackAnimationController.springStack(stackBounds.right, stackBounds.bottom);
+ mStackAnimationController.springStackAfterFling(stackBounds.right, stackBounds.bottom);
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 793f8b9..60c8c4e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -16,8 +16,10 @@
package com.android.systemui.bubbles.animation;
+import android.annotation.NonNull;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.View;
@@ -31,6 +33,8 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.systemui.R;
+import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.animation.PhysicsAnimator;
import com.google.android.collect.Sets;
@@ -95,6 +99,12 @@
*/
private PointF mStackPosition = new PointF(-1, -1);
+ /**
+ * The area that Bubbles will occupy after all animations end. This is used to move other
+ * floating content out of the way proactively.
+ */
+ private Rect mAnimatingToBounds = new Rect();
+
/** Whether or not the stack's start position has been set. */
private boolean mStackMovedToStartPosition = false;
@@ -163,11 +173,70 @@
/** Height of the status bar. */
private float mStatusBarHeight;
+ /** FloatingContentCoordinator instance for resolving floating content conflicts. */
+ private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ /**
+ * FloatingContent instance that returns the stack's location on the screen, and moves it when
+ * requested.
+ */
+ private final FloatingContentCoordinator.FloatingContent mStackFloatingContent =
+ new FloatingContentCoordinator.FloatingContent() {
+
+ private final Rect mFloatingBoundsOnScreen = new Rect();
+
+ @Override
+ public void moveToBounds(@NonNull Rect bounds) {
+ springStack(bounds.left, bounds.top, SpringForce.STIFFNESS_LOW);
+ }
+
+ @NonNull
+ @Override
+ public Rect getAllowedFloatingBoundsRegion() {
+ final Rect floatingBounds = getFloatingBoundsOnScreen();
+ final Rect allowableStackArea = new Rect();
+ getAllowableStackPositionRegion().roundOut(allowableStackArea);
+ allowableStackArea.right += floatingBounds.width();
+ allowableStackArea.bottom += floatingBounds.height();
+ return allowableStackArea;
+ }
+
+ @NonNull
+ @Override
+ public Rect getFloatingBoundsOnScreen() {
+ if (!mAnimatingToBounds.isEmpty()) {
+ return mAnimatingToBounds;
+ }
+
+ if (mLayout.getChildCount() > 0) {
+ // Calculate the bounds using stack position + bubble size so that we don't need to
+ // wait for the bubble views to lay out.
+ mFloatingBoundsOnScreen.set(
+ (int) mStackPosition.x,
+ (int) mStackPosition.y,
+ (int) mStackPosition.x + mBubbleSize,
+ (int) mStackPosition.y + mBubbleSize + mBubblePaddingTop);
+ } else {
+ mFloatingBoundsOnScreen.setEmpty();
+ }
+
+ return mFloatingBoundsOnScreen;
+ }
+ };
+
+ public StackAnimationController(
+ FloatingContentCoordinator floatingContentCoordinator) {
+ mFloatingContentCoordinator = floatingContentCoordinator;
+ }
+
/**
* Instantly move the first bubble to the given point, and animate the rest of the stack behind
* it with the 'following' effect.
*/
public void moveFirstBubbleWithStackFollowing(float x, float y) {
+ // If we're moving the bubble around, we're not animating to any bounds.
+ mAnimatingToBounds.setEmpty();
+
// If we manually move the bubbles with the IME open, clear the return point since we don't
// want the stack to snap away from the new position.
mPreImeY = Float.MIN_VALUE;
@@ -204,23 +273,33 @@
* Note that we need new SpringForce instances per animation despite identical configs because
* SpringAnimation uses SpringForce's internal (changing) velocity while the animation runs.
*/
- public void springStack(float destinationX, float destinationY) {
+ public void springStack(float destinationX, float destinationY, float stiffness) {
+ notifyFloatingCoordinatorStackAnimatingTo(destinationX, destinationY);
+
springFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X,
new SpringForce()
- .setStiffness(SPRING_AFTER_FLING_STIFFNESS)
+ .setStiffness(stiffness)
.setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO),
0 /* startXVelocity */,
destinationX);
springFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y,
new SpringForce()
- .setStiffness(SPRING_AFTER_FLING_STIFFNESS)
+ .setStiffness(stiffness)
.setDampingRatio(SPRING_AFTER_FLING_DAMPING_RATIO),
0 /* startYVelocity */,
destinationY);
}
/**
+ * Springs the stack to the specified x/y coordinates, with the stiffness used for springs after
+ * flings.
+ */
+ public void springStackAfterFling(float destinationX, float destinationY) {
+ springStack(destinationX, destinationY, SPRING_AFTER_FLING_STIFFNESS);
+ }
+
+ /**
* Flings the stack starting with the given velocities, springing it to the nearest edge
* afterward.
*
@@ -253,6 +332,13 @@
final float minimumVelocityToReachEdge =
(destinationRelativeX - x) * (FLING_FRICTION_X * 4.2f);
+ final float estimatedY = PhysicsAnimator.estimateFlingEndValue(
+ mStackPosition.y, velY,
+ new PhysicsAnimator.FlingConfig(
+ FLING_FRICTION_Y, stackBounds.top, stackBounds.bottom));
+
+ notifyFloatingCoordinatorStackAnimatingTo(destinationRelativeX, estimatedY);
+
// Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity so
// that it'll make it all the way to the side of the screen.
final float startXVelocity = stackShouldFlingLeft
@@ -426,14 +512,28 @@
.setStiffness(SpringForce.STIFFNESS_LOW),
/* startVel */ 0f,
destinationY);
+
+ notifyFloatingCoordinatorStackAnimatingTo(mStackPosition.x, destinationY);
}
}
/**
- * Returns the region within which the stack is allowed to rest. This goes slightly off the left
+ * Notifies the floating coordinator that we're moving, and sets {@link #mAnimatingToBounds} so
+ * we return these bounds from
+ * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
+ */
+ private void notifyFloatingCoordinatorStackAnimatingTo(float x, float y) {
+ final Rect floatingBounds = mStackFloatingContent.getFloatingBoundsOnScreen();
+ floatingBounds.offsetTo((int) x, (int) y);
+ mAnimatingToBounds = floatingBounds;
+ mFloatingContentCoordinator.onContentMoved(mStackFloatingContent);
+ }
+
+ /**
+ * Returns the region that the stack position must stay within. This goes slightly off the left
* and right sides of the screen, below the status bar/cutout and above the navigation bar.
- * While the stack is not allowed to rest outside of these bounds, it can temporarily be
- * animated or dragged beyond them.
+ * While the stack position is not allowed to rest outside of these bounds, it can temporarily
+ * be animated or dragged beyond them.
*/
public RectF getAllowableStackPositionRegion() {
final WindowInsets insets = mLayout.getRootWindowInsets();
@@ -690,6 +790,10 @@
setStackPosition(mRestingStackPosition == null
? getDefaultStartPosition()
: mRestingStackPosition);
+
+ // Remove the stack from the coordinator since we don't have any bubbles and aren't
+ // visible.
+ mFloatingContentCoordinator.onContentRemoved(mStackFloatingContent);
}
}
@@ -741,6 +845,10 @@
// Animate in the top bubble now that we're visible.
if (mLayout.getChildCount() > 0) {
+ // Add the stack to the floating content coordinator now that we have a bubble and
+ // are visible.
+ mFloatingContentCoordinator.onContentAdded(mStackFloatingContent);
+
animateInBubble(mLayout.getChildAt(0), 0 /* index */);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 0337ee3..f057d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.FloatingContentCoordinator;
import javax.inject.Singleton;
@@ -60,7 +61,8 @@
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
- DumpController dumpController) {
+ DumpController dumpController,
+ FloatingContentCoordinator floatingContentCoordinator) {
return new BubbleController(
context,
notificationShadeWindowController,
@@ -76,6 +78,7 @@
entryManager,
notifPipeline,
featureFlags,
- dumpController);
+ dumpController,
+ floatingContentCoordinator);
}
}