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