diff options
Diffstat (limited to 'libs')
21 files changed, 224 insertions, 89 deletions
diff --git a/libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml index 54b08c6f220e..81656fe7e80d 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml @@ -35,7 +35,7 @@ android:layout_height="wrap_content" android:focusable="true" android:text="@string/manage_bubbles_text" - android:textColor="?android:attr/textColorPrimaryInverse" + android:textColor="?android:attr/textColorPrimary" /> </com.android.wm.shell.bubbles.BubbleExpandedView> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java index d066cf92650c..eea5c08818cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java @@ -64,7 +64,7 @@ public class SplitScreenTransitions implements Transitions.TransitionHandler { /** Keeps track of currently running animations */ private final ArrayList<Animator> mAnimations = new ArrayList<>(); - private Runnable mFinishCallback = null; + private Transitions.TransitionFinishCallback mFinishCallback = null; private SurfaceControl.Transaction mFinishTransaction; SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, @@ -203,7 +203,8 @@ public class SplitScreenTransitions implements Transitions.TransitionHandler { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + @NonNull SurfaceControl.Transaction t, + @NonNull Transitions.TransitionFinishCallback finishCallback) { if (transition != mPendingDismiss && transition != mPendingEnter) { // If we're not in split-mode, just abort if (!mSplitScreen.isDividerVisible()) return false; @@ -330,7 +331,7 @@ public class SplitScreenTransitions implements Transitions.TransitionHandler { mFinishTransaction.apply(); mTransactionPool.release(mFinishTransaction); mFinishTransaction = null; - mFinishCallback.run(); + mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); mFinishCallback = null; if (mAnimatingTransition == mPendingEnter) { mPendingEnter = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 87f9d5b7c189..aeea10d47a67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -56,6 +56,8 @@ public class PipAnimationController { public static final int TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN = 4; public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5; public static final int TRANSITION_DIRECTION_SNAP_AFTER_RESIZE = 6; + public static final int TRANSITION_DIRECTION_USER_RESIZE = 7; + public static final int TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND = 8; @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = { TRANSITION_DIRECTION_NONE, @@ -64,7 +66,9 @@ public class PipAnimationController { TRANSITION_DIRECTION_LEAVE_PIP, TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN, TRANSITION_DIRECTION_REMOVE_STACK, - TRANSITION_DIRECTION_SNAP_AFTER_RESIZE + TRANSITION_DIRECTION_SNAP_AFTER_RESIZE, + TRANSITION_DIRECTION_USER_RESIZE, + TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND }) @Retention(RetentionPolicy.SOURCE) public @interface TransitionDirection {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 5224bcc7c539..a777a2766ee7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -151,6 +151,18 @@ public class PipSurfaceTransactionHelper { return this; } + /** + * Re-parents the snapshot to the parent's surface control and shows it. + */ + public PipSurfaceTransactionHelper reparentAndShowSurfaceSnapshot( + SurfaceControl.Transaction t, SurfaceControl parent, SurfaceControl snapshot) { + t.reparent(snapshot, parent); + t.setLayer(snapshot, Integer.MAX_VALUE); + t.show(snapshot); + t.apply(); + return this; + } + public interface SurfaceControlTransactionFactory { SurfaceControl.Transaction getTransaction(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index f43f44b03aff..1279cd36724b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -25,6 +25,7 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_NONE; @@ -32,9 +33,13 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -820,13 +825,22 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleAnimateResizePip(Rect toBounds, int duration, Consumer<Rect> updateBoundsCallback) { + scheduleAnimateResizePip(toBounds, duration, TRANSITION_DIRECTION_NONE, + updateBoundsCallback); + } + + /** + * Animates resizing of the pinned stack given the duration. + */ + public void scheduleAnimateResizePip(Rect toBounds, int duration, + @PipAnimationController.TransitionDirection int direction, + Consumer<Rect> updateBoundsCallback) { if (mWaitForFixedRotation) { Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred"); return; } scheduleAnimateResizePip(mPipBoundsState.getBounds(), toBounds, 0 /* startingAngle */, - null /* sourceHintRect */, TRANSITION_DIRECTION_NONE, duration, - updateBoundsCallback); + null /* sourceHintRect */, direction, duration, updateBoundsCallback); } /** @@ -919,7 +933,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, scheduleFinishResizePip(destinationBounds, TRANSITION_DIRECTION_NONE, updateBoundsCallback); } - private void scheduleFinishResizePip(Rect destinationBounds, + /** + * Finish an intermediate resize operation. This is expected to be called after + * {@link #scheduleResizePip}. + * + * @param destinationBounds the final bounds of the PIP after resizing + * @param direction the transition direction + * @param updateBoundsCallback a callback to invoke after finishing the resize + */ + public void scheduleFinishResizePip(Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, Consumer<Rect> updateBoundsCallback) { if (mState.shouldBlockResizeRequest()) { @@ -1049,7 +1071,60 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(destinationBounds, direction, tx, wct); - applyFinishBoundsResize(wct, direction); + + // Only corner drag, pinch or expand/un-expand resizing may lead to animating the finish + // resize operation. + final boolean mayAnimateFinishResize = direction == TRANSITION_DIRECTION_USER_RESIZE + || direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE + || direction == TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; + // Animate with a cross-fade if enabled and seamless resize is disables by the app. + final boolean animateCrossFadeResize = mayAnimateFinishResize + && !mPictureInPictureParams.isSeamlessResizeEnabled(); + if (animateCrossFadeResize) { + // Take a snapshot of the PIP task and hide it. We'll show it and fade it out after + // the wct transaction is applied and the activity is laid out again. + final SurfaceControl snapshotSurface = mTaskOrganizer.takeScreenshot(mToken); + mSurfaceTransactionHelper.reparentAndShowSurfaceSnapshot( + mSurfaceControlTransactionFactory.getTransaction(), mLeash, snapshotSurface); + mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() { + @Override + public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) { + // Scale the snapshot from its pre-resize bounds to the post-resize bounds. + final Rect snapshotSrc = new Rect(0, 0, snapshotSurface.getWidth(), + snapshotSurface.getHeight()); + final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(), + destinationBounds.height()); + mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest); + t.apply(); + + mUpdateHandler.post(() -> { + // Start animation to fade out the snapshot. + final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f); + animator.setDuration(mEnterExitAnimationDuration); + animator.addUpdateListener(animation -> { + final float alpha = (float) animation.getAnimatedValue(); + final SurfaceControl.Transaction tx = + mSurfaceControlTransactionFactory.getTransaction(); + tx.setAlpha(snapshotSurface, alpha); + tx.apply(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + final SurfaceControl.Transaction tx = + mSurfaceControlTransactionFactory.getTransaction(); + tx.remove(snapshotSurface); + tx.apply(); + } + }); + animator.start(); + }); + } + }); + } else { + applyFinishBoundsResize(wct, direction); + } + finishResizeForMenu(destinationBounds); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index df7c75332675..8c8f5c6d4940 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip.phone; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; @@ -624,7 +625,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // Intentionally resize here even if the current bounds match the destination bounds. // This is so all the proper callbacks are performed. - mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback); + mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, + TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND, mUpdateBoundsCallback); setAnimatingToBounds(toBounds); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 1f6df3aa7dc7..762b73800c23 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -45,6 +45,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.policy.TaskResizingAlgorithm; import com.android.wm.shell.R; +import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipTaskOrganizer; @@ -542,6 +543,7 @@ public class PipResizeGestureHandler { }); } else { mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, + PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, (Rect bounds) -> { mHandler.post(callback); }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index 7f98965d7f1f..2f2e325aafad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -51,8 +51,7 @@ class MainStage extends StageTaskListener { if (mIsActive) return; final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setHidden(rootToken, false) - .setBounds(rootToken, rootBounds) + wct.setBounds(rootToken, rootBounds) .setLaunchRoot( rootToken, CONTROLLED_WINDOWING_MODES, @@ -63,7 +62,7 @@ class MainStage extends StageTaskListener { CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES, true /* onTop */) - // Moving the root task to top after the child tasks were repareted , or the root + // Moving the root task to top after the child tasks were re-parented , or the root // task cannot be visible and focused. .reorder(rootToken, true /* onTop */); @@ -71,13 +70,16 @@ class MainStage extends StageTaskListener { } void deactivate(WindowContainerTransaction wct) { + deactivate(wct, false /* toTop */); + } + + void deactivate(WindowContainerTransaction wct, boolean toTop) { if (!mIsActive) return; mIsActive = false; if (mRootTaskInfo == null) return; final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setHidden(rootToken, true) - .setLaunchRoot( + wct.setLaunchRoot( rootToken, null, null) @@ -86,7 +88,9 @@ class MainStage extends StageTaskListener { null /* newParent */, CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, CONTROLLED_ACTIVITY_TYPES, - false /* onTop */) + toTop) + // We want this re-order to the bottom regardless since we are re-parenting + // all its tasks. .reorder(rootToken, false /* onTop */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index cd44e4b7cebf..e7cd38fb4bca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -16,8 +16,6 @@ package com.android.wm.shell.splitscreen; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; - import android.app.ActivityManager; import android.graphics.Rect; import android.window.WindowContainerToken; @@ -50,14 +48,14 @@ class SideStage extends StageTaskListener { .reorder(rootToken, true); } - boolean removeAllTasks(WindowContainerTransaction wct) { + boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, CONTROLLED_ACTIVITY_TYPES, - false /* onTop */); + toTop); return true; } @@ -70,12 +68,4 @@ class SideStage extends StageTaskListener { .reparent(task.token, newParent, false /* onTop */); return true; } - - int getTopVisibleTaskId() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.valueAt(i); - if (task.isVisible) return task.taskId; - } - return INVALID_TASK_ID; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index dd41957935d7..d571e7514542 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -146,10 +146,16 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } void exitSplitScreen() { + exitSplitScreen(null /* childrenToTop */); + } + + private void exitSplitScreen(StageTaskListener childrenToTop) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.removeAllTasks(wct); - mMainStage.deactivate(wct); + mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); + mMainStage.deactivate(wct, childrenToTop == mMainStage); mTaskOrganizer.applyTransaction(wct); + // Reset divider position. + mSplitLayout.resetDividerPosition(); } void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { @@ -272,41 +278,29 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } private void onStageHasChildrenChanged(StageListenerImpl stageListener) { - if (stageListener == mSideStageListener) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mSideStageListener.mHasChildren) { - // Make sure the main stage is active. - mMainStage.activate(getMainStageBounds(), wct); - } else { - // The side stage no long has children so we can deactivate the main stage. - mMainStage.deactivate(wct); + final boolean hasChildren = stageListener.mHasChildren; + final boolean isSideStage = stageListener == mSideStageListener; + if (!hasChildren) { + if (isSideStage && mMainStageListener.mVisible) { + // Exit to main stage if side stage no longer has children. + exitSplitScreen(mMainStage); + } else if (!isSideStage && mSideStageListener.mVisible) { + // Exit to side stage if main stage no longer has children. + exitSplitScreen(mSideStage); } + } else if (isSideStage) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + // Make sure the main stage is active. + mMainStage.activate(getMainStageBounds(), wct); mTaskOrganizer.applyTransaction(wct); } } @Override public void onSnappedToDismiss(boolean bottomOrRight) { - if (mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT && bottomOrRight) { - // Main stage was fully expanded...Just side side-stage. - setSideStageVisibility(false); - } else { - // Side stage was fully expanded...Move its top task to the main stage - // and hide side-stage. - // TODO: Would UX prefer the side-stage go into fullscreen mode here? - final int taskId = mSideStage.getTopVisibleTaskId(); - if (taskId == INVALID_TASK_ID) { - throw new IllegalStateException("Side stage doesn't have visible task? " - + mSideStage); - } - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.removeTask(taskId, mMainStage.mRootTaskInfo.getToken(), wct); - mSideStage.setVisibility(false, wct); - mTaskOrganizer.applyTransaction(wct); - } - - // Reset divider position. - mSplitLayout.resetDividerPosition(); + final boolean mainStageToTop = bottomOrRight + && mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT; + exitSplitScreen(mainStageToTop ? mMainStage : mSideStage); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index f1e06f7f5724..59f8c1df1213 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -56,7 +56,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + @NonNull SurfaceControl.Transaction t, + @NonNull Transitions.TransitionFinishCallback finishCallback) { if (mAnimations.containsKey(transition)) { throw new IllegalStateException("Got a duplicate startAnimation call for " + transition); @@ -68,7 +69,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; mAnimations.remove(transition); - finishCallback.run(); + finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); }; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index cf141c6a4826..8271b0689053 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -23,9 +23,9 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; -import android.view.IRemoteAnimationFinishedCallback; import android.view.SurfaceControl; import android.window.IRemoteTransition; +import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionFilter; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -54,7 +54,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } void addFiltered(TransitionFilter filter, IRemoteTransition remote) { - mFilters.add(new Pair<TransitionFilter, IRemoteTransition>(filter, remote)); + mFilters.add(new Pair<>(filter, remote)); } void removeFiltered(IRemoteTransition remote) { @@ -67,7 +67,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + @NonNull SurfaceControl.Transaction t, + @NonNull Transitions.TransitionFinishCallback finishCallback) { IRemoteTransition pendingRemote = mPendingRemotes.remove(transition); if (pendingRemote == null) { // If no explicit remote, search filters until one matches @@ -84,15 +85,17 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { final IRemoteTransition remote = pendingRemote; final IBinder.DeathRecipient remoteDied = () -> { Log.e(Transitions.TAG, "Remote transition died, finishing"); - mMainExecutor.execute(finishCallback); + mMainExecutor.execute( + () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); }; - IRemoteAnimationFinishedCallback cb = new IRemoteAnimationFinishedCallback.Stub() { + IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { @Override - public void onAnimationFinished() throws RemoteException { + public void onTransitionFinished(WindowContainerTransaction wct) { if (remote.asBinder() != null) { remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); } - mMainExecutor.execute(finishCallback); + mMainExecutor.execute( + () -> finishCallback.onTransitionFinished(wct, null /* wctCB */)); } }; try { @@ -101,8 +104,12 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } remote.startAnimation(info, t, cb); } catch (RemoteException e) { + if (remote.asBinder() != null) { + remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); + } Log.e(Transitions.TAG, "Error running remote transition.", e); - mMainExecutor.execute(finishCallback); + mMainExecutor.execute( + () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); } return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index c085168a3317..2ab4e0bdd76f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -38,6 +38,7 @@ import android.window.TransitionFilter; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; import androidx.annotation.BinderThread; @@ -232,22 +233,22 @@ public class Transitions { // Invalid root-leash implies that the transition is empty/no-op, so just do // housekeeping and return. t.apply(); - onFinish(transitionToken); + onFinish(transitionToken, null /* wct */, null /* wctCB */); return; } setupStartState(info, t); - final Runnable finishRunnable = () -> onFinish(transitionToken); + final TransitionFinishCallback finishCb = (wct, cb) -> onFinish(transitionToken, wct, cb); // If a handler chose to uniquely run this animation, try delegating to it. if (active.mFirstHandler != null && active.mFirstHandler.startAnimation( - transitionToken, info, t, finishRunnable)) { + transitionToken, info, t, finishCb)) { return; } // Otherwise give every other handler a chance (in order) for (int i = mHandlers.size() - 1; i >= 0; --i) { if (mHandlers.get(i) == active.mFirstHandler) continue; - if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishRunnable)) { + if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishCb)) { return; } } @@ -255,7 +256,8 @@ public class Transitions { "This shouldn't happen, maybe the default handler is broken."); } - private void onFinish(IBinder transition) { + private void onFinish(IBinder transition, @Nullable WindowContainerTransaction wct, + @Nullable WindowContainerTransactionCallback wctCB) { if (!mActiveTransitions.containsKey(transition)) { Log.e(TAG, "Trying to finish a non-running transition. Maybe remote crashed?"); return; @@ -263,7 +265,7 @@ public class Transitions { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animations finished, notifying core %s", transition); mActiveTransitions.remove(transition); - mOrganizer.finishTransition(transition, null, null); + mOrganizer.finishTransition(transition, wct, wctCB); } void requestStartTransition(@NonNull IBinder transitionToken, @@ -298,6 +300,23 @@ public class Transitions { } /** + * Interface for a callback that must be called after a TransitionHandler finishes playing an + * animation. + */ + public interface TransitionFinishCallback { + /** + * This must be called on the main thread when a transition finishes playing an animation. + * The transition must not touch the surfaces after this has been called. + * + * @param wct A WindowContainerTransaction to run along with the transition clean-up. + * @param wctCB A sync callback that will be run when the transition clean-up is done and + * wct has been applied. + */ + void onTransitionFinished(@Nullable WindowContainerTransaction wct, + @Nullable WindowContainerTransactionCallback wctCB); + } + + /** * Interface for something which can handle a subset of transitions. */ public interface TransitionHandler { @@ -310,7 +329,8 @@ public class Transitions { * @return true if transition was handled, false if not (falls-back to default). */ boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback); + @NonNull SurfaceControl.Transaction t, + @NonNull TransitionFinishCallback finishCallback); /** * Potentially handles a startTransition request. diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt index 32ffd80a2741..c436eb2d01d5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.apppairs import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -27,9 +28,11 @@ import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.appPairsDividerIsVisible @@ -46,6 +49,7 @@ import org.junit.runners.Parameterized * Test open apps to app pairs and rotate. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -72,7 +76,8 @@ class RotateTwoLaunchedAppsInAppPairsMode( } assertions { layersTrace { - navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation) + navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation, + enabled = !configuration.startRotation.isRotated()) statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation) appPairsDividerIsVisible() appPairsPrimaryBoundsIsVisible(configuration.endRotation, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index 5820e78c897b..cd4f4f62d1b3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.apppairs import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -27,9 +28,11 @@ import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.appPairsDividerIsVisible @@ -46,6 +49,7 @@ import org.junit.runners.Parameterized * Test open apps to app pairs and rotate. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -72,7 +76,8 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( } assertions { layersTrace { - navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation) + navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation, + enabled = !configuration.startRotation.isRotated()) statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation) appPairsDividerIsVisible() appPairsPrimaryBoundsIsVisible(configuration.endRotation, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt index ff4e0b275c9d..88dab51f9b9f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt @@ -67,7 +67,8 @@ class NonResizableLaunchInLegacySplitScreenTest( visibleLayersShownMoreThanOneConsecutiveEntry( listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName, LETTER_BOX_NAME, - TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME) + TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME), + enabled = false ) } windowManagerTrace { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index 3524f7ff80d4..5a3d18d9feef 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.runFlicker @@ -38,6 +39,7 @@ import org.junit.runners.Parameterized * Test Pip launch and exit. * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt index b2df6845ea2b..292d0efeaeea 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.flicker.pip import android.content.Intent +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.runFlicker @@ -44,6 +45,7 @@ import org.junit.runners.Parameterized * Test Pip with orientation changes. * To run this test: `atest WMShellFlickerTests:PipOrientationTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index f2ccf8d8f12a..4a6a5b504e2c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -48,6 +49,7 @@ import org.junit.runners.Parameterized * Test Pip Stack in bounds after rotations. * To run this test: `atest WMShellFlickerTests:PipRotationTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index f3bee4ba27e8..5eca3e75a7b8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -44,10 +44,10 @@ import android.app.ActivityManager.RunningTaskInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.view.IRemoteAnimationFinishedCallback; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.IRemoteTransition; +import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionFilter; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -119,7 +119,8 @@ public class ShellTransitionTests { TestTransitionHandler testHandler = new TestTransitionHandler() { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + @NonNull SurfaceControl.Transaction t, + @NonNull Transitions.TransitionFinishCallback finishCallback) { for (TransitionInfo.Change chg : info.getChanges()) { if (chg.getMode() == TRANSIT_CHANGE) { return super.startAnimation(transition, info, t, finishCallback); @@ -192,12 +193,13 @@ public class ShellTransitionTests { transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; + final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction(); IRemoteTransition testRemote = new IRemoteTransition.Stub() { @Override public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t, - IRemoteAnimationFinishedCallback finishCallback) throws RemoteException { + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { remoteCalled[0] = true; - finishCallback.onAnimationFinished(); + finishCallback.onTransitionFinished(remoteFinishWCT); } }; IBinder transitToken = new Binder(); @@ -211,7 +213,7 @@ public class ShellTransitionTests { assertTrue(remoteCalled[0]); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), eq(remoteFinishWCT), any()); } @Test @@ -261,9 +263,9 @@ public class ShellTransitionTests { IRemoteTransition testRemote = new IRemoteTransition.Stub() { @Override public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t, - IRemoteAnimationFinishedCallback finishCallback) throws RemoteException { + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { remoteCalled[0] = true; - finishCallback.onAnimationFinished(); + finishCallback.onTransitionFinished(null /* wct */); } }; @@ -317,11 +319,12 @@ public class ShellTransitionTests { } class TestTransitionHandler implements Transitions.TransitionHandler { - final ArrayList<Runnable> mFinishes = new ArrayList<>(); + final ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>(); @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + @NonNull SurfaceControl.Transaction t, + @NonNull Transitions.TransitionFinishCallback finishCallback) { mFinishes.add(finishCallback); return true; } @@ -335,7 +338,7 @@ public class ShellTransitionTests { void finishAll() { for (int i = mFinishes.size() - 1; i >= 0; --i) { - mFinishes.get(i).run(); + mFinishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */); } mFinishes.clear(); } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index d14dc3672b99..80eddafbde4f 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -276,7 +276,10 @@ bool SkiaPipeline::setupMultiFrameCapture() { // we need to keep it until after mMultiPic.close() // procs is passed as a pointer, but just as a method of having an optional default. // procs doesn't need to outlive this Make call. - mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs); + mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs, + [sharingCtx = mSerialContext.get()](const SkPicture* pic) { + SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx); + }); return true; } else { ALOGE("Could not open \"%s\" for writing.", mCapturedFile.c_str()); |