Merge "Add tests to verify last pip component name updates"
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 d506ca8..37a5919 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.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.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 @@
     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 @@
 
     protected PipMenuActivityController mMenuController;
     protected PipTaskOrganizer mPipTaskOrganizer;
+    protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
+            new PipControllerPinnedStackListener();
 
     /**
      * Handler for display rotation changes.
@@ -150,12 +152,12 @@
             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 @@
 
         @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<RemoteAction> 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 @@
 
         @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 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 @@
             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 @@
         mPipBoundsHandler = pipBoundsHandler;
         mPipBoundsState = pipBoundsState;
         mPipTaskOrganizer = pipTaskOrganizer;
+        mMainExecutor = mainExecutor;
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
             final DisplayInfo newDisplayInfo = new DisplayInfo();
@@ -254,8 +258,7 @@
         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 @@
 
     @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 @@
 
     @Override
     public void onActivityUnpinned(ComponentName topActivity) {
-        mHandler.post(() -> {
+        mMainExecutor.execute(() -> {
             mMenuController.onActivityUnpinned();
             mTouchHandler.onActivityUnpinned(topActivity);
             mAppOpsListener.onActivityUnpinned();
@@ -299,7 +302,7 @@
 
     @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 @@
      */
     @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 @@
 
     @Override
     public void setPinnedStackAnimationType(int animationType) {
-        mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
+        mMainExecutor.execute(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
     }
 
     @Override
     public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
-        mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback);
+        mMainExecutor.execute(() -> mPinnedStackAnimationRecentsCallback = callback);
     }
 
     @Override
@@ -476,7 +479,8 @@
             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 @@
 
         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 39a96a2..1d10a84 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 @@
     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,
@@ -101,29 +103,57 @@
     }
 
     @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 @@
         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 de9d864..5f0f196 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 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 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 @@
     @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 @@
         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 @@
         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 81f448a..91ae08e 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.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 @@
     @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 @@
             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 f376df0..b6fbd58 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.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 @@
             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