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