Merge changes I789945c1,I532b0928,I62c6df8b,Ib2bd81ad into oc-dev
* changes: Fixing missing movement bounds notification to SystemUI. Fixing animating bounds regression. Tightening up rotation behavior for PIP (2/3) Tightening up rotation behavior for PIP (1/3)
This commit is contained in:
committed by
Android (Google) Code Review
commit
4191d99614
@@ -86,6 +86,7 @@ public class PipManager implements BasePipManager {
|
||||
|
||||
ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext,
|
||||
mActivityManager);
|
||||
mMenuController.hideMenu();
|
||||
mNotificationController.onActivityUnpinned(topPipActivity);
|
||||
|
||||
SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null);
|
||||
|
||||
@@ -7864,9 +7864,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
|
||||
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
|
||||
final Rect sourceBounds = r.pictureInPictureArgs.getSourceRectHint();
|
||||
final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
|
||||
aspectRatio);
|
||||
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, destBounds,
|
||||
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
|
||||
true /* moveHomeStackToFront */, "enterPictureInPictureMode");
|
||||
final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
|
||||
stack.setPictureInPictureAspectRatio(aspectRatio);
|
||||
@@ -7927,7 +7925,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
// if it is not already expanding to fullscreen. Otherwise, the arguments will
|
||||
// be used the next time the activity enters PiP
|
||||
final PinnedActivityStack stack = r.getStack();
|
||||
if (!stack.isBoundsAnimatingToFullscreen()) {
|
||||
if (!stack.isAnimatingBoundsToFullscreen()) {
|
||||
stack.setPictureInPictureAspectRatio(
|
||||
r.pictureInPictureArgs.getAspectRatio());
|
||||
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
|
||||
|
||||
@@ -174,6 +174,7 @@ import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.am.ActivityStack.ActivityState;
|
||||
import com.android.server.wm.PinnedStackWindowController;
|
||||
import com.android.server.wm.WindowManagerService;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -2492,11 +2493,21 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
}
|
||||
|
||||
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
|
||||
final ActivityStack stack = getStack(PINNED_STACK_ID);
|
||||
final PinnedActivityStack stack = getStack(PINNED_STACK_ID);
|
||||
if (stack == null) {
|
||||
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// It is possible for the bounds animation from the WM to call this but be delayed by
|
||||
// another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
|
||||
// incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
|
||||
// for the AMS lock to be freed. So check and make sure these bounds are still good.
|
||||
final PinnedStackWindowController stackController = stack.getWindowContainerController();
|
||||
if (stackController.pinnedStackResizeAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
|
||||
mWindowManager.deferSurfaceLayout();
|
||||
try {
|
||||
@@ -2857,12 +2868,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
return false;
|
||||
}
|
||||
|
||||
moveActivityToPinnedStackLocked(r, null /* sourceBounds */, destBounds,
|
||||
moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
|
||||
true /* moveHomeStackToFront */, "moveTopActivityToPinnedStack");
|
||||
return true;
|
||||
}
|
||||
|
||||
void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, Rect destBounds,
|
||||
void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, float aspectRatio,
|
||||
boolean moveHomeStackToFront, String reason) {
|
||||
|
||||
mWindowManager.deferSurfaceLayout();
|
||||
@@ -2932,6 +2943,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
|
||||
resumeFocusedStackTopActivityLocked();
|
||||
|
||||
// Calculate the default bounds (don't use existing stack bounds as we may have just created
|
||||
// the stack
|
||||
final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
|
||||
aspectRatio, false /* useExistingStackBounds */);
|
||||
|
||||
// TODO(b/36099777): Schedule the PiP mode change here immediately until we can defer all
|
||||
// callbacks until after the bounds animation
|
||||
scheduleUpdatePictureInPictureModeIfNeeded(r.getTask(), destBounds, true /* immediate */);
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.graphics.Rect;
|
||||
|
||||
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
|
||||
import com.android.server.wm.PinnedStackWindowController;
|
||||
import com.android.server.wm.StackWindowController;
|
||||
import com.android.server.wm.PinnedStackWindowListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -29,7 +29,8 @@ import java.util.List;
|
||||
/**
|
||||
* State and management of the pinned stack of activities.
|
||||
*/
|
||||
class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> {
|
||||
class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
|
||||
implements PinnedStackWindowListener {
|
||||
|
||||
PinnedActivityStack(ActivityContainer activityContainer,
|
||||
RecentTasks recentTasks, boolean onTop) {
|
||||
@@ -55,8 +56,8 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> {
|
||||
getWindowContainerController().setPictureInPictureActions(actions);
|
||||
}
|
||||
|
||||
boolean isBoundsAnimatingToFullscreen() {
|
||||
return getWindowContainerController().isBoundsAnimatingToFullscreen();
|
||||
boolean isAnimatingBoundsToFullscreen() {
|
||||
return getWindowContainerController().isAnimatingBoundsToFullscreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,8 @@ import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.WindowManagerInternal;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Enables animating bounds of objects.
|
||||
*
|
||||
@@ -103,7 +105,8 @@ public class BoundsAnimationController {
|
||||
com.android.internal.R.interpolator.fast_out_slow_in);
|
||||
}
|
||||
|
||||
private final class BoundsAnimator extends ValueAnimator
|
||||
@VisibleForTesting
|
||||
final class BoundsAnimator extends ValueAnimator
|
||||
implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
|
||||
private final AnimateBoundsUser mTarget;
|
||||
private final Rect mFrom = new Rect();
|
||||
@@ -113,10 +116,12 @@ public class BoundsAnimationController {
|
||||
private final boolean mMoveToFullScreen;
|
||||
// True if this this animation was cancelled and will be replaced the another animation from
|
||||
// the same {@link #AnimateBoundsUser} target.
|
||||
private boolean mSkipAnimationEnd;
|
||||
private boolean mSkipFinalResize;
|
||||
// True if this animation replaced a previous animation of the same
|
||||
// {@link #AnimateBoundsUser} target.
|
||||
private final boolean mSkipAnimationStart;
|
||||
// True if this animation was cancelled by the user, not as a part of a replacing animation
|
||||
private boolean mSkipAnimationEnd;
|
||||
// True if this animation is not replacing a previous animation, or if the previous
|
||||
// animation is animating to a different fullscreen state than the current animation.
|
||||
// We use this to ensure that we always provide a consistent set/order of callbacks when we
|
||||
@@ -200,7 +205,11 @@ public class BoundsAnimationController {
|
||||
// Whoops, the target doesn't feel like animating anymore. Let's immediately finish
|
||||
// any further animation.
|
||||
if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
|
||||
animation.cancel();
|
||||
|
||||
// Since we are cancelling immediately without a replacement animation, send the
|
||||
// animation end to maintain callback parity, but also skip any further resizes
|
||||
prepareCancel(false /* skipAnimationEnd */, true /* skipFinalResize */);
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +217,7 @@ public class BoundsAnimationController {
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
|
||||
+ " mMoveToFullScreen=" + mMoveToFullScreen
|
||||
+ " mSkipAnimationEnd=" + mSkipAnimationEnd
|
||||
+ " mSkipFinalResize=" + mSkipFinalResize
|
||||
+ " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
|
||||
+ " mAppTransitionIsRunning=" + mAppTransition.isRunning());
|
||||
|
||||
@@ -222,10 +231,15 @@ public class BoundsAnimationController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSkipFinalResize) {
|
||||
// If not cancelled, resize the pinned stack to the final size. All calls to
|
||||
// setPinnedStackSize() must be done between onAnimationStart() and onAnimationEnd()
|
||||
mTarget.setPinnedStackSize(mTo, null);
|
||||
}
|
||||
|
||||
finishAnimation();
|
||||
|
||||
mTarget.setPinnedStackSize(mTo, null);
|
||||
if (mMoveToFullScreen && !mSkipAnimationEnd) {
|
||||
if (mMoveToFullScreen && !mSkipFinalResize) {
|
||||
mTarget.moveToFullscreen();
|
||||
}
|
||||
}
|
||||
@@ -235,10 +249,16 @@ public class BoundsAnimationController {
|
||||
finishAnimation();
|
||||
}
|
||||
|
||||
public void prepareCancel(boolean skipAnimationEnd, boolean skipFinalResize) {
|
||||
if (DEBUG) Slog.d(TAG, "prepareCancel: skipAnimationEnd=" + skipAnimationEnd
|
||||
+ " skipFinalResize=" + skipFinalResize);
|
||||
mSkipAnimationEnd = skipAnimationEnd;
|
||||
mSkipFinalResize = skipFinalResize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
mSkipAnimationEnd = true;
|
||||
if (DEBUG) Slog.d(TAG, "cancel: willReplace mTarget=" + mTarget);
|
||||
if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
@@ -273,19 +293,15 @@ public class BoundsAnimationController {
|
||||
|
||||
public interface AnimateBoundsUser {
|
||||
/**
|
||||
* Asks the target to directly (without any intermediate steps, like scheduling animation)
|
||||
* resize its bounds.
|
||||
*
|
||||
* @return Whether the target still wants to be animated and successfully finished the
|
||||
* operation. If it returns false, the animation will immediately be cancelled. The target
|
||||
* should return false when something abnormal happened, e.g. it was completely removed
|
||||
* from the hierarchy and is not valid anymore.
|
||||
*/
|
||||
boolean setSize(Rect bounds);
|
||||
/**
|
||||
* Behaves as setSize, but freezes the bounds of any tasks in the target at taskBounds,
|
||||
* Sets the size of the target (without any intermediate steps, like scheduling animation)
|
||||
* but freezes the bounds of any tasks in the target at taskBounds,
|
||||
* to allow for more flexibility during resizing. Only works for the pinned stack at the
|
||||
* moment.
|
||||
*
|
||||
* @return Whether the target should continue to be animated and this call was successful.
|
||||
* If false, the animation will be cancelled because the user has determined that the
|
||||
* animation is now invalid and not required. In such a case, the cancel will trigger the
|
||||
* animation end callback as well, but will not send any further size changes.
|
||||
*/
|
||||
boolean setPinnedStackSize(Rect bounds, Rect taskBounds);
|
||||
|
||||
@@ -310,11 +326,20 @@ public class BoundsAnimationController {
|
||||
*/
|
||||
void onAnimationEnd();
|
||||
|
||||
/**
|
||||
* Callback for the target to inform it to reparent to the fullscreen stack.
|
||||
*/
|
||||
void moveToFullscreen();
|
||||
}
|
||||
|
||||
void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration,
|
||||
boolean moveToFullscreen) {
|
||||
public void animateBounds(final AnimateBoundsUser target, Rect from, Rect to,
|
||||
int animationDuration, boolean moveToFullscreen) {
|
||||
animateBoundsImpl(target, from, to, animationDuration, moveToFullscreen);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
BoundsAnimator animateBoundsImpl(final AnimateBoundsUser target, Rect from, Rect to,
|
||||
int animationDuration, boolean moveToFullscreen) {
|
||||
final BoundsAnimator existing = mRunningAnimations.get(target);
|
||||
final boolean replacing = existing != null;
|
||||
final boolean animatingToNewFullscreenState = (existing == null) ||
|
||||
@@ -326,12 +351,15 @@ public class BoundsAnimationController {
|
||||
|
||||
if (replacing) {
|
||||
if (existing.isAnimatingTo(to)) {
|
||||
// Just les the current animation complete if it has the same destination as the
|
||||
// Just let the current animation complete if it has the same destination as the
|
||||
// one we are trying to start.
|
||||
if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
|
||||
+ " ignoring...");
|
||||
return;
|
||||
return existing;
|
||||
}
|
||||
// Since we are replacing, we skip both animation start and end callbacks, and don't
|
||||
// animate to the final bounds when cancelling
|
||||
existing.prepareCancel(true /* skipAnimationEnd */, true /* skipFinalResize */);
|
||||
existing.cancel();
|
||||
}
|
||||
final BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen,
|
||||
@@ -342,5 +370,6 @@ public class BoundsAnimationController {
|
||||
: DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
animator.start();
|
||||
return animator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1420,6 +1420,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
changedStackList.add(stack.mStackId);
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no pinned stack, we still need to notify the controller of the display info
|
||||
// update as a result of the config change. We do this here to consolidate the flow between
|
||||
// changes when there is and is not a stack.
|
||||
if (getStackById(PINNED_STACK_ID) == null) {
|
||||
mPinnedStackControllerLocked.onDisplayInfoChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -147,7 +147,6 @@ class PinnedStackController {
|
||||
|
||||
void onConfigurationChanged() {
|
||||
reloadResources();
|
||||
notifyMovementBoundsChanged(false /* fromImeAdjustment */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,14 +239,32 @@ class PinnedStackController {
|
||||
return defaultBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the case where the display rotation is changed but there is no stack, we can't depend on
|
||||
* onTaskStackBoundsChanged() to be called. But we still should update our known display info
|
||||
* with the new state so that we can update SystemUI.
|
||||
*/
|
||||
synchronized void onDisplayInfoChanged() {
|
||||
mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
|
||||
notifyMovementBoundsChanged(false /* fromImeAdjustment */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display info, calculating and returning the new stack and movement bounds in the
|
||||
* new orientation of the device if necessary.
|
||||
*/
|
||||
void onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
|
||||
boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
|
||||
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
|
||||
if (mDisplayInfo.equals(displayInfo)) {
|
||||
return;
|
||||
// We are already in the right orientation, ignore
|
||||
outBounds.setEmpty();
|
||||
return false;
|
||||
} else if (targetBounds.isEmpty()) {
|
||||
// The stack is null, we are just initializing the stack, so just store the display info
|
||||
// and ignore
|
||||
mDisplayInfo.copyFrom(displayInfo);
|
||||
outBounds.setEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
mTmpRect.set(targetBounds);
|
||||
@@ -272,6 +289,7 @@ class PinnedStackController {
|
||||
notifyMovementBoundsChanged(false /* fromImeAdjustment */);
|
||||
|
||||
outBounds.set(postChangeStackBounds);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +389,7 @@ class PinnedStackController {
|
||||
final Rect animatingBounds = mTmpAnimatingBoundsRect;
|
||||
final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
|
||||
if (pinnedStack != null) {
|
||||
pinnedStack.getAnimatingBounds(animatingBounds);
|
||||
pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
|
||||
} else {
|
||||
animatingBounds.set(normalBounds);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ public class PinnedStackWindowController extends StackWindowController {
|
||||
|
||||
private Rect mTmpBoundsRect = new Rect();
|
||||
|
||||
public PinnedStackWindowController(int stackId, StackWindowListener listener, int displayId,
|
||||
boolean onTop, Rect outBounds) {
|
||||
public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
|
||||
int displayId, boolean onTop, Rect outBounds) {
|
||||
super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class PinnedStackWindowController extends StackWindowController {
|
||||
|
||||
final Rect originalBounds = new Rect();
|
||||
mContainer.getBounds(originalBounds);
|
||||
mContainer.setAnimatingBounds(sourceBounds, toBounds);
|
||||
mContainer.setAnimationFinalBounds(sourceBounds, toBounds);
|
||||
UiThread.getHandler().post(() -> {
|
||||
if (mContainer == null) {
|
||||
return;
|
||||
@@ -84,9 +84,10 @@ public class PinnedStackWindowController extends StackWindowController {
|
||||
}
|
||||
|
||||
final int displayId = mContainer.getDisplayContent().getDisplayId();
|
||||
final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
|
||||
final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio,
|
||||
true /* useExistingStackBounds */);
|
||||
final Rect targetBounds = new Rect();
|
||||
mContainer.getAnimatingBounds(targetBounds);
|
||||
mContainer.getAnimationOrCurrentBounds(targetBounds);
|
||||
final PinnedStackController pinnedStackController =
|
||||
mContainer.getDisplayContent().getPinnedStackController();
|
||||
|
||||
@@ -117,8 +118,12 @@ public class PinnedStackWindowController extends StackWindowController {
|
||||
/**
|
||||
* @return whether the bounds are currently animating to fullscreen.
|
||||
*/
|
||||
public boolean isBoundsAnimatingToFullscreen() {
|
||||
return mContainer.isBoundsAnimatingToFullscreen();
|
||||
public boolean isAnimatingBoundsToFullscreen() {
|
||||
return mContainer.isAnimatingBoundsToFullscreen();
|
||||
}
|
||||
|
||||
public boolean pinnedStackResizeAllowed() {
|
||||
return mContainer.pinnedStackResizeAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,4 +137,16 @@ public class PinnedStackWindowController extends StackWindowController {
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following calls are made from WM to AM.
|
||||
*/
|
||||
|
||||
/** Calls directly into activity manager so window manager lock shouldn't held. */
|
||||
public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
|
||||
if (mListener != null) {
|
||||
PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
|
||||
listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* Interface used by the creator of {@link PinnedStackWindowController} to listen to changes with
|
||||
* the stack container.
|
||||
*/
|
||||
public interface PinnedStackWindowListener extends StackWindowListener {
|
||||
|
||||
/**
|
||||
* Called when the stack container pinned stack animation will change the picture-in-picture
|
||||
* mode. This is a direct call into ActivityManager.
|
||||
*/
|
||||
default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
|
||||
}
|
||||
@@ -368,13 +368,6 @@ public class StackWindowController
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls directly into activity manager so window manager lock shouldn't held. */
|
||||
void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
|
||||
if (mListener != null) {
|
||||
mListener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void requestResize(Rect bounds) {
|
||||
mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
|
||||
}
|
||||
|
||||
@@ -26,10 +26,4 @@ public interface StackWindowListener extends WindowContainerListener {
|
||||
|
||||
/** Called when the stack container would like its controller to resize. */
|
||||
void requestResize(Rect bounds);
|
||||
|
||||
/**
|
||||
* Called when the stack container pinned stack animation will change the picture-in-picture
|
||||
* mode. This is a direct call into ActivityManager.
|
||||
*/
|
||||
default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
|
||||
}
|
||||
|
||||
@@ -595,8 +595,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
|
||||
* we will have a jump at the end.
|
||||
*/
|
||||
boolean isFloating() {
|
||||
return StackId.tasksAreFloating(mStack.mStackId)
|
||||
&& !mStack.isBoundsAnimatingToFullscreen();
|
||||
return StackId.tasksAreFloating(mStack.mStackId) && !mStack.isAnimatingBoundsToFullscreen();
|
||||
}
|
||||
|
||||
WindowState getTopVisibleAppMainWindow() {
|
||||
|
||||
@@ -77,6 +77,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
/** For comparison with DisplayContent bounds. */
|
||||
private Rect mTmpRect = new Rect();
|
||||
private Rect mTmpRect2 = new Rect();
|
||||
private Rect mTmpRect3 = new Rect();
|
||||
|
||||
/** Content limits relative to the DisplayContent this sits in. */
|
||||
private Rect mBounds = new Rect();
|
||||
@@ -125,7 +126,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
// perfectly fit the region it would have been cropped to. We may also avoid certain logic we
|
||||
// would otherwise apply while resizing, while resizing in the bounds animating mode.
|
||||
private boolean mBoundsAnimating = false;
|
||||
// Set when an animation has been requested but has not yet started from the UI thread. This is
|
||||
// cleared when the animation actually starts.
|
||||
private boolean mBoundsAnimatingRequested = false;
|
||||
private boolean mBoundsAnimatingToFullscreen = false;
|
||||
private boolean mCancelCurrentBoundsAnimation = false;
|
||||
private Rect mBoundsAnimationTarget = new Rect();
|
||||
private Rect mBoundsAnimationSourceBounds = new Rect();
|
||||
|
||||
@@ -262,12 +267,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
|
||||
if (mDisplayContent != null) {
|
||||
mDisplayContent.mDimLayerController.updateDimLayer(this);
|
||||
if (mStackId == PINNED_STACK_ID) {
|
||||
// Update the bounds based on any changes to the display info
|
||||
getAnimatingBounds(mTmpRect2);
|
||||
mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(mTmpRect2,
|
||||
bounds);
|
||||
}
|
||||
mAnimationBackgroundSurface.setBounds(bounds);
|
||||
}
|
||||
|
||||
@@ -320,10 +319,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bounds animation target bounds. This can't currently be done in onAnimationStart()
|
||||
* since that is started on the UiThread.
|
||||
* Sets the bounds animation target bounds ahead of an animation. This can't currently be done
|
||||
* in onAnimationStart() since that is started on the UiThread.
|
||||
*/
|
||||
void setAnimatingBounds(Rect sourceBounds, Rect destBounds) {
|
||||
void setAnimationFinalBounds(Rect sourceBounds, Rect destBounds) {
|
||||
mBoundsAnimatingRequested = true;
|
||||
if (sourceBounds != null) {
|
||||
mBoundsAnimationSourceBounds.set(sourceBounds);
|
||||
} else {
|
||||
@@ -337,23 +337,26 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the source bounds for the bounds animation.
|
||||
* @return the final bounds for the bounds animation.
|
||||
*/
|
||||
void getAnimatingSourceBounds(Rect outBounds) {
|
||||
if (!mBoundsAnimationSourceBounds.isEmpty()) {
|
||||
outBounds.set(mBoundsAnimationSourceBounds);
|
||||
return;
|
||||
}
|
||||
outBounds.setEmpty();
|
||||
void getFinalAnimationBounds(Rect outBounds) {
|
||||
outBounds.set(mBoundsAnimationTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bounds that the task stack is currently being animated towards, or the current
|
||||
* stack bounds if there is no animation in progress.
|
||||
* @return the final source bounds for the bounds animation.
|
||||
*/
|
||||
void getAnimatingBounds(Rect outBounds) {
|
||||
if (!mBoundsAnimationTarget.isEmpty()) {
|
||||
outBounds.set(mBoundsAnimationTarget);
|
||||
void getFinalAnimationSourceBounds(Rect outBounds) {
|
||||
outBounds.set(mBoundsAnimationSourceBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the final animation bounds if the task stack is currently being animated, or the
|
||||
* current stack bounds otherwise.
|
||||
*/
|
||||
void getAnimationOrCurrentBounds(Rect outBounds) {
|
||||
if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
|
||||
getFinalAnimationBounds(outBounds);
|
||||
return;
|
||||
}
|
||||
getBounds(outBounds);
|
||||
@@ -398,6 +401,24 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
// as it's going away soon anyway.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mStackId == PINNED_STACK_ID) {
|
||||
getAnimationOrCurrentBounds(mTmpRect2);
|
||||
boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
|
||||
mTmpRect2, mTmpRect3);
|
||||
if (updated) {
|
||||
mBoundsAfterRotation.set(mTmpRect3);
|
||||
|
||||
// Once we've set the bounds based on the rotation of the old bounds in the new
|
||||
// orientation, clear the animation target bounds since they are obsolete, and
|
||||
// cancel any currently running animations
|
||||
mBoundsAnimationTarget.setEmpty();
|
||||
mBoundsAnimationSourceBounds.setEmpty();
|
||||
mCancelCurrentBoundsAnimation = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final int newRotation = getDisplayInfo().rotation;
|
||||
final int newDensity = getDisplayInfo().logicalDensityDpi;
|
||||
|
||||
@@ -413,20 +434,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StackId.tasksAreFloating(mStackId)) {
|
||||
// Update stack bounds again since the display info has changed since updateDisplayInfo,
|
||||
// however, for floating tasks, we don't need to apply the new rotation to the bounds,
|
||||
// we can just update and return them here
|
||||
setBounds(mBounds);
|
||||
mBoundsAfterRotation.set(mBounds);
|
||||
|
||||
// Once we've set the bounds based on the rotation of the old bounds in the new
|
||||
// orientation, clear the animation target bounds since they are obsolete
|
||||
mBoundsAnimationTarget.setEmpty();
|
||||
mBoundsAnimationSourceBounds.setEmpty();
|
||||
return true;
|
||||
}
|
||||
|
||||
mTmpRect2.set(mBounds);
|
||||
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
|
||||
switch (mStackId) {
|
||||
@@ -692,6 +699,14 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
|
||||
mDisplayContent.mDividerControllerLocked.getContentWidth(),
|
||||
dockedOnTopOrLeft);
|
||||
} else if (mStackId == PINNED_STACK_ID) {
|
||||
// Update the bounds based on any changes to the display info
|
||||
getAnimationOrCurrentBounds(mTmpRect2);
|
||||
boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
|
||||
mTmpRect2, mTmpRect3);
|
||||
if (updated) {
|
||||
bounds = new Rect(mTmpRect3);
|
||||
}
|
||||
}
|
||||
|
||||
updateDisplayInfo(bounds);
|
||||
@@ -1443,21 +1458,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
}
|
||||
}
|
||||
|
||||
@Override // AnimatesBounds
|
||||
public boolean setSize(Rect bounds) {
|
||||
synchronized (mService.mWindowMap) {
|
||||
if (mDisplayContent == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false, -1);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) {
|
||||
if (mCancelCurrentBoundsAnimation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds);
|
||||
} catch (RemoteException e) {
|
||||
@@ -1469,8 +1474,10 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
@Override // AnimatesBounds
|
||||
public void onAnimationStart(boolean toFullscreen) {
|
||||
synchronized (mService.mWindowMap) {
|
||||
mBoundsAnimatingRequested = false;
|
||||
mBoundsAnimating = true;
|
||||
mBoundsAnimatingToFullscreen = toFullscreen;
|
||||
mCancelCurrentBoundsAnimation = false;
|
||||
}
|
||||
|
||||
if (mStackId == PINNED_STACK_ID) {
|
||||
@@ -1484,7 +1491,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
|
||||
@Override // AnimatesBounds
|
||||
public void updatePictureInPictureMode(Rect targetStackBounds) {
|
||||
final StackWindowController controller = getController();
|
||||
final PinnedStackWindowController controller =
|
||||
(PinnedStackWindowController) getController();
|
||||
if (controller != null) {
|
||||
controller.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
|
||||
}
|
||||
@@ -1523,14 +1531,21 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|
||||
return mBoundsAnimating;
|
||||
}
|
||||
|
||||
public boolean getBoundsAnimating() {
|
||||
public boolean isAnimatingBounds() {
|
||||
return mBoundsAnimating;
|
||||
}
|
||||
|
||||
public boolean isBoundsAnimatingToFullscreen() {
|
||||
public boolean isAnimatingBoundsToFullscreen() {
|
||||
return mBoundsAnimating && mBoundsAnimatingToFullscreen;
|
||||
}
|
||||
|
||||
public boolean pinnedStackResizeAllowed() {
|
||||
if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if a removal action is still being deferred. */
|
||||
boolean checkCompleteDeferredRemoval() {
|
||||
if (isAnimating()) {
|
||||
|
||||
@@ -31,7 +31,6 @@ import static android.os.Process.SHELL_UID;
|
||||
import static android.os.Process.SYSTEM_UID;
|
||||
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myTid;
|
||||
import static android.os.UserHandle.USER_NULL;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.DOCKED_INVALID;
|
||||
@@ -148,7 +147,6 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.PowerSaveState;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.StrictMode;
|
||||
@@ -2758,7 +2756,12 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
mDockedStackCreateBounds = bounds;
|
||||
}
|
||||
|
||||
public Rect getPictureInPictureBounds(int displayId, float aspectRatio) {
|
||||
/**
|
||||
* @param useExistingStackBounds Apply {@param aspectRatio} to the existing target stack bounds
|
||||
* if possible
|
||||
*/
|
||||
public Rect getPictureInPictureBounds(int displayId, float aspectRatio,
|
||||
boolean useExistingStackBounds) {
|
||||
synchronized (mWindowMap) {
|
||||
if (!mSupportsPictureInPicture) {
|
||||
return null;
|
||||
@@ -2773,11 +2776,11 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
final PinnedStackController pinnedStackController =
|
||||
displayContent.getPinnedStackController();
|
||||
final TaskStack stack = displayContent.getStackById(PINNED_STACK_ID);
|
||||
if (stack != null) {
|
||||
if (stack != null && useExistingStackBounds) {
|
||||
// If the stack exists, then use its final bounds to calculate the new aspect ratio
|
||||
// bounds.
|
||||
stackBounds = new Rect();
|
||||
stack.getAnimatingBounds(stackBounds);
|
||||
stack.getAnimationOrCurrentBounds(stackBounds);
|
||||
} else {
|
||||
// Otherwise, just calculate the aspect ratio bounds from the default bounds
|
||||
stackBounds = pinnedStackController.getDefaultBounds();
|
||||
|
||||
@@ -1090,7 +1090,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
// notify the client of frame changes in this case. Not only is it a lot of churn, but
|
||||
// the frame may not correspond to the surface size or the onscreen area at various
|
||||
// phases in the animation, and the client will become sad and confused.
|
||||
if (task != null && task.mStack.getBoundsAnimating()) {
|
||||
if (task != null && task.mStack.isAnimatingBounds()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1360,11 +1360,11 @@ class WindowStateAnimator {
|
||||
int posX = mTmpSize.left;
|
||||
int posY = mTmpSize.top;
|
||||
task.mStack.getDimBounds(mTmpStackBounds);
|
||||
task.mStack.getAnimatingSourceBounds(mTmpSourceBounds);
|
||||
task.mStack.getFinalAnimationSourceBounds(mTmpSourceBounds);
|
||||
if (!mTmpSourceBounds.isEmpty()) {
|
||||
// Get the final target stack bounds, if we are not animating, this is just the
|
||||
// current stack bounds
|
||||
task.mStack.getAnimatingBounds(mTmpAnimatingBounds);
|
||||
task.mStack.getFinalAnimationBounds(mTmpAnimatingBounds);
|
||||
|
||||
// Calculate the current progress and interpolate the difference between the target
|
||||
// and source bounds
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.annotation.UiThreadTest;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.WindowManagerInternal.AppTransitionListener;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
|
||||
|
||||
/**
|
||||
* Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
|
||||
* depending on the various interactions.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BoundsAnimationControllerTests extends WindowTestsBase {
|
||||
|
||||
/**
|
||||
* Mock value animator to simulate updates with.
|
||||
*/
|
||||
private class MockValueAnimator extends ValueAnimator {
|
||||
|
||||
private float mFraction;
|
||||
|
||||
public MockValueAnimator getWithValue(float fraction) {
|
||||
mFraction = fraction;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAnimatedValue() {
|
||||
return mFraction;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock app transition to fire notifications to the bounds animator.
|
||||
*/
|
||||
private class MockAppTransition extends AppTransition {
|
||||
|
||||
private AppTransitionListener mListener;
|
||||
|
||||
MockAppTransition(Context context) {
|
||||
super(context, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void registerListenerLocked(AppTransitionListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void notifyTransitionPending() {
|
||||
mListener.onAppTransitionPendingLocked();
|
||||
}
|
||||
|
||||
public void notifyTransitionCancelled(int transit) {
|
||||
mListener.onAppTransitionCancelledLocked(transit);
|
||||
}
|
||||
|
||||
public void notifyTransitionStarting(int transit) {
|
||||
mListener.onAppTransitionStartingLocked(transit, null, null, null, null);
|
||||
}
|
||||
|
||||
public void notifyTransitionFinished() {
|
||||
mListener.onAppTransitionFinishedLocked(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test animate bounds user to track callbacks from the bounds animation.
|
||||
*/
|
||||
private class TestAnimateBoundsUser implements BoundsAnimationController.AnimateBoundsUser {
|
||||
|
||||
boolean mMovedToFullscreen;
|
||||
boolean mAnimationStarted;
|
||||
boolean mAnimationStartedToFullscreen;
|
||||
boolean mAnimationEnded;
|
||||
boolean mUpdatedPictureInPictureModeWithBounds;
|
||||
boolean mBoundsUpdated;
|
||||
Rect mStackBounds;
|
||||
Rect mTaskBounds;
|
||||
|
||||
boolean mRequestCancelAnimation = false;
|
||||
|
||||
void reinitialize(Rect stackBounds, Rect taskBounds) {
|
||||
mMovedToFullscreen = false;
|
||||
mAnimationStarted = false;
|
||||
mAnimationStartedToFullscreen = false;
|
||||
mAnimationEnded = false;
|
||||
mUpdatedPictureInPictureModeWithBounds = false;
|
||||
mStackBounds = stackBounds;
|
||||
mTaskBounds = taskBounds;
|
||||
mBoundsUpdated = false;
|
||||
mRequestCancelAnimation = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(boolean toFullscreen) {
|
||||
mAnimationStarted = true;
|
||||
mAnimationStartedToFullscreen = toFullscreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePictureInPictureMode(Rect targetStackBounds) {
|
||||
mUpdatedPictureInPictureModeWithBounds = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
|
||||
// TODO: Once we break the runs apart, we should fail() here if this is called outside
|
||||
// of onAnimationStart() and onAnimationEnd()
|
||||
if (mRequestCancelAnimation) {
|
||||
return false;
|
||||
} else {
|
||||
mBoundsUpdated = true;
|
||||
mStackBounds = stackBounds;
|
||||
mTaskBounds = taskBounds;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd() {
|
||||
mAnimationEnded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveToFullscreen() {
|
||||
mMovedToFullscreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static final boolean MOVE_TO_FULLSCREEN = true;
|
||||
|
||||
// Some dummy bounds to represent fullscreen and floating bounds
|
||||
private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100);
|
||||
private static final Rect BOUNDS_FLOATING = new Rect(80, 80, 95, 95);
|
||||
private static final Rect BOUNDS_ALT_FLOATING = new Rect(60, 60, 95, 95);
|
||||
|
||||
// Some dummy duration
|
||||
private static final int DURATION = 100;
|
||||
|
||||
// Common
|
||||
private MockAppTransition mAppTransition;
|
||||
private MockValueAnimator mAnimator;
|
||||
private TestAnimateBoundsUser mTarget;
|
||||
private BoundsAnimationController mController;
|
||||
|
||||
// Temp
|
||||
private Rect mTmpRect = new Rect();
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
mAppTransition = new MockAppTransition(context);
|
||||
mAnimator = new MockValueAnimator();
|
||||
mTarget = new TestAnimateBoundsUser();
|
||||
mController = new BoundsAnimationController(context, mAppTransition, handler);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testFullscreenToFloatingTransition() throws Exception {
|
||||
// Create and start the animation
|
||||
mTarget.reinitialize(BOUNDS_FULL, null);
|
||||
final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
|
||||
BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
|
||||
|
||||
// Assert that when we are started, and that we are not going to fullscreen
|
||||
assertTrue(mTarget.mAnimationStarted);
|
||||
assertFalse(mTarget.mAnimationStartedToFullscreen);
|
||||
// Ensure we are not triggering a PiP mode change
|
||||
assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
|
||||
// Ensure that the task stack bounds are already frozen to the larger source stack bounds
|
||||
assertEquals(BOUNDS_FULL, mTarget.mStackBounds);
|
||||
assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
|
||||
|
||||
// Drive some animation updates, ensure that only the stack bounds change and the task
|
||||
// bounds are frozen to the original stack bounds (adjusted for the offset)
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
|
||||
assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds);
|
||||
assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f));
|
||||
assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds);
|
||||
assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
|
||||
|
||||
// Finish the animation, ensure that it reaches the final bounds with the given state
|
||||
boundsAnimator.end();
|
||||
assertTrue(mTarget.mAnimationEnded);
|
||||
assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
|
||||
assertNull(mTarget.mTaskBounds);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testFloatingToFullscreenTransition() throws Exception {
|
||||
// Create and start the animation
|
||||
mTarget.reinitialize(BOUNDS_FULL, null);
|
||||
final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FLOATING,
|
||||
BOUNDS_FULL, DURATION, MOVE_TO_FULLSCREEN);
|
||||
|
||||
// Assert that when we are started, and that we are going to fullscreen
|
||||
assertTrue(mTarget.mAnimationStarted);
|
||||
assertTrue(mTarget.mAnimationStartedToFullscreen);
|
||||
// Ensure that we update the PiP mode change with the new fullscreen bounds
|
||||
assertTrue(mTarget.mUpdatedPictureInPictureModeWithBounds);
|
||||
// Ensure that the task stack bounds are already frozen to the larger target stack bounds
|
||||
assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
|
||||
assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
|
||||
|
||||
// Drive some animation updates, ensure that only the stack bounds change and the task
|
||||
// bounds are frozen to the original stack bounds (adjusted for the offset)
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
|
||||
assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
|
||||
assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f));
|
||||
assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
|
||||
assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
|
||||
|
||||
// Finish the animation, ensure that it reaches the final bounds with the given state
|
||||
boundsAnimator.end();
|
||||
assertTrue(mTarget.mAnimationEnded);
|
||||
assertEquals(BOUNDS_FULL, mTarget.mStackBounds);
|
||||
assertNull(mTarget.mTaskBounds);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testInterruptAnimationFromUser() throws Exception {
|
||||
// Create and start the animation
|
||||
mTarget.reinitialize(BOUNDS_FULL, null);
|
||||
final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
|
||||
BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
|
||||
|
||||
// Cancel the animation on the next update from the user
|
||||
mTarget.mRequestCancelAnimation = true;
|
||||
mTarget.mBoundsUpdated = false;
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
|
||||
// Ensure that we got no more updates after returning false and the bounds are not updated
|
||||
// to the end value
|
||||
assertFalse(mTarget.mBoundsUpdated);
|
||||
assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
|
||||
assertNotEquals(BOUNDS_FLOATING, mTarget.mTaskBounds);
|
||||
// Ensure that we received the animation end call
|
||||
assertTrue(mTarget.mAnimationEnded);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testCancelAnimationFromNewAnimationToExistingBounds() throws Exception {
|
||||
// Create and start the animation
|
||||
mTarget.reinitialize(BOUNDS_FULL, null);
|
||||
final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
|
||||
BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
|
||||
|
||||
// Drive some animation updates
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
|
||||
|
||||
// Cancel the animation as a restart to the same bounds
|
||||
mTarget.reinitialize(null, null);
|
||||
final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
|
||||
BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
|
||||
// Ensure the animator is the same
|
||||
assertSame(boundsAnimator, altBoundsAnimator);
|
||||
// Ensure we haven't restarted or finished the animation
|
||||
assertFalse(mTarget.mAnimationStarted);
|
||||
assertFalse(mTarget.mAnimationEnded);
|
||||
// Ensure that we haven't tried to update the PiP mode
|
||||
assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testCancelAnimationFromNewAnimationToNewBounds() throws Exception {
|
||||
// Create and start the animation
|
||||
mTarget.reinitialize(BOUNDS_FULL, null);
|
||||
final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
|
||||
BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
|
||||
|
||||
// Drive some animation updates
|
||||
boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
|
||||
|
||||
// Cancel the animation as a restart to new bounds
|
||||
mTarget.reinitialize(null, null);
|
||||
final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
|
||||
BOUNDS_ALT_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
|
||||
// Ensure the animator is not the same
|
||||
assertNotSame(boundsAnimator, altBoundsAnimator);
|
||||
// Ensure that we did not get an animation start/end callback
|
||||
assertFalse(mTarget.mAnimationStarted);
|
||||
assertFalse(mTarget.mAnimationEnded);
|
||||
// Ensure that we haven't tried to update the PiP mode
|
||||
assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bounds offset to zero/zero.
|
||||
*/
|
||||
private Rect offsetToZero(Rect bounds) {
|
||||
mTmpRect.set(bounds);
|
||||
mTmpRect.offsetTo(0, 0);
|
||||
return mTmpRect;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user