Merge changes I639852a4,I599024a1
* changes: Modifies PIP to use the FloatingContentCoordinator. Modifies Bubbles to use the FloatingContentCoordinator.
This commit is contained in:
committed by
Android (Google) Code Review
commit
0e0454f517
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,22 +273,32 @@ 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 */);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.res.Configuration;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
|
||||
public interface BasePipManager {
|
||||
void showPictureInPictureMenu();
|
||||
default void expandPip() {}
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
import com.android.systemui.util.FloatingContentCoordinator;
|
||||
import com.android.systemui.wm.DisplayChangeController;
|
||||
import com.android.systemui.wm.DisplayController;
|
||||
|
||||
@@ -229,7 +230,8 @@ public class PipManager implements BasePipManager {
|
||||
|
||||
@Inject
|
||||
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
|
||||
DisplayController displayController) {
|
||||
DisplayController displayController,
|
||||
FloatingContentCoordinator floatingContentCoordinator) {
|
||||
mContext = context;
|
||||
mActivityManager = ActivityManager.getService();
|
||||
mActivityTaskManager = ActivityTaskManager.getService();
|
||||
@@ -247,7 +249,8 @@ public class PipManager implements BasePipManager {
|
||||
mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
|
||||
mInputConsumerController);
|
||||
mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
|
||||
mMenuController, mInputConsumerController, mPipBoundsHandler);
|
||||
mMenuController, mInputConsumerController, mPipBoundsHandler,
|
||||
floatingContentCoordinator);
|
||||
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
|
||||
mTouchHandler.getMotionHelper());
|
||||
displayController.addDisplayChangingController(mRotationController);
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.pip.phone;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
import android.app.IActivityManager;
|
||||
@@ -41,6 +42,7 @@ import com.android.internal.os.SomeArgs;
|
||||
import com.android.systemui.pip.PipSnapAlgorithm;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
import com.android.systemui.util.FloatingContentCoordinator;
|
||||
import com.android.systemui.util.animation.FloatProperties;
|
||||
import com.android.systemui.util.animation.PhysicsAnimator;
|
||||
|
||||
@@ -49,7 +51,8 @@ import java.io.PrintWriter;
|
||||
/**
|
||||
* A helper to animate and manipulate the PiP.
|
||||
*/
|
||||
public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback {
|
||||
public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback,
|
||||
FloatingContentCoordinator.FloatingContent {
|
||||
|
||||
private static final String TAG = "PipMotionHelper";
|
||||
private static final boolean DEBUG = false;
|
||||
@@ -85,6 +88,12 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
/** PIP's current bounds on the screen. */
|
||||
private final Rect mBounds = new Rect();
|
||||
|
||||
/** The bounds within which PIP's top-left coordinate is allowed to move. */
|
||||
private Rect mMovementBounds = new Rect();
|
||||
|
||||
/** The region that all of PIP must stay within. */
|
||||
private Rect mFloatingAllowedArea = new Rect();
|
||||
|
||||
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
|
||||
new SfVsyncFrameCallbackProvider();
|
||||
|
||||
@@ -93,6 +102,12 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
*/
|
||||
private final Rect mAnimatedBounds = new Rect();
|
||||
|
||||
/** The destination bounds to which PIP is animating. */
|
||||
private Rect mAnimatingToBounds = new Rect();
|
||||
|
||||
/** Coordinator instance for resolving conflicts with other floating content. */
|
||||
private FloatingContentCoordinator mFloatingContentCoordinator;
|
||||
|
||||
/**
|
||||
* PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
|
||||
*/
|
||||
@@ -119,9 +134,15 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
new PhysicsAnimator.SpringConfig(
|
||||
SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
|
||||
/** SpringConfig to use for springing PIP away from conflicting floating content. */
|
||||
private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
|
||||
new PhysicsAnimator.SpringConfig(
|
||||
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
|
||||
public PipMotionHelper(Context context, IActivityManager activityManager,
|
||||
IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
|
||||
PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils) {
|
||||
PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
|
||||
FloatingContentCoordinator floatingContentCoordinator) {
|
||||
mContext = context;
|
||||
mHandler = new Handler(ForegroundThread.get().getLooper(), this);
|
||||
mActivityManager = activityManager;
|
||||
@@ -129,9 +150,27 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
mMenuController = menuController;
|
||||
mSnapAlgorithm = snapAlgorithm;
|
||||
mFlingAnimationUtils = flingAnimationUtils;
|
||||
mFloatingContentCoordinator = floatingContentCoordinator;
|
||||
onConfigurationChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Rect getFloatingBoundsOnScreen() {
|
||||
return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : mBounds;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Rect getAllowedFloatingBoundsRegion() {
|
||||
return mFloatingAllowedArea;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveToBounds(@NonNull Rect bounds) {
|
||||
animateToBounds(bounds, mConflictResolutionSpringConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates whenever the configuration changes.
|
||||
*/
|
||||
@@ -157,9 +196,24 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to the move the pinned stack to the given {@param bounds}.
|
||||
* Tries to move the pinned stack to the given {@param bounds}.
|
||||
*/
|
||||
void movePip(Rect toBounds) {
|
||||
movePip(toBounds, false /* isDragging */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to move the pinned stack to the given {@param bounds}.
|
||||
*
|
||||
* @param isDragging Whether this movement is the result of a drag touch gesture. If so, we
|
||||
* won't notify the floating content coordinator of this move, since that will
|
||||
* happen when the gesture ends.
|
||||
*/
|
||||
void movePip(Rect toBounds, boolean isDragging) {
|
||||
if (!isDragging) {
|
||||
mFloatingContentCoordinator.onContentMoved(this);
|
||||
}
|
||||
|
||||
cancelAnimations();
|
||||
resizePipUnchecked(toBounds);
|
||||
mBounds.set(toBounds);
|
||||
@@ -211,6 +265,18 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
});
|
||||
}
|
||||
|
||||
/** Sets the movement bounds to use to constrain PIP position animations. */
|
||||
void setCurrentMovementBounds(Rect movementBounds) {
|
||||
mMovementBounds.set(movementBounds);
|
||||
rebuildFlingConfigs();
|
||||
|
||||
// The movement bounds represent the area within which we can move PIP's top-left position.
|
||||
// The allowed area for all of PIP is those bounds plus PIP's width and height.
|
||||
mFloatingAllowedArea.set(mMovementBounds);
|
||||
mFloatingAllowedArea.right += mBounds.width();
|
||||
mFloatingAllowedArea.bottom += mBounds.height();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the PiP bounds.
|
||||
*/
|
||||
@@ -221,11 +287,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
/**
|
||||
* @return the closest minimized PiP bounds.
|
||||
*/
|
||||
Rect getClosestMinimizedBounds(Rect stackBounds, Rect movementBounds) {
|
||||
Rect getClosestMinimizedBounds(Rect stackBounds) {
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, stackBounds);
|
||||
mSnapAlgorithm.applyMinimizedOffset(toBounds, movementBounds, displaySize, mStableInsets);
|
||||
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds);
|
||||
mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets);
|
||||
return toBounds;
|
||||
}
|
||||
|
||||
@@ -264,11 +330,10 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
/**
|
||||
* Animates the PiP to the minimized state, slightly offscreen.
|
||||
*/
|
||||
void animateToClosestMinimizedState(Rect movementBounds, @Nullable Runnable updateAction) {
|
||||
final Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
|
||||
|
||||
prepareForBoundsAnimation(movementBounds);
|
||||
void animateToClosestMinimizedState(@Nullable Runnable updateAction) {
|
||||
final Rect toBounds = getClosestMinimizedBounds(mBounds);
|
||||
|
||||
mAnimatedBounds.set(mBounds);
|
||||
mAnimatedBoundsPhysicsAnimator
|
||||
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
|
||||
.spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
|
||||
@@ -285,10 +350,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
* Flings the PiP to the closest snap target.
|
||||
*/
|
||||
void flingToSnapTarget(
|
||||
float velocityX, float velocityY, Rect movementBounds, Runnable updateAction,
|
||||
@Nullable Runnable endAction) {
|
||||
prepareForBoundsAnimation(movementBounds);
|
||||
|
||||
float velocityX, float velocityY, Runnable updateAction, @Nullable Runnable endAction) {
|
||||
mAnimatedBounds.set(mBounds);
|
||||
mAnimatedBoundsPhysicsAnimator
|
||||
.flingThenSpring(
|
||||
FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
|
||||
@@ -298,21 +361,39 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
.addUpdateListener((target, values) -> updateAction.run())
|
||||
.withEndActions(endAction);
|
||||
|
||||
final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
|
||||
final float estimatedFlingYEndValue =
|
||||
PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
|
||||
|
||||
setAnimatingToBounds(new Rect(
|
||||
(int) xEndValue,
|
||||
(int) estimatedFlingYEndValue,
|
||||
(int) xEndValue + mBounds.width(),
|
||||
(int) estimatedFlingYEndValue + mBounds.height()));
|
||||
|
||||
startBoundsAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the PiP to the closest snap target.
|
||||
*/
|
||||
void animateToClosestSnapTarget(Rect movementBounds) {
|
||||
prepareForBoundsAnimation(movementBounds);
|
||||
void animateToClosestSnapTarget() {
|
||||
final Rect newBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, mBounds);
|
||||
animateToBounds(newBounds, mSpringConfig);
|
||||
}
|
||||
|
||||
final Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
|
||||
/**
|
||||
* Animates PIP to the provided bounds, using physics animations and the given spring
|
||||
* configuration
|
||||
*/
|
||||
void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
|
||||
mAnimatedBounds.set(mBounds);
|
||||
mAnimatedBoundsPhysicsAnimator
|
||||
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
|
||||
.spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
|
||||
|
||||
.spring(FloatProperties.RECT_X, bounds.left, springConfig)
|
||||
.spring(FloatProperties.RECT_Y, bounds.top, springConfig);
|
||||
startBoundsAnimation();
|
||||
|
||||
setAnimatingToBounds(bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,9 +404,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
|
||||
final Point dismissEndPoint = getDismissEndPoint(mBounds, velocityX, velocityY, isFling);
|
||||
|
||||
// Set the animated bounds to start at the current bounds. We don't need to rebuild the
|
||||
// fling configs here via prepareForBoundsAnimation, since animateDismiss isn't provided
|
||||
// with new movement bounds.
|
||||
mAnimatedBounds.set(mBounds);
|
||||
|
||||
// Animate to the dismiss end point, and then dismiss PIP.
|
||||
@@ -366,9 +444,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
currentMovementBounds);
|
||||
}
|
||||
mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
|
||||
|
||||
if (minimized) {
|
||||
normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
|
||||
normalBounds = getClosestMinimizedBounds(normalBounds);
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
movePip(normalBounds);
|
||||
} else {
|
||||
@@ -400,19 +480,15 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
*/
|
||||
private void cancelAnimations() {
|
||||
mAnimatedBoundsPhysicsAnimator.cancel();
|
||||
mAnimatingToBounds.setEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new fling configs whose min/max values respect the given movement bounds, and set the
|
||||
* animated bounds to PIP's current 'real' bounds.
|
||||
*/
|
||||
private void prepareForBoundsAnimation(Rect movementBounds) {
|
||||
/** Set new fling configs whose min/max values respect the given movement bounds. */
|
||||
private void rebuildFlingConfigs() {
|
||||
mFlingConfigX = new PhysicsAnimator.FlingConfig(
|
||||
DEFAULT_FRICTION, movementBounds.left, movementBounds.right);
|
||||
DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
|
||||
mFlingConfigY = new PhysicsAnimator.FlingConfig(
|
||||
DEFAULT_FRICTION, movementBounds.top, movementBounds.bottom);
|
||||
|
||||
mAnimatedBounds.set(mBounds);
|
||||
DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,6 +507,16 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the floating coordinator that we're moving, and sets {@link #mAnimatingToBounds} so
|
||||
* we return these bounds from
|
||||
* {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
|
||||
*/
|
||||
private void setAnimatingToBounds(Rect bounds) {
|
||||
mAnimatingToBounds = bounds;
|
||||
mFloatingContentCoordinator.onContentMoved(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly resizes the PiP to the given {@param bounds}.
|
||||
*/
|
||||
@@ -459,6 +545,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
args.arg1 = toBounds;
|
||||
args.argi1 = duration;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
|
||||
setAnimatingToBounds(toBounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.systemui.pip.PipBoundsHandler;
|
||||
import com.android.systemui.pip.PipSnapAlgorithm;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
import com.android.systemui.util.FloatingContentCoordinator;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -127,6 +128,7 @@ public class PipTouchHandler {
|
||||
// Touch state
|
||||
private final PipTouchState mTouchState;
|
||||
private final FlingAnimationUtils mFlingAnimationUtils;
|
||||
private final FloatingContentCoordinator mFloatingContentCoordinator;
|
||||
private final PipMotionHelper mMotionHelper;
|
||||
private PipTouchGesture mGesture;
|
||||
|
||||
@@ -152,7 +154,7 @@ public class PipTouchHandler {
|
||||
@Override
|
||||
public void onPipMinimize() {
|
||||
setMinimizedStateInternal(true);
|
||||
mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateAction */);
|
||||
mMotionHelper.animateToClosestMinimizedState(null /* updateAction */);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,7 +174,8 @@ public class PipTouchHandler {
|
||||
public PipTouchHandler(Context context, IActivityManager activityManager,
|
||||
IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
|
||||
InputConsumerController inputConsumerController,
|
||||
PipBoundsHandler pipBoundsHandler) {
|
||||
PipBoundsHandler pipBoundsHandler,
|
||||
FloatingContentCoordinator floatingContentCoordinator) {
|
||||
|
||||
// Initialize the Pip input consumer
|
||||
mContext = context;
|
||||
@@ -188,7 +191,7 @@ public class PipTouchHandler {
|
||||
2.5f);
|
||||
mGesture = new DefaultPipTouchGesture();
|
||||
mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
|
||||
mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
|
||||
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
|
||||
mPipResizeGestureHandler =
|
||||
new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
|
||||
mTouchState = new PipTouchState(mViewConfig, mHandler,
|
||||
@@ -207,6 +210,7 @@ public class PipTouchHandler {
|
||||
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
|
||||
|
||||
mPipBoundsHandler = pipBoundsHandler;
|
||||
mFloatingContentCoordinator = floatingContentCoordinator;
|
||||
mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
|
||||
this::onAccessibilityShowMenu, mHandler);
|
||||
}
|
||||
@@ -228,15 +232,18 @@ public class PipTouchHandler {
|
||||
}
|
||||
|
||||
public void onActivityPinned() {
|
||||
cleanUp();
|
||||
cleanUpDismissTarget();
|
||||
mShowPipMenuOnAnimationEnd = true;
|
||||
mPipResizeGestureHandler.onActivityPinned();
|
||||
mFloatingContentCoordinator.onContentAdded(mMotionHelper);
|
||||
}
|
||||
|
||||
public void onActivityUnpinned(ComponentName topPipActivity) {
|
||||
if (topPipActivity == null) {
|
||||
// Clean up state after the last PiP activity is removed
|
||||
cleanUp();
|
||||
cleanUpDismissTarget();
|
||||
|
||||
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
|
||||
}
|
||||
mPipResizeGestureHandler.onActivityUnpinned();
|
||||
}
|
||||
@@ -501,8 +508,7 @@ public class PipTouchHandler {
|
||||
if (fromController) {
|
||||
if (isMinimized) {
|
||||
// Move the PiP to the new bounds immediately if minimized
|
||||
mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
|
||||
mMovementBounds));
|
||||
mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds));
|
||||
}
|
||||
} else if (mPinnedStackController != null) {
|
||||
try {
|
||||
@@ -654,7 +660,7 @@ public class PipTouchHandler {
|
||||
|
||||
mTmpBounds.set(mMotionHelper.getBounds());
|
||||
mTmpBounds.offsetTo((int) left, (int) top);
|
||||
mMotionHelper.movePip(mTmpBounds);
|
||||
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
|
||||
|
||||
if (mEnableDimissDragToEdge) {
|
||||
updateDismissFraction();
|
||||
@@ -724,7 +730,6 @@ public class PipTouchHandler {
|
||||
mMenuController.hideMenu();
|
||||
} else {
|
||||
mMotionHelper.animateToClosestMinimizedState(
|
||||
mMovementBounds,
|
||||
PipTouchHandler.this::updateDismissFraction /* updateAction */);
|
||||
}
|
||||
return true;
|
||||
@@ -748,16 +753,15 @@ public class PipTouchHandler {
|
||||
}
|
||||
|
||||
if (isFling) {
|
||||
mMotionHelper.flingToSnapTarget(
|
||||
vel.x, vel.y, mMovementBounds,
|
||||
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
|
||||
PipTouchHandler.this::updateDismissFraction /* updateAction */,
|
||||
endAction /* endAction */);
|
||||
} else {
|
||||
mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
|
||||
mMotionHelper.animateToClosestSnapTarget();
|
||||
}
|
||||
} else if (mIsMinimized) {
|
||||
// This was a tap, so no longer minimized
|
||||
mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
|
||||
mMotionHelper.animateToClosestSnapTarget();
|
||||
setMinimizedStateInternal(false);
|
||||
} else if (mTouchState.isDoubleTap()) {
|
||||
// Expand to fullscreen if this is a double tap
|
||||
@@ -789,6 +793,7 @@ public class PipTouchHandler {
|
||||
: mNormalMovementBounds;
|
||||
mPipBoundsHandler.setMinEdgeSize(
|
||||
isMenuExpanded ? mExpandedShortestEdgeSize : 0);
|
||||
mMotionHelper.setCurrentMovementBounds(mMovementBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -799,16 +804,6 @@ public class PipTouchHandler {
|
||||
mDismissViewController.destroyDismissTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets some states related to the touch handling.
|
||||
*/
|
||||
private void cleanUp() {
|
||||
if (mIsMinimized) {
|
||||
setMinimizedStateInternal(false);
|
||||
}
|
||||
cleanUpDismissTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the menu will resize as a part of showing the full menu.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user