Loading libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +20 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import java.io.PrintWriter; import java.util.concurrent.Executor; Loading Loading @@ -292,7 +293,11 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; final SurfaceControl taskLeash = mTaskLeash; if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) { handleAndNotifyTaskRemoval(taskInfo); } else { handleAndNotifyTaskRemoval(mTaskInfo); } mTransaction.reparent(taskLeash, null).apply(); resetTaskInfo(); Loading Loading @@ -454,6 +459,20 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { if (mListener == null) return; ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TaskController.notifyTaskRemovalStarted(): taskView=%d " + "task=%s", hashCode(), taskInfo); if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) { // Update mTaskInfo to reflect the latest task state before notifying the listener, as // it may have been changed by ShellTaskOrganizer#onTaskInfoChanged(), which triggers // task listener updates via ShellTaskOrganizer#updateTaskListenerIfNeeded() when a // task's info changes, resulting in onTaskVanished() being called on the old listener; // without updating mTaskInfo here would leave it with outdated information (e.g., // windowing mode), potentially causing incorrect state checks and unintended cleanup // actions in consumers of TaskViewTaskController, such as task removal in // BubbleTaskView#cleanup. mTaskInfo = taskInfo; mTaskToken = mTaskInfo.token; } final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId)); } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +54 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.taskview; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static com.google.common.truth.Truth.assertThat; Loading @@ -32,6 +33,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; Loading Loading @@ -67,6 +69,7 @@ import com.android.wm.shell.TestHandler; import com.android.wm.shell.common.HandlerExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import com.android.wm.shell.transition.Transitions; import org.junit.After; Loading @@ -79,11 +82,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import java.util.List; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; import java.util.List; @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) Loading @@ -91,7 +94,10 @@ public class TaskViewTest extends ShellTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf(Flags.FLAG_TASK_VIEW_REPOSITORY); return FlagsParameterization.allCombinationsOf( Flags.FLAG_TASK_VIEW_REPOSITORY, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE ); } @Mock Loading Loading @@ -433,6 +439,51 @@ public class TaskViewTest extends ShellTestCase { verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId)); } @Test public void testOnTaskVanished_withTaskInfoUpdate_notifiesTaskRemoval() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); // Capture task info when onTaskRemovalStarted is triggered on the task view listener. final ActivityManager.RunningTaskInfo[] capturedTaskInfo = new ActivityManager.RunningTaskInfo[1]; final int taskId = mTaskInfo.taskId; doAnswer(invocation -> { capturedTaskInfo[0] = mTaskView.getTaskInfo(); return null; }).when(mViewListener).onTaskRemovalStarted(taskId); // Set up a mock TaskViewBase to verify notified task info. final TaskViewBase mockTaskViewBase = mock(TaskViewBase.class); mTaskViewTaskController.setTaskViewBase(mockTaskViewBase); // Prepare and trigger task opening animation with mTaskInfo. mTaskViewTransitions.prepareOpenAnimation(mTaskViewTaskController, true /* newTask */, new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo, mLeash, new WindowContainerTransaction()); mTaskView.surfaceCreated(mock(SurfaceHolder.class)); // Simulate task info change with windowing mode update. final ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); newTaskInfo.token = mTaskInfo.token; newTaskInfo.taskId = taskId; newTaskInfo.taskDescription = mTaskInfo.taskDescription; newTaskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); // Invoke onTaskVanished with updated task info. mTaskViewTaskController.onTaskVanished(newTaskInfo); verify(mViewListener).onTaskRemovalStarted(taskId); if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) { // Verify TaskViewBase and listener updates with new task info. verify(mockTaskViewBase).onTaskVanished(same(newTaskInfo)); assertThat(capturedTaskInfo[0]).isSameInstanceAs(newTaskInfo); } else { // Verify TaskViewBase and listener updates with old task info. verify(mockTaskViewBase).onTaskVanished(same(mTaskInfo)); assertThat(capturedTaskInfo[0]).isSameInstanceAs(mTaskInfo); } } @Test public void testOnBackPressedOnTaskRoot() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +20 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import java.io.PrintWriter; import java.util.concurrent.Executor; Loading Loading @@ -292,7 +293,11 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; final SurfaceControl taskLeash = mTaskLeash; if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) { handleAndNotifyTaskRemoval(taskInfo); } else { handleAndNotifyTaskRemoval(mTaskInfo); } mTransaction.reparent(taskLeash, null).apply(); resetTaskInfo(); Loading Loading @@ -454,6 +459,20 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { if (mListener == null) return; ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TaskController.notifyTaskRemovalStarted(): taskView=%d " + "task=%s", hashCode(), taskInfo); if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) { // Update mTaskInfo to reflect the latest task state before notifying the listener, as // it may have been changed by ShellTaskOrganizer#onTaskInfoChanged(), which triggers // task listener updates via ShellTaskOrganizer#updateTaskListenerIfNeeded() when a // task's info changes, resulting in onTaskVanished() being called on the old listener; // without updating mTaskInfo here would leave it with outdated information (e.g., // windowing mode), potentially causing incorrect state checks and unintended cleanup // actions in consumers of TaskViewTaskController, such as task removal in // BubbleTaskView#cleanup. mTaskInfo = taskInfo; mTaskToken = mTaskInfo.token; } final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId)); } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +54 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.taskview; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static com.google.common.truth.Truth.assertThat; Loading @@ -32,6 +33,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; Loading Loading @@ -67,6 +69,7 @@ import com.android.wm.shell.TestHandler; import com.android.wm.shell.common.HandlerExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import com.android.wm.shell.transition.Transitions; import org.junit.After; Loading @@ -79,11 +82,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import java.util.List; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; import java.util.List; @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) Loading @@ -91,7 +94,10 @@ public class TaskViewTest extends ShellTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf(Flags.FLAG_TASK_VIEW_REPOSITORY); return FlagsParameterization.allCombinationsOf( Flags.FLAG_TASK_VIEW_REPOSITORY, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE ); } @Mock Loading Loading @@ -433,6 +439,51 @@ public class TaskViewTest extends ShellTestCase { verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId)); } @Test public void testOnTaskVanished_withTaskInfoUpdate_notifiesTaskRemoval() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); // Capture task info when onTaskRemovalStarted is triggered on the task view listener. final ActivityManager.RunningTaskInfo[] capturedTaskInfo = new ActivityManager.RunningTaskInfo[1]; final int taskId = mTaskInfo.taskId; doAnswer(invocation -> { capturedTaskInfo[0] = mTaskView.getTaskInfo(); return null; }).when(mViewListener).onTaskRemovalStarted(taskId); // Set up a mock TaskViewBase to verify notified task info. final TaskViewBase mockTaskViewBase = mock(TaskViewBase.class); mTaskViewTaskController.setTaskViewBase(mockTaskViewBase); // Prepare and trigger task opening animation with mTaskInfo. mTaskViewTransitions.prepareOpenAnimation(mTaskViewTaskController, true /* newTask */, new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo, mLeash, new WindowContainerTransaction()); mTaskView.surfaceCreated(mock(SurfaceHolder.class)); // Simulate task info change with windowing mode update. final ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); newTaskInfo.token = mTaskInfo.token; newTaskInfo.taskId = taskId; newTaskInfo.taskDescription = mTaskInfo.taskDescription; newTaskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); // Invoke onTaskVanished with updated task info. mTaskViewTaskController.onTaskVanished(newTaskInfo); verify(mViewListener).onTaskRemovalStarted(taskId); if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) { // Verify TaskViewBase and listener updates with new task info. verify(mockTaskViewBase).onTaskVanished(same(newTaskInfo)); assertThat(capturedTaskInfo[0]).isSameInstanceAs(newTaskInfo); } else { // Verify TaskViewBase and listener updates with old task info. verify(mockTaskViewBase).onTaskVanished(same(mTaskInfo)); assertThat(capturedTaskInfo[0]).isSameInstanceAs(mTaskInfo); } } @Test public void testOnBackPressedOnTaskRoot() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); Loading