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