1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.quickstep 18 19 import android.content.ComponentName 20 import android.content.Intent 21 import android.platform.test.flag.junit.SetFlagsRule 22 import com.android.dx.mockito.inline.extended.ExtendedMockito 23 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession 24 import com.android.dx.mockito.inline.extended.StaticMockitoSession 25 import com.android.launcher3.AbstractFloatingView 26 import com.android.launcher3.AbstractFloatingViewHelper 27 import com.android.launcher3.logging.StatsLogManager 28 import com.android.launcher3.logging.StatsLogManager.LauncherEvent 29 import com.android.launcher3.model.data.WorkspaceItemInfo 30 import com.android.launcher3.uioverrides.QuickstepLauncher 31 import com.android.launcher3.util.SplitConfigurationOptions 32 import com.android.launcher3.util.TransformingTouchDelegate 33 import com.android.quickstep.TaskOverlayFactory.TaskOverlay 34 import com.android.quickstep.views.LauncherRecentsView 35 import com.android.quickstep.views.TaskThumbnailViewDeprecated 36 import com.android.quickstep.views.TaskView 37 import com.android.quickstep.views.TaskViewIcon 38 import com.android.systemui.shared.recents.model.Task 39 import com.android.systemui.shared.recents.model.Task.TaskKey 40 import com.android.window.flags.Flags 41 import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource 42 import com.android.wm.shell.shared.DesktopModeStatus 43 import com.google.common.truth.Truth.assertThat 44 import org.junit.After 45 import org.junit.Before 46 import org.junit.Rule 47 import org.junit.Test 48 import org.mockito.kotlin.any 49 import org.mockito.kotlin.doReturn 50 import org.mockito.kotlin.eq 51 import org.mockito.kotlin.mock 52 import org.mockito.kotlin.spy 53 import org.mockito.kotlin.verify 54 import org.mockito.kotlin.whenever 55 import org.mockito.quality.Strictness 56 57 /** Test for DesktopSystemShortcut */ 58 class DesktopSystemShortcutTest { 59 60 @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT) 61 62 private val launcher: QuickstepLauncher = mock() 63 private val statsLogManager: StatsLogManager = mock() 64 private val statsLogger: StatsLogManager.StatsLogger = mock() 65 private val recentsView: LauncherRecentsView = mock() 66 private val taskView: TaskView = mock() 67 private val workspaceItemInfo: WorkspaceItemInfo = mock() 68 private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock() 69 private val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = mock() 70 private val iconView: TaskViewIcon = mock() 71 private val transformingTouchDelegate: TransformingTouchDelegate = mock() 72 private val factory: TaskShortcutFactory = 73 DesktopSystemShortcut.createFactory(abstractFloatingViewHelper) 74 private val overlayFactory: TaskOverlayFactory = mock() 75 private val overlay: TaskOverlay<*> = mock() 76 77 private lateinit var mockitoSession: StaticMockitoSession 78 79 @Before setUpnull80 fun setUp() { 81 mockitoSession = 82 mockitoSession() 83 .strictness(Strictness.LENIENT) 84 .spyStatic(DesktopModeStatus::class.java) 85 .startMocking() 86 ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.enforceDeviceRestrictions() } 87 ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } 88 whenever(overlayFactory.createOverlay(any())).thenReturn(overlay) 89 } 90 91 @After tearDownnull92 fun tearDown() { 93 mockitoSession.finishMocking() 94 } 95 96 @Test createDesktopTaskShortcutFactory_desktopModeDisablednull97 fun createDesktopTaskShortcutFactory_desktopModeDisabled() { 98 setFlagsRule.disableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) 99 100 val task = 101 Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply { 102 isDockable = true 103 } 104 val taskContainer = createTaskContainer(task) 105 106 val shortcuts = factory.getShortcuts(launcher, taskContainer) 107 assertThat(shortcuts).isNull() 108 } 109 110 @Test createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupportednull111 fun createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported() { 112 setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) 113 ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } 114 115 val taskContainer = createTaskContainer(createTask()) 116 117 val shortcuts = factory.getShortcuts(launcher, taskContainer) 118 assertThat(shortcuts).isNull() 119 } 120 121 @Test createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported_OverrideEnablednull122 fun createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported_OverrideEnabled() { 123 setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) 124 ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } 125 ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.enforceDeviceRestrictions() } 126 127 val taskContainer = spy(createTaskContainer(createTask())) 128 doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo 129 130 val shortcuts = factory.getShortcuts(launcher, taskContainer) 131 assertThat(shortcuts).isNotNull() 132 } 133 134 @Test createDesktopTaskShortcutFactory_undockablenull135 fun createDesktopTaskShortcutFactory_undockable() { 136 setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) 137 138 val unDockableTask = createTask().apply { isDockable = false } 139 val taskContainer = createTaskContainer(unDockableTask) 140 141 val shortcuts = factory.getShortcuts(launcher, taskContainer) 142 assertThat(shortcuts).isNull() 143 } 144 145 @Test desktopSystemShortcutClickednull146 fun desktopSystemShortcutClicked() { 147 setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) 148 149 val task = createTask() 150 val taskContainer = spy(createTaskContainer(task)) 151 152 whenever(launcher.getOverviewPanel<LauncherRecentsView>()).thenReturn(recentsView) 153 whenever(launcher.statsLogManager).thenReturn(statsLogManager) 154 whenever(statsLogManager.logger()).thenReturn(statsLogger) 155 whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger) 156 whenever(recentsView.moveTaskToDesktop(any(), any(), any())).thenAnswer { 157 val successCallback = it.getArgument<Runnable>(2) 158 successCallback.run() 159 } 160 doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo 161 162 val shortcuts = factory.getShortcuts(launcher, taskContainer) 163 assertThat(shortcuts).hasSize(1) 164 assertThat(shortcuts!!.first()).isInstanceOf(DesktopSystemShortcut::class.java) 165 166 val desktopShortcut = shortcuts.first() as DesktopSystemShortcut 167 168 desktopShortcut.onClick(taskView) 169 170 val allTypesExceptRebindSafe = 171 AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv() 172 verify(abstractFloatingViewHelper).closeOpenViews(launcher, true, allTypesExceptRebindSafe) 173 verify(recentsView) 174 .moveTaskToDesktop( 175 eq(taskContainer), 176 eq(DesktopModeTransitionSource.APP_FROM_OVERVIEW), 177 any() 178 ) 179 verify(statsLogger).withItemInfo(workspaceItemInfo) 180 verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP) 181 } 182 createTasknull183 private fun createTask(): Task { 184 return Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply { 185 isDockable = true 186 } 187 } 188 createTaskContainernull189 private fun createTaskContainer(task: Task): TaskView.TaskContainer { 190 return taskView.TaskContainer( 191 task, 192 thumbnailView = null, 193 thumbnailViewDeprecated, 194 iconView, 195 transformingTouchDelegate, 196 SplitConfigurationOptions.STAGE_POSITION_UNDEFINED, 197 digitalWellBeingToast = null, 198 showWindowsView = null, 199 overlayFactory 200 ) 201 } 202 } 203