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