Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -205,6 +205,7 @@ public abstract class WMShellModule { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RecentsTransitionHandler recentsTransitionHandler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { if (DesktopModeStatus.isEnabled()) { return new DesktopModeWindowDecorViewModel( Loading @@ -218,6 +219,7 @@ public abstract class WMShellModule { syncQueue, transitions, desktopTasksController, recentsTransitionHandler, rootTaskDisplayAreaOrganizer); } return new CaptionWindowDecorViewModel( Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +3 −0 Original line number Diff line number Diff line Loading @@ -134,6 +134,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, mixedHandler == null ? this : mixedHandler); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onTransitionStarted(transition); } if (mixer != null) { mixer.setRecentsTransition(transition); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java +5 −0 Original line number Diff line number Diff line Loading @@ -16,10 +16,15 @@ package com.android.wm.shell.recents; import android.os.IBinder; /** The listener for the events from {@link RecentsTransitionHandler}. */ public interface RecentsTransitionStateListener { /** Notifies whether the recents animation is running. */ default void onAnimationStateChanged(boolean running) { } /** Notifies that a recents shell transition has started. */ default void onTransitionStarted(IBinder transition) {} } libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +24 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE; Loading Loading @@ -73,6 +74,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.recents.RecentsTransitionStateListener; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.KeyguardChangeListener; Loading Loading @@ -102,6 +105,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final Optional<DesktopTasksController> mDesktopTasksController; private final RecentsTransitionHandler mRecentsTransitionHandler; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); Loading Loading @@ -135,6 +139,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RecentsTransitionHandler recentsTransitionHandler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer ) { this( Loading @@ -148,6 +153,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { syncQueue, transitions, desktopTasksController, recentsTransitionHandler, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, Loading @@ -167,6 +173,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RecentsTransitionHandler recentsTransitionHandler, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, Loading @@ -182,6 +189,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue = syncQueue; mTransitions = transitions; mDesktopTasksController = desktopTasksController; mRecentsTransitionHandler = recentsTransitionHandler; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; Loading @@ -194,6 +202,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private void onInit() { mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener); mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() { @Override public void onTransitionStarted(IBinder transition) { onRecentsTransitionStarted(transition); } }); } @Override Loading Loading @@ -319,6 +333,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } private void onRecentsTransitionStarted(IBinder transition) { // Block relayout on window decorations originating from #onTaskInfoChanges until the // animation completes to avoid interfering with the transition animation. for (int i = 0; i < mWindowDecorByTaskId.size(); i++) { final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i); decor.incrementRelayoutBlock(); decor.addTransitionPausingRelayout(transition); } } private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, DragDetector.MotionEventHandler{ Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +44 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; Loading @@ -38,6 +39,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.view.Choreographer; import android.view.Display; Loading @@ -54,14 +56,18 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.recents.RecentsTransitionStateListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.util.ArrayList; Loading Loading @@ -96,18 +102,21 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { @Mock private SurfaceControl.Transaction mTransaction; @Mock private Display mDisplay; @Mock private ShellController mShellController; @Mock private ShellInit mShellInit; @Mock private ShellExecutor mShellExecutor; @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener; @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; @Mock private RecentsTransitionHandler mRecentsTransitionHandler; private final List<InputManager> mMockInputManagers = new ArrayList<>(); private ShellInit mShellInit; private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel; @Before public void setUp() { mMockInputManagers.add(mInputManager); mShellInit = new ShellInit(mShellExecutor); mDesktopModeWindowDecorViewModel = new DesktopModeWindowDecorViewModel( mContext, Loading @@ -120,6 +129,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mSyncQueue, mTransitions, Optional.of(mDesktopTasksController), mRecentsTransitionHandler, mDesktopModeWindowDecorFactory, mMockInputMonitorFactory, mTransactionFactory, Loading @@ -143,6 +153,8 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mDesktopModeWindowDecoration.mDisplay = mDisplay; doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId(); mShellInit.init(); } @Test Loading Loading @@ -296,6 +308,36 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { .create(any(), any(), any(), any(), any(), any(), any(), any(), any()); } @Test public void testRelayoutBlockedDuringRecentsTransition() throws Exception { final ArgumentCaptor<RecentsTransitionStateListener> recentsCaptor = ArgumentCaptor.forClass(RecentsTransitionStateListener.class); verify(mRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture()); final IBinder transition = mock(IBinder.class); final DesktopModeWindowDecoration decoration = mock(DesktopModeWindowDecoration.class); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final int taskId = 1; final SurfaceControl taskSurface = new SurfaceControl(); final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); doReturn(decoration).when(mDesktopModeWindowDecorFactory) .create(any(), any(), any(), eq(taskInfo), eq(taskSurface), any(), any(), any(), any()); runOnMainThread(() -> { // Make sure a window decorations exists first by launching a freeform task. mDesktopModeWindowDecorViewModel.onTaskOpening( taskInfo, taskSurface, startT, finishT); // Now call back when as a Recents transition starts. recentsCaptor.getValue().onTransitionStarted(transition); }); verify(decoration).incrementRelayoutBlock(); verify(decoration).addTransitionPausingRelayout(transition); } private void runOnMainThread(Runnable r) throws Exception { final Handler mainHandler = new Handler(Looper.getMainLooper()); final CountDownLatch latch = new CountDownLatch(1); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -205,6 +205,7 @@ public abstract class WMShellModule { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RecentsTransitionHandler recentsTransitionHandler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { if (DesktopModeStatus.isEnabled()) { return new DesktopModeWindowDecorViewModel( Loading @@ -218,6 +219,7 @@ public abstract class WMShellModule { syncQueue, transitions, desktopTasksController, recentsTransitionHandler, rootTaskDisplayAreaOrganizer); } return new CaptionWindowDecorViewModel( Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +3 −0 Original line number Diff line number Diff line Loading @@ -134,6 +134,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, mixedHandler == null ? this : mixedHandler); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onTransitionStarted(transition); } if (mixer != null) { mixer.setRecentsTransition(transition); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java +5 −0 Original line number Diff line number Diff line Loading @@ -16,10 +16,15 @@ package com.android.wm.shell.recents; import android.os.IBinder; /** The listener for the events from {@link RecentsTransitionHandler}. */ public interface RecentsTransitionStateListener { /** Notifies whether the recents animation is running. */ default void onAnimationStateChanged(boolean running) { } /** Notifies that a recents shell transition has started. */ default void onTransitionStarted(IBinder transition) {} }
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +24 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE; Loading Loading @@ -73,6 +74,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.recents.RecentsTransitionStateListener; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.KeyguardChangeListener; Loading Loading @@ -102,6 +105,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final Optional<DesktopTasksController> mDesktopTasksController; private final RecentsTransitionHandler mRecentsTransitionHandler; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); Loading Loading @@ -135,6 +139,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RecentsTransitionHandler recentsTransitionHandler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer ) { this( Loading @@ -148,6 +153,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { syncQueue, transitions, desktopTasksController, recentsTransitionHandler, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, Loading @@ -167,6 +173,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RecentsTransitionHandler recentsTransitionHandler, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, Loading @@ -182,6 +189,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue = syncQueue; mTransitions = transitions; mDesktopTasksController = desktopTasksController; mRecentsTransitionHandler = recentsTransitionHandler; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; Loading @@ -194,6 +202,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private void onInit() { mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener); mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() { @Override public void onTransitionStarted(IBinder transition) { onRecentsTransitionStarted(transition); } }); } @Override Loading Loading @@ -319,6 +333,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } private void onRecentsTransitionStarted(IBinder transition) { // Block relayout on window decorations originating from #onTaskInfoChanges until the // animation completes to avoid interfering with the transition animation. for (int i = 0; i < mWindowDecorByTaskId.size(); i++) { final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i); decor.incrementRelayoutBlock(); decor.addTransitionPausingRelayout(transition); } } private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, DragDetector.MotionEventHandler{ Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +44 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; Loading @@ -38,6 +39,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.view.Choreographer; import android.view.Display; Loading @@ -54,14 +56,18 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.recents.RecentsTransitionStateListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.util.ArrayList; Loading Loading @@ -96,18 +102,21 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { @Mock private SurfaceControl.Transaction mTransaction; @Mock private Display mDisplay; @Mock private ShellController mShellController; @Mock private ShellInit mShellInit; @Mock private ShellExecutor mShellExecutor; @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener; @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; @Mock private RecentsTransitionHandler mRecentsTransitionHandler; private final List<InputManager> mMockInputManagers = new ArrayList<>(); private ShellInit mShellInit; private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel; @Before public void setUp() { mMockInputManagers.add(mInputManager); mShellInit = new ShellInit(mShellExecutor); mDesktopModeWindowDecorViewModel = new DesktopModeWindowDecorViewModel( mContext, Loading @@ -120,6 +129,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mSyncQueue, mTransitions, Optional.of(mDesktopTasksController), mRecentsTransitionHandler, mDesktopModeWindowDecorFactory, mMockInputMonitorFactory, mTransactionFactory, Loading @@ -143,6 +153,8 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mDesktopModeWindowDecoration.mDisplay = mDisplay; doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId(); mShellInit.init(); } @Test Loading Loading @@ -296,6 +308,36 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { .create(any(), any(), any(), any(), any(), any(), any(), any(), any()); } @Test public void testRelayoutBlockedDuringRecentsTransition() throws Exception { final ArgumentCaptor<RecentsTransitionStateListener> recentsCaptor = ArgumentCaptor.forClass(RecentsTransitionStateListener.class); verify(mRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture()); final IBinder transition = mock(IBinder.class); final DesktopModeWindowDecoration decoration = mock(DesktopModeWindowDecoration.class); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final int taskId = 1; final SurfaceControl taskSurface = new SurfaceControl(); final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); doReturn(decoration).when(mDesktopModeWindowDecorFactory) .create(any(), any(), any(), eq(taskInfo), eq(taskSurface), any(), any(), any(), any()); runOnMainThread(() -> { // Make sure a window decorations exists first by launching a freeform task. mDesktopModeWindowDecorViewModel.onTaskOpening( taskInfo, taskSurface, startT, finishT); // Now call back when as a Recents transition starts. recentsCaptor.getValue().onTransitionStarted(transition); }); verify(decoration).incrementRelayoutBlock(); verify(decoration).addTransitionPausingRelayout(transition); } private void runOnMainThread(Runnable r) throws Exception { final Handler mainHandler = new Handler(Looper.getMainLooper()); final CountDownLatch latch = new CountDownLatch(1); Loading