Merge "Remove Bubbles from the WM, only after all bubbles finish animating out." into rvc-dev

This commit is contained in:
Josh Tsuji
2020-05-21 00:25:11 +00:00
committed by Android (Google) Code Review
6 changed files with 132 additions and 44 deletions

View File

@@ -185,9 +185,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Used to post to main UI thread // Used to post to main UI thread
private Handler mHandler = new Handler(); private Handler mHandler = new Handler();
/** LayoutParams used to add the BubbleStackView to the window maanger. */ /** LayoutParams used to add the BubbleStackView to the window manager. */
private WindowManager.LayoutParams mWmLayoutParams; private WindowManager.LayoutParams mWmLayoutParams;
/** Whether or not the BubbleStackView has been added to the WindowManager. */
private boolean mAddedToWindowManager = false;
// Used for determining view rect for touch interaction // Used for determining view rect for touch interaction
private Rect mTempRect = new Rect(); private Rect mTempRect = new Rect();
@@ -595,9 +596,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mStackView == null) { if (mStackView == null) {
mStackView = new BubbleStackView( mStackView = new BubbleStackView(
mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
mSysUiState, mNotificationShadeWindowController); mSysUiState, mNotificationShadeWindowController, this::onAllBubblesAnimatedOut);
mStackView.addView(mBubbleScrim); mStackView.addView(mBubbleScrim);
addToWindowManager();
if (mExpandListener != null) { if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener); mStackView.setExpandListener(mExpandListener);
} }
@@ -605,10 +605,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.setUnbubbleConversationCallback(notificationEntry -> mStackView.setUnbubbleConversationCallback(notificationEntry ->
onUserChangedBubble(notificationEntry, false /* shouldBubble */)); onUserChangedBubble(notificationEntry, false /* shouldBubble */));
} }
addToWindowManagerMaybe();
} }
/** Adds the BubbleStackView to the WindowManager. */ /** Adds the BubbleStackView to the WindowManager if it's not already there. */
private void addToWindowManager() { private void addToWindowManagerMaybe() {
// If the stack is null, or already added, don't add it.
if (mStackView == null || mAddedToWindowManager) {
return;
}
mWmLayoutParams = new WindowManager.LayoutParams( mWmLayoutParams = new WindowManager.LayoutParams(
// Fill the screen so we can use translation animations to position the bubble // Fill the screen so we can use translation animations to position the bubble
// stack. We'll use touchable regions to ignore touches that are not on the bubbles // stack. We'll use touchable regions to ignore touches that are not on the bubbles
@@ -629,9 +636,37 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mWmLayoutParams.packageName = mContext.getPackageName(); mWmLayoutParams.packageName = mContext.getPackageName();
mWmLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mWmLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowManager.addView(mStackView, mWmLayoutParams); try {
mAddedToWindowManager = true;
mWindowManager.addView(mStackView, mWmLayoutParams);
} catch (IllegalStateException e) {
// This means the stack has already been added. This shouldn't happen, since we keep
// track of that, but just in case, update the previously added view's layout params.
e.printStackTrace();
updateWmFlags();
}
} }
/** Removes the BubbleStackView from the WindowManager if it's there. */
private void removeFromWindowManagerMaybe() {
if (!mAddedToWindowManager) {
return;
}
try {
mAddedToWindowManager = false;
mWindowManager.removeView(mStackView);
} catch (IllegalArgumentException e) {
// This means the stack has already been removed - it shouldn't happen, but ignore if it
// does, since we wanted it removed anyway.
e.printStackTrace();
}
}
/**
* Updates the BubbleStackView's WindowManager.LayoutParams, and updates the WindowManager with
* the new params if the stack has been added.
*/
private void updateWmFlags() { private void updateWmFlags() {
if (isStackExpanded()) { if (isStackExpanded()) {
// If we're expanded, we want to be focusable so that the ActivityView can receive focus // If we're expanded, we want to be focusable so that the ActivityView can receive focus
@@ -643,7 +678,25 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
} }
mWindowManager.updateViewLayout(mStackView, mWmLayoutParams); if (mStackView != null && mAddedToWindowManager) {
try {
mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
} catch (IllegalArgumentException e) {
// If the stack is somehow not there, ignore the attempt to update it.
e.printStackTrace();
}
}
}
/**
* Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
* added in the meantime.
*/
private void onAllBubblesAnimatedOut() {
if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
removeFromWindowManagerMaybe();
}
} }
/** /**
@@ -833,10 +886,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
} }
void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
if (mStackView == null) { // Lazy init stack view when a bubble is created
// Lazy init stack view when a bubble is created ensureStackViewCreated();
ensureStackViewCreated();
}
// If this is an interruptive notif, mark that it's interrupted // If this is an interruptive notif, mark that it's interrupted
if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
notif.setInterruption(); notif.setInterruption();
@@ -1196,11 +1248,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mStackView == null) { if (mStackView == null) {
return; return;
} }
if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
// Bubbles only appear in unlocked shade if (mStatusBarStateListener.getCurrentState() != SHADE) {
mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE); // Bubbles don't appear over the locked shade.
} else if (mStackView != null) {
mStackView.setVisibility(INVISIBLE); mStackView.setVisibility(INVISIBLE);
} else if (hasBubbles()) {
// If we're unlocked, show the stack if we have bubbles. If we don't have bubbles, the
// stack will be set to INVISIBLE in onAllBubblesAnimatedOut after the bubbles animate
// out.
mStackView.setVisibility(VISIBLE);
} }
mStackView.updateContentDescription(); mStackView.updateContentDescription();

View File

@@ -656,7 +656,8 @@ public class BubbleStackView extends FrameLayout
@Nullable SurfaceSynchronizer synchronizer, @Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator, FloatingContentCoordinator floatingContentCoordinator,
SysUiState sysUiState, SysUiState sysUiState,
NotificationShadeWindowController notificationShadeWindowController) { NotificationShadeWindowController notificationShadeWindowController,
Runnable allBubblesAnimatedOutAction) {
super(context); super(context);
mBubbleData = data; mBubbleData = data;
@@ -691,11 +692,18 @@ public class BubbleStackView extends FrameLayout
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
final Runnable onBubbleAnimatedOut = () -> {
if (getBubbleCount() == 0) {
allBubblesAnimatedOutAction.run();
}
};
mStackAnimationController = new StackAnimationController( mStackAnimationController = new StackAnimationController(
floatingContentCoordinator, this::getBubbleCount); floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut);
mExpandedAnimationController = new ExpandedAnimationController( mExpandedAnimationController = new ExpandedAnimationController(
mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation); mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation,
onBubbleAnimatedOut);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
setUpUserEducation(); setUpUserEducation();

View File

@@ -32,6 +32,7 @@ import androidx.dynamicanimation.animation.SpringForce;
import com.android.systemui.Interpolators; import com.android.systemui.Interpolators;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.util.animation.PhysicsAnimator;
import com.android.systemui.util.magnetictarget.MagnetizedObject; import com.android.systemui.util.magnetictarget.MagnetizedObject;
import com.google.android.collect.Sets; import com.google.android.collect.Sets;
@@ -69,6 +70,10 @@ public class ExpandedAnimationController
*/ */
private static final float FLING_TO_DISMISS_MIN_VELOCITY = 6000f; private static final float FLING_TO_DISMISS_MIN_VELOCITY = 6000f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
new PhysicsAnimator.SpringConfig(
EXPAND_COLLAPSE_ANIM_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
/** Horizontal offset between bubbles, which we need to know to re-stack them. */ /** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx; private float mStackOffsetPx;
/** Space between status bar and bubbles in the expanded state. */ /** Space between status bar and bubbles in the expanded state. */
@@ -116,10 +121,17 @@ public class ExpandedAnimationController
private int mExpandedViewPadding; private int mExpandedViewPadding;
/**
* Callback to run whenever any bubble is animated out. The BubbleStackView will check if the
* end of this animation means we have no bubbles left, and notify the BubbleController.
*/
private Runnable mOnBubbleAnimatedOutAction;
public ExpandedAnimationController(Point displaySize, int expandedViewPadding, public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
int orientation) { int orientation, Runnable onBubbleAnimatedOutAction) {
updateResources(orientation, displaySize); updateResources(orientation, displaySize);
mExpandedViewPadding = expandedViewPadding; mExpandedViewPadding = expandedViewPadding;
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
} }
/** /**
@@ -355,8 +367,8 @@ public class ExpandedAnimationController
} }
animationForChild(bubble) animationForChild(bubble)
.withStiffness(SpringForce.STIFFNESS_HIGH) .withStiffness(SpringForce.STIFFNESS_HIGH)
.scaleX(1.1f) .scaleX(0f)
.scaleY(1.1f) .scaleY(0f)
.translationY(bubble.getTranslationY() + translationYBy) .translationY(bubble.getTranslationY() + translationYBy)
.alpha(0f, after) .alpha(0f, after)
.start(); .start();
@@ -500,18 +512,17 @@ public class ExpandedAnimationController
@Override @Override
void onChildRemoved(View child, int index, Runnable finishRemoval) { void onChildRemoved(View child, int index, Runnable finishRemoval) {
final PhysicsAnimationLayout.PhysicsPropertyAnimator animator = animationForChild(child);
// If we're removing the dragged-out bubble, that means it got dismissed. // If we're removing the dragged-out bubble, that means it got dismissed.
if (child.equals(getDraggedOutBubble())) { if (child.equals(getDraggedOutBubble())) {
mMagnetizedBubbleDraggingOut = null; mMagnetizedBubbleDraggingOut = null;
finishRemoval.run(); finishRemoval.run();
mOnBubbleAnimatedOutAction.run();
} else { } else {
animator.alpha(0f, finishRemoval /* endAction */) PhysicsAnimator.getInstance(child)
.withStiffness(SpringForce.STIFFNESS_HIGH) .spring(DynamicAnimation.ALPHA, 0f)
.withDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY) .spring(DynamicAnimation.SCALE_X, 0f, mAnimateOutSpringConfig)
.scaleX(1.1f) .spring(DynamicAnimation.SCALE_Y, 0f, mAnimateOutSpringConfig)
.scaleY(1.1f) .withEndActions(finishRemoval, mOnBubbleAnimatedOutAction)
.start(); .start();
} }

View File

@@ -74,6 +74,10 @@ public class StackAnimationController extends
private static final int FLING_FOLLOW_STIFFNESS = 20000; private static final int FLING_FOLLOW_STIFFNESS = 20000;
public static final float DEFAULT_BOUNCINESS = 0.9f; public static final float DEFAULT_BOUNCINESS = 0.9f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
new PhysicsAnimator.SpringConfig(
ANIMATE_IN_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
/** /**
* Friction applied to fling animations. Since the stack must land on one of the sides of the * Friction applied to fling animations. Since the stack must land on one of the sides of the
* screen, we want less friction horizontally so that the stack has a better chance of making it * screen, we want less friction horizontally so that the stack has a better chance of making it
@@ -248,12 +252,19 @@ public class StackAnimationController extends
/** Returns the number of 'real' bubbles (excluding the overflow bubble). */ /** Returns the number of 'real' bubbles (excluding the overflow bubble). */
private IntSupplier mBubbleCountSupplier; private IntSupplier mBubbleCountSupplier;
/**
* Callback to run whenever any bubble is animated out. The BubbleStackView will check if the
* end of this animation means we have no bubbles left, and notify the BubbleController.
*/
private Runnable mOnBubbleAnimatedOutAction;
public StackAnimationController( public StackAnimationController(
FloatingContentCoordinator floatingContentCoordinator, FloatingContentCoordinator floatingContentCoordinator,
IntSupplier bubbleCountSupplier) { IntSupplier bubbleCountSupplier,
Runnable onBubbleAnimatedOutAction) {
mFloatingContentCoordinator = floatingContentCoordinator; mFloatingContentCoordinator = floatingContentCoordinator;
mBubbleCountSupplier = bubbleCountSupplier; mBubbleCountSupplier = bubbleCountSupplier;
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
} }
/** /**
@@ -652,8 +663,8 @@ public class StackAnimationController extends
public void animateStackDismissal(float translationYBy, Runnable after) { public void animateStackDismissal(float translationYBy, Runnable after) {
animationsForChildrenFromIndex(0, (index, animation) -> animationsForChildrenFromIndex(0, (index, animation) ->
animation animation
.scaleX(0.5f) .scaleX(0f)
.scaleY(0.5f) .scaleY(0f)
.alpha(0f) .alpha(0f)
.translationY( .translationY(
mLayout.getChildAt(index).getTranslationY() + translationYBy) mLayout.getChildAt(index).getTranslationY() + translationYBy)
@@ -760,13 +771,11 @@ public class StackAnimationController extends
@Override @Override
void onChildRemoved(View child, int index, Runnable finishRemoval) { void onChildRemoved(View child, int index, Runnable finishRemoval) {
// Animate the removing view in the opposite direction of the stack. PhysicsAnimator.getInstance(child)
final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); .spring(DynamicAnimation.ALPHA, 0f)
animationForChild(child) .spring(DynamicAnimation.SCALE_X, 0f, mAnimateOutSpringConfig)
.alpha(0f, finishRemoval /* after */) .spring(DynamicAnimation.SCALE_Y, 0f, mAnimateOutSpringConfig)
.scaleX(ANIMATE_IN_STARTING_SCALE) .withEndActions(finishRemoval, mOnBubbleAnimatedOutAction)
.scaleY(ANIMATE_IN_STARTING_SCALE)
.translationX(mStackPosition.x - (-xOffset * ANIMATE_TRANSLATION_FACTOR))
.start(); .start();
// If there are other bubbles, pull them into the correct position. // If there are other bubbles, pull them into the correct position.

View File

@@ -49,11 +49,13 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
private int mOrientation = Configuration.ORIENTATION_PORTRAIT; private int mOrientation = Configuration.ORIENTATION_PORTRAIT;
private float mLauncherGridDiff = 30f; private float mLauncherGridDiff = 30f;
private Runnable mOnBubbleAnimatedOutAction = Mockito.mock(Runnable.class);
@Spy @Spy
private ExpandedAnimationController mExpandedController = private ExpandedAnimationController mExpandedController =
new ExpandedAnimationController( new ExpandedAnimationController(
new Point(mDisplayWidth, mDisplayHeight) /* displaySize */, new Point(mDisplayWidth, mDisplayHeight) /* displaySize */,
mExpandedViewPadding, mOrientation); mExpandedViewPadding, mOrientation, mOnBubbleAnimatedOutAction);
private int mStackOffset; private int mStackOffset;
private float mBubblePaddingTop; private float mBubblePaddingTop;

View File

@@ -40,6 +40,7 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -66,7 +67,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
public int getAsInt() { public int getAsInt() {
return mLayout.getChildCount(); return mLayout.getChildCount();
} }
})); }, Mockito.mock(Runnable.class)));
mLayout.setActiveController(mStackController); mLayout.setActiveController(mStackController);
addOneMoreThanBubbleLimitBubbles(); addOneMoreThanBubbleLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset); mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -303,8 +304,9 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
private class TestableStackController extends StackAnimationController { private class TestableStackController extends StackAnimationController {
TestableStackController( TestableStackController(
FloatingContentCoordinator floatingContentCoordinator, FloatingContentCoordinator floatingContentCoordinator,
IntSupplier bubbleCountSupplier) { IntSupplier bubbleCountSupplier,
super(floatingContentCoordinator, bubbleCountSupplier); Runnable onBubbleAnimatedOutAction) {
super(floatingContentCoordinator, bubbleCountSupplier, onBubbleAnimatedOutAction);
} }
@Override @Override