diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt index e073264998..e46012aabc 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt +++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt @@ -45,7 +45,11 @@ import java.io.PrintWriter * * After setting the correct fields for initial/second.* variables, this converts them into the * correct [PendingIntent] and [ShortcutInfo] objects where applicable and sends the necessary - * data back via [getSplitLaunchData]. + * data back via [getSplitLaunchData]. Note: there should be only one "initial" field and one + * "second" field set, with the rest remaining null. (Exception: [Intent] and [UserHandle] are + * always passed in together as a set, and are converted to a single [PendingIntent] or + * [ShortcutInfo]+[PendingIntent] before launch.) + * * [SplitLaunchType] indicates the type of tasks/apps/intents being launched given the provided * state */ @@ -80,21 +84,21 @@ class SplitSelectDataHolder( const val SPLIT_SINGLE_SHORTCUT_FULLSCREEN = 8 } - @StagePosition private var initialStagePosition: Int = STAGE_POSITION_UNDEFINED - private var initialTaskId: Int = INVALID_TASK_ID - private var secondTaskId: Int = INVALID_TASK_ID - private var initialUser: UserHandle? = null - private var secondUser: UserHandle? = null - private var initialIntent: Intent? = null - private var secondIntent: Intent? = null - private var secondPendingIntent: PendingIntent? = null private var itemInfo: ItemInfo? = null private var splitEvent: EventEnum? = null + + private var initialTaskId: Int = INVALID_TASK_ID + private var secondTaskId: Int = INVALID_TASK_ID + private var initialIntent: Intent? = null + private var secondIntent: Intent? = null + private var initialUser: UserHandle? = null + private var secondUser: UserHandle? = null + private var initialPendingIntent: PendingIntent? = null + private var secondPendingIntent: PendingIntent? = null private var initialShortcut: ShortcutInfo? = null private var secondShortcut: ShortcutInfo? = null - private var initialPendingIntent: PendingIntent? = null /** * @param alreadyRunningTask if set to [android.app.ActivityTaskManager.INVALID_TASK_ID] @@ -217,7 +221,7 @@ class SplitSelectDataHolder( * split task in fullscreen */ fun getFullscreenLaunchData() : SplitLaunchData { - // Convert all intents to shortcut infos to see if determine if we launch shortcut or intent + // Convert all intents to shortcut infos to determine if we launch shortcut or intent convertIntentsToFinalTypes() val splitLaunchType = getFullscreenLaunchType() diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt new file mode 100644 index 0000000000..fc767fae72 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.quickstep.util + +import android.app.ActivityManager.RunningTaskInfo +import android.app.ActivityTaskManager.INVALID_TASK_ID +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.os.UserHandle +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.shortcuts.ShortcutKey +import com.android.launcher3.ui.AbstractLauncherUiTest +import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_PENDINGINTENT_PENDINGINTENT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_PENDINGINTENT_TASK +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SHORTCUT_TASK +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SINGLE_INTENT_FULLSCREEN +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SINGLE_SHORTCUT_FULLSCREEN +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_SINGLE_TASK_FULLSCREEN +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_TASK_PENDINGINTENT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_TASK_SHORTCUT +import com.android.quickstep.util.SplitSelectDataHolder.Companion.SPLIT_TASK_TASK +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class SplitSelectDataHolderTest { + private lateinit var splitSelectDataHolder: SplitSelectDataHolder + + private val context: Context = + ContextWrapper(InstrumentationRegistry.getInstrumentation().targetContext) + private val sampleTaskInfo = RunningTaskInfo() + private val sampleTaskId = 10 + private val sampleTaskId2 = 11 + private val sampleUser = UserHandle(0) + private val sampleIntent = Intent() + private val sampleIntent2 = Intent() + private val sampleShortcut = Intent() + private val sampleShortcut2 = Intent() + private val sampleItemInfo = ItemInfo() + private val samplePackage = + AbstractLauncherUiTest.resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR) + + @Before + fun setup() { + splitSelectDataHolder = SplitSelectDataHolder(context) + + sampleTaskInfo.taskId = sampleTaskId + sampleItemInfo.user = sampleUser + sampleIntent.setPackage(samplePackage) + sampleIntent2.setPackage(samplePackage) + sampleShortcut.setPackage(samplePackage) + sampleShortcut2.setPackage(samplePackage) + sampleShortcut.putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "sampleShortcut") + sampleShortcut2.putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "sampleShortcut2") + } + + @Test + fun setInitialAsTask() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + null, + null + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setInitialAsIntent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setInitialAsIntentWithAlreadyRunningTask() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + sampleTaskId + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setInitialAsShortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + assertTrue(splitSelectDataHolder.isSplitSelectActive()) + } + + @Test + fun setSecondAsTask() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleTaskId) + assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed()) + } + + @Test + fun setSecondAsIntent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + null, + null + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed()) + } + + @Test + fun setSecondAsShortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser) + assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed()) + } + + @Test + fun generateLaunchData_Task_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + splitSelectDataHolder.setSecondTask(sampleTaskId2) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_TASK_TASK) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain a valid task ID for second app, and no intent or shortcut + assertNotEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Task_Intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_TASK_PENDINGINTENT) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain a valid intent for second app, and no task ID or shortcut + assertNotNull(launchData.secondPendingIntent) + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Task_Shortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_TASK_SHORTCUT) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain a valid shortcut and intent for second app, and no task ID + assertNotNull(launchData.secondShortcut) + assertNotNull(launchData.secondPendingIntent) + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + } + + @Test + fun generateLaunchData_Intent_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleTaskId) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_PENDINGINTENT_TASK) + + // should contain a valid intent for first app, and no task ID or shortcut + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialShortcut) + + // should contain a valid task ID for second app, and no intent or shortcut + assertNotEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Shortcut_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleTaskId) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SHORTCUT_TASK) + + // should contain a valid shortcut and intent for first app, and no task ID + assertNotNull(launchData.initialShortcut) + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + + // should contain a valid task ID for second app, and no intent or shortcut + assertNotEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Intent_Intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleIntent2, sampleUser) + val launchData = splitSelectDataHolder.getSplitLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_PENDINGINTENT_PENDINGINTENT) + + // should contain a valid intent for first app, and no task ID or shortcut + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialShortcut) + + // should contain a valid intent for second app, and no task ID or shortcut + assertNotNull(launchData.secondPendingIntent) + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Single_Task() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null + ) + val launchData = splitSelectDataHolder.getFullscreenLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SINGLE_TASK_FULLSCREEN) + + // should contain a valid task ID for first app, and no intent or shortcut + assertNotEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialPendingIntent) + assertNull(launchData.initialShortcut) + + // should contain no task ID, intent, or shortcut for second app + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Single_Intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + val launchData = splitSelectDataHolder.getFullscreenLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SINGLE_INTENT_FULLSCREEN) + + // should contain a valid intent for first app, and no task ID or shortcut + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + assertNull(launchData.initialShortcut) + + // should contain no task ID, intent, or shortcut for second app + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun generateLaunchData_Single_Shortcut() { + splitSelectDataHolder.setInitialTaskSelect( + sampleShortcut, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + val launchData = splitSelectDataHolder.getFullscreenLaunchData() + + assertEquals(launchData.splitLaunchType, SPLIT_SINGLE_SHORTCUT_FULLSCREEN) + + // should contain a valid shortcut and intent for first app, and no task ID + assertNotNull(launchData.initialShortcut) + assertNotNull(launchData.initialPendingIntent) + assertEquals(launchData.initialTaskId, INVALID_TASK_ID) + + // should contain no task ID, intent, or shortcut for second app + assertEquals(launchData.secondTaskId, INVALID_TASK_ID) + assertNull(launchData.secondPendingIntent) + assertNull(launchData.secondShortcut) + } + + @Test + fun clearState() { + splitSelectDataHolder.setInitialTaskSelect( + sampleTaskInfo, + STAGE_POSITION_TOP_OR_LEFT, + null, + null + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + splitSelectDataHolder.resetState() + assertFalse(splitSelectDataHolder.isSplitSelectActive()) + } +}