diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index cc71155dc88d9..bc5f67bb50d04 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; -import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -28,12 +27,12 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS; import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE; import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; -import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteCallback; @@ -45,6 +44,7 @@ import android.util.proto.ProtoOutputStream; import android.view.IWindowFocusObserver; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.WindowInsets; import android.window.BackAnimationAdapter; import android.window.BackNavigationInfo; import android.window.IBackAnimationFinishedCallback; @@ -54,8 +54,11 @@ import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; +import com.android.server.wm.utils.InsetUtils; +import java.io.PrintWriter; import java.util.ArrayList; +import java.util.function.Consumer; /** * Controller to handle actions related to the back gesture on the server side. @@ -69,7 +72,7 @@ class BackNavigationController { private boolean mShowWallpaper; private Runnable mPendingAnimation; - private final AnimationTargets mAnimationTargets = new AnimationTargets(); + private AnimationHandler mAnimationHandler; private final ArrayList mTmpOpenApps = new ArrayList<>(); private final ArrayList mTmpCloseApps = new ArrayList<>(); @@ -233,7 +236,6 @@ class BackNavigationController { return infoBuilder.build(); } - mBackAnimationInProgress = true; // We don't have an application callback, let's find the destination of the back gesture // The search logic should align with ActivityClientController#finishActivity prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); @@ -314,8 +316,17 @@ class BackNavigationController { } if (prepareAnimation) { - prepareAnimationIfNeeded(currentTask, prevTask, prevActivity, - removedWindowContainer, backType, adapter); + mPendingAnimation = mAnimationHandler.scheduleAnimation(backType, adapter, + currentTask, prevTask, currentActivity, prevActivity); + prepareAnimation = mPendingAnimation != null; + mBackAnimationInProgress = prepareAnimation; + if (prepareAnimation) { + mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + if (mShowWallpaper) { + currentTask.getDisplayContent().mWallpaperController + .adjustWallpaperWindows(); + } + } } infoBuilder.setPrepareRemoteAnimation(prepareAnimation); } // Release wm Lock @@ -333,7 +344,7 @@ class BackNavigationController { } boolean isWaitBackTransition() { - return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition; + return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition; } boolean isKeyguardOccluded(WindowState focusWindow) { @@ -359,18 +370,19 @@ class BackNavigationController { boolean result = false; // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from // mOpeningApps if there is no visibility change. - if (mAnimationTargets.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) { + if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) { // remove close target from close list, open target from open list; // but the open target can be in close list. for (int i = openApps.size() - 1; i >= 0; --i) { final ActivityRecord ar = openApps.valueAt(i); - if (mAnimationTargets.isTarget(ar, true /* open */)) { + if (mAnimationHandler.isTarget(ar, true /* open */)) { openApps.removeAt(i); + mAnimationHandler.mOpenTransitionTargetMatch = true; } } for (int i = closeApps.size() - 1; i >= 0; --i) { final ActivityRecord ar = closeApps.valueAt(i); - if (mAnimationTargets.isTarget(ar, false /* open */)) { + if (mAnimationHandler.isTarget(ar, false /* open */)) { closeApps.removeAt(i); } } @@ -391,7 +403,7 @@ class BackNavigationController { * animations, and shouldn't join next transition. */ boolean containsBackAnimationTargets(Transition transition) { - if (!mAnimationTargets.mComposed + if (!mAnimationHandler.mComposed || (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) { return false; } @@ -408,18 +420,22 @@ class BackNavigationController { mTmpCloseApps.add(wc); } } - final boolean result = mAnimationTargets.containsBackAnimationTargets( + final boolean result = mAnimationHandler.containsBackAnimationTargets( mTmpOpenApps, mTmpCloseApps); + if (result) { + mAnimationHandler.mOpenTransitionTargetMatch = + mAnimationHandler.containTarget(mTmpOpenApps, true); + } mTmpOpenApps.clear(); mTmpCloseApps.clear(); return result; } boolean isMonitorTransitionTarget(WindowContainer wc) { - if (!mAnimationTargets.mComposed || !mAnimationTargets.mWaitTransition) { + if (!mAnimationHandler.mComposed || !mAnimationHandler.mWaitTransition) { return false; } - return mAnimationTargets.isTarget(wc, wc.isVisibleRequested() /* open */); + return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */); } /** @@ -428,60 +444,106 @@ class BackNavigationController { * cleanup together. */ void clearBackAnimations(SurfaceControl.Transaction cleanupTransaction) { - mAnimationTargets.clearBackAnimateTarget(cleanupTransaction); + mAnimationHandler.clearBackAnimateTarget(cleanupTransaction); } /** - * TODO: Animation composer - * prepareAnimationIfNeeded will become too complicated in order to support - * ActivityRecord/WindowState, using a factory class to create the RemoteAnimationTargets for - * different scenario. + * Create and handling animations status for an open/close animation targets. */ - private static class AnimationTargets { - ActivityRecord mCloseTarget; // Must be activity - WindowContainer mOpenTarget; // Can be activity or task if activity was removed + private static class AnimationHandler { + private final WindowManagerService mWindowManagerService; + private BackWindowAnimationAdaptor mCloseAdaptor; + private BackWindowAnimationAdaptor mOpenAdaptor; private boolean mComposed; private boolean mWaitTransition; private int mSwitchType = UNKNOWN; private SurfaceControl.Transaction mFinishedTransaction; + // This will be set before transition happen, to know whether the real opening target + // exactly match animating target. When target match, reparent the starting surface to + // the opening target like starting window do. + private boolean mOpenTransitionTargetMatch; + // The starting surface task Id. Used to clear the starting surface if the animation has + // request one during animating. + private int mRequestedStartingSurfaceTaskId; + private SurfaceControl mStartingSurface; + AnimationHandler(WindowManagerService wms) { + mWindowManagerService = wms; + } private static final int UNKNOWN = 0; private static final int TASK_SWITCH = 1; private static final int ACTIVITY_SWITCH = 2; - void reset(@NonNull WindowContainer close, @NonNull WindowContainer open) { - clearBackAnimateTarget(null); - if (close == null || open == null) { - Slog.e(TAG, "reset animation with null target close: " - + close + " open: " + open); - return; - } + private void initiate(WindowContainer close, WindowContainer open) { + WindowContainer closeTarget; if (close.asActivityRecord() != null && open.asActivityRecord() != null && (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) { mSwitchType = ACTIVITY_SWITCH; - mCloseTarget = close.asActivityRecord(); + closeTarget = close.asActivityRecord(); } else if (close.asTask() != null && open.asTask() != null && close.asTask() != open.asTask()) { mSwitchType = TASK_SWITCH; - mCloseTarget = close.asTask().getTopNonFinishingActivity(); + closeTarget = close.asTask().getTopNonFinishingActivity(); } else { mSwitchType = UNKNOWN; return; } - mOpenTarget = open; - mComposed = false; - mWaitTransition = false; + mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */); + mOpenAdaptor = createAdaptor(open, true /* isOpen */); + + if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) { + Slog.w(TAG, "composeNewAnimations fail, skip"); + clearBackAnimateTarget(null /* cleanupTransaction */); + } } - void composeNewAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) { - reset(close, open); - if (mSwitchType == UNKNOWN || mComposed || mCloseTarget == mOpenTarget - || mCloseTarget == null || mOpenTarget == null) { - return; + boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) { + clearBackAnimateTarget(null /* cleanupTransaction */); + if (close == null || open == null) { + Slog.e(TAG, "reset animation with null target close: " + + close + " open: " + open); + return false; + } + initiate(close, open); + if (mSwitchType == UNKNOWN) { + return false; } mComposed = true; mWaitTransition = false; + return true; + } + + RemoteAnimationTarget[] getAnimationTargets() { + return mComposed ? new RemoteAnimationTarget[] { + mCloseAdaptor.mAnimationTarget, mOpenAdaptor.mAnimationTarget} : null; + } + + boolean isSupportWindowlessSurface() { + return mWindowManagerService.mAtmService.mTaskOrganizerController + .isSupportWindowlessStartingSurface(); + } + + void createStartingSurface(TaskSnapshot snapshot) { + if (!mComposed) { + return; + } + + final ActivityRecord topActivity = getTopOpenActivity(); + if (topActivity == null) { + Slog.e(TAG, "createStartingSurface fail, no open activity: " + this); + return; + } + // TODO (b/257857570) draw snapshot by starting surface. + } + + private ActivityRecord getTopOpenActivity() { + if (mSwitchType == ACTIVITY_SWITCH) { + return mOpenAdaptor.mTarget.asActivityRecord(); + } else if (mSwitchType == TASK_SWITCH) { + return mOpenAdaptor.mTarget.asTask().getTopNonFinishingActivity(); + } + return null; } boolean containTarget(ArrayList wcs, boolean open) { @@ -495,13 +557,13 @@ class BackNavigationController { boolean isTarget(WindowContainer wc, boolean open) { if (open) { - return wc == mOpenTarget || mOpenTarget.hasChild(wc); + return wc == mOpenAdaptor.mTarget || mOpenAdaptor.mTarget.hasChild(wc); } if (mSwitchType == TASK_SWITCH) { - return wc == mCloseTarget - || (wc.asTask() != null && wc.hasChild(mCloseTarget)); + return wc == mCloseAdaptor.mTarget + || (wc.asTask() != null && wc.hasChild(mCloseAdaptor.mTarget)); } else if (mSwitchType == ACTIVITY_SWITCH) { - return wc == mCloseTarget; + return wc == mCloseAdaptor.mTarget; } return false; } @@ -519,19 +581,53 @@ class BackNavigationController { return; } final SurfaceControl.Transaction pt = t != null ? t - : mOpenTarget.getPendingTransaction(); + : mOpenAdaptor.mTarget.getPendingTransaction(); if (mFinishedTransaction != null) { pt.merge(mFinishedTransaction); mFinishedTransaction = null; } + cleanUpWindowlessSurface(); + + if (mCloseAdaptor != null) { + mCloseAdaptor.mTarget.cancelAnimation(); + mCloseAdaptor = null; + } + if (mOpenAdaptor != null) { + mOpenAdaptor.mTarget.cancelAnimation(); + mOpenAdaptor = null; + } + } + + private void cleanUpWindowlessSurface() { + final ActivityRecord ar = getTopOpenActivity(); + if (ar == null) { + Slog.w(TAG, "finishPresentAnimations without top activity: " + this); + } + final SurfaceControl.Transaction pendingT = ar != null ? ar.getPendingTransaction() + : mOpenAdaptor.mTarget.getPendingTransaction(); + // ensure open target is visible before cancel animation. + mOpenTransitionTargetMatch &= ar != null; + if (mOpenTransitionTargetMatch) { + pendingT.show(ar.getSurfaceControl()); + } + if (mRequestedStartingSurfaceTaskId != 0) { + // If open target match, reparent to open activity + if (mStartingSurface != null && mOpenTransitionTargetMatch) { + pendingT.reparent(mStartingSurface, ar.getSurfaceControl()); + } + // remove starting surface. + mStartingSurface = null; + // TODO (b/257857570) draw snapshot by starting surface. + mRequestedStartingSurfaceTaskId = 0; + } } void clearBackAnimateTarget(SurfaceControl.Transaction cleanupTransaction) { finishPresentAnimations(cleanupTransaction); - mCloseTarget = null; - mOpenTarget = null; mComposed = false; mWaitTransition = false; + mOpenTransitionTargetMatch = false; + mRequestedStartingSurfaceTaskId = 0; mSwitchType = UNKNOWN; if (mFinishedTransaction != null) { Slog.w(TAG, "Clear back animation, found un-processed finished transaction"); @@ -555,211 +651,284 @@ class BackNavigationController { @Override public String toString() { - final StringBuilder sb = new StringBuilder(128); - sb.append("AnimationTargets{"); - sb.append(" mOpenTarget= "); - sb.append(mOpenTarget); - sb.append(" mCloseTarget= "); - sb.append(mCloseTarget); - sb.append(" mSwitchType= "); - sb.append(mSwitchType); - sb.append(" mComposed= "); - sb.append(mComposed); - sb.append(" mWaitTransition= "); - sb.append(mWaitTransition); - sb.append('}'); - return sb.toString(); + return "AnimationTargets{" + + " openTarget= " + + mOpenAdaptor.mTarget + + " closeTarget= " + + mCloseAdaptor.mTarget + + " mSwitchType= " + + mSwitchType + + " mComposed= " + + mComposed + + " mWaitTransition= " + + mWaitTransition + + '}'; } - } - private void prepareAnimationIfNeeded(Task currentTask, - Task prevTask, ActivityRecord prevActivity, WindowContainer removedWindowContainer, - int backType, BackAnimationAdapter adapter) { - final ArrayList leashes = new ArrayList<>(); - final SurfaceControl.Transaction startedTransaction = currentTask.getPendingTransaction(); - final SurfaceControl.Transaction finishedTransaction = new SurfaceControl.Transaction(); - // Prepare a leash to animate for the departing window - final SurfaceControl animLeash = currentTask.makeAnimationLeash() - .setName("BackPreview Leash for " + currentTask) - .setHidden(false) - .build(); - removedWindowContainer.reparentSurfaceControl(startedTransaction, animLeash); + private static BackWindowAnimationAdaptor createAdaptor( + WindowContainer target, boolean isOpen) { + final BackWindowAnimationAdaptor adaptor = + new BackWindowAnimationAdaptor(target, isOpen); + target.startAnimation(target.getPendingTransaction(), adaptor, false /* hidden */, + ANIMATION_TYPE_PREDICT_BACK); + return adaptor; + } - final RemoteAnimationTarget topAppTarget = createRemoteAnimationTargetLocked( - currentTask, animLeash, MODE_CLOSING); + private static class BackWindowAnimationAdaptor implements AnimationAdapter { + SurfaceControl mCapturedLeash; + private final Rect mBounds = new Rect(); + private final WindowContainer mTarget; + private final boolean mIsOpen; + private RemoteAnimationTarget mAnimationTarget; - // reset leash after animation finished. - leashes.add(animLeash); - removedWindowContainer.reparentSurfaceControl(finishedTransaction, - removedWindowContainer.getParentSurfaceControl()); + BackWindowAnimationAdaptor(WindowContainer closeTarget, boolean isOpen) { + mBounds.set(closeTarget.getBounds()); + mTarget = closeTarget; + mIsOpen = isOpen; + } + @Override + public boolean getShowWallpaper() { + return false; + } - // Prepare a leash to animate for the entering window. - RemoteAnimationTarget behindAppTarget = null; - if (needsScreenshot(backType)) { - HardwareBuffer screenshotBuffer = null; - Task backTargetTask = prevTask; - switch(backType) { - case BackNavigationInfo.TYPE_CROSS_TASK: - int prevTaskId = prevTask != null ? prevTask.mTaskId : 0; - int prevUserId = prevTask != null ? prevTask.mUserId : 0; - screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId); - break; + @Override + public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, + int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { + mCapturedLeash = animationLeash; + createRemoteAnimationTarget(mIsOpen); + } + + @Override + public void onAnimationCancelled(SurfaceControl animationLeash) { + if (mCapturedLeash == animationLeash) { + mCapturedLeash = null; + } + } + + @Override + public long getDurationHint() { + return 0; + } + + @Override + public long getStatusBarTransitionsStartTime() { + return 0; + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix + "BackWindowAnimationAdaptor mCapturedLeash="); + pw.print(mCapturedLeash); + pw.println(); + } + + @Override + public void dumpDebug(ProtoOutputStream proto) { + + } + + RemoteAnimationTarget createRemoteAnimationTarget(boolean isOpen) { + if (mAnimationTarget != null) { + return mAnimationTarget; + } + Task t = mTarget.asTask(); + final ActivityRecord r = t != null ? t.getTopNonFinishingActivity() + : mTarget.asActivityRecord(); + if (t == null && r != null) { + t = r.getTask(); + } + if (t == null || r == null) { + Slog.e(TAG, "createRemoteAnimationTarget fail " + mTarget); + return null; + } + final WindowState mainWindow = r.findMainWindow(); + Rect insets; + if (mainWindow != null) { + insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( + mBounds, WindowInsets.Type.systemBars(), + false /* ignoreVisibility */).toRect(); + InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); + } else { + insets = new Rect(); + } + final int mode = isOpen ? MODE_OPENING : MODE_CLOSING; + mAnimationTarget = new RemoteAnimationTarget(t.mTaskId, mode, mCapturedLeash, + !r.fillsParent(), new Rect(), + insets, r.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), + mBounds, mBounds, t.getWindowConfiguration(), + true /* isNotInRecents */, null, null, t.getTaskInfo(), + r.checkEnterPictureInPictureAppOpsState()); + return mAnimationTarget; + } + } + + Runnable scheduleAnimation(int backType, BackAnimationAdapter adapter, + Task currentTask, Task previousTask, ActivityRecord currentActivity, + ActivityRecord previousActivity) { + switch (backType) { + case BackNavigationInfo.TYPE_RETURN_TO_HOME: + return new ScheduleAnimationBuilder(backType, adapter) + .setIsLaunchBehind(true) + .setComposeTarget(currentTask, previousTask) + .build(); case BackNavigationInfo.TYPE_CROSS_ACTIVITY: - if (prevActivity != null && prevActivity.mActivityComponent != null) { - screenshotBuffer = getActivitySnapshot(currentTask, prevActivity); + return new ScheduleAnimationBuilder(backType, adapter) + .setComposeTarget(currentActivity, previousActivity) + .setOpeningSnapshot(getActivitySnapshot(previousActivity)).build(); + case BackNavigationInfo.TYPE_CROSS_TASK: + return new ScheduleAnimationBuilder(backType, adapter) + .setComposeTarget(currentTask, previousTask) + .setOpeningSnapshot(getTaskSnapshot(previousTask)).build(); + } + return null; + } + + private class ScheduleAnimationBuilder { + final int mType; + final BackAnimationAdapter mBackAnimationAdapter; + WindowContainer mCloseTarget; + WindowContainer mOpenTarget; + TaskSnapshot mOpenSnapshot; + boolean mIsLaunchBehind; + + ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) { + mType = type; + mBackAnimationAdapter = backAnimationAdapter; + } + + ScheduleAnimationBuilder setComposeTarget(WindowContainer close, WindowContainer open) { + mCloseTarget = close; + mOpenTarget = open; + return this; + } + + ScheduleAnimationBuilder setOpeningSnapshot(TaskSnapshot snapshot) { + mOpenSnapshot = snapshot; + return this; + } + + ScheduleAnimationBuilder setIsLaunchBehind(boolean launchBehind) { + mIsLaunchBehind = launchBehind; + return this; + } + + Runnable build() { + if (mOpenTarget == null || mCloseTarget == null) { + return null; + } + final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface(); + final ActivityRecord launchBehindActivity = !shouldLaunchBehind ? null + : mOpenTarget.asTask() != null + ? mOpenTarget.asTask().getTopNonFinishingActivity() + : mOpenTarget.asActivityRecord() != null + ? mOpenTarget.asActivityRecord() : null; + if (shouldLaunchBehind && launchBehindActivity == null) { + Slog.e(TAG, "No opening activity"); + return null; + } + + if (!composeAnimations(mCloseTarget, mOpenTarget)) { + return null; + } + if (launchBehindActivity != null) { + setLaunchBehind(launchBehindActivity); + } else { + createStartingSurface(mOpenSnapshot); + } + + final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback( + launchBehindActivity != null ? triggerBack -> { + if (!triggerBack) { + restoreLaunchBehind(launchBehindActivity); + } + } : null, + mCloseTarget); + final RemoteAnimationTarget[] targets = getAnimationTargets(); + + return () -> { + try { + mBackAnimationAdapter.getRunner().onAnimationStart(mType, + targets, null, null, callback); + } catch (RemoteException e) { + e.printStackTrace(); } - backTargetTask = currentTask; - break; + }; } - // Find a screenshot of the previous activity if we actually have an animation - SurfaceControl animationLeashParent = removedWindowContainer.getAnimationLeashParent(); - if (screenshotBuffer != null) { - final SurfaceControl screenshotSurface = new SurfaceControl.Builder() - .setName("BackPreview Screenshot for " + prevActivity) - .setHidden(false) - .setParent(animationLeashParent) - .setBLASTLayer() - .build(); - startedTransaction.setBuffer(screenshotSurface, screenshotBuffer); - - // The Animation leash needs to be above the screenshot surface, but the animation - // leash needs to be added before to be in the synchronized block. - startedTransaction.setLayer(topAppTarget.leash, 1); - - behindAppTarget = - createRemoteAnimationTargetLocked( - backTargetTask, screenshotSurface, MODE_OPENING); - - // reset leash after animation finished. - leashes.add(screenshotSurface); - } - } else if (prevTask != null && prevActivity != null) { - // Make previous task show from behind by marking its top activity as visible - // and launch-behind to bump its visibility for the duration of the back gesture. - setLaunchBehind(prevActivity); - - final SurfaceControl leash = prevActivity.makeAnimationLeash() - .setName("BackPreview Leash for " + prevActivity) - .setHidden(false) - .build(); - prevActivity.reparentSurfaceControl(startedTransaction, leash); - behindAppTarget = createRemoteAnimationTargetLocked( - prevTask, leash, MODE_OPENING); - - // reset leash after animation finished. - leashes.add(leash); - prevActivity.reparentSurfaceControl(finishedTransaction, - prevActivity.getParentSurfaceControl()); - } - - if (mShowWallpaper) { - currentTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows(); - // TODO(b/241808055): If the current animation need to show wallpaper and animate the - // wallpaper, start the wallpaper animation to collect wallpaper target and deliver it - // to the back animation controller. - } - - final RemoteAnimationTarget[] targets = (behindAppTarget == null) - ? new RemoteAnimationTarget[] {topAppTarget} - : new RemoteAnimationTarget[] {topAppTarget, behindAppTarget}; - - final ActivityRecord finalPrevActivity = prevActivity; - final IBackAnimationFinishedCallback callback = - new IBackAnimationFinishedCallback.Stub() { + private IBackAnimationFinishedCallback makeAnimationFinishedCallback( + Consumer b, WindowContainer closeTarget) { + return new IBackAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished(boolean triggerBack) { - for (SurfaceControl sc: leashes) { - finishedTransaction.remove(sc); - } + final SurfaceControl.Transaction finishedTransaction = + new SurfaceControl.Transaction(); synchronized (mWindowManagerService.mGlobalLock) { + if (b != null) { + b.accept(triggerBack); + } if (triggerBack) { final SurfaceControl surfaceControl = - removedWindowContainer.getSurfaceControl(); + closeTarget.getSurfaceControl(); if (surfaceControl != null && surfaceControl.isValid()) { - // The animation is finish and start waiting for transition, - // hide the task surface before it re-parented to avoid flicker. + // Hide the close target surface when transition start. finishedTransaction.hide(surfaceControl); } - } else if (!needsScreenshot(backType)) { - restoreLaunchBehind(finalPrevActivity); } - if (!mAnimationTargets.setFinishTransaction(finishedTransaction)) { + if (!setFinishTransaction(finishedTransaction)) { finishedTransaction.apply(); } if (!triggerBack) { - mAnimationTargets.clearBackAnimateTarget(null); + clearBackAnimateTarget( + null /* cleanupTransaction */); } else { - mAnimationTargets.mWaitTransition = true; + mWaitTransition = true; } } // TODO Add timeout monitor if transition didn't happen } }; - if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) { - mAnimationTargets.composeNewAnimations(removedWindowContainer, prevActivity); - } else if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME - || backType == BackNavigationInfo.TYPE_CROSS_TASK) { - mAnimationTargets.composeNewAnimations(removedWindowContainer, prevTask); - } - scheduleAnimationLocked(backType, targets, adapter, callback); - } - - @NonNull - private static RemoteAnimationTarget createRemoteAnimationTargetLocked( - Task task, SurfaceControl animLeash, int mode) { - ActivityRecord topApp = task.getTopRealVisibleActivity(); - if (topApp == null) { - topApp = task.getTopNonFinishingActivity(); - } - - final WindowState mainWindow = topApp != null - ? topApp.findMainWindow() - : null; - int windowType = INVALID_WINDOW_TYPE; - if (mainWindow != null) { - windowType = mainWindow.getWindowType(); - } - - Rect bounds = new Rect(task.getBounds()); - Rect localBounds = new Rect(bounds); - Point tmpPos = new Point(); - task.getRelativePosition(tmpPos); - localBounds.offsetTo(tmpPos.x, tmpPos.y); - - return new RemoteAnimationTarget( - task.mTaskId, - mode, - animLeash, - false /* isTransluscent */, - new Rect() /* clipRect */, - new Rect() /* contentInsets */, - task.getPrefixOrderIndex(), - tmpPos /* position */, - localBounds /* localBounds */, - bounds /* screenSpaceBounds */, - task.getWindowConfiguration(), - true /* isNotInRecent */, - null, - null, - task.getTaskInfo(), - false, - windowType); - } - - @VisibleForTesting - void scheduleAnimationLocked(@BackNavigationInfo.BackTargetType int type, - RemoteAnimationTarget[] targets, BackAnimationAdapter backAnimationAdapter, - IBackAnimationFinishedCallback callback) { - mPendingAnimation = () -> { - try { - backAnimationAdapter.getRunner().onAnimationStart(type, - targets, null, null, callback); - } catch (RemoteException e) { - e.printStackTrace(); } - }; - mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + + private void setLaunchBehind(ActivityRecord activity) { + if (activity == null) { + return; + } + if (!activity.isVisibleRequested()) { + activity.setVisibility(true); + } + activity.mLaunchTaskBehind = true; + + // Handle fixed rotation launching app. + final DisplayContent dc = activity.mDisplayContent; + dc.rotateInDifferentOrientationIfNeeded(activity); + if (activity.hasFixedRotationTransform()) { + // Set the record so we can recognize it to continue to update display + // orientation if the previous activity becomes the top later. + dc.setFixedRotationLaunchingApp(activity, + activity.getWindowConfiguration().getRotation()); + } + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); + activity.mTaskSupervisor.mStoppingActivities.remove(activity); + activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, + 0 /* configChanges */, false /* preserveWindows */, true); + } + private void restoreLaunchBehind(ActivityRecord activity) { + if (activity == null) { + return; + } + + activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + + // Restore the launch-behind state. + activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); + activity.mLaunchTaskBehind = false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + activity); + } + } } void checkAnimationReady(WallpaperController wallpaperController) { @@ -796,80 +965,36 @@ class BackNavigationController { mShowWallpaper = false; } - private HardwareBuffer getActivitySnapshot(@NonNull Task task, ActivityRecord r) { - return task.getSnapshotForActivityRecord(r); - } - - private HardwareBuffer getTaskSnapshot(int taskId, int userId) { - if (mWindowManagerService.mTaskSnapshotController == null) { + private static TaskSnapshot getActivitySnapshot(@NonNull ActivityRecord r) { + if (!isScreenshotEnabled()) { return null; } - TaskSnapshot snapshot = mWindowManagerService.mTaskSnapshotController.getSnapshot(taskId, - userId, true /* restoreFromDisk */, false /* isLowResolution */); - return snapshot != null ? snapshot.getHardwareBuffer() : null; + // Check if we have a screenshot of the previous activity, indexed by its + // component name. + // TODO return TaskSnapshot when feature complete. +// final HardwareBuffer hw = r.getTask().getSnapshotForActivityRecord(r); + return null; } - private boolean needsScreenshot(int backType) { + private static TaskSnapshot getTaskSnapshot(Task task) { if (!isScreenshotEnabled()) { - return false; + return null; } - switch (backType) { - case BackNavigationInfo.TYPE_RETURN_TO_HOME: - case BackNavigationInfo.TYPE_DIALOG_CLOSE: - return false; - } - return true; + // Don't read from disk!! + return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( + task.mTaskId, task.mUserId, false /* restoreFromDisk */, + false /* isLowResolution */); } void setWindowManager(WindowManagerService wm) { mWindowManagerService = wm; - } - - private void setLaunchBehind(ActivityRecord activity) { - if (activity == null) { - return; - } - if (!activity.isVisibleRequested()) { - activity.setVisibility(true); - } - activity.mLaunchTaskBehind = true; - - // Handle fixed rotation launching app. - final DisplayContent dc = activity.mDisplayContent; - dc.rotateInDifferentOrientationIfNeeded(activity); - if (activity.hasFixedRotationTransform()) { - // Set the record so we can recognize it to continue to update display orientation - // if the previous activity becomes the top later. - dc.setFixedRotationLaunchingApp(activity, - activity.getWindowConfiguration().getRotation()); - } - - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); - activity.mTaskSupervisor.mStoppingActivities.remove(activity); - activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, - 0 /* configChanges */, false /* preserveWindows */, true); - } - - private void restoreLaunchBehind(ActivityRecord activity) { - if (activity == null) { - return; - } - - activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); - - // Restore the launch-behind state. - activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); - activity.mLaunchTaskBehind = false; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - activity); + mAnimationHandler = new AnimationHandler(wm); } boolean isWallpaperVisible(WindowState w) { - return mAnimationTargets.mComposed && mShowWallpaper + return mAnimationHandler.mComposed && mShowWallpaper && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null - && mAnimationTargets.isTarget(w.mActivityRecord, true /* open */); + && mAnimationHandler.isTarget(w.mActivityRecord, true /* open */); } // Called from WindowManagerService to write to a protocol buffer output stream. diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index ca86db9e4f7c6..408ea6eb8c1aa 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -564,6 +564,11 @@ class SurfaceAnimator { */ public static final int ANIMATION_TYPE_STARTING_REVEAL = 1 << 7; + /** + * Animation when a back gesture animation is applied to a window container. + * @hide + */ + public static final int ANIMATION_TYPE_PREDICT_BACK = 1 << 8; /** * Bitmask to include all animation types. This is NOT an {@link AnimationType} * @hide @@ -583,7 +588,8 @@ class SurfaceAnimator { ANIMATION_TYPE_WINDOW_ANIMATION, ANIMATION_TYPE_INSETS_CONTROL, ANIMATION_TYPE_TOKEN_TRANSFORM, - ANIMATION_TYPE_STARTING_REVEAL + ANIMATION_TYPE_STARTING_REVEAL, + ANIMATION_TYPE_PREDICT_BACK }) @Retention(RetentionPolicy.SOURCE) @interface AnimationType {} @@ -602,6 +608,7 @@ class SurfaceAnimator { case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation"; case ANIMATION_TYPE_TOKEN_TRANSFORM: return "token_transform"; case ANIMATION_TYPE_STARTING_REVEAL: return "starting_reveal"; + case ANIMATION_TYPE_PREDICT_BACK: return "predict_back"; default: return "unknown type:" + type; } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 8570db275a080..3a30e4b0cf1f4 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -722,6 +722,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } + boolean isSupportWindowlessStartingSurface() { + // Enable after ag/20426257 + final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); + if (lastOrganizer == null) { + return false; + } + return false; + } /** * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has * removed the splash screen view. diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index ee8a98870dd57..ff5ede7a316ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -34,7 +34,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -96,9 +95,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); // verify if back animation would start. - verify(mBackNavigationController).scheduleAnimationLocked( - eq(BackNavigationInfo.TYPE_RETURN_TO_HOME), any(), eq(mBackAnimationAdapter), - any()); + assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation()); } @Test @@ -115,9 +112,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK)); // verify if back animation would start. - verify(mBackNavigationController).scheduleAnimationLocked( - eq(BackNavigationInfo.TYPE_CROSS_TASK), any(), eq(mBackAnimationAdapter), - any()); + assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation()); // reset drawning status topTask.forAllWindows(w -> {