Loading libs/WindowManager/Shell/aconfig/multitasking.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -273,6 +273,16 @@ flag { } } flag { name: "fix_task_view_rotation_animation" namespace: "multitasking" description: "Fixes jump-cut animation and un-synced bounds (including bubbles) when rotating." bug: "351813733" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "fix_bubbles_expanded_sysui_flag" namespace: "multitasking" Loading libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +153 −13 Original line number Diff line number Diff line Loading @@ -98,6 +98,13 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV /** A temp transaction used for quick things. */ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); /** * A display change transition that also involves a TaskView. The display animation is deferred * until the TaskView's bounds are updated. This object holds the logic to dispatch the display * transition once the TaskView is ready. */ private PendingRedirectTransition mPendingRedirectTransition; /** * TaskView makes heavy use of startTransition. Only one shell-initiated transition can be * in-flight (collecting) at a time (because otherwise, the operations could get merged into Loading Loading @@ -636,7 +643,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.setTaskBoundsInTransition(): taskView=%d " + "bounds=%s", taskView.hashCode(), boundsOnScreen); WindowContainerTransaction wct = new WindowContainerTransaction(); // If there is a pending redirect transition, it may have a WCT with other operations. final WindowContainerTransaction wct = mPendingRedirectTransition != null ? mPendingRedirectTransition.takePendingWct() : new WindowContainerTransaction(); wct.setBounds(taskView.getTaskInfo().token, boundsOnScreen); mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */)); startNextTransition(); Loading Loading @@ -677,12 +686,12 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @NonNull SurfaceControl.Transaction finishTransaction) { if (!aborted) return; if (!Flags.fixTaskViewRotationAnimation() && !aborted) return; final PendingTransition pending = findPending(transition); if (pending == null) return; ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.onTransitionConsumed(): taskView=%d " + "consumed type=%s transition=%s", pending.mTaskView.hashCode(), transitTypeToString(pending.mType), transition); + "consumed type=%s transition=%s aborted=%b", pending.mTaskView.hashCode(), transitTypeToString(pending.mType), transition, aborted); mPending.remove(pending); startNextTransition(); } Loading Loading @@ -771,6 +780,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV return false; } boolean stillNeedsMatchingLaunch = pending != null && pending.mLaunchCookie != null; int changingDisplayId = -1; WindowContainerTransaction wct = null; // Collect all the tasks views that this handler can handle Loading @@ -785,6 +795,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } } else { alienChanges.add(chg); if (Flags.fixTaskViewRotationAnimation() && chg.hasFlags(TransitionInfo.FLAG_IS_DISPLAY) && chg.getMode() == TRANSIT_CHANGE && mPendingRedirectTransition == null) { changingDisplayId = chg.getEndDisplayId(); } } } if (inDataCollectionModeOnly) { Loading Loading @@ -840,6 +855,13 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV if (wct == null) wct = new WindowContainerTransaction(); updateBounds(infoTv, boundsOnScreen, startTransaction, finishTransaction, taskInfo, leash, wct); if (changingDisplayId == task.getEndDisplayId()) { ProtoLog.d(WM_SHELL_BUBBLES, "Transitions.startAnimation(): " + "display change, taskView=%d", infoTv.hashCode()); // Remove the change from TransitionInfo to avoid the transition from // being handled by another TaskViewTransitions instance. info.getChanges().remove(task); } } else { startTransaction.reparent(leash, infoTv.getSurfaceControl()); finishTransaction.reparent(leash, infoTv.getSurfaceControl()) Loading Loading @@ -882,6 +904,20 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV // Just some house-keeping, let another handler animate. return false; } if (changingDisplayId > -1) { // Wait for setTaskBoundsInTransition -> mergeAnimation to let DefaultTransitionHandler // run display level animation after the new bounds of TaskView is set. mPendingRedirectTransition = new PendingRedirectTransition(() -> mTransitions.dispatchTransition(transition, info, startTransaction, finishTransaction, finishCallback, this), wct); mTransitions.getMainExecutor().executeDelayed(() -> { if (mPendingRedirectTransition != null) { Slog.w(TAG, "Timed out to wait for transition of setTaskBounds"); executePendingRedirectTransition(); } }, PendingRedirectTransition.TIMEOUT_MS); return true; } // No animation, just show it immediately. startTransaction.apply(); finishCallback.onTransitionFinished(wct); Loading @@ -908,10 +944,17 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV return false; } boolean stillNeedsMatchingLaunch = pending != null && pending.mLaunchCookie != null; ArrayList<TransitionInfo.Change> taskViewChanges = null; int changingDisplayId = -1; int changesHandled = 0; WindowContainerTransaction wct = null; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change chg = info.getChanges().get(i); if (Flags.fixTaskViewRotationAnimation() && chg.hasFlags(TransitionInfo.FLAG_IS_DISPLAY) && chg.getMode() == TRANSIT_CHANGE && mPendingRedirectTransition == null) { changingDisplayId = chg.getEndDisplayId(); continue; } final ActivityManager.RunningTaskInfo taskInfo = chg.getTaskInfo(); if (taskInfo == null) continue; if (TransitionUtil.isClosingType(chg.getMode())) { Loading Loading @@ -990,6 +1033,10 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } continue; } if (taskViewChanges == null) { taskViewChanges = new ArrayList<>(); } taskViewChanges.add(chg); final Rect boundsOnScreen = tv.prepareOpen(chg.getTaskInfo(), chg.getLeash()); if (boundsOnScreen != null) { if (wct == null) wct = new WindowContainerTransaction(); Loading @@ -1012,6 +1059,25 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV // Just some house-keeping, let another handler animate. return false; } if (changingDisplayId > -1 && taskViewChanges != null) { ProtoLog.d(WM_SHELL_BUBBLES, "Transitions.startAnimationLegacy(): " + "handle display change"); // Remove the change from TransitionInfo to avoid being handled by // another TaskViewTransitions instance. info.getChanges().removeAll(taskViewChanges); // Wait for setTaskBoundsInTransition -> mergeAnimation to let DefaultTransitionHandler // run display level animation after the new bounds of TaskView is set. mPendingRedirectTransition = new PendingRedirectTransition(() -> mTransitions.dispatchTransition(transition, info, startTransaction, finishTransaction, finishCallback, this), wct); mTransitions.getMainExecutor().executeDelayed(() -> { if (mPendingRedirectTransition != null) { Slog.w(TAG, "Timed out to wait for transition of setTaskBounds"); executePendingRedirectTransition(); } }, PendingRedirectTransition.TIMEOUT_MS); return true; } // No animation, just show it immediately. startTransaction.apply(); finishCallback.onTransitionFinished(wct); Loading Loading @@ -1076,18 +1142,26 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV mPending.remove(pendingTransition); // reparent the task under the task view surface and set the bounds on it startTransaction.reparent(leash, taskView.getSurfaceControl()) updateSurface(leash, startTransaction, finishTransaction, taskView, bounds.width(), bounds.height()); updateBoundsState(taskView, bounds); return true; } /** Updates the surface properties for a TaskView's task leash. */ private void updateSurface(@NonNull SurfaceControl leash, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull TaskViewTaskController taskView, int width, int height) { // Reparent the task under the task view surface and set the bounds on it. startT.reparent(leash, taskView.getSurfaceControl()) .setPosition(leash, 0, 0) .setWindowCrop(leash, bounds.width(), bounds.height()) .setWindowCrop(leash, width, height) .show(leash); // the finish transaction would reparent the task back to the transition root, so reparent // it again to the task view surface finishTransaction.reparent(leash, taskView.getSurfaceControl()) // The finish transaction would reparent the task back to the window hierarchy parent, so // reparent it to the task view surface. finishT.reparent(leash, taskView.getSurfaceControl()) .setPosition(leash, 0, 0) .setWindowCrop(leash, bounds.width(), bounds.height()); updateBoundsState(taskView, bounds); return true; .setWindowCrop(leash, width, height); } private void updateBounds(TaskViewTaskController taskView, Rect boundsOnScreen, Loading @@ -1114,6 +1188,44 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV taskView.applyCaptionInsetsIfNeeded(); } private void executePendingRedirectTransition() { if (mPendingRedirectTransition != null) { mPendingRedirectTransition.dispatchTransition(); mPendingRedirectTransition = null; } } @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (!Flags.fixTaskViewRotationAnimation()) return; final PendingTransition pending = findPending(transition); if (pending != null) { mPending.remove(pending); } executePendingRedirectTransition(); boolean hasHandledTaskView = false; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo == null) continue; final TaskViewTaskController taskView = findTaskView(taskInfo); if (taskView == null) continue; final SurfaceControl leash = change.getLeash(); final Rect endBounds = change.getEndAbsBounds(); updateSurface(leash, startT, finishT, taskView, endBounds.width(), endBounds.height()); hasHandledTaskView = true; } ProtoLog.d(WM_SHELL_BUBBLES, "mergeAnimation(): matchedPending=%b hasHandledTaskView=%b", pending != null, hasHandledTaskView); if (hasHandledTaskView) { startT.apply(); finishCallback.onTransitionFinished(null /* wct */); } } /** Dumps TaskViewTransitions state. */ public void dump(PrintWriter pw) { pw.println("TaskViewTransitions state:"); Loading @@ -1126,6 +1238,34 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } } /** * This holds a transition that is deferred, for example a display-change that also affects a * TaskView. The transition is dispatched once the TaskView reports its new bounds (i.e. * {@link #setTaskBounds}), which happens via a {@link #mergeAnimation} call, or on timeout. */ private static class PendingRedirectTransition { static final long TIMEOUT_MS = 500; private final Runnable mDispatchTransition; private WindowContainerTransaction mWct; PendingRedirectTransition(@NonNull Runnable dispatch, @Nullable WindowContainerTransaction wct) { mDispatchTransition = dispatch; mWct = wct; } @NonNull WindowContainerTransaction takePendingWct() { final WindowContainerTransaction wct = mWct; mWct = null; return wct != null ? wct : new WindowContainerTransaction(); } void dispatchTransition() { mDispatchTransition.run(); } } /** Interface for running an external transition in this object's pending queue. */ public interface ExternalTransition { /** Starts a transition and returns an identifying key for lookup. */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +6 −1 Original line number Diff line number Diff line Loading @@ -863,7 +863,12 @@ public class DefaultMixedHandler implements MixedTransitionHandler, if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) { return null; } return DefaultMixedTransition.getChangeForBubblingTask(info, mBubbleTransitions); final TransitionInfo.Change change = DefaultMixedTransition.getChangeForBubblingTask(info, mBubbleTransitions); if (!com.android.wm.shell.Flags.fixTaskViewRotationAnimation()) { return change; } return change != null && TransitionUtil.isOpeningMode(change.getMode()) ? change : null; } /** Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionStartAnimationTest.java +42 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import com.android.wm.shell.transition.TransitionDispatchState; Loading Loading @@ -384,6 +385,47 @@ public class TaskViewTransitionStartAnimationTest extends ShellTestCase { prepareOpenAnimationAssertions(pending, wct, true /* newTask */, mUnregisteredTokenBinder); } @Test public void testMergeAnimation_dispatchDisplayTransition() { assumeTrue(com.android.wm.shell.Flags.fixTaskViewRotationAnimation()); final TransitionInfo.Change displayChange = new TransitionInfo.Change( /* container= */ null, new SurfaceControl()); displayChange.setStartAbsBounds(new Rect(0, 0, 500, 1000)); displayChange.setEndAbsBounds(new Rect(0, 0, 1000, 500)); displayChange.setMode(TRANSIT_CHANGE); displayChange.setFlags(TransitionInfo.FLAG_IS_DISPLAY); final TransitionInfo.Change taskViewChange = getTaskView(TRANSIT_CHANGE); final TransitionInfo displayChangeInfo = new TransitionInfoBuilder(TRANSIT_CHANGE) .addChange(taskViewChange).addChange(displayChange).build(); when(mTransitions.getMainExecutor()).thenReturn(mock(ShellExecutor.class)); final IBinder displayTransition = mock(IBinder.class); final boolean handled = mTaskViewTransitions.startAnimation(displayTransition, displayChangeInfo, mStartTransaction, mFinishTransaction, mFinishCallback); assertWithMessage("Handler should have consumed display change transition") .that(handled).isTrue(); assertWithMessage("TaskView change should be consumed") .that(displayChangeInfo.getChanges()).doesNotContain(taskViewChange); verify(mFinishCallback, never()).onTransitionFinished(any()); // Assume that TaskViewTransitions#setTaskBounds starts another transition. final IBinder setTaskBoundsTransition = mock(IBinder.class); final TransitionInfo.Change boundsChange = getTaskView(TRANSIT_CHANGE); final Rect newBounds = new Rect(100, 100, 500, 500); when(boundsChange.getEndAbsBounds()).thenReturn(newBounds); final TransitionInfo mergeTransitionInfo = new TransitionInfoBuilder(TRANSIT_CHANGE) .addChange(boundsChange).build(); mTaskViewTransitions.mergeAnimation(displayTransition, mergeTransitionInfo, mStartTransaction, mFinishTransaction, setTaskBoundsTransition, mFinishCallback); verify(mStartTransaction).setWindowCrop(eq(boundsChange.getLeash()), eq(newBounds.width()), eq(newBounds.height())); verify(mTransitions).dispatchTransition(eq(displayTransition), eq(displayChangeInfo), eq(mStartTransaction), eq(mFinishTransaction), eq(mFinishCallback), eq(mTaskViewTransitions)); } @Test public void changingTaskViewHandled() { TaskViewTransitions.PendingTransition pending = Loading Loading
libs/WindowManager/Shell/aconfig/multitasking.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -273,6 +273,16 @@ flag { } } flag { name: "fix_task_view_rotation_animation" namespace: "multitasking" description: "Fixes jump-cut animation and un-synced bounds (including bubbles) when rotating." bug: "351813733" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "fix_bubbles_expanded_sysui_flag" namespace: "multitasking" Loading
libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +153 −13 Original line number Diff line number Diff line Loading @@ -98,6 +98,13 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV /** A temp transaction used for quick things. */ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); /** * A display change transition that also involves a TaskView. The display animation is deferred * until the TaskView's bounds are updated. This object holds the logic to dispatch the display * transition once the TaskView is ready. */ private PendingRedirectTransition mPendingRedirectTransition; /** * TaskView makes heavy use of startTransition. Only one shell-initiated transition can be * in-flight (collecting) at a time (because otherwise, the operations could get merged into Loading Loading @@ -636,7 +643,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.setTaskBoundsInTransition(): taskView=%d " + "bounds=%s", taskView.hashCode(), boundsOnScreen); WindowContainerTransaction wct = new WindowContainerTransaction(); // If there is a pending redirect transition, it may have a WCT with other operations. final WindowContainerTransaction wct = mPendingRedirectTransition != null ? mPendingRedirectTransition.takePendingWct() : new WindowContainerTransaction(); wct.setBounds(taskView.getTaskInfo().token, boundsOnScreen); mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */)); startNextTransition(); Loading Loading @@ -677,12 +686,12 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @NonNull SurfaceControl.Transaction finishTransaction) { if (!aborted) return; if (!Flags.fixTaskViewRotationAnimation() && !aborted) return; final PendingTransition pending = findPending(transition); if (pending == null) return; ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.onTransitionConsumed(): taskView=%d " + "consumed type=%s transition=%s", pending.mTaskView.hashCode(), transitTypeToString(pending.mType), transition); + "consumed type=%s transition=%s aborted=%b", pending.mTaskView.hashCode(), transitTypeToString(pending.mType), transition, aborted); mPending.remove(pending); startNextTransition(); } Loading Loading @@ -771,6 +780,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV return false; } boolean stillNeedsMatchingLaunch = pending != null && pending.mLaunchCookie != null; int changingDisplayId = -1; WindowContainerTransaction wct = null; // Collect all the tasks views that this handler can handle Loading @@ -785,6 +795,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } } else { alienChanges.add(chg); if (Flags.fixTaskViewRotationAnimation() && chg.hasFlags(TransitionInfo.FLAG_IS_DISPLAY) && chg.getMode() == TRANSIT_CHANGE && mPendingRedirectTransition == null) { changingDisplayId = chg.getEndDisplayId(); } } } if (inDataCollectionModeOnly) { Loading Loading @@ -840,6 +855,13 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV if (wct == null) wct = new WindowContainerTransaction(); updateBounds(infoTv, boundsOnScreen, startTransaction, finishTransaction, taskInfo, leash, wct); if (changingDisplayId == task.getEndDisplayId()) { ProtoLog.d(WM_SHELL_BUBBLES, "Transitions.startAnimation(): " + "display change, taskView=%d", infoTv.hashCode()); // Remove the change from TransitionInfo to avoid the transition from // being handled by another TaskViewTransitions instance. info.getChanges().remove(task); } } else { startTransaction.reparent(leash, infoTv.getSurfaceControl()); finishTransaction.reparent(leash, infoTv.getSurfaceControl()) Loading Loading @@ -882,6 +904,20 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV // Just some house-keeping, let another handler animate. return false; } if (changingDisplayId > -1) { // Wait for setTaskBoundsInTransition -> mergeAnimation to let DefaultTransitionHandler // run display level animation after the new bounds of TaskView is set. mPendingRedirectTransition = new PendingRedirectTransition(() -> mTransitions.dispatchTransition(transition, info, startTransaction, finishTransaction, finishCallback, this), wct); mTransitions.getMainExecutor().executeDelayed(() -> { if (mPendingRedirectTransition != null) { Slog.w(TAG, "Timed out to wait for transition of setTaskBounds"); executePendingRedirectTransition(); } }, PendingRedirectTransition.TIMEOUT_MS); return true; } // No animation, just show it immediately. startTransaction.apply(); finishCallback.onTransitionFinished(wct); Loading @@ -908,10 +944,17 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV return false; } boolean stillNeedsMatchingLaunch = pending != null && pending.mLaunchCookie != null; ArrayList<TransitionInfo.Change> taskViewChanges = null; int changingDisplayId = -1; int changesHandled = 0; WindowContainerTransaction wct = null; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change chg = info.getChanges().get(i); if (Flags.fixTaskViewRotationAnimation() && chg.hasFlags(TransitionInfo.FLAG_IS_DISPLAY) && chg.getMode() == TRANSIT_CHANGE && mPendingRedirectTransition == null) { changingDisplayId = chg.getEndDisplayId(); continue; } final ActivityManager.RunningTaskInfo taskInfo = chg.getTaskInfo(); if (taskInfo == null) continue; if (TransitionUtil.isClosingType(chg.getMode())) { Loading Loading @@ -990,6 +1033,10 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } continue; } if (taskViewChanges == null) { taskViewChanges = new ArrayList<>(); } taskViewChanges.add(chg); final Rect boundsOnScreen = tv.prepareOpen(chg.getTaskInfo(), chg.getLeash()); if (boundsOnScreen != null) { if (wct == null) wct = new WindowContainerTransaction(); Loading @@ -1012,6 +1059,25 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV // Just some house-keeping, let another handler animate. return false; } if (changingDisplayId > -1 && taskViewChanges != null) { ProtoLog.d(WM_SHELL_BUBBLES, "Transitions.startAnimationLegacy(): " + "handle display change"); // Remove the change from TransitionInfo to avoid being handled by // another TaskViewTransitions instance. info.getChanges().removeAll(taskViewChanges); // Wait for setTaskBoundsInTransition -> mergeAnimation to let DefaultTransitionHandler // run display level animation after the new bounds of TaskView is set. mPendingRedirectTransition = new PendingRedirectTransition(() -> mTransitions.dispatchTransition(transition, info, startTransaction, finishTransaction, finishCallback, this), wct); mTransitions.getMainExecutor().executeDelayed(() -> { if (mPendingRedirectTransition != null) { Slog.w(TAG, "Timed out to wait for transition of setTaskBounds"); executePendingRedirectTransition(); } }, PendingRedirectTransition.TIMEOUT_MS); return true; } // No animation, just show it immediately. startTransaction.apply(); finishCallback.onTransitionFinished(wct); Loading Loading @@ -1076,18 +1142,26 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV mPending.remove(pendingTransition); // reparent the task under the task view surface and set the bounds on it startTransaction.reparent(leash, taskView.getSurfaceControl()) updateSurface(leash, startTransaction, finishTransaction, taskView, bounds.width(), bounds.height()); updateBoundsState(taskView, bounds); return true; } /** Updates the surface properties for a TaskView's task leash. */ private void updateSurface(@NonNull SurfaceControl leash, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull TaskViewTaskController taskView, int width, int height) { // Reparent the task under the task view surface and set the bounds on it. startT.reparent(leash, taskView.getSurfaceControl()) .setPosition(leash, 0, 0) .setWindowCrop(leash, bounds.width(), bounds.height()) .setWindowCrop(leash, width, height) .show(leash); // the finish transaction would reparent the task back to the transition root, so reparent // it again to the task view surface finishTransaction.reparent(leash, taskView.getSurfaceControl()) // The finish transaction would reparent the task back to the window hierarchy parent, so // reparent it to the task view surface. finishT.reparent(leash, taskView.getSurfaceControl()) .setPosition(leash, 0, 0) .setWindowCrop(leash, bounds.width(), bounds.height()); updateBoundsState(taskView, bounds); return true; .setWindowCrop(leash, width, height); } private void updateBounds(TaskViewTaskController taskView, Rect boundsOnScreen, Loading @@ -1114,6 +1188,44 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV taskView.applyCaptionInsetsIfNeeded(); } private void executePendingRedirectTransition() { if (mPendingRedirectTransition != null) { mPendingRedirectTransition.dispatchTransition(); mPendingRedirectTransition = null; } } @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (!Flags.fixTaskViewRotationAnimation()) return; final PendingTransition pending = findPending(transition); if (pending != null) { mPending.remove(pending); } executePendingRedirectTransition(); boolean hasHandledTaskView = false; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo == null) continue; final TaskViewTaskController taskView = findTaskView(taskInfo); if (taskView == null) continue; final SurfaceControl leash = change.getLeash(); final Rect endBounds = change.getEndAbsBounds(); updateSurface(leash, startT, finishT, taskView, endBounds.width(), endBounds.height()); hasHandledTaskView = true; } ProtoLog.d(WM_SHELL_BUBBLES, "mergeAnimation(): matchedPending=%b hasHandledTaskView=%b", pending != null, hasHandledTaskView); if (hasHandledTaskView) { startT.apply(); finishCallback.onTransitionFinished(null /* wct */); } } /** Dumps TaskViewTransitions state. */ public void dump(PrintWriter pw) { pw.println("TaskViewTransitions state:"); Loading @@ -1126,6 +1238,34 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV } } /** * This holds a transition that is deferred, for example a display-change that also affects a * TaskView. The transition is dispatched once the TaskView reports its new bounds (i.e. * {@link #setTaskBounds}), which happens via a {@link #mergeAnimation} call, or on timeout. */ private static class PendingRedirectTransition { static final long TIMEOUT_MS = 500; private final Runnable mDispatchTransition; private WindowContainerTransaction mWct; PendingRedirectTransition(@NonNull Runnable dispatch, @Nullable WindowContainerTransaction wct) { mDispatchTransition = dispatch; mWct = wct; } @NonNull WindowContainerTransaction takePendingWct() { final WindowContainerTransaction wct = mWct; mWct = null; return wct != null ? wct : new WindowContainerTransaction(); } void dispatchTransition() { mDispatchTransition.run(); } } /** Interface for running an external transition in this object's pending queue. */ public interface ExternalTransition { /** Starts a transition and returns an identifying key for lookup. */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +6 −1 Original line number Diff line number Diff line Loading @@ -863,7 +863,12 @@ public class DefaultMixedHandler implements MixedTransitionHandler, if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) { return null; } return DefaultMixedTransition.getChangeForBubblingTask(info, mBubbleTransitions); final TransitionInfo.Change change = DefaultMixedTransition.getChangeForBubblingTask(info, mBubbleTransitions); if (!com.android.wm.shell.Flags.fixTaskViewRotationAnimation()) { return change; } return change != null && TransitionUtil.isOpeningMode(change.getMode()) ? change : null; } /** Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionStartAnimationTest.java +42 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import com.android.wm.shell.transition.TransitionDispatchState; Loading Loading @@ -384,6 +385,47 @@ public class TaskViewTransitionStartAnimationTest extends ShellTestCase { prepareOpenAnimationAssertions(pending, wct, true /* newTask */, mUnregisteredTokenBinder); } @Test public void testMergeAnimation_dispatchDisplayTransition() { assumeTrue(com.android.wm.shell.Flags.fixTaskViewRotationAnimation()); final TransitionInfo.Change displayChange = new TransitionInfo.Change( /* container= */ null, new SurfaceControl()); displayChange.setStartAbsBounds(new Rect(0, 0, 500, 1000)); displayChange.setEndAbsBounds(new Rect(0, 0, 1000, 500)); displayChange.setMode(TRANSIT_CHANGE); displayChange.setFlags(TransitionInfo.FLAG_IS_DISPLAY); final TransitionInfo.Change taskViewChange = getTaskView(TRANSIT_CHANGE); final TransitionInfo displayChangeInfo = new TransitionInfoBuilder(TRANSIT_CHANGE) .addChange(taskViewChange).addChange(displayChange).build(); when(mTransitions.getMainExecutor()).thenReturn(mock(ShellExecutor.class)); final IBinder displayTransition = mock(IBinder.class); final boolean handled = mTaskViewTransitions.startAnimation(displayTransition, displayChangeInfo, mStartTransaction, mFinishTransaction, mFinishCallback); assertWithMessage("Handler should have consumed display change transition") .that(handled).isTrue(); assertWithMessage("TaskView change should be consumed") .that(displayChangeInfo.getChanges()).doesNotContain(taskViewChange); verify(mFinishCallback, never()).onTransitionFinished(any()); // Assume that TaskViewTransitions#setTaskBounds starts another transition. final IBinder setTaskBoundsTransition = mock(IBinder.class); final TransitionInfo.Change boundsChange = getTaskView(TRANSIT_CHANGE); final Rect newBounds = new Rect(100, 100, 500, 500); when(boundsChange.getEndAbsBounds()).thenReturn(newBounds); final TransitionInfo mergeTransitionInfo = new TransitionInfoBuilder(TRANSIT_CHANGE) .addChange(boundsChange).build(); mTaskViewTransitions.mergeAnimation(displayTransition, mergeTransitionInfo, mStartTransaction, mFinishTransaction, setTaskBoundsTransition, mFinishCallback); verify(mStartTransaction).setWindowCrop(eq(boundsChange.getLeash()), eq(newBounds.width()), eq(newBounds.height())); verify(mTransitions).dispatchTransition(eq(displayTransition), eq(displayChangeInfo), eq(mStartTransaction), eq(mFinishTransaction), eq(mFinishCallback), eq(mTaskViewTransitions)); } @Test public void changingTaskViewHandled() { TaskViewTransitions.PendingTransition pending = Loading