mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 03:08:19 +00:00
Merge "Launch app pair when one of them is in freeform." into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
feb96ff7d3
@@ -26,6 +26,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.systemui.shared.recents.utilities.Utilities.isFreeformTask;
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE;
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.getIndex;
|
||||
@@ -69,6 +70,7 @@ import com.android.quickstep.views.TaskContainer;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
|
||||
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -337,10 +339,12 @@ public class AppPairsController {
|
||||
* c) App B is on-screen, but App A isn't.
|
||||
* d) Neither is on-screen.
|
||||
*
|
||||
* If the user tapped an app pair while inside a single app, there are 3 cases:
|
||||
* a) The on-screen app is App A of the app pair.
|
||||
* b) The on-screen app is App B of the app pair.
|
||||
* c) It is neither.
|
||||
* If the user tapped an app pair while a fullscreen or freeform app is visible on screen,
|
||||
* there are 4 cases:
|
||||
* a) At least one of the apps in the app pair is in freeform windowing mode.
|
||||
* b) The on-screen app is App A of the app pair.
|
||||
* c) The on-screen app is App B of the app pair.
|
||||
* d) It is neither.
|
||||
*
|
||||
* For each case, we call the appropriate animation and split launch type.
|
||||
*/
|
||||
@@ -422,6 +426,14 @@ public class AppPairsController {
|
||||
foundTasks -> {
|
||||
Task foundTask1 = foundTasks[0];
|
||||
Task foundTask2 = foundTasks[1];
|
||||
|
||||
if (DesktopModeStatus.canEnterDesktopMode(context) && (isFreeformTask(
|
||||
foundTask1) || isFreeformTask(foundTask2))) {
|
||||
launchAppPair(launchingIconView,
|
||||
CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean task1IsOnScreen;
|
||||
boolean task2IsOnScreen;
|
||||
if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package com.android.quickstep.util
|
||||
|
||||
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.launcher3.apppairs.AppPairIcon
|
||||
import com.android.launcher3.logging.StatsLogManager
|
||||
@@ -28,6 +30,7 @@ import com.android.quickstep.TopTaskTracker
|
||||
import com.android.quickstep.TopTaskTracker.CachedTaskInfo
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey
|
||||
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
|
||||
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66
|
||||
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
|
||||
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33
|
||||
@@ -54,6 +57,7 @@ import org.mockito.kotlin.whenever
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppPairsControllerTest {
|
||||
@Mock lateinit var context: Context
|
||||
@Mock lateinit var resources: Resources
|
||||
@Mock lateinit var splitSelectStateController: SplitSelectStateController
|
||||
@Mock lateinit var statsLogManager: StatsLogManager
|
||||
|
||||
@@ -109,6 +113,8 @@ class AppPairsControllerTest {
|
||||
doNothing()
|
||||
.whenever(spyAppPairsController)
|
||||
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
|
||||
whenever(mockAppPairIcon.context.resources).thenReturn(resources)
|
||||
whenever(DesktopModeStatus.canEnterDesktopMode(mockAppPairIcon.context)).thenReturn(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -391,6 +397,68 @@ class AppPairsControllerTest {
|
||||
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleAppPairLaunchInApp_freeformTask1IsOnScreen_shouldLaunchAppPair() {
|
||||
whenever(DesktopModeStatus.canEnterDesktopMode(mockAppPairIcon.context)).thenReturn(true)
|
||||
/// Test launching apps 1 and 2 from app pair
|
||||
whenever(mockTaskKey1.getId()).thenReturn(1)
|
||||
whenever(mockTaskKey2.getId()).thenReturn(2)
|
||||
// Task 1 is in freeform windowing mode
|
||||
mockTaskKey1.windowingMode = WINDOWING_MODE_FREEFORM
|
||||
// ... and app 1 is already on screen
|
||||
if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
|
||||
whenever(mockCachedTaskInfo.topGroupedTaskContainsTask(eq(1))).thenReturn(true)
|
||||
} else {
|
||||
whenever(mockCachedTaskInfo.taskId).thenReturn(1)
|
||||
}
|
||||
|
||||
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
|
||||
spyAppPairsController.handleAppPairLaunchInApp(
|
||||
mockAppPairIcon,
|
||||
listOf(mockItemInfo1, mockItemInfo2),
|
||||
)
|
||||
verify(splitSelectStateController)
|
||||
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
|
||||
val callback: Consumer<Array<Task>> = callbackCaptor.value
|
||||
callback.accept(arrayOf(mockTask1, mockTask2))
|
||||
|
||||
// Verify that launchAppPair was called
|
||||
verify(spyAppPairsController, times(1)).launchAppPair(any(), any())
|
||||
verify(spyAppPairsController, never())
|
||||
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleAppPairLaunchInApp_freeformTask2IsOnScreen_shouldLaunchAppPair() {
|
||||
whenever(DesktopModeStatus.canEnterDesktopMode(mockAppPairIcon.context)).thenReturn(true)
|
||||
/// Test launching apps 1 and 2 from app pair
|
||||
whenever(mockTaskKey1.getId()).thenReturn(1)
|
||||
whenever(mockTaskKey2.getId()).thenReturn(2)
|
||||
// Task 2 is in freeform windowing mode
|
||||
mockTaskKey1.windowingMode = WINDOWING_MODE_FREEFORM
|
||||
// ... and app 2 is already on screen
|
||||
if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
|
||||
whenever(mockCachedTaskInfo.topGroupedTaskContainsTask(eq(2))).thenReturn(true)
|
||||
} else {
|
||||
whenever(mockCachedTaskInfo.taskId).thenReturn(2)
|
||||
}
|
||||
|
||||
// Trigger app pair launch, capture and run callback from findLastActiveTasksAndRunCallback
|
||||
spyAppPairsController.handleAppPairLaunchInApp(
|
||||
mockAppPairIcon,
|
||||
listOf(mockItemInfo1, mockItemInfo2),
|
||||
)
|
||||
verify(splitSelectStateController)
|
||||
.findLastActiveTasksAndRunCallback(any(), any(), callbackCaptor.capture())
|
||||
val callback: Consumer<Array<Task>> = callbackCaptor.value
|
||||
callback.accept(arrayOf(mockTask1, mockTask2))
|
||||
|
||||
// Verify that launchAppPair was called
|
||||
verify(spyAppPairsController, times(1)).launchAppPair(any(), any())
|
||||
verify(spyAppPairsController, never())
|
||||
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleAppPairLaunchInApp_shouldLaunchAppPairNormallyWhenUnrelatedSingleAppIsFullscreen() {
|
||||
// Test launching apps 1 and 2 from app pair
|
||||
|
||||
Reference in New Issue
Block a user