diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index d506ca8d6470f..37a5919b1c9ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -29,7 +29,6 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.graphics.Rect; -import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -45,6 +44,7 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; 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.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; @@ -62,7 +62,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private static final String TAG = "PipController"; private Context mContext; - private Handler mHandler = new Handler(); + private ShellExecutor mMainExecutor; private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); private final Rect mTmpInsetBounds = new Rect(); @@ -82,6 +82,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac protected PipMenuActivityController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; + protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = + new PipControllerPinnedStackListener(); /** * Handler for display rotation changes. @@ -150,12 +152,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PinnedStackListenerForwarder.PinnedStackListener { @Override public void onListenerRegistered(IPinnedStackController controller) { - mHandler.post(() -> mTouchHandler.setPinnedStackController(controller)); + mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller)); } @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight); mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); }); @@ -163,19 +165,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mHandler.post(() -> updateMovementBounds(null /* toBounds */, + mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */, false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */, null /* windowContainerTransaction */)); } @Override public void onActionsChanged(ParceledListSlice actions) { - mHandler.post(() -> mMenuController.setAppActions(actions)); + mMainExecutor.execute(() -> mMenuController.setAppActions(actions)); } @Override public void onActivityHidden(ComponentName componentName) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { // The activity was removed, we don't want to restore to the reentry state // saved for this component anymore. @@ -186,12 +188,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> mPipBoundsState.setDisplayInfo(displayInfo)); + mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo)); } @Override public void onConfigurationChanged() { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsHandler.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); }); @@ -201,7 +203,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac public void onAspectRatioChanged(float aspectRatio) { // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params // change. - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsState.setAspectRatio(aspectRatio); mTouchHandler.onAspectRatioChanged(); }); @@ -217,7 +219,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper + WindowManagerShellWrapper windowManagerShellWrapper, + ShellExecutor mainExecutor ) { // Ensure that we are the primary user's SystemUI. final int processUser = UserManager.get(context).getUserHandle(); @@ -231,6 +234,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsHandler = pipBoundsHandler; mPipBoundsState = pipBoundsState; mPipTaskOrganizer = pipTaskOrganizer; + mMainExecutor = mainExecutor; mPipTaskOrganizer.registerPipTransitionCallback(this); mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> { final DisplayInfo newDisplayInfo = new DisplayInfo(); @@ -254,8 +258,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsState.setDisplayInfo(displayInfo); try { - mWindowManagerShellWrapper.addPinnedStackListener( - new PipControllerPinnedStackListener()); + mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); } catch (RemoteException e) { Slog.e(TAG, "Failed to register pinned stack listener", e); } @@ -263,14 +266,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onDensityOrFontScaleChanged() { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); }); } @Override public void onActivityPinned(String packageName) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); @@ -280,7 +283,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onActivityUnpinned(ComponentName topActivity) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mMenuController.onActivityUnpinned(); mTouchHandler.onActivityUnpinned(topActivity); mAppOpsListener.onActivityUnpinned(); @@ -299,7 +302,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onOverlayChanged() { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay())); updateMovementBounds(null /* toBounds */, false /* fromRotation */, false /* fromImeAdjustment */, @@ -358,7 +361,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac */ @Override public void setShelfHeight(boolean visible, int height) { - mHandler.post(() -> setShelfHeightLocked(visible, height)); + mMainExecutor.execute(() -> setShelfHeightLocked(visible, height)); } private void setShelfHeightLocked(boolean visible, int height) { @@ -374,12 +377,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void setPinnedStackAnimationType(int animationType) { - mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); + mMainExecutor.execute(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); } @Override public void setPinnedStackAnimationListener(Consumer callback) { - mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback); + mMainExecutor.execute(() -> mPinnedStackAnimationRecentsCallback = callback); } @Override @@ -476,7 +479,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsState pipBoundsState, PipMediaController pipMediaController, PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper) { + WindowManagerShellWrapper windowManagerShellWrapper, + ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { Slog.w(TAG, "Device doesn't support Pip feature"); return null; @@ -484,6 +488,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, pipMenuActivityController, - pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper); + pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, mainExecutor); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 39a96a291c863..1d10a84c53b94 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -69,11 +69,13 @@ public class PipTaskOrganizerTest extends PipTestCase { private PipBoundsState mPipBoundsState; private ComponentName mComponent1; + private ComponentName mComponent2; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mComponent1 = new ComponentName(mContext, "component1"); + mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, @@ -100,30 +102,58 @@ public class PipTaskOrganizerTest extends PipTestCase { assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); } + @Test + public void startSwipePipToHome_updatesLastPipComponentName() { + mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, null); + + assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName()); + } + @Test public void onTaskAppeared_updatesAspectRatio() { final Rational aspectRatio = new Rational(2, 1); - mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo( + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(aspectRatio)), null /* leash */); assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); } + @Test + public void onTaskAppeared_updatesLastPipComponentName() { + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)), + null /* leash */); + + assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName()); + } + @Test public void onTaskInfoChanged_updatesAspectRatioIfChanged() { final Rational startAspectRatio = new Rational(2, 1); final Rational newAspectRatio = new Rational(1, 2); - mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo( + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(startAspectRatio)), null /* leash */); - mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(createPipParams(newAspectRatio))); + mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1, + createPipParams(newAspectRatio))); assertEquals(newAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); } + @Test + public void onTaskInfoChanged_updatesLastPipComponentName() { + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, + createPipParams(null)), null /* leash */); + + mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2, + createPipParams(null))); + + assertEquals(mComponent2, mPipBoundsState.getLastPipComponentName()); + } + private void preparePipTaskOrg() { final DisplayInfo info = new DisplayInfo(); + mPipBoundsState.setDisplayInfo(info); when(mMockPipBoundsHandler.getDestinationBounds(any(), any())).thenReturn(new Rect()); when(mMockPipBoundsHandler.getDestinationBounds(any(), any(), anyBoolean())) .thenReturn(new Rect()); @@ -133,10 +163,12 @@ public class PipTaskOrganizerTest extends PipTestCase { doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any()); } - private static ActivityManager.RunningTaskInfo createTaskInfo(PictureInPictureParams params) { + private static ActivityManager.RunningTaskInfo createTaskInfo( + ComponentName componentName, PictureInPictureParams params) { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.token = mock(WindowContainerToken.class); info.pictureInPictureParams = params; + info.topActivity = componentName; return info; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index de9d8648f43ce..5f0f1964814fb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -20,11 +20,14 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; @@ -34,6 +37,7 @@ import android.testing.TestableLooper; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; @@ -64,6 +68,7 @@ public class PipControllerTest extends PipTestCase { @Mock private PipTouchHandler mMockPipTouchHandler; @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper; @Mock private PipBoundsState mMockPipBoundsState; + @Mock private ShellExecutor mMockExecutor; @Before public void setUp() throws RemoteException { @@ -71,7 +76,11 @@ public class PipControllerTest extends PipTestCase { mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper); + mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mMockExecutor).execute(any()); } @Test @@ -99,6 +108,27 @@ public class PipControllerTest extends PipTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper)); + mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor)); + } + + @Test + public void onActivityHidden_isLastPipComponentName_clearLastPipComponent() { + final ComponentName component1 = new ComponentName(mContext, "component1"); + when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); + + mPipController.mPinnedStackListener.onActivityHidden(component1); + + verify(mMockPipBoundsState).setLastPipComponentName(null); + } + + @Test + public void onActivityHidden_isNotLastPipComponentName_lastPipComponentNotCleared() { + final ComponentName component1 = new ComponentName(mContext, "component1"); + final ComponentName component2 = new ComponentName(mContext, "component2"); + when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); + + mPipController.mPinnedStackListener.onActivityHidden(component2); + + verify(mMockPipBoundsState, never()).setLastPipComponentName(null); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 81f448afa13f1..91ae08e07677d 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -34,6 +34,7 @@ import com.android.wm.shell.common.AnimationThread; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.HandlerExecutor; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; @@ -157,9 +158,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue, - @Main Handler handler, TransactionPool transactionPool) { + ShellExecutor mainExecutor, TransactionPool transactionPool) { return new ShellTaskOrganizer(syncQueue, transactionPool, - new HandlerExecutor(handler), AnimationThread.instance().getExecutor()); + mainExecutor, AnimationThread.instance().getExecutor()); } @BindsOptionalOf @@ -174,4 +175,11 @@ public abstract class WMShellBaseModule { DisplayController displayController) { return Optional.ofNullable(OneHandedController.create(context, displayController)); } + + @WMSingleton + @Provides + static ShellExecutor provideMainShellExecutor(@Main Handler handler) { + return new HandlerExecutor(handler); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index f376df06badd8..b6fbd589ae2c0 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -27,6 +27,7 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; @@ -82,11 +83,12 @@ public class WMShellModule { PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler, PipBoundsState pipBoundsState, PipMediaController pipMediaController, PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper) { + PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, - windowManagerShellWrapper)); + windowManagerShellWrapper, mainExecutor)); } @WMSingleton