Use fade-in launcher for app pair launches from in-app taskbar

This CL temporarily patches a crash in this situation:

Inside of an app > Launch an unrelated app pair from Taskbar > Crash

The crash occurs because TaskbarActivityContext can't create a launcher animation (because Launcher isn't running). A different launch animation is needed for this case (similar to single apps), but for now, falling back to the fade-in launch at least allows the user to proceed without a crash.

Bug: 316485863
Test: Enter an app, launch an unrelated app pair, no crash
Flag: ACONFIG com.android.wm.shell.enable_app_pairs DEVELOPMENT
Change-Id: I72b2956b5b44616367e24263f2287824ee1e8212
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index b2d1b43..ad9f5ea 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -49,6 +49,7 @@
 import com.android.launcher3.statehandlers.DepthController
 import com.android.launcher3.statemanager.StateManager
 import com.android.launcher3.statemanager.StatefulActivity
+import com.android.launcher3.taskbar.TaskbarActivityContext
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
 import com.android.launcher3.views.BaseDragLayer
 import com.android.quickstep.TaskViewUtils
@@ -390,6 +391,14 @@
                 "trying to launch an app pair icon, but encountered an unexpected null"
             }
 
+            // If launching an app pair from Taskbar inside of an app context, use fade-in animation
+            // TODO (b/316485863): Replace with desired app pair launch animation
+            if (launchingIconView.context is TaskbarActivityContext) {
+                composeFadeInSplitLaunchAnimator(
+                    initialTaskId, secondTaskId, info, t, finishCallback)
+                return
+            }
+
             composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
         } else {
             // Fallback case: simple fade-in animation
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index 929bd8e..de152fa 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -19,6 +19,7 @@
 
 import android.graphics.Bitmap
 import android.graphics.drawable.Drawable
+import android.view.ContextThemeWrapper
 import android.view.SurfaceControl.Transaction
 import android.view.View
 import android.window.TransitionInfo
@@ -26,6 +27,7 @@
 import com.android.launcher3.apppairs.AppPairIcon
 import com.android.launcher3.statehandlers.DepthController
 import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.taskbar.TaskbarActivityContext
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.quickstep.views.GroupedTaskView
 import com.android.quickstep.views.IconView
@@ -64,6 +66,8 @@
     private val mockTaskIdAttributeContainer: TaskIdAttributeContainer = mock()
     // AppPairIcon
     private val mockAppPairIcon: AppPairIcon = mock()
+    private val mockContextThemeWrapper: ContextThemeWrapper = mock()
+    private val mockTaskbarActivityContext: TaskbarActivityContext = mock()
 
     // SplitSelectSource
     private val splitSelectSource: SplitConfigurationOptions.SplitSelectSource = mock()
@@ -247,6 +251,7 @@
     @Test
     fun playsAppropriateSplitLaunchAnimation_playsIconLaunchCorrectly() {
         val spySplitAnimationController = spy(splitAnimationController)
+        whenever(mockAppPairIcon.context).thenReturn(mockContextThemeWrapper)
         doNothing()
             .whenever(spySplitAnimationController)
             .composeIconSplitLaunchAnimator(any(), any(), any(), any())
@@ -271,6 +276,33 @@
     }
 
     @Test
+    fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarContextCorrectly() {
+        val spySplitAnimationController = spy(splitAnimationController)
+        whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
+        doNothing()
+            .whenever(spySplitAnimationController)
+            .composeFadeInSplitLaunchAnimator(any(), any(), any(), any(), any())
+
+        spySplitAnimationController.playSplitLaunchAnimation(
+            null /* launchingTaskView */,
+            mockAppPairIcon,
+            taskId,
+            taskId2,
+            null /* apps */,
+            null /* wallpapers */,
+            null /* nonApps */,
+            stateManager,
+            depthController,
+            transitionInfo,
+            transaction,
+            {} /* finishCallback */
+        )
+
+        verify(spySplitAnimationController)
+            .composeFadeInSplitLaunchAnimator(any(), any(), any(), any(), any())
+    }
+
+    @Test
     fun playsAppropriateSplitLaunchAnimation_playsFadeInLaunchCorrectly() {
         val spySplitAnimationController = spy(splitAnimationController)
         doNothing()