1 /* 2 * Copyright (C) 2023 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.celllayout.board 17 18 import android.content.ComponentName 19 import android.content.Context 20 import android.graphics.Rect 21 import android.os.Process 22 import android.os.UserHandle 23 import android.util.Log 24 import androidx.test.core.app.ApplicationProvider 25 import androidx.test.platform.app.InstrumentationRegistry 26 import com.android.launcher3.InvariantDeviceProfile 27 import com.android.launcher3.LauncherSettings 28 import com.android.launcher3.celllayout.FavoriteItemsTransaction 29 import com.android.launcher3.model.data.AppInfo 30 import com.android.launcher3.model.data.FolderInfo 31 import com.android.launcher3.model.data.ItemInfo 32 import com.android.launcher3.model.data.WorkspaceItemInfo 33 import com.android.launcher3.ui.TestViewHelpers 34 import com.android.launcher3.util.WidgetUtils 35 import java.util.function.Supplier 36 37 class TestWorkspaceBuilder(private val mContext: Context) { 38 39 private var appComponentName = 40 ComponentName("com.google.android.calculator", "com.android.calculator2.Calculator") 41 private val myUser: UserHandle = Process.myUserHandle() 42 43 /** Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. */ fillWithWidgetsnull44 private fun fillWithWidgets( 45 widgetRect: WidgetRect, 46 transaction: FavoriteItemsTransaction, 47 screenId: Int, 48 ): FavoriteItemsTransaction { 49 val initX = widgetRect.cellX 50 val initY = widgetRect.cellY 51 for (x in initX until initX + widgetRect.spanX) { 52 for (y in initY until initY + widgetRect.spanY) { 53 try { 54 // this widgets are filling, we don't care if we can't place them 55 transaction.addItem( 56 createWidgetInCell(WidgetRect(CellType.IGNORE, Rect(x, y, x, y)), screenId) 57 ) 58 } catch (e: Exception) { 59 Log.d(TAG, "Unable to place filling widget at $x,$y") 60 } 61 } 62 } 63 return transaction 64 } 65 appnull66 private fun app() = 67 AppInfo(appComponentName, "test icon", myUser, AppInfo.makeLaunchIntent(appComponentName)) 68 69 /** 70 * Helper to set the app to use for the test workspace, using activity-alias from 71 * AndroidManifest-common. 72 * 73 * @param testAppName the android:name field of the test app activity-alias to use 74 */ 75 fun setTestAppActivityAlias(testAppName: String) { 76 appComponentName = 77 ComponentName( 78 InstrumentationRegistry.getInstrumentation().context.packageName, 79 TEST_ACTIVITY_PACKAGE_PREFIX + testAppName, 80 ) 81 } 82 83 /** 84 * Sets the test app for app icons to the specified Component 85 * 86 * @param testAppComponent ComponentName to use for app icons 87 */ setTestAppComponentnull88 fun setTestAppComponent(testAppComponent: ComponentName) { 89 appComponentName = testAppComponent 90 } 91 addCorrespondingWidgetRectnull92 private fun addCorrespondingWidgetRect( 93 widgetRect: WidgetRect, 94 transaction: FavoriteItemsTransaction, 95 screenId: Int, 96 ) { 97 if (widgetRect.type == 'x') { 98 fillWithWidgets(widgetRect, transaction, screenId) 99 } else { 100 transaction.addItem(createWidgetInCell(widgetRect, screenId)) 101 } 102 } 103 104 /** Builds the given board into the transaction */ buildFromBoardnull105 fun buildFromBoard( 106 board: CellLayoutBoard, 107 transaction: FavoriteItemsTransaction, 108 screenId: Int, 109 ): FavoriteItemsTransaction { 110 board.widgets.forEach { addCorrespondingWidgetRect(it, transaction, screenId) } 111 board.icons.forEach { transaction.addItem { createIconInCell(it, screenId) } } 112 board.folders.forEach { transaction.addItem { createFolderInCell(it, screenId) } } 113 return transaction 114 } 115 116 /** 117 * Fills the hotseat row with apps instead of suggestions, for this to work the workspace should 118 * be clean otherwise this doesn't overrides the existing icons. 119 */ fillHotseatIconsnull120 fun fillHotseatIcons(transaction: FavoriteItemsTransaction): FavoriteItemsTransaction { 121 for (i in 0..<InvariantDeviceProfile.INSTANCE[mContext].numDatabaseHotseatIcons) { 122 transaction.addItem { getHotseatValues(i) } 123 } 124 return transaction 125 } 126 createWidgetInCellnull127 private fun createWidgetInCell(widgetRect: WidgetRect, paramScreenId: Int): Supplier<ItemInfo> { 128 // Create the widget lazily since the appWidgetId can get lost during setup 129 return Supplier<ItemInfo> { 130 WidgetUtils.createWidgetInfo( 131 TestViewHelpers.findWidgetProvider(false), 132 ApplicationProvider.getApplicationContext(), 133 true, 134 ) 135 .apply { 136 cellX = widgetRect.cellX 137 cellY = widgetRect.cellY 138 spanX = widgetRect.spanX 139 spanY = widgetRect.spanY 140 screenId = paramScreenId 141 } 142 } 143 } 144 createFolderInCellnull145 fun createFolderInCell(folderPoint: FolderPoint, paramScreenId: Int): FolderInfo = 146 FolderInfo().apply { 147 screenId = paramScreenId 148 container = LauncherSettings.Favorites.CONTAINER_DESKTOP 149 cellX = folderPoint.coord.x 150 cellY = folderPoint.coord.y 151 spanY = 1 152 spanX = 1 153 minSpanX = 1 154 minSpanY = 1 155 setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, null) 156 for (i in 0 until folderPoint.numberIconsInside) { 157 add(getDefaultWorkspaceItem(paramScreenId)) 158 } 159 } 160 getDefaultWorkspaceItemnull161 private fun getDefaultWorkspaceItem(paramScreenId: Int): WorkspaceItemInfo = 162 WorkspaceItemInfo(app()).apply { 163 screenId = paramScreenId 164 spanY = 1 165 spanX = 1 166 minSpanX = 1 167 minSpanY = 1 168 container = LauncherSettings.Favorites.CONTAINER_DESKTOP 169 } 170 createIconInCellnull171 private fun createIconInCell(iconPoint: IconPoint, paramScreenId: Int) = 172 WorkspaceItemInfo(app()).apply { 173 screenId = paramScreenId 174 cellX = iconPoint.coord.x 175 cellY = iconPoint.coord.y 176 spanY = 1 177 spanX = 1 178 minSpanX = 1 179 minSpanY = 1 180 container = LauncherSettings.Favorites.CONTAINER_DESKTOP 181 } 182 getHotseatValuesnull183 private fun getHotseatValues(x: Int) = 184 WorkspaceItemInfo(app()).apply { 185 cellX = x 186 cellY = 0 187 spanY = 1 188 spanX = 1 189 minSpanX = 1 190 minSpanY = 1 191 rank = x 192 screenId = x 193 container = LauncherSettings.Favorites.CONTAINER_HOTSEAT 194 } 195 196 companion object { 197 private const val TAG = "CellLayoutBoardBuilder" 198 private const val TEST_ACTIVITY_PACKAGE_PREFIX = "com.android.launcher3.tests." 199 } 200 } 201