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