Merge "Ensure that PiP mode changed callback if animation is interrupted" into oc-mr1-dev

This commit is contained in:
Winson Chung
2017-08-31 23:01:48 +00:00
committed by Android (Google) Code Review
9 changed files with 90 additions and 63 deletions

View File

@@ -659,14 +659,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
}
void updatePictureInPictureMode(Rect targetStackBounds) {
void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
if (task == null || task.getStack() == null || app == null || app.thread == null) {
return;
}
final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
(targetStackBounds != null);
if (inPictureInPictureMode != mLastReportedPictureInPictureMode) {
if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order
mLastReportedPictureInPictureMode = inPictureInPictureMode;
@@ -681,8 +681,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
app.thread.schedulePictureInPictureModeChanged(appToken,
mLastReportedPictureInPictureMode,
overrideConfig);
mLastReportedPictureInPictureMode, overrideConfig);
} catch (Exception e) {
// If process died, no one cares.
}

View File

@@ -4410,31 +4410,29 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return;
}
scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */);
scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds);
}
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds,
boolean immediate) {
if (immediate) {
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
r.updatePictureInPictureMode(targetStackBounds);
}
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
mPipModeChangedActivities.add(r);
}
} else {
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
mPipModeChangedActivities.add(r);
}
}
mPipModeChangedTargetStackBounds = targetStackBounds;
}
mPipModeChangedTargetStackBounds = targetStackBounds;
if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
}
}
void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
}
}
@@ -4496,7 +4494,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
synchronized (mService) {
for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPipModeChangedActivities.remove(i);
r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds);
r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
false /* forceUpdate */);
}
}
} break;

View File

@@ -91,15 +91,16 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
return mWindowContainerController.deferScheduleMultiWindowModeChanged();
}
public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
boolean forceUpdate) {
// It is guaranteed that the activities requiring the update will be in the pinned stack at
// this point (either reparented before the animation into PiP, or before reparenting after
// the animation out of PiP)
synchronized(this) {
ArrayList<TaskRecord> tasks = getAllTasks();
for (int i = 0; i < tasks.size(); i++ ) {
mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(tasks.get(i),
targetStackBounds, true /* immediate */);
mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
forceUpdate);
}
}
}

View File

@@ -21,7 +21,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.AnimationHandler;
import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -32,13 +31,11 @@ import android.os.IBinder;
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Choreographer;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.WindowManagerInternal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -142,9 +139,6 @@ public class BoundsAnimationController {
// True if this this animation was canceled and will be replaced the another animation from
// the same {@link #BoundsAnimationTarget} target.
private boolean mSkipFinalResize;
// True if this animation replaced a previous animation of the same
// {@link #BoundsAnimationTarget} target.
private final boolean mSkipAnimationStart;
// True if this animation was canceled by the user, not as a part of a replacing animation
private boolean mSkipAnimationEnd;
@@ -159,6 +153,7 @@ public class BoundsAnimationController {
// Whether to schedule PiP mode changes on animation start/end
private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
// Depending on whether we are animating from
// a smaller to a larger size
@@ -171,14 +166,14 @@ public class BoundsAnimationController {
BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
@SchedulePipModeChangedState int schedulePipModeChangedState,
boolean moveFromFullscreen, boolean moveToFullscreen,
boolean replacingExistingAnimation) {
@SchedulePipModeChangedState int prevShedulePipModeChangedState,
boolean moveFromFullscreen, boolean moveToFullscreen) {
super();
mTarget = target;
mFrom.set(from);
mTo.set(to);
mSkipAnimationStart = replacingExistingAnimation;
mSchedulePipModeChangedState = schedulePipModeChangedState;
mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
mMoveFromFullscreen = moveFromFullscreen;
mMoveToFullscreen = moveToFullscreen;
addUpdateListener(this);
@@ -200,7 +195,7 @@ public class BoundsAnimationController {
@Override
public void onAnimationStart(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
+ " mSkipAnimationStart=" + mSkipAnimationStart
+ " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
+ " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
mFinishAnimationAfterTransition = false;
mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
@@ -210,18 +205,26 @@ public class BoundsAnimationController {
// running
updateBooster();
// Ensure that we have prepared the target for animation before
// we trigger any size changes, so it can swap surfaces
// in to appropriate modes, or do as it wishes otherwise.
if (!mSkipAnimationStart) {
// Ensure that we have prepared the target for animation before we trigger any size
// changes, so it can swap surfaces in to appropriate modes, or do as it wishes
// otherwise.
if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
mTarget.onAnimationStart(mSchedulePipModeChangedState ==
SCHEDULE_PIP_MODE_CHANGED_ON_START);
SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
// When starting an animation from fullscreen, pause here and wait for the
// windows-drawn signal before we start the rest of the transition down into PiP.
if (mMoveFromFullscreen) {
pause();
}
} else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
// We are replacing a running animation into PiP, but since it hasn't completed, the
// client will not currently receive any picture-in-picture mode change callbacks.
// However, we still need to report to them that they are leaving PiP, so this will
// force an update via a mode changed callback.
mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
true /* forceUpdate */);
}
// Immediately update the task bounds if they have to become larger, but preserve
@@ -388,6 +391,8 @@ public class BoundsAnimationController {
boolean moveFromFullscreen, boolean moveToFullscreen) {
final BoundsAnimator existing = mRunningAnimations.get(target);
final boolean replacing = existing != null;
@SchedulePipModeChangedState int prevSchedulePipModeChangedState =
NO_PIP_MODE_CHANGED_CALLBACKS;
if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
+ " schedulePipModeChangedState=" + schedulePipModeChangedState
@@ -403,6 +408,9 @@ public class BoundsAnimationController {
return existing;
}
// Save the previous state
prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
// Update the PiP callback states if we are replacing the animation
if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
@@ -428,7 +436,8 @@ public class BoundsAnimationController {
existing.cancel();
}
final BoundsAnimator animator = new BoundsAnimator(target, from, to,
schedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, replacing);
schedulePipModeChangedState, prevSchedulePipModeChangedState,
moveFromFullscreen, moveToFullscreen);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
animator.setDuration((animationDuration != -1 ? animationDuration

View File

@@ -31,7 +31,7 @@ interface BoundsAnimationTarget {
* @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
* callbacks
*/
void onAnimationStart(boolean schedulePipModeChangedCallback);
void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
/**
* Sets the size of the target (without any intermediate steps, like scheduling animation)

View File

@@ -202,10 +202,12 @@ public class PinnedStackWindowController extends StackWindowController {
*/
/** Calls directly into activity manager so window manager lock shouldn't held. */
public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
boolean forceUpdate) {
if (mListener != null) {
PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds,
forceUpdate);
}
}
}

View File

@@ -28,5 +28,6 @@ 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) {}
default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
boolean forceUpdate) {}
}

View File

@@ -1498,7 +1498,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
@Override // AnimatesBounds
public void onAnimationStart(boolean schedulePipModeChangedCallback) {
public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mService.mWindowMap) {
mBoundsAnimatingRequested = false;
@@ -1523,9 +1523,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
final PinnedStackWindowController controller =
(PinnedStackWindowController) getController();
if (schedulePipModeChangedCallback && controller != null) {
// We need to schedule the PiP mode change after the animation down, so use the
// final bounds
controller.updatePictureInPictureModeForPinnedStackAnimation(null);
// We need to schedule the PiP mode change before the animation up. It is possible
// in this case for the animation down to not have been completed, so always
// force-schedule and update to the client to ensure that it is notified that it
// is no longer in picture-in-picture mode
controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
}
}
}
@@ -1553,7 +1555,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
// We need to schedule the PiP mode change after the animation down, so use the
// final bounds
controller.updatePictureInPictureModeForPinnedStackAnimation(
mBoundsAnimationTarget);
mBoundsAnimationTarget, false /* forceUpdate */);
}
if (finalStackSize != null) {

View File

@@ -126,6 +126,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
boolean mMovedToFullscreen;
boolean mAnimationStarted;
boolean mSchedulePipModeChangedOnStart;
boolean mForcePipModeChangedCallback;
boolean mAnimationEnded;
Rect mAnimationEndFinalStackBounds;
boolean mSchedulePipModeChangedOnEnd;
@@ -140,6 +141,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mAnimationStarted = false;
mAnimationEnded = false;
mAnimationEndFinalStackBounds = null;
mForcePipModeChangedCallback = false;
mSchedulePipModeChangedOnStart = false;
mSchedulePipModeChangedOnEnd = false;
mStackBounds = from;
@@ -148,10 +150,11 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
}
@Override
public void onAnimationStart(boolean schedulePipModeChangedCallback) {
public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
mAwaitingAnimationStart = false;
mAnimationStarted = true;
mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
mForcePipModeChangedCallback = forceUpdate;
}
@Override
@@ -232,7 +235,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
return this;
}
BoundsAnimationDriver restart(Rect to) {
BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
if (mAnimator == null) {
throw new IllegalArgumentException("Call start() to start a new animation");
}
@@ -251,8 +254,15 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
assertSame(oldAnimator, mAnimator);
}
// No animation start for replacing animation
assertTrue(!mTarget.mAnimationStarted);
if (expectStartedAndPipModeChangedCallback) {
// Replacing animation with pending pip mode changed callback, ensure we update
assertTrue(mTarget.mAnimationStarted);
assertTrue(mTarget.mSchedulePipModeChangedOnStart);
assertTrue(mTarget.mForcePipModeChangedCallback);
} else {
// No animation start for replacing animation
assertTrue(!mTarget.mAnimationStarted);
}
mTarget.mAnimationStarted = true;
return this;
}
@@ -467,7 +477,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_FLOATING)
.restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@@ -478,7 +488,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_SMALLER_FLOATING)
.restart(BOUNDS_SMALLER_FLOATING,
false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@@ -486,10 +497,12 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
@UiThreadTest
@Test
public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
// When animating from fullscreen and the animation is interruped, we expect the animation
// start callback to be made, with a forced pip mode change callback
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_FULL)
.restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@@ -512,7 +525,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_FULL)
.restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@@ -523,7 +536,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase {
mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_SMALLER_FLOATING)
.restart(BOUNDS_SMALLER_FLOATING,
false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}