diff options
| author | 2020-02-13 16:14:29 -0500 | |
|---|---|---|
| committer | 2020-02-18 11:27:10 -0500 | |
| commit | 7155bf102fac826bd5f07082b571e1722555ef23 (patch) | |
| tree | b2e5dfc422fa998ccfa59a72f462197067e20662 | |
| parent | cd1ca07b3ebbb8e42f2ebd0a4cf45068f9d0549a (diff) | |
Modifies Bubbles to use the FloatingContentCoordinator.
Test: atest SystemUITests
Bug: 138115889
Change-Id: I599024a140f9c9c2e54e835ba3f32d180e07c39a
8 files changed, 182 insertions, 25 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 1f94dbd5e59a..de707b9527bb 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.ShadeController; 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ 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 9a6295a80c1c..3d9865c05222 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.ExpandedAnimationController; 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 @@ public class BubbleStackView extends FrameLayout { 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 @@ public class BubbleStackView extends FrameLayout { 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 @@ public class BubbleStackView extends FrameLayout { 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 793f8b9bc05b..60c8c4e10cf0 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.SpringAnimation; 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 @@ public class StackAnimationController extends */ 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 @@ public class StackAnimationController extends /** 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 @@ public class StackAnimationController extends * 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 @@ public class StackAnimationController extends 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 @@ public class StackAnimationController extends .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 @@ public class StackAnimationController extends 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 @@ public class StackAnimationController extends // 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 0337ee37bd37..f057d0b65294 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.NotificationShadeWindowController; 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 @@ public interface BubbleModule { NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, - DumpController dumpController) { + DumpController dumpController, + FloatingContentCoordinator floatingContentCoordinator) { return new BubbleController( context, notificationShadeWindowController, @@ -76,6 +78,7 @@ public interface BubbleModule { entryManager, notifPipeline, featureFlags, - dumpController); + dumpController, + floatingContentCoordinator); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index fc79fcb9dc66..daea7a7a8dc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.InjectionInflationController; import org.junit.Before; @@ -129,6 +130,8 @@ public class BubbleControllerTest extends SysuiTestCase { private SysuiStatusBarStateController mStatusBarStateController; @Mock private KeyguardBypassController mKeyguardBypassController; + @Mock + private FloatingContentCoordinator mFloatingContentCoordinator; @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor; @@ -243,7 +246,8 @@ public class BubbleControllerTest extends SysuiTestCase { mNotificationEntryManager, mNotifPipeline, mFeatureFlagsOldPipeline, - mDumpController); + mDumpController, + mFloatingContentCoordinator); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index 24f8a7b52f91..b412ca5b47c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -79,6 +79,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.InjectionInflationController; import org.junit.Before; @@ -126,6 +127,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private SysuiStatusBarStateController mStatusBarStateController; @Mock private KeyguardBypassController mKeyguardBypassController; + @Mock + private FloatingContentCoordinator mFloatingContentCoordinator; @Captor private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; @@ -232,7 +235,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mNotificationEntryManager, mNotifPipeline, mFeatureFlagsNewPipeline, - mDumpController); + mDumpController, + mFloatingContentCoordinator); mBubbleController.addNotifCallback(mNotifCallback); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index 338abf59c918..f9849f46d24d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.phone.NotificationShadeWindowController; 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; /** * Testable BubbleController subclass that immediately synchronizes surfaces. @@ -50,12 +51,13 @@ public class TestableBubbleController extends BubbleController { NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, - DumpController dumpController) { + DumpController dumpController, + FloatingContentCoordinator floatingContentCoordinator) { super(context, notificationShadeWindowController, statusBarStateController, shadeController, data, Runnable::run, configurationController, interruptionStateProvider, zenModeController, lockscreenUserManager, groupManager, entryManager, - notifPipeline, featureFlags, dumpController); + notifPipeline, featureFlags, dumpController, floatingContentCoordinator); setInflateSynchronously(true); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java index d79128ca5c78..9cc034996687 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java @@ -18,6 +18,10 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.graphics.PointF; @@ -30,13 +34,14 @@ import androidx.dynamicanimation.animation.SpringForce; import androidx.test.filters.SmallTest; import com.android.systemui.R; +import com.android.systemui.util.FloatingContentCoordinator; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.Spy; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -45,8 +50,10 @@ import java.util.concurrent.TimeUnit; @RunWith(AndroidTestingRunner.class) public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase { - @Spy - private TestableStackController mStackController = new TestableStackController(); + @Mock + private FloatingContentCoordinator mFloatingContentCoordinator; + + private TestableStackController mStackController; private int mStackOffset; private Runnable mCheckStartPosSet; @@ -54,6 +61,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase @Before public void setUp() throws Exception { super.setUp(); + mStackController = spy(new TestableStackController(mFloatingContentCoordinator)); mLayout.setActiveController(mStackController); addOneMoreThanBubbleLimitBubbles(); mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset); @@ -288,6 +296,21 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase assertEquals(30, mStackController.getStackPosition().y, 1f); } + @Test + public void testFloatingCoordinator() { + // We should have called onContentAdded only once while adding all of the bubbles in + // setup(). + verify(mFloatingContentCoordinator, times(1)).onContentAdded(any()); + verify(mFloatingContentCoordinator, never()).onContentRemoved(any()); + + // Remove all views and verify that we called onContentRemoved only once. + while (mLayout.getChildCount() > 0) { + mLayout.removeView(mLayout.getChildAt(0)); + } + + verify(mFloatingContentCoordinator, times(1)).onContentRemoved(any()); + } + /** * Checks every child view to make sure it's stacked at the given coordinates, off to the left * or right side depending on offset multiplier. @@ -328,6 +351,11 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase * Testable version of the stack controller that dispatches its animations on the main thread. */ private class TestableStackController extends StackAnimationController { + TestableStackController( + FloatingContentCoordinator floatingContentCoordinator) { + super(floatingContentCoordinator); + } + @Override protected void flingThenSpringFirstBubbleWithStackFollowing( DynamicAnimation.ViewProperty property, float vel, float friction, |