Merge "Initial support for display rotation in shell transitions" into sc-dev
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.window;
|
||||
|
||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_NONE;
|
||||
@@ -31,6 +32,7 @@ import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -284,6 +286,8 @@ public final class TransitionInfo implements Parcelable {
|
||||
private final Rect mEndAbsBounds = new Rect();
|
||||
private final Point mEndRelOffset = new Point();
|
||||
private ActivityManager.RunningTaskInfo mTaskInfo = null;
|
||||
private int mStartRotation = ROTATION_UNDEFINED;
|
||||
private int mEndRotation = ROTATION_UNDEFINED;
|
||||
|
||||
public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
|
||||
mContainer = container;
|
||||
@@ -301,6 +305,8 @@ public final class TransitionInfo implements Parcelable {
|
||||
mEndAbsBounds.readFromParcel(in);
|
||||
mEndRelOffset.readFromParcel(in);
|
||||
mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
|
||||
mStartRotation = in.readInt();
|
||||
mEndRotation = in.readInt();
|
||||
}
|
||||
|
||||
/** Sets the parent of this change's container. The parent must be a participant or null. */
|
||||
@@ -341,6 +347,12 @@ public final class TransitionInfo implements Parcelable {
|
||||
mTaskInfo = taskInfo;
|
||||
}
|
||||
|
||||
/** Sets the start and end rotation of this container. */
|
||||
public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) {
|
||||
mStartRotation = start;
|
||||
mEndRotation = end;
|
||||
}
|
||||
|
||||
/** @return the container that is changing. May be null if non-remotable (eg. activity) */
|
||||
@Nullable
|
||||
public WindowContainerToken getContainer() {
|
||||
@@ -404,6 +416,14 @@ public final class TransitionInfo implements Parcelable {
|
||||
return mTaskInfo;
|
||||
}
|
||||
|
||||
public int getStartRotation() {
|
||||
return mStartRotation;
|
||||
}
|
||||
|
||||
public int getEndRotation() {
|
||||
return mEndRotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
/** @hide */
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
@@ -416,6 +436,8 @@ public final class TransitionInfo implements Parcelable {
|
||||
mEndAbsBounds.writeToParcel(dest, flags);
|
||||
mEndRelOffset.writeToParcel(dest, flags);
|
||||
dest.writeTypedObject(mTaskInfo, flags);
|
||||
dest.writeInt(mStartRotation);
|
||||
dest.writeInt(mEndRotation);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -442,7 +464,8 @@ public final class TransitionInfo implements Parcelable {
|
||||
public String toString() {
|
||||
return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
|
||||
+ " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
|
||||
+ mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}";
|
||||
+ mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
|
||||
+ mStartRotation + "->" + mEndRotation + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1231,6 +1231,12 @@
|
||||
"group": "WM_DEBUG_TASKS",
|
||||
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
|
||||
},
|
||||
"-672355406": {
|
||||
"message": " Rejecting as no-op: %s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
|
||||
"at": "com\/android\/server\/wm\/Transition.java"
|
||||
},
|
||||
"-672228342": {
|
||||
"message": "resumeTopActivityLocked: Top activity resumed %s",
|
||||
"level": "DEBUG",
|
||||
|
||||
@@ -28,7 +28,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
|
||||
// with those in the framework ProtoLogGroup
|
||||
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
|
||||
Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
|
||||
@@ -96,6 +96,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
};
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (change.getMode() == TRANSIT_CHANGE) {
|
||||
// No default animation for this, so just update bounds/position.
|
||||
t.setPosition(change.getLeash(),
|
||||
change.getEndAbsBounds().left - change.getEndRelOffset().x,
|
||||
change.getEndAbsBounds().top - change.getEndRelOffset().y);
|
||||
if (change.getTaskInfo() != null) {
|
||||
// Skip non-tasks since those usually have null bounds.
|
||||
t.setWindowCrop(change.getLeash(),
|
||||
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
|
||||
}
|
||||
}
|
||||
|
||||
// Don't animate anything that isn't independent.
|
||||
if (!TransitionInfo.isIndependent(change, info)) continue;
|
||||
|
||||
@@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.view.WindowManager.TransitionOldType;
|
||||
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
@@ -35,6 +36,8 @@ import android.window.IRemoteTransition;
|
||||
import android.window.IRemoteTransitionFinishedCallback;
|
||||
import android.window.TransitionInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @see RemoteAnimationAdapter
|
||||
*/
|
||||
@@ -100,6 +103,52 @@ public class RemoteAnimationAdapterCompat {
|
||||
};
|
||||
}
|
||||
|
||||
private static class CounterRotator {
|
||||
SurfaceControl mSurface = null;
|
||||
ArrayList<SurfaceControl> mRotateChildren = null;
|
||||
|
||||
void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
|
||||
float displayW, float displayH) {
|
||||
if (rotateDelta == 0) return;
|
||||
mRotateChildren = new ArrayList<>();
|
||||
// We want to counter-rotate, so subtract from 4
|
||||
rotateDelta = 4 - (rotateDelta + 4) % 4;
|
||||
mSurface = new SurfaceControl.Builder()
|
||||
.setName("Transition Unrotate")
|
||||
.setContainerLayer()
|
||||
.setParent(parent)
|
||||
.build();
|
||||
// column-major
|
||||
if (rotateDelta == 1) {
|
||||
t.setMatrix(mSurface, 0, 1, -1, 0);
|
||||
t.setPosition(mSurface, displayW, 0);
|
||||
} else if (rotateDelta == 2) {
|
||||
t.setMatrix(mSurface, -1, 0, 0, -1);
|
||||
t.setPosition(mSurface, displayW, displayH);
|
||||
} else if (rotateDelta == 3) {
|
||||
t.setMatrix(mSurface, 0, -1, 1, 0);
|
||||
t.setPosition(mSurface, 0, displayH);
|
||||
}
|
||||
t.show(mSurface);
|
||||
}
|
||||
|
||||
void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
|
||||
if (mSurface == null) return;
|
||||
t.reparent(child, mSurface);
|
||||
mRotateChildren.add(child);
|
||||
}
|
||||
|
||||
void cleanUp(SurfaceControl rootLeash) {
|
||||
if (mSurface == null) return;
|
||||
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
|
||||
t.reparent(mRotateChildren.get(i), rootLeash);
|
||||
}
|
||||
t.remove(mSurface);
|
||||
t.apply();
|
||||
}
|
||||
}
|
||||
|
||||
private static IRemoteTransition.Stub wrapRemoteTransition(
|
||||
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
|
||||
return new IRemoteTransition.Stub() {
|
||||
@@ -116,17 +165,46 @@ public class RemoteAnimationAdapterCompat {
|
||||
|
||||
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
|
||||
boolean isReturnToHome = false;
|
||||
TransitionInfo.Change launcherTask = null;
|
||||
TransitionInfo.Change wallpaper = null;
|
||||
int launcherLayer = 0;
|
||||
int rotateDelta = 0;
|
||||
float displayW = 0;
|
||||
float displayH = 0;
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (change.getTaskInfo() != null
|
||||
&& change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
|
||||
isReturnToHome = change.getMode() == TRANSIT_OPEN
|
||||
|| change.getMode() == TRANSIT_TO_FRONT;
|
||||
break;
|
||||
launcherTask = change;
|
||||
launcherLayer = info.getChanges().size() - i;
|
||||
} else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
|
||||
wallpaper = change;
|
||||
}
|
||||
if (change.getParent() == null && change.getEndRotation() >= 0
|
||||
&& change.getEndRotation() != change.getStartRotation()) {
|
||||
rotateDelta = change.getEndRotation() - change.getStartRotation();
|
||||
displayW = change.getEndAbsBounds().width();
|
||||
displayH = change.getEndAbsBounds().height();
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for rotation if there is one
|
||||
final CounterRotator counterLauncher = new CounterRotator();
|
||||
final CounterRotator counterWallpaper = new CounterRotator();
|
||||
if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) {
|
||||
counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(),
|
||||
rotateDelta, displayW, displayH);
|
||||
if (counterLauncher.mSurface != null) {
|
||||
t.setLayer(counterLauncher.mSurface, launcherLayer);
|
||||
}
|
||||
}
|
||||
|
||||
if (isReturnToHome) {
|
||||
if (counterLauncher.mSurface != null) {
|
||||
t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);
|
||||
}
|
||||
// Need to "boost" the closing things since that's what launcher expects.
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
@@ -136,6 +214,7 @@ public class RemoteAnimationAdapterCompat {
|
||||
if (!TransitionInfo.isIndependent(change, info)) continue;
|
||||
if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
|
||||
t.setLayer(leash, info.getChanges().size() * 3 - i);
|
||||
counterLauncher.addChild(t, leash);
|
||||
}
|
||||
}
|
||||
// Make wallpaper visible immediately since launcher apparently won't do this.
|
||||
@@ -143,6 +222,18 @@ public class RemoteAnimationAdapterCompat {
|
||||
t.show(wallpapersCompat[i].leash.getSurfaceControl());
|
||||
t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);
|
||||
}
|
||||
} else {
|
||||
if (launcherTask != null) {
|
||||
counterLauncher.addChild(t, launcherTask.getLeash());
|
||||
}
|
||||
if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) {
|
||||
counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(),
|
||||
rotateDelta, displayW, displayH);
|
||||
if (counterWallpaper.mSurface != null) {
|
||||
t.setLayer(counterWallpaper.mSurface, -1);
|
||||
counterWallpaper.addChild(t, wallpaper.getLeash());
|
||||
}
|
||||
}
|
||||
}
|
||||
t.apply();
|
||||
|
||||
@@ -150,6 +241,8 @@ public class RemoteAnimationAdapterCompat {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
counterLauncher.cleanUp(info.getRootLeash());
|
||||
counterWallpaper.cleanUp(info.getRootLeash());
|
||||
finishCallback.onTransitionFinished(null /* wct */);
|
||||
} catch (RemoteException e) {
|
||||
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
|
||||
|
||||
@@ -56,6 +56,7 @@ public class RemoteAnimationTargetCompat {
|
||||
public final boolean isNotInRecents;
|
||||
public final Rect contentInsets;
|
||||
public final PictureInPictureParams pictureInPictureParams;
|
||||
public final int rotationChange;
|
||||
|
||||
private final SurfaceControl mStartLeash;
|
||||
|
||||
@@ -74,6 +75,7 @@ public class RemoteAnimationTargetCompat {
|
||||
contentInsets = app.contentInsets;
|
||||
activityType = app.windowConfiguration.getActivityType();
|
||||
pictureInPictureParams = app.pictureInPictureParams;
|
||||
rotationChange = 0;
|
||||
|
||||
mStartLeash = app.startLeash;
|
||||
}
|
||||
@@ -102,7 +104,7 @@ public class RemoteAnimationTargetCompat {
|
||||
localBounds = new Rect(change.getEndAbsBounds());
|
||||
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
|
||||
sourceContainerBounds = null;
|
||||
screenSpaceBounds = change.getEndAbsBounds();
|
||||
screenSpaceBounds = new Rect(change.getEndAbsBounds());
|
||||
prefixOrderIndex = order;
|
||||
// TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
|
||||
contentInsets = new Rect(0, 0, 0, 0);
|
||||
@@ -115,6 +117,7 @@ public class RemoteAnimationTargetCompat {
|
||||
}
|
||||
pictureInPictureParams = null;
|
||||
mStartLeash = null;
|
||||
rotationChange = change.getEndRotation() - change.getStartRotation();
|
||||
}
|
||||
|
||||
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
|
||||
|
||||
@@ -1428,7 +1428,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
computeScreenConfiguration(config);
|
||||
} else if (currentConfig != null
|
||||
// If waiting for a remote rotation, don't prematurely update configuration.
|
||||
&& !mDisplayRotation.isWaitingForRemoteRotation()) {
|
||||
&& !(mDisplayRotation.isWaitingForRemoteRotation()
|
||||
|| mAtmService.getTransitionController().isCollecting(this))) {
|
||||
// No obvious action we need to take, but if our current state mismatches the
|
||||
// activity manager's, update it, disregarding font scale, which should remain set
|
||||
// to the value of the previous configuration.
|
||||
@@ -1470,6 +1471,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
return mDisplayRotation.updateOrientation(orientation, forceUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSyncFinished() {
|
||||
if (mDisplayRotation.isWaitingForRemoteRotation()) return false;
|
||||
return super.isSyncFinished();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid rotation if the activity can use different orientation than the display.
|
||||
* Otherwise {@link #ROTATION_UNDEFINED}.
|
||||
@@ -1763,7 +1770,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
* Update rotation of the display.
|
||||
*
|
||||
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
|
||||
* {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN.
|
||||
* {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN unless using Shell transitions.
|
||||
*/
|
||||
boolean updateRotationUnchecked() {
|
||||
return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);
|
||||
@@ -1778,8 +1785,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
*/
|
||||
private void applyRotation(final int oldRotation, final int rotation) {
|
||||
mDisplayRotation.applyCurrentRotation(rotation);
|
||||
final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly();
|
||||
final Transaction transaction = getPendingTransaction();
|
||||
final boolean shellTransitions =
|
||||
mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null;
|
||||
final boolean rotateSeamlessly =
|
||||
mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
|
||||
final Transaction transaction =
|
||||
shellTransitions ? getSyncTransaction() : getPendingTransaction();
|
||||
ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
|
||||
? null : getRotationAnimation();
|
||||
// We need to update our screen size information to match the new rotation. If the rotation
|
||||
@@ -1795,9 +1806,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
screenRotationAnimation.setRotation(transaction, rotation);
|
||||
}
|
||||
|
||||
forAllWindows(w -> {
|
||||
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
|
||||
}, true /* traverseTopToBottom */);
|
||||
if (!shellTransitions) {
|
||||
forAllWindows(w -> {
|
||||
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
|
||||
}, true /* traverseTopToBottom */);
|
||||
}
|
||||
|
||||
mWmService.mDisplayManagerInternal.performTraversal(transaction);
|
||||
scheduleAnimation();
|
||||
@@ -4573,6 +4586,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
return mWmService.makeSurfaceBuilder(mSession).setParent(getOverlayLayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceControl.Builder makeAnimationLeash() {
|
||||
return mWmService.makeSurfaceBuilder(mSession).setParent(mSurfaceControl)
|
||||
.setContainerLayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reparents the given surface to {@link #mOverlayLayer} SurfaceControl.
|
||||
*/
|
||||
@@ -5919,4 +5938,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options,
|
||||
ownerCanManageAppToken, roundedCornerOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
DisplayContent asDisplayContent() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFA
|
||||
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
|
||||
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
|
||||
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
|
||||
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
|
||||
@@ -408,8 +409,11 @@ public class DisplayRotation {
|
||||
* THE SCREEN.
|
||||
*/
|
||||
boolean updateRotationUnchecked(boolean forceUpdate) {
|
||||
final boolean useShellTransitions =
|
||||
mService.mAtmService.getTransitionController().getTransitionPlayer() != null;
|
||||
|
||||
final int displayId = mDisplayContent.getDisplayId();
|
||||
if (!forceUpdate) {
|
||||
if (!forceUpdate && !useShellTransitions) {
|
||||
if (mDeferredRotationPauseCount > 0) {
|
||||
// Rotation updates have been paused temporarily. Defer the update until updates
|
||||
// have been resumed.
|
||||
@@ -472,6 +476,12 @@ public class DisplayRotation {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Transition t = (useShellTransitions
|
||||
&& !mService.mAtmService.getTransitionController().isCollecting())
|
||||
? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE)
|
||||
: null;
|
||||
mService.mAtmService.getTransitionController().collect(mDisplayContent);
|
||||
|
||||
ProtoLog.v(WM_DEBUG_ORIENTATION,
|
||||
"Display id=%d rotation changed to %d from %d, lastOrientation=%d",
|
||||
displayId, rotation, oldRotation, lastOrientation);
|
||||
@@ -484,6 +494,20 @@ public class DisplayRotation {
|
||||
|
||||
mDisplayContent.setLayoutNeeded();
|
||||
|
||||
if (useShellTransitions) {
|
||||
if (t != null) {
|
||||
// This created its own transition, so send a start request.
|
||||
mService.mAtmService.getTransitionController().requestStartTransition(
|
||||
t, null /* trigger */, null /* remote */);
|
||||
} else {
|
||||
// Use remote-rotation infra since the transition has already been requested
|
||||
// TODO(shell-transitions): Remove this once lifecycle management can cover all
|
||||
// rotation cases.
|
||||
startRemoteRotation(oldRotation, mRotation);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
|
||||
mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
|
||||
mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
|
||||
@@ -503,6 +527,19 @@ public class DisplayRotation {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to get a rotating displaycontent from a Transition.
|
||||
* @return null if the transition doesn't contain a rotating display.
|
||||
*/
|
||||
static DisplayContent getDisplayFromTransition(Transition transition) {
|
||||
for (int i = transition.mParticipants.size() - 1; i >= 0; --i) {
|
||||
final WindowContainer wc = transition.mParticipants.valueAt(i);
|
||||
if (!(wc instanceof DisplayContent)) continue;
|
||||
return (DisplayContent) wc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Remote rotation is when we are waiting for some registered (remote)
|
||||
* {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations
|
||||
@@ -537,6 +574,22 @@ public class DisplayRotation {
|
||||
}
|
||||
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
|
||||
mIsWaitingForRemoteRotation = false;
|
||||
|
||||
if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
|
||||
if (!mService.mAtmService.getTransitionController().isCollecting()) {
|
||||
throw new IllegalStateException("Trying to rotate outside a transition");
|
||||
}
|
||||
mService.mAtmService.getTransitionController().collect(mDisplayContent);
|
||||
// Go through all tasks and collect them before the rotation
|
||||
// TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
|
||||
// handling is synchronized.
|
||||
mDisplayContent.forAllTasks(task -> {
|
||||
if (task.isVisible()) {
|
||||
mService.mAtmService.getTransitionController().collect(task);
|
||||
}
|
||||
});
|
||||
mDisplayContent.getInsetsStateController().addProvidersToTransition();
|
||||
}
|
||||
mService.mAtmService.deferWindowLayout();
|
||||
try {
|
||||
mDisplayContent.sendNewConfiguration();
|
||||
|
||||
@@ -248,6 +248,16 @@ class InsetsStateController {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addProvidersToTransition() {
|
||||
for (int i = mProviders.size() - 1; i >= 0; --i) {
|
||||
final InsetsSourceProvider p = mProviders.valueAt(i);
|
||||
if (p == null) continue;
|
||||
final WindowContainer wc = p.mWin;
|
||||
if (wc == null) continue;
|
||||
mDisplayContent.mAtmService.getTransitionController().collect(wc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The provider of a specific type.
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
|
||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
@@ -411,8 +412,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
private static boolean reportIfNotTop(WindowContainer wc) {
|
||||
// Organized tasks need to be reported anyways because Core won't show() their surfaces
|
||||
// and we can't rely on onTaskAppeared because it isn't in sync.
|
||||
// Also report wallpaper so it can be handled properly during display change/rotation.
|
||||
// TODO(shell-transitions): switch onTaskAppeared usage over to transitions OPEN.
|
||||
return wc.isOrganized();
|
||||
return wc.isOrganized() || isWallpaper(wc);
|
||||
}
|
||||
|
||||
/** @return the depth of child within ancestor, 0 if child == ancestor, or -1 if not a child. */
|
||||
@@ -430,7 +432,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
}
|
||||
|
||||
private static boolean isWallpaper(WindowContainer wc) {
|
||||
return wc instanceof WallpaperWindowToken;
|
||||
return wc.asWallpaperToken() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,7 +578,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
final ArrayList<WindowContainer> tmpList = new ArrayList<>();
|
||||
|
||||
// Build initial set of top-level participants by removing any participants that are no-ops
|
||||
// or children of other participants or are otherwise invalid.
|
||||
// or children of other participants or are otherwise invalid; however, keep around a list
|
||||
// of participants that should always be reported even if they aren't top.
|
||||
for (WindowContainer wc : participants) {
|
||||
// Don't include detached windows.
|
||||
if (!wc.isAttached()) continue;
|
||||
@@ -584,7 +587,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
final ChangeInfo changeInfo = changes.get(wc);
|
||||
|
||||
// Reject no-ops
|
||||
if (!changeInfo.hasChanged(wc)) continue;
|
||||
if (!changeInfo.hasChanged(wc)) {
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
|
||||
" Rejecting as no-op: %s", wc);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search through ancestors to find the top-most participant (if one exists)
|
||||
WindowContainer topParent = null;
|
||||
@@ -651,10 +658,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
|
||||
/** Gets the leash surface for a window container */
|
||||
private static SurfaceControl getLeashSurface(WindowContainer wc) {
|
||||
final DisplayContent asDC = wc.asDisplayContent();
|
||||
if (asDC != null) {
|
||||
// DisplayContent is the "root", so we use the windowing layer instead to avoid
|
||||
// hardware-screen-level surfaces.
|
||||
return asDC.getWindowingLayer();
|
||||
}
|
||||
return wc.getSurfaceControl();
|
||||
}
|
||||
|
||||
private static SurfaceControl getOrigParentSurface(WindowContainer wc) {
|
||||
if (wc.asDisplayContent() != null) {
|
||||
// DisplayContent is the "root", so we reinterpret it's wc as the window layer
|
||||
// making the parent surface the displaycontent's surface.
|
||||
return wc.getSurfaceControl();
|
||||
}
|
||||
return wc.getParent().getSurfaceControl();
|
||||
}
|
||||
|
||||
@@ -744,6 +762,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left,
|
||||
target.getBounds().top - target.getParent().getBounds().top);
|
||||
change.setFlags(info.getChangeFlags(target));
|
||||
change.setRotation(info.mRotation, target.getWindowConfiguration().getRotation());
|
||||
final Task task = target.asTask();
|
||||
if (task != null) {
|
||||
final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
|
||||
@@ -773,12 +792,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
int mWindowingMode;
|
||||
final Rect mAbsoluteBounds = new Rect();
|
||||
boolean mShowWallpaper;
|
||||
int mRotation = ROTATION_UNDEFINED;
|
||||
|
||||
ChangeInfo(@NonNull WindowContainer origState) {
|
||||
mVisible = origState.isVisibleRequested();
|
||||
mWindowingMode = origState.getWindowingMode();
|
||||
mAbsoluteBounds.set(origState.getBounds());
|
||||
mShowWallpaper = origState.showWallpaper();
|
||||
mRotation = origState.getWindowConfiguration().getRotation();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -797,7 +818,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
// if mWindowingMode is 0, this container wasn't attached at collect time, so
|
||||
// assume no change in windowing-mode.
|
||||
|| (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode)
|
||||
|| !newState.getBounds().equals(mAbsoluteBounds);
|
||||
|| !newState.getBounds().equals(mAbsoluteBounds)
|
||||
|| mRotation != newState.getWindowConfiguration().getRotation();
|
||||
}
|
||||
|
||||
@TransitionInfo.TransitionMode
|
||||
@@ -826,8 +848,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
// checks to use requested visibility.
|
||||
flags |= FLAG_TRANSLUCENT;
|
||||
}
|
||||
if (wc instanceof ActivityRecord
|
||||
&& wc.asActivityRecord().mUseTransferredAnimation) {
|
||||
if (wc.asActivityRecord() != null && wc.asActivityRecord().mUseTransferredAnimation) {
|
||||
flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
|
||||
}
|
||||
if (isWallpaper(wc)) {
|
||||
|
||||
@@ -3084,6 +3084,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Cheap way of doing cast and instanceof. */
|
||||
DisplayContent asDisplayContent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if window container is manage by a
|
||||
* {@link android.window.WindowOrganizer}
|
||||
|
||||
@@ -4148,12 +4148,19 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
.notifyOnActivityRotation(displayContent.mDisplayId);
|
||||
}
|
||||
|
||||
if (!rotationChanged || forceRelayout) {
|
||||
displayContent.setLayoutNeeded();
|
||||
layoutNeeded = true;
|
||||
}
|
||||
if (rotationChanged || alwaysSendConfiguration) {
|
||||
displayContent.sendNewConfiguration();
|
||||
final boolean pendingRemoteRotation = rotationChanged
|
||||
&& (displayContent.getDisplayRotation().isWaitingForRemoteRotation()
|
||||
|| mAtmService.getTransitionController().isCollecting());
|
||||
// Even if alwaysSend, we are waiting for a transition or remote to provide
|
||||
// rotated configuration, so we can't update configuration yet.
|
||||
if (!pendingRemoteRotation) {
|
||||
if (!rotationChanged || forceRelayout) {
|
||||
displayContent.setLayoutNeeded();
|
||||
layoutNeeded = true;
|
||||
}
|
||||
if (rotationChanged || alwaysSendConfiguration) {
|
||||
displayContent.sendNewConfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
|
||||
mService.deferWindowLayout();
|
||||
try {
|
||||
if (transition != null) {
|
||||
// First check if we have a display rotation transition and if so, update it.
|
||||
final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition);
|
||||
if (dc != null && transition.mChanges.get(dc).mRotation != dc.getRotation()) {
|
||||
// Go through all tasks and collect them before the rotation
|
||||
// TODO(shell-transitions): move collect() to onConfigurationChange once
|
||||
// wallpaper handling is synchronized.
|
||||
dc.forAllTasks(task -> {
|
||||
if (task.isVisible()) transition.collect(task);
|
||||
});
|
||||
dc.getInsetsStateController().addProvidersToTransition();
|
||||
dc.sendNewConfiguration();
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
}
|
||||
}
|
||||
ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
|
||||
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
|
||||
t.getChanges().entrySet().iterator();
|
||||
|
||||
@@ -119,6 +119,7 @@ import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -1610,6 +1611,54 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
assertFalse(mWm.mDisplayFrozen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShellTransitRotation() {
|
||||
DisplayContent dc = createNewDisplay();
|
||||
|
||||
// Set-up mock shell transitions
|
||||
final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
|
||||
mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
|
||||
mAtm.getTransitionController().registerTransitionPlayer(testPlayer);
|
||||
|
||||
final DisplayRotation dr = dc.getDisplayRotation();
|
||||
doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
|
||||
// Rotate 180 degree so the display doesn't have configuration change. This condition is
|
||||
// used for the later verification of stop-freezing (without setting mWaitingForConfig).
|
||||
doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
|
||||
mWm.mDisplayRotationController =
|
||||
new IDisplayWindowRotationController.Stub() {
|
||||
@Override
|
||||
public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
|
||||
IDisplayWindowRotationCallback callback) {
|
||||
try {
|
||||
callback.continueRotateDisplay(toRotation, null);
|
||||
} catch (RemoteException e) {
|
||||
assertTrue(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// kill any existing rotation animation (vestigial from test setup).
|
||||
dc.setRotationAnimation(null);
|
||||
|
||||
final int origRot = dc.getConfiguration().windowConfiguration.getRotation();
|
||||
|
||||
mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */);
|
||||
// Should create a transition request without performing rotation
|
||||
assertNotNull(testPlayer.mLastRequest);
|
||||
assertEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
|
||||
|
||||
// Once transition starts, rotation is applied and transition shows DC rotating.
|
||||
testPlayer.start();
|
||||
assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
|
||||
assertNotNull(testPlayer.mLastReady);
|
||||
assertEquals(dc, DisplayRotation.getDisplayFromTransition(testPlayer.mLastTransit));
|
||||
WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken();
|
||||
assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(),
|
||||
testPlayer.mLastReady.getChange(dcToken).getStartRotation());
|
||||
testPlayer.finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOrCreateRootHomeTask_defaultDisplay() {
|
||||
TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
|
||||
|
||||
@@ -60,6 +60,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.IApplicationThread;
|
||||
@@ -90,7 +91,10 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.DisplayImePolicy;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.ITransitionPlayer;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
|
||||
import com.android.internal.policy.AttributeCache;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
@@ -1359,4 +1363,48 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
mHasSurface = hadSurface;
|
||||
}
|
||||
}
|
||||
|
||||
class TestTransitionPlayer extends ITransitionPlayer.Stub {
|
||||
final TransitionController mController;
|
||||
final WindowOrganizerController mOrganizer;
|
||||
Transition mLastTransit = null;
|
||||
TransitionRequestInfo mLastRequest = null;
|
||||
TransitionInfo mLastReady = null;
|
||||
|
||||
TestTransitionPlayer(@NonNull TransitionController controller,
|
||||
@NonNull WindowOrganizerController organizer) {
|
||||
mController = controller;
|
||||
mOrganizer = organizer;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mLastTransit = null;
|
||||
mLastReady = null;
|
||||
mLastRequest = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
|
||||
SurfaceControl.Transaction transaction) throws RemoteException {
|
||||
mLastTransit = Transition.fromBinder(transitToken);
|
||||
mLastReady = transitionInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestStartTransition(IBinder transitToken,
|
||||
TransitionRequestInfo request) throws RemoteException {
|
||||
mLastTransit = Transition.fromBinder(transitToken);
|
||||
mLastRequest = request;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null);
|
||||
mLastTransit.onTransactionReady(mLastTransit.getSyncId(),
|
||||
mock(SurfaceControl.Transaction.class));
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
mController.finishTransition(mLastTransit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user