Each displays can have individual app transition.
Include below refectoring items to support per display AppTransition:
WMS / AM refectoring parts:
- Move AppTransition related stuff from WMS into DisplayContent.
- Move WMS.prepareAppTransition into DisplayWindowController.
- Move WMS.executeAppTransition to DisplayWindowController.
- Move ATM.isNextTransitionForward to DisplayWindowController.
- Move WMS.getPendingAppTransition to DisplayWindowController.
- Move WMS.overrideAppTransition like APIs to DisplayWindowController.
- Move ActivityRecord.applyOptionsLocked to AppContainerController.
- Support tracing all display's AppTransition status for
DisplayContent.pendingLayoutChanges & window hierachy update.
- Modify logics for AppTransition related caller parts.
- Move WindowSurfacePlacer.handleAppTransitionReadyLocked related
stuffs into added class AppTransitionController.
WM unit test parts:
- Add test case for verifying app transition state per display:
- AppTransitionTests.testAppTransitionStateForMultiDisplay
- AppTransitionTests.testCleanAppTransitionWhenTaskStackReparent
- Rename WindowSurfacePlacerTest to AppTransitionControllerTest since
the test is related handle AppTransition flow.
Bug: 111362605
Test: go/wm-smoke
Test: atest ActivityManagerTransitionSelectionTests
Test: atest ActivityManagerMultiDisplayTests
Test: atest FrameworksServicesTests for DisplayContent / AppTransition
related tests.
Change-Id: Ic1793aa794eb161bec31fda57847a6ba2ff4f84f
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e38e229..9baafcb 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -111,6 +111,7 @@
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.function.Consumer;
class AppTokenList extends ArrayList<AppWindowToken> {
}
@@ -500,14 +501,14 @@
setClientHidden(!visible);
}
- if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) {
+ if (!getDisplayContent().mClosingApps.contains(this)
+ && !getDisplayContent().mOpeningApps.contains(this)) {
// The token is not closing nor opening, so even if there is an animation set, that
// doesn't mean that it goes through the normal app transition cycle so we have
// to inform the docked controller about visibility change.
// TODO(multi-display): notify docked divider on all displays where visibility was
// affected.
- mService.getDefaultDisplayContentLocked().getDockedDividerController()
- .notifyAppVisibilityChanged();
+ getDisplayContent().getDockedDividerController().notifyAppVisibilityChanged();
// Take the screenshot before possibly hiding the WSA, otherwise the screenshot
// will not be taken.
@@ -524,7 +525,7 @@
// no animation but there will still be a transition set.
// We still need to delay hiding the surface such that it
// can be synchronized with showing the next surface in the transition.
- if (isHidden() && !delayed && !mService.mAppTransition.isTransitionSet()) {
+ if (isHidden() && !delayed && !getDisplayContent().mAppTransition.isTransitionSet()) {
SurfaceControl.openTransaction();
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).mWinAnimator.hide("immediately hidden");
@@ -630,14 +631,14 @@
boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
- mService.mOpeningApps.remove(this);
- mService.mUnknownAppVisibilityController.appRemovedOrHidden(this);
+ getDisplayContent().mOpeningApps.remove(this);
+ getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mService.mTaskSnapshotController.onAppRemoved(this);
waitingToShow = false;
- if (mService.mClosingApps.contains(this)) {
+ if (getDisplayContent().mClosingApps.contains(this)) {
delayed = true;
- } else if (mService.mAppTransition.isTransitionSet()) {
- mService.mClosingApps.add(this);
+ } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
+ getDisplayContent().mClosingApps.add(this);
delayed = true;
}
@@ -652,10 +653,10 @@
}
// If this window was animating, then we need to ensure that the app transition notifies
- // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so
- // add to that list now
+ // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
+ // so add to that list now
if (isSelfAnimating()) {
- mService.mNoAnimationNotifyOnTransitionFinished.add(token);
+ getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
}
final TaskStack stack = getStack();
@@ -795,7 +796,7 @@
if (task == null) {
// It is possible we have been marked as a closing app earlier. We must remove ourselves
// from this list so we do not participate in any future animations.
- mService.mClosingApps.remove(this);
+ getDisplayContent().mClosingApps.remove(this);
} else if (mLastParent != null && mLastParent.mStack != null) {
task.mStack.mExitingAppTokens.remove(this);
}
@@ -1219,7 +1220,7 @@
if (tStartingWindow != null && fromToken.startingSurface != null) {
// In this case, the starting icon has already been displayed, so start
// letting windows get shown immediately without any more transitions.
- mService.mSkipAppTransitionAnimation = true;
+ getDisplayContent().mSkipAppTransitionAnimation = true;
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving existing starting " + tStartingWindow
+ " from " + fromToken + " to " + this);
@@ -1269,7 +1270,7 @@
// When transferring an animation, we no longer need to apply an animation to the
// the token we transfer the animation over. Thus, remove the animation from
// pending opening apps.
- mService.mOpeningApps.remove(this);
+ getDisplayContent().mOpeningApps.remove(this);
mService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
@@ -1323,8 +1324,8 @@
// The {@link AppWindowToken} should only specify an orientation when it is not closing or
// going to the bottom. Allowing closing {@link AppWindowToken} to participate can lead to
// an Activity in another task being started in the wrong orientation during the transition.
- if (!(sendingToBottom || mService.mClosingApps.contains(this))
- && (isVisible() || mService.mOpeningApps.contains(this))) {
+ if (!(sendingToBottom || getDisplayContent().mClosingApps.contains(this))
+ && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
return mOrientation;
}
@@ -1398,7 +1399,7 @@
setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
// We can now show all of the drawn windows!
- if (!mService.mOpeningApps.contains(this) && canShowWindows()) {
+ if (!getDisplayContent().mOpeningApps.contains(this) && canShowWindows()) {
showAllWindowsLocked();
}
}
@@ -1572,6 +1573,11 @@
return forAllWindowsUnchecked(callback, traverseTopToBottom);
}
+ @Override
+ void forAllAppWindows(Consumer<AppWindowToken> callback) {
+ callback.accept(this);
+ }
+
boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
return super.forAllWindows(callback, traverseTopToBottom);
@@ -1629,7 +1635,8 @@
final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
|| containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
- mService.notifyKeyguardFlagsChanged(null /* callback */);
+ mService.notifyKeyguardFlagsChanged(null /* callback */,
+ getDisplayContent().getDisplayId());
}
mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
mLastContainsShowWhenLockedWindow = containsShowWhenLocked;
@@ -1787,19 +1794,20 @@
getAnimationBounds(mTmpPoint, mTmpRect);
// Delaying animation start isn't compatible with remote animations at all.
- if (mService.mAppTransition.getRemoteAnimationController() != null
+ if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
&& !mSurfaceAnimator.isAnimationStartDelayed()) {
- adapter = mService.mAppTransition.getRemoteAnimationController()
+ adapter = getDisplayContent().mAppTransition.getRemoteAnimationController()
.createAnimationAdapter(this, mTmpPoint, mTmpRect);
} else {
- final int appStackClipMode = mService.mAppTransition.getAppStackClipMode();
+ final int appStackClipMode =
+ getDisplayContent().mAppTransition.getAppStackClipMode();
mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
if (a != null) {
adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- mService.mAppTransition.canSkipFirstFrame(),
+ getDisplayContent().mAppTransition.canSkipFirstFrame(),
appStackClipMode,
true /* isAppAnimation */),
mService.mSurfaceAnimationRunner);
@@ -1807,7 +1815,7 @@
mNeedsZBoost = true;
}
mTransit = transit;
- mTransitFlags = mService.mAppTransition.getTransitFlags();
+ mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
adapter = null;
}
@@ -1877,7 +1885,7 @@
+ " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
+ " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
final Configuration displayConfig = displayContent.getConfiguration();
- final Animation a = mService.mAppTransition.loadAnimation(lp, transit, enter,
+ final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId);
if (a != null) {
@@ -2017,7 +2025,7 @@
final ArrayList<WindowState> children = new ArrayList<>(mChildren);
children.forEach(WindowState::onExitAnimationDone);
- mService.mAppTransition.notifyAppTransitionFinishedLocked(token);
+ getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
scheduleAnimation();
}
@@ -2048,8 +2056,9 @@
}
boolean isWaitingForTransitionStart() {
- return mService.mAppTransition.isTransitionSet()
- && (mService.mOpeningApps.contains(this) || mService.mClosingApps.contains(this));
+ return getDisplayContent().mAppTransition.isTransitionSet()
+ && (getDisplayContent().mOpeningApps.contains(this)
+ || getDisplayContent().mClosingApps.contains(this));
}
public int getTransit() {
@@ -2066,7 +2075,7 @@
}
final int taskId = getTask().mTaskId;
final GraphicBuffer thumbnailHeader =
- mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
+ getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId);
if (thumbnailHeader == null) {
if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
return;
@@ -2095,14 +2104,14 @@
? R.drawable.ic_account_circle
: R.drawable.ic_corp_badge;
final GraphicBuffer thumbnail =
- mService.mAppTransition
+ getDisplayContent().mAppTransition
.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
if (thumbnail == null) {
return;
}
mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
final Animation animation =
- mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
+ getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
win.getFrameLw());
mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
frame.top));
@@ -2119,7 +2128,7 @@
new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
final Rect insets = win != null ? win.getContentInsets() : null;
final Configuration displayConfig = mDisplayContent.getConfiguration();
- return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(
+ return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
displayConfig.orientation);
}
@@ -2357,4 +2366,11 @@
return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
true /* topToBottom */);
}
+
+ void removeFromPendingTransition() {
+ if (isWaitingForTransitionStart() && mDisplayContent != null) {
+ mDisplayContent.mOpeningApps.remove(this);
+ mDisplayContent.mClosingApps.remove(this);
+ }
+ }
}