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.graphics.PointF 19 import android.graphics.Rect 20 import android.platform.test.rule.AllowedDevices 21 import android.platform.test.rule.DeviceProduct 22 import android.platform.test.rule.IgnoreLimit 23 import android.platform.test.rule.LimitDevicesRule 24 import android.util.SparseArray 25 import com.android.launcher3.DeviceProfile.DEFAULT_DIMENSION_PROVIDER 26 import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER 27 import com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE 28 import com.android.launcher3.util.DisplayController.Info 29 import com.android.launcher3.util.SandboxApplication 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 @get:Rule val context = SandboxApplication() 50 51 protected lateinit var inv: InvariantDeviceProfile 52 protected val info = mock<Info>() 53 protected lateinit var windowBounds: WindowBounds 54 private var transposeLayoutWithOrientation = false 55 private var useTwoPanels = false 56 private var isGestureMode = true 57 private var isTransientTaskbar = true 58 59 @Rule @JvmField val limitDevicesRule = LimitDevicesRule() 60 61 @Before setUpnull62 open fun setUp() { 63 // make sure to reset values 64 useTwoPanels = false 65 isGestureMode = true 66 } 67 newDPnull68 protected fun newDP(): DeviceProfile = 69 DeviceProfile( 70 context, 71 inv, 72 info, 73 context.appComponent.wmProxy, 74 context.appComponent.themeManager, 75 windowBounds, 76 SparseArray(), 77 /*isMultiWindowMode=*/ false, 78 transposeLayoutWithOrientation, 79 useTwoPanels, 80 isGestureMode, 81 DEFAULT_PROVIDER, 82 DEFAULT_DIMENSION_PROVIDER, 83 isTransientTaskbar, 84 ) 85 86 protected fun initializeVarsForPhone( 87 isGestureMode: Boolean = true, 88 isVerticalBar: Boolean = false, 89 ) { 90 val (x, y) = if (isVerticalBar) Pair(2400, 1080) else Pair(1080, 2400) 91 92 windowBounds = 93 WindowBounds( 94 Rect(0, 0, x, y), 95 Rect( 96 if (isVerticalBar) 118 else 0, 97 if (isVerticalBar) 74 else 118, 98 if (!isGestureMode && isVerticalBar) 126 else 0, 99 if (isGestureMode) 63 else if (isVerticalBar) 0 else 126, 100 ), 101 ) 102 103 whenever(info.isTablet(any())).thenReturn(false) 104 whenever(info.getDensityDpi()).thenReturn(420) 105 whenever(info.smallestSizeDp(any())).thenReturn(411f) 106 107 this.isGestureMode = isGestureMode 108 this.isTransientTaskbar = false 109 transposeLayoutWithOrientation = true 110 111 inv = 112 context.appComponent.idp.apply { 113 numRows = 5 114 numColumns = 4 115 numSearchContainerColumns = 4 116 117 iconSize = floatArrayOf(60f, 54f, 60f, 60f) 118 iconTextSize = FloatArray(4) { 14f } 119 deviceType = InvariantDeviceProfile.TYPE_PHONE 120 121 minCellSize = 122 listOf( 123 PointF(80f, 104f), 124 PointF(80f, 104f), 125 PointF(80f, 104f), 126 PointF(80f, 104f), 127 ) 128 .toTypedArray() 129 130 borderSpaces = 131 listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f)) 132 .toTypedArray() 133 134 numFolderRows = intArrayOf(3, 3, 3, 3) 135 numFolderColumns = intArrayOf(3, 3, 3, 3) 136 folderStyle = R.style.FolderStyleDefault 137 138 inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split 139 140 horizontalMargin = FloatArray(4) { 22f } 141 142 allAppsStyle = R.style.AllAppsStyleDefault 143 allAppsCellSize = 144 listOf( 145 PointF(80f, 104f), 146 PointF(80f, 104f), 147 PointF(80f, 104f), 148 PointF(80f, 104f), 149 ) 150 .toTypedArray() 151 allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f) 152 allAppsIconTextSize = FloatArray(4) { 14f } 153 allAppsBorderSpaces = 154 listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f)) 155 .toTypedArray() 156 157 numShownHotseatIcons = 4 158 159 numDatabaseHotseatIcons = 4 160 161 hotseatBarBottomSpace = FloatArray(4) { 48f } 162 hotseatQsbSpace = FloatArray(4) { 36f } 163 164 numAllAppsColumns = 4 165 166 isScalable = true 167 168 transientTaskbarIconSize = FloatArray(4) { 44f } 169 startAlignTaskbar = BooleanArray(4) { false } 170 171 inlineQsb = BooleanArray(4) { false } 172 173 devicePaddingId = R.xml.paddings_handhelds 174 175 isFixedLandscape = false 176 workspaceSpecsId = INVALID_RESOURCE_HANDLE 177 allAppsSpecsId = INVALID_RESOURCE_HANDLE 178 folderSpecsId = INVALID_RESOURCE_HANDLE 179 hotseatSpecsId = INVALID_RESOURCE_HANDLE 180 workspaceCellSpecsId = INVALID_RESOURCE_HANDLE 181 allAppsCellSpecsId = INVALID_RESOURCE_HANDLE 182 } 183 } 184 initializeVarsForTabletnull185 protected fun initializeVarsForTablet( 186 isLandscape: Boolean = false, 187 isGestureMode: Boolean = true, 188 ) { 189 val (x, y) = if (isLandscape) Pair(2560, 1600) else Pair(1600, 2560) 190 191 windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0)) 192 193 whenever(info.isTablet(any())).thenReturn(true) 194 whenever(info.getDensityDpi()).thenReturn(320) 195 whenever(info.smallestSizeDp(any())).thenReturn(800f) 196 197 this.isGestureMode = isGestureMode 198 this.isTransientTaskbar = true 199 useTwoPanels = false 200 201 inv = 202 context.appComponent.idp.apply { 203 numRows = 5 204 numColumns = 6 205 numSearchContainerColumns = 3 206 207 iconSize = FloatArray(4) { 60f } 208 iconTextSize = FloatArray(4) { 14f } 209 deviceType = InvariantDeviceProfile.TYPE_TABLET 210 211 minCellSize = 212 listOf( 213 PointF(102f, 120f), 214 PointF(120f, 104f), 215 PointF(102f, 120f), 216 PointF(102f, 120f), 217 ) 218 .toTypedArray() 219 220 borderSpaces = 221 listOf(PointF(16f, 64f), PointF(64f, 16f), PointF(16f, 64f), PointF(16f, 64f)) 222 .toTypedArray() 223 224 numFolderRows = intArrayOf(3, 3, 3, 3) 225 numFolderColumns = intArrayOf(3, 3, 3, 3) 226 folderStyle = R.style.FolderStyleDefault 227 228 inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5 229 230 horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f) 231 232 allAppsStyle = R.style.AllAppsStyleDefault 233 allAppsCellSize = 234 listOf( 235 PointF(96f, 142f), 236 PointF(126f, 126f), 237 PointF(96f, 142f), 238 PointF(96f, 142f), 239 ) 240 .toTypedArray() 241 allAppsIconSize = FloatArray(4) { 60f } 242 allAppsIconTextSize = FloatArray(4) { 14f } 243 allAppsBorderSpaces = 244 listOf(PointF(8f, 16f), PointF(16f, 16f), PointF(8f, 16f), PointF(8f, 16f)) 245 .toTypedArray() 246 247 numShownHotseatIcons = 6 248 249 numDatabaseHotseatIcons = 6 250 251 hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f) 252 hotseatQsbSpace = FloatArray(4) { 32f } 253 254 numAllAppsColumns = 6 255 256 isScalable = true 257 devicePaddingId = R.xml.paddings_6x5 258 259 transientTaskbarIconSize = FloatArray(4) { 44f } 260 startAlignTaskbar = booleanArrayOf(true, false, true, true) 261 262 inlineQsb = booleanArrayOf(false, true, false, false) 263 264 devicePaddingId = R.xml.paddings_handhelds 265 266 isFixedLandscape = false 267 workspaceSpecsId = INVALID_RESOURCE_HANDLE 268 allAppsSpecsId = INVALID_RESOURCE_HANDLE 269 folderSpecsId = INVALID_RESOURCE_HANDLE 270 hotseatSpecsId = INVALID_RESOURCE_HANDLE 271 workspaceCellSpecsId = INVALID_RESOURCE_HANDLE 272 allAppsCellSpecsId = INVALID_RESOURCE_HANDLE 273 } 274 } 275 initializeVarsForTwoPanelnull276 protected fun initializeVarsForTwoPanel( 277 isLandscape: Boolean = false, 278 isGestureMode: Boolean = true, 279 rows: Int = 4, 280 cols: Int = 4, 281 ) { 282 val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208) 283 284 windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 110, 0, 0)) 285 286 whenever(info.isTablet(any())).thenReturn(true) 287 whenever(info.getDensityDpi()).thenReturn(420) 288 whenever(info.smallestSizeDp(any())).thenReturn(700f) 289 290 this.isGestureMode = isGestureMode 291 this.isTransientTaskbar = true 292 useTwoPanels = true 293 294 inv = 295 context.appComponent.idp.apply { 296 numRows = rows 297 numColumns = cols 298 numSearchContainerColumns = cols 299 300 iconSize = floatArrayOf(60f, 52f, 52f, 60f) 301 iconTextSize = floatArrayOf(14f, 14f, 12f, 14f) 302 deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY 303 304 minCellSize = 305 listOf( 306 PointF(80f, 104f), 307 PointF(80f, 104f), 308 PointF(68f, 116f), 309 PointF(80f, 102f), 310 ) 311 .toTypedArray() 312 313 borderSpaces = 314 listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f)) 315 .toTypedArray() 316 317 numFolderRows = intArrayOf(3, 3, 3, 3) 318 numFolderColumns = intArrayOf(3, 3, 3, 3) 319 folderStyle = R.style.FolderStyleDefault 320 321 inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split 322 323 horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f) 324 325 allAppsStyle = R.style.AllAppsStyleDefault 326 allAppsCellSize = 327 listOf(PointF(0f, 0f), PointF(0f, 0f), PointF(68f, 104f), PointF(80f, 104f)) 328 .toTypedArray() 329 allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f) 330 allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f) 331 allAppsBorderSpaces = 332 listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 28f), PointF(20f, 16f)) 333 .toTypedArray() 334 335 numShownHotseatIcons = 6 336 337 numDatabaseHotseatIcons = 6 338 339 hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f) 340 hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f) 341 342 numAllAppsColumns = 6 343 numDatabaseAllAppsColumns = 6 344 345 isScalable = true 346 347 transientTaskbarIconSize = FloatArray(4) { 44f } 348 startAlignTaskbar = BooleanArray(4) { true } 349 350 inlineQsb = booleanArrayOf(false, false, false, false) 351 352 devicePaddingId = R.xml.paddings_handhelds 353 354 isFixedLandscape = false 355 workspaceSpecsId = INVALID_RESOURCE_HANDLE 356 allAppsSpecsId = INVALID_RESOURCE_HANDLE 357 folderSpecsId = INVALID_RESOURCE_HANDLE 358 hotseatSpecsId = INVALID_RESOURCE_HANDLE 359 workspaceCellSpecsId = INVALID_RESOURCE_HANDLE 360 allAppsCellSpecsId = INVALID_RESOURCE_HANDLE 361 } 362 } 363 dumpnull364 fun dump(dp: DeviceProfile): String { 365 val stringWriter = StringWriter() 366 val printWriter = PrintWriter(stringWriter) 367 dp.dump(context, "", printWriter) 368 printWriter.flush() 369 return stringWriter.toString() 370 } 371 } 372