1 /*
2  * Copyright (C) 2022 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 package com.android.launcher3
17 
18 import android.content.Context
19 import android.graphics.PointF
20 import android.graphics.Rect
21 import android.platform.test.rule.AllowedDevices
22 import android.platform.test.rule.DeviceProduct
23 import android.platform.test.rule.IgnoreLimit
24 import android.platform.test.rule.LimitDevicesRule
25 import android.util.SparseArray
26 import androidx.test.core.app.ApplicationProvider
27 import com.android.launcher3.DeviceProfile.DEFAULT_DIMENSION_PROVIDER
28 import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER
29 import com.android.launcher3.util.DisplayController.Info
30 import com.android.launcher3.util.WindowBounds
31 import java.io.PrintWriter
32 import java.io.StringWriter
33 import org.junit.Before
34 import org.junit.Rule
35 import org.mockito.kotlin.any
36 import org.mockito.kotlin.mock
37 import org.mockito.kotlin.whenever
38 
39 /**
40  * This is an abstract class for DeviceProfile tests that don't need the real Context and mock an
41  * InvariantDeviceProfile instead of creating one based on real values.
42  *
43  * For an implementation that creates InvariantDeviceProfile, use [AbstractDeviceProfileTest]
44  */
45 @AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
46 @IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
47 abstract class FakeInvariantDeviceProfileTest {
48 
49     protected lateinit var context: Context
50     protected var inv: InvariantDeviceProfile? = null
51     protected val info: Info = mock()
52     protected var windowBounds: WindowBounds? = null
53     protected var isMultiWindowMode: Boolean = false
54     protected var transposeLayoutWithOrientation: Boolean = false
55     protected var useTwoPanels: Boolean = false
56     protected var isGestureMode: Boolean = true
57     protected var isTransientTaskbar: Boolean = true
58 
59     @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
60 
61     @Before
setUpnull62     fun setUp() {
63         context = ApplicationProvider.getApplicationContext()
64         // make sure to reset values
65         useTwoPanels = false
66         isGestureMode = true
67     }
68 
newDPnull69     protected fun newDP(): DeviceProfile =
70         DeviceProfile(
71             context,
72             inv,
73             info,
74             windowBounds,
75             SparseArray(),
76             isMultiWindowMode,
77             transposeLayoutWithOrientation,
78             useTwoPanels,
79             isGestureMode,
80             DEFAULT_PROVIDER,
81             DEFAULT_DIMENSION_PROVIDER,
82             isTransientTaskbar,
83         )
84 
85     protected fun initializeVarsForPhone(
86         isGestureMode: Boolean = true,
87         isVerticalBar: Boolean = false
88     ) {
89         val (x, y) = if (isVerticalBar) Pair(2400, 1080) else Pair(1080, 2400)
90 
91         windowBounds =
92             WindowBounds(
93                 Rect(0, 0, x, y),
94                 Rect(
95                     if (isVerticalBar) 118 else 0,
96                     if (isVerticalBar) 74 else 118,
97                     if (!isGestureMode && isVerticalBar) 126 else 0,
98                     if (isGestureMode) 63 else if (isVerticalBar) 0 else 126
99                 )
100             )
101 
102         whenever(info.isTablet(any())).thenReturn(false)
103         whenever(info.getDensityDpi()).thenReturn(420)
104         whenever(info.smallestSizeDp(any())).thenReturn(411f)
105 
106         this.isGestureMode = isGestureMode
107         this.isTransientTaskbar = false
108         transposeLayoutWithOrientation = true
109 
110         inv =
111             InvariantDeviceProfile().apply {
112                 numRows = 5
113                 numColumns = 4
114                 numSearchContainerColumns = 4
115 
116                 iconSize = floatArrayOf(60f, 54f, 60f, 60f)
117                 iconTextSize = FloatArray(4) { 14f }
118                 deviceType = InvariantDeviceProfile.TYPE_PHONE
119 
120                 minCellSize =
121                     listOf(
122                             PointF(80f, 104f),
123                             PointF(80f, 104f),
124                             PointF(80f, 104f),
125                             PointF(80f, 104f)
126                         )
127                         .toTypedArray()
128 
129                 borderSpaces =
130                     listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f))
131                         .toTypedArray()
132 
133                 numFolderRows = intArrayOf(3, 3, 3, 3)
134                 numFolderColumns = intArrayOf(3, 3, 3, 3)
135                 folderStyle = R.style.FolderStyleDefault
136 
137                 inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
138 
139                 horizontalMargin = FloatArray(4) { 22f }
140 
141                 allAppsStyle = R.style.AllAppsStyleDefault
142                 allAppsCellSize =
143                     listOf(
144                             PointF(80f, 104f),
145                             PointF(80f, 104f),
146                             PointF(80f, 104f),
147                             PointF(80f, 104f)
148                         )
149                         .toTypedArray()
150                 allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f)
151                 allAppsIconTextSize = FloatArray(4) { 14f }
152                 allAppsBorderSpaces =
153                     listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f))
154                         .toTypedArray()
155 
156                 numShownHotseatIcons = 4
157 
158                 numDatabaseHotseatIcons = 4
159 
160                 hotseatBarBottomSpace = FloatArray(4) { 48f }
161                 hotseatQsbSpace = FloatArray(4) { 36f }
162 
163                 numAllAppsColumns = 4
164 
165                 isScalable = true
166 
167                 transientTaskbarIconSize = FloatArray(4) { 44f }
168                 startAlignTaskbar = BooleanArray(4) { false }
169 
170                 inlineQsb = BooleanArray(4) { false }
171 
172                 devicePaddingId = R.xml.paddings_handhelds
173             }
174     }
175 
initializeVarsForTabletnull176     protected fun initializeVarsForTablet(
177         isLandscape: Boolean = false,
178         isGestureMode: Boolean = true
179     ) {
180         val (x, y) = if (isLandscape) Pair(2560, 1600) else Pair(1600, 2560)
181 
182         windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0))
183 
184         whenever(info.isTablet(any())).thenReturn(true)
185         whenever(info.getDensityDpi()).thenReturn(320)
186         whenever(info.smallestSizeDp(any())).thenReturn(800f)
187 
188         this.isGestureMode = isGestureMode
189         this.isTransientTaskbar = true
190         useTwoPanels = false
191 
192         inv =
193             InvariantDeviceProfile().apply {
194                 numRows = 5
195                 numColumns = 6
196                 numSearchContainerColumns = 3
197 
198                 iconSize = FloatArray(4) { 60f }
199                 iconTextSize = FloatArray(4) { 14f }
200                 deviceType = InvariantDeviceProfile.TYPE_TABLET
201 
202                 minCellSize =
203                     listOf(
204                             PointF(102f, 120f),
205                             PointF(120f, 104f),
206                             PointF(102f, 120f),
207                             PointF(102f, 120f)
208                         )
209                         .toTypedArray()
210 
211                 borderSpaces =
212                     listOf(PointF(16f, 64f), PointF(64f, 16f), PointF(16f, 64f), PointF(16f, 64f))
213                         .toTypedArray()
214 
215                 numFolderRows = intArrayOf(3, 3, 3, 3)
216                 numFolderColumns = intArrayOf(3, 3, 3, 3)
217                 folderStyle = R.style.FolderStyleDefault
218 
219                 inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5
220 
221                 horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f)
222 
223                 allAppsStyle = R.style.AllAppsStyleDefault
224                 allAppsCellSize =
225                     listOf(
226                             PointF(96f, 142f),
227                             PointF(126f, 126f),
228                             PointF(96f, 142f),
229                             PointF(96f, 142f)
230                         )
231                         .toTypedArray()
232                 allAppsIconSize = FloatArray(4) { 60f }
233                 allAppsIconTextSize = FloatArray(4) { 14f }
234                 allAppsBorderSpaces =
235                     listOf(PointF(8f, 16f), PointF(16f, 16f), PointF(8f, 16f), PointF(8f, 16f))
236                         .toTypedArray()
237 
238                 numShownHotseatIcons = 6
239 
240                 numDatabaseHotseatIcons = 6
241 
242                 hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f)
243                 hotseatQsbSpace = FloatArray(4) { 32f }
244 
245                 numAllAppsColumns = 6
246 
247                 isScalable = true
248                 devicePaddingId = R.xml.paddings_6x5
249 
250                 transientTaskbarIconSize = FloatArray(4) { 44f }
251                 startAlignTaskbar = booleanArrayOf(true, false, true, true)
252 
253                 inlineQsb = booleanArrayOf(false, true, false, false)
254 
255                 devicePaddingId = R.xml.paddings_handhelds
256             }
257     }
258 
initializeVarsForTwoPanelnull259     protected fun initializeVarsForTwoPanel(
260         isLandscape: Boolean = false,
261         isGestureMode: Boolean = true,
262         rows: Int = 4,
263         cols: Int = 4,
264     ) {
265         val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208)
266 
267         windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 110, 0, 0))
268 
269         whenever(info.isTablet(any())).thenReturn(true)
270         whenever(info.getDensityDpi()).thenReturn(420)
271         whenever(info.smallestSizeDp(any())).thenReturn(700f)
272 
273         this.isGestureMode = isGestureMode
274         this.isTransientTaskbar = true
275         useTwoPanels = true
276 
277         inv =
278             InvariantDeviceProfile().apply {
279                 numRows = rows
280                 numColumns = cols
281                 numSearchContainerColumns = cols
282 
283                 iconSize = floatArrayOf(60f, 52f, 52f, 60f)
284                 iconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
285                 deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY
286 
287                 minCellSize =
288                     listOf(
289                             PointF(80f, 104f),
290                             PointF(80f, 104f),
291                             PointF(68f, 116f),
292                             PointF(80f, 102f)
293                         )
294                         .toTypedArray()
295 
296                 borderSpaces =
297                     listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f))
298                         .toTypedArray()
299 
300                 numFolderRows = intArrayOf(3, 3, 3, 3)
301                 numFolderColumns = intArrayOf(3, 3, 3, 3)
302                 folderStyle = R.style.FolderStyleDefault
303 
304                 inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
305 
306                 horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f)
307 
308                 allAppsStyle = R.style.AllAppsStyleDefault
309                 allAppsCellSize =
310                     listOf(PointF(0f, 0f), PointF(0f, 0f), PointF(68f, 104f), PointF(80f, 104f))
311                         .toTypedArray()
312                 allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f)
313                 allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
314                 allAppsBorderSpaces =
315                     listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 28f), PointF(20f, 16f))
316                         .toTypedArray()
317 
318                 numShownHotseatIcons = 6
319 
320                 numDatabaseHotseatIcons = 6
321 
322                 hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f)
323                 hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f)
324 
325                 numAllAppsColumns = 6
326                 numDatabaseAllAppsColumns = 6
327 
328                 isScalable = true
329 
330                 transientTaskbarIconSize = FloatArray(4) { 44f }
331                 startAlignTaskbar = BooleanArray(4) { true }
332 
333                 inlineQsb = booleanArrayOf(false, false, false, false)
334 
335                 devicePaddingId = R.xml.paddings_handhelds
336             }
337     }
338 
dumpnull339     fun dump(dp: DeviceProfile): String {
340         val stringWriter = StringWriter()
341         val printWriter = PrintWriter(stringWriter)
342         dp.dump(context, "", printWriter)
343         printWriter.flush()
344         return stringWriter.toString()
345     }
346 }
347