Merge "Apply fixed rotation animation for pip to fullscreen" into sc-dev am: ca7f10993f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13425308

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I64eee759eff3ea5147d45dc431a45cfbb7470df7
This commit is contained in:
TreeHugger Robot
2021-02-09 07:17:29 +00:00
committed by Automerger Merge Worker
12 changed files with 241 additions and 87 deletions

View File

@@ -5685,6 +5685,13 @@ public final class ActivityThread extends ClientTransactionHandler {
final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
amOverrideConfig, contextThemeWrapperOverrideConfig);
mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
final Resources res = activity.getResources();
if (res.hasOverrideDisplayAdjustments()) {
// If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
// configuration of activity may be sent later than the adjustments. In this case, the
// adjustments need to be updated for the consistency of display info.
res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
}
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = new Configuration(newConfig);

View File

@@ -16,6 +16,9 @@
package com.android.wm.shell.pip;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.RectEvaluator;
@@ -24,11 +27,13 @@ import android.annotation.IntDef;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -132,15 +137,21 @@ public class PipAnimationController {
* leash bounds before transformation/any animation. This is so when we try to construct
* the different transformation matrices for the animation, we are constructing this based off
* the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
*
* If non-zero {@param rotationDelta} is given, it means that the display will be rotated by
* leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
* rotation change.
*/
@VisibleForTesting
public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, float startingAngle) {
@PipAnimationController.TransitionDirection int direction, float startingAngle,
@Surface.Rotation int rotationDelta) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
endBounds, sourceHintRect, direction, 0 /* startingAngle */));
endBounds, sourceHintRect, direction, 0 /* startingAngle */,
rotationDelta));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -156,7 +167,7 @@ public class PipAnimationController {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
endBounds, sourceHintRect, direction, startingAngle));
endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
}
return mCurrentAnimator;
}
@@ -410,7 +421,8 @@ public class PipAnimationController {
static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, float startingAngle) {
@PipAnimationController.TransitionDirection int direction, float startingAngle,
@Surface.Rotation int rotationDelta) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
final Rect initialSourceValue;
@@ -431,6 +443,16 @@ public class PipAnimationController {
}
final Rect sourceInsets = new Rect(0, 0, 0, 0);
final Rect rotatedEndRect;
if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
// Rotate the end bounds according to the rotation delta because the display will
// be rotated to the same orientation.
rotatedEndRect = new Rect(endValue);
DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
} else {
rotatedEndRect = null;
}
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
@@ -444,6 +466,12 @@ public class PipAnimationController {
final Rect base = getBaseValue();
final Rect start = getStartValue();
final Rect end = getEndValue();
if (rotatedEndRect != null) {
// Animate the bounds in a different orientation. It only happens when
// leaving PiP to fullscreen.
applyRotation(tx, leash, fraction, start, end, rotatedEndRect);
return;
}
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
float angle = (1.0f - fraction) * startingAngle;
setCurrentValue(bounds);
@@ -469,6 +497,25 @@ public class PipAnimationController {
tx.apply();
}
private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
float fraction, Rect start, Rect end, Rect rotatedEndRect) {
final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect);
setCurrentValue(bounds);
final float degree, x, y;
if (rotationDelta == ROTATION_90) {
degree = 90 * fraction;
x = fraction * (end.right - start.left) + start.left;
y = fraction * (end.top - start.top) + start.top;
} else {
degree = -90 * fraction;
x = fraction * (end.left - start.left) + start.left;
y = fraction * (end.bottom - start.top) + start.top;
}
getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds,
rotatedEndRect, degree, x, y);
tx.apply();
}
@Override
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()

View File

@@ -126,6 +126,32 @@ public class PipSurfaceTransactionHelper {
return this;
}
/**
* Operates the rotation according to the given degrees and scale (setMatrix) according to the
* source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees,
float positionX, float positionY) {
mTmpDestinationRect.set(sourceBounds);
final int dw = destinationBounds.width();
final int dh = destinationBounds.height();
// Scale by the short side so there won't be empty area if the aspect ratio of source and
// destination are different.
final float scale = dw <= dh
? (float) sourceBounds.width() / dw
: (float) sourceBounds.height() / dh;
// Inverse scale for crop to fit in screen coordinates.
mTmpDestinationRect.scale(1 / scale);
mTmpTransform.setRotate(degrees);
mTmpTransform.postScale(scale, scale);
mTmpTransform.postTranslate(positionX, positionY);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height());
return this;
}
/**
* Resets the scale (setMatrix) on a given transaction and leash if there's any
*

View File

@@ -48,13 +48,12 @@ import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Rational;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
@@ -72,8 +71,6 @@ import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
@@ -134,7 +131,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
private final Optional<LegacySplitScreen> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -191,6 +187,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
private boolean mWaitForFixedRotation;
/**
* The rotation that the display will apply after expanding PiP to fullscreen. This is only
* meaningful if {@link #mWaitForFixedRotation} is true.
*/
private @Surface.Rotation int mNextRotation;
/**
* If set to {@code true}, no entering PiP transition would be kicked off and most likely
* it's due to the fact that Launcher is handling the transition directly when swiping
@@ -313,61 +315,40 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
if (initialConfig == null) {
Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
return;
}
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
!= mPipBoundsState.getDisplayLayout().rotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
if (orientationDiffers) {
mState = State.EXITING_PIP;
// Send started callback though animation is ignored.
sendOnPipTransitionStarted(direction);
// Don't bother doing an animation if the display rotation differs or if it's in
// a non-supported windowing mode
applyWindowingModeChangeOnExit(wct, direction);
mTaskOrganizer.applyTransaction(wct);
// Send finished callback though animation is ignored.
sendOnPipTransitionFinished(direction);
} else {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
mPipBoundsState.getBounds());
tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken,
direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
wct.setBoundsChangeTransaction(mToken, tx);
mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
mMainExecutor.execute(() -> {
t.apply();
// Make sure to grab the latest source hint rect as it could have been
// updated right after applying the windowing mode change.
final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
mPictureInPictureParams, destinationBounds);
scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
0 /* startingAngle */, sourceHintRect, direction,
animationDurationMs, null /* updateBoundsCallback */);
mState = State.EXITING_PIP;
});
}
});
}
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken,
direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
wct.setBoundsChangeTransaction(mToken, tx);
mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
mMainExecutor.execute(() -> {
t.apply();
// Make sure to grab the latest source hint rect as it could have been
// updated right after applying the windowing mode change.
final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
mPictureInPictureParams, destinationBounds);
scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
0 /* startingAngle */, sourceHintRect, direction,
animationDurationMs, null /* updateBoundsCallback */);
mState = State.EXITING_PIP;
});
}
});
}
private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
@@ -399,7 +380,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start();
mInitialState.remove(mToken.asBinder());
mState = State.EXITING_PIP;
}
@@ -424,7 +404,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mToken = mTaskInfo.token;
mState = State.TASK_APPEARED;
mLeash = leash;
mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
mTaskInfo.topActivityInfo);
@@ -606,6 +585,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public void onFixedRotationStarted(int displayId, int newRotation) {
mNextRotation = newRotation;
mWaitForFixedRotation = true;
}
@@ -645,7 +625,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
if (mState.isInPip() && fromRotation) {
if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) {
// Update bounds state to final destination first. It's important to do this
// before finishing & cancelling the transition animation so that the MotionHelper
// bounds are synchronized to the destination bounds when the animation ends.
@@ -1052,11 +1032,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Log.w(TAG, "Abort animation, invalid leash");
return;
}
final int rotationDelta = mWaitForFixedRotation
? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4
: Surface.ROTATION_0;
Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
sourceHintRect, direction, startingAngle)
sourceHintRect, direction, startingAngle, rotationDelta)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
@@ -1101,11 +1084,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mState=" + mState);
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
pw.println(innerPrefix + "mInitialState:");
for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
pw.println(innerPrefix + " binder=" + e.getKey()
+ " winConfig=" + e.getValue().windowConfiguration);
}
}
@Override

View File

@@ -30,6 +30,7 @@ import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -112,7 +113,7 @@ public class PipTransition extends PipTransitionController {
taskInfo.pictureInPictureParams, currentBounds);
animator = mPipAnimationController.getAnimator(taskInfo, leash,
currentBounds, currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
t.setAlpha(leash, 0f);
t.apply();

View File

@@ -16,6 +16,9 @@
package com.android.wm.shell.pip;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -34,6 +37,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
@@ -84,7 +88,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
TRANSITION_DIRECTION_TO_PIP, 0);
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -98,13 +102,13 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP, 0);
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
TRANSITION_DIRECTION_TO_PIP, 0);
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -127,6 +131,21 @@ public class PipAnimationControllerTest extends ShellTestCase {
animator.getTransitionDirection(), TRANSITION_DIRECTION_LEAVE_PIP);
}
@Test
public void pipTransitionAnimator_rotatedEndValue() {
final Rect startBounds = new Rect(200, 700, 400, 800);
final Rect endBounds = new Rect(0, 0, 500, 1000);
final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_90);
// Apply fraction 1 to compute the end value.
animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1);
final Rect rotatedEndBounds = new Rect(endBounds);
DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue);
}
@Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() {
@@ -136,7 +155,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP, 0);
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
animator.updateEndValue(endValue2);
@@ -150,7 +169,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
TRANSITION_DIRECTION_TO_PIP, 0);
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);

View File

@@ -7005,7 +7005,32 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
}
super.onConfigurationChanged(newParentConfig);
final DisplayContent display = mDisplayContent;
if (inPinnedWindowingMode() && attachedToProcess() && display != null) {
// If the PIP activity is changing to fullscreen with display orientation change, the
// fixed rotation will take effect that requires to send fixed rotation adjustments
// before the process configuration (if the process is a configuration listener of the
// activity). So when performing process configuration on client side, it can apply
// the adjustments (see WindowToken#onFixedRotationStatePrepared).
try {
app.pauseConfigurationDispatch();
super.onConfigurationChanged(newParentConfig);
if (mVisibleRequested && !inMultiWindowMode()) {
final int rotation = display.rotationForActivityInDifferentOrientation(this);
if (rotation != ROTATION_UNDEFINED) {
app.resumeConfigurationDispatch();
display.setFixedRotationLaunchingApp(this, rotation);
}
}
} finally {
if (app.resumeConfigurationDispatch()) {
app.dispatchConfiguration(app.getConfiguration());
}
}
} else {
super.onConfigurationChanged(newParentConfig);
}
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
@@ -7014,7 +7039,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
onMergedOverrideConfigurationChanged();
}
final DisplayContent display = mDisplayContent;
if (display == null) {
return;
}

View File

@@ -1603,7 +1603,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
&& mFixedRotationLaunchingApp != mFixedRotationTransitionListener.mAnimatingRecents;
}
@VisibleForTesting
boolean isFixedRotationLaunchingApp(ActivityRecord r) {
return mFixedRotationLaunchingApp == r;
}

View File

@@ -809,6 +809,16 @@ class Task extends WindowContainer<WindowContainer> {
// false.
private boolean mDeferTaskAppear;
/**
* Forces this task to be unorganized. Currently it is used for deferring the control of
* organizer when windowing mode is changing from PiP to fullscreen with orientation change.
* It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation.
*
* TODO(b/179235349): Remove this field by making surface operations from task organizer sync
* with display rotation.
*/
private boolean mForceNotOrganized;
/**
* This task was created by the task organizer which has the following implementations.
* <ul>
@@ -2246,6 +2256,21 @@ class Task extends WindowContainer<WindowContainer> {
if (pipChanging) {
mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true);
// If the top activity is using fixed rotation, it should be changing from PiP to
// fullscreen with display orientation change. Do not notify fullscreen task organizer
// because the restoration of task surface and the transformation of activity surface
// need to be done synchronously.
final ActivityRecord r = topRunningActivity();
if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
mForceNotOrganized = true;
}
} else if (mForceNotOrganized) {
// If the display orientation change is done, let the corresponding task organizer take
// back the control of this task.
final ActivityRecord r = topRunningActivity();
if (r == null || !mDisplayContent.isFixedRotationLaunchingApp(r)) {
mForceNotOrganized = false;
}
}
try {
// We have 2 reasons why we need to report orientation change here.
@@ -2835,6 +2860,9 @@ class Task extends WindowContainer<WindowContainer> {
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
}
// Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
// mode that may cause the bounds to be miscalculated, e.g. letterboxed.
getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
Rect outOverrideBounds =
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
@@ -4533,6 +4561,9 @@ class Task extends WindowContainer<WindowContainer> {
pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
pw.print(" isResizeable="); pw.println(isResizeable());
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
if (mForceNotOrganized) {
pw.print(prefix); pw.println("mForceNotOrganized=true");
}
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
}
@@ -4977,6 +5008,9 @@ class Task extends WindowContainer<WindowContainer> {
}
private boolean canBeOrganized() {
if (mForceNotOrganized) {
return false;
}
// All root tasks can be organized
if (isRootTask()) {
return true;

View File

@@ -1329,6 +1329,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mHasPendingConfigurationChange = true;
return;
}
dispatchConfiguration(config);
}
void dispatchConfiguration(Configuration config) {
mHasPendingConfigurationChange = false;
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1367,8 +1371,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mPauseConfigurationDispatchCount++;
}
void resumeConfigurationDispatch() {
/** Returns {@code true} if the configuration change is pending to dispatch. */
boolean resumeConfigurationDispatch() {
if (mPauseConfigurationDispatchCount == 0) {
return false;
}
mPauseConfigurationDispatchCount--;
return mHasPendingConfigurationChange;
}
/**

View File

@@ -630,9 +630,14 @@ class WindowToken extends WindowContainer<WindowState> {
void updateSurfacePosition(SurfaceControl.Transaction t) {
super.updateSurfacePosition(t);
if (isFixedRotationTransforming()) {
// The window is layouted in a simulated rotated display but the real display hasn't
// rotated, so here transforms its surface to fit in the real display.
mFixedRotationTransformState.transform(this);
final ActivityRecord r = asActivityRecord();
final Task rootTask = r != null ? r.getRootTask() : null;
// Don't transform the activity in PiP because the PiP task organizer will handle it.
if (rootTask == null || !rootTask.inPinnedWindowingMode()) {
// The window is laid out in a simulated rotated display but the real display hasn't
// rotated, so here transforms its surface to fit in the real display.
mFixedRotationTransformState.transform(this);
}
}
}

View File

@@ -1379,7 +1379,7 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
public void testNoFixedRotationWithPip() {
public void testFixedRotationWithPip() {
final DisplayContent displayContent = mDefaultDisplay;
unblockDisplayRotation(displayContent);
// Make resume-top really update the activity state.
@@ -1406,15 +1406,20 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(homeConfigOrientation, displayConfig.orientation);
clearInvocations(mWm);
// Leave PiP to fullscreen. The orientation can be updated from
// ActivityRecord#reportDescendantOrientationChangeIfNeeded.
pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
// Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity
// to fullscreen, so fixed rotation will apply on it.
pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
homeActivity.setState(Task.ActivityState.STOPPED, "test");
assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
assertEquals(pinnedConfigOrientation, displayConfig.orientation);
assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
assertNotEquals(pinnedConfigOrientation, displayConfig.orientation);
// Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
assertEquals(pinnedConfigOrientation, displayConfig.orientation);
clearInvocations(mWm);
// Enter PiP from fullscreen. The orientation can be updated from