1 package com.android.launcher3.model; 2 3 import android.content.ContentValues; 4 import android.content.Intent; 5 import android.database.Cursor; 6 import android.graphics.Point; 7 import android.test.ProviderTestCase2; 8 9 import com.android.launcher3.InvariantDeviceProfile; 10 import com.android.launcher3.LauncherAppState; 11 import com.android.launcher3.LauncherModel; 12 import com.android.launcher3.LauncherSettings; 13 import com.android.launcher3.config.ProviderConfig; 14 import com.android.launcher3.util.TestLauncherProvider; 15 16 import java.util.ArrayList; 17 import java.util.HashMap; 18 import java.util.HashSet; 19 20 /** 21 * Unit tests for {@link GridSizeMigrationTask} 22 */ 23 public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherProvider> { 24 25 private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP; 26 private static final long HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT; 27 28 private static final int APPLICATION = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 29 private static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 30 31 private static final String TEST_PACKAGE = "com.android.launcher3.validpackage"; 32 private static final String VALID_INTENT = 33 new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0); 34 35 private HashSet<String> mValidPackages; 36 private InvariantDeviceProfile mIdp; 37 GridSizeMigrationTaskTest()38 public GridSizeMigrationTaskTest() { 39 super(TestLauncherProvider.class, ProviderConfig.AUTHORITY); 40 } 41 42 @Override setUp()43 protected void setUp() throws Exception { 44 super.setUp(); 45 mValidPackages = new HashSet<>(); 46 mValidPackages.add(TEST_PACKAGE); 47 48 mIdp = new InvariantDeviceProfile(); 49 } 50 testHotseatMigration_apps_dropped()51 public void testHotseatMigration_apps_dropped() throws Exception { 52 long[] hotseatItems = { 53 addItem(APPLICATION, 0, HOTSEAT, 0, 0), 54 addItem(SHORTCUT, 1, HOTSEAT, 0, 0), 55 -1, 56 addItem(SHORTCUT, 3, HOTSEAT, 0, 0), 57 addItem(APPLICATION, 4, HOTSEAT, 0, 0), 58 }; 59 60 new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1) 61 .migrateHotseat(); 62 // First & last items are dropped as they have the least weight. 63 verifyHotseat(hotseatItems[1], -1, hotseatItems[3]); 64 } 65 testHotseatMigration_shortcuts_dropped()66 public void testHotseatMigration_shortcuts_dropped() throws Exception { 67 long[] hotseatItems = { 68 addItem(APPLICATION, 0, HOTSEAT, 0, 0), 69 addItem(30, 1, HOTSEAT, 0, 0), 70 -1, 71 addItem(SHORTCUT, 3, HOTSEAT, 0, 0), 72 addItem(10, 4, HOTSEAT, 0, 0), 73 }; 74 75 new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1) 76 .migrateHotseat(); 77 // First & third items are dropped as they have the least weight. 78 verifyHotseat(hotseatItems[1], -1, hotseatItems[4]); 79 } 80 verifyHotseat(long... sortedIds)81 private void verifyHotseat(long... sortedIds) { 82 int screenId = 0; 83 int total = 0; 84 85 for (long id : sortedIds) { 86 Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, 87 new String[]{LauncherSettings.Favorites._ID}, 88 "container=-101 and screen=" + screenId, null, null, null); 89 90 if (id == -1) { 91 assertEquals(0, c.getCount()); 92 } else { 93 assertEquals(1, c.getCount()); 94 c.moveToNext(); 95 assertEquals(id, c.getLong(0)); 96 total ++; 97 } 98 c.close(); 99 100 screenId++; 101 } 102 103 // Verify that not other entry exist in the DB. 104 Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, 105 new String[]{LauncherSettings.Favorites._ID}, 106 "container=-101", null, null, null); 107 assertEquals(total, c.getCount()); 108 c.close(); 109 } 110 testWorkspace_empty_row_column_removed()111 public void testWorkspace_empty_row_column_removed() throws Exception { 112 long[][][] ids = createGrid(new int[][][]{{ 113 { 0, 0, -1, 1}, 114 { 3, 1, -1, 4}, 115 { -1, -1, -1, -1}, 116 { 5, 2, -1, 6}, 117 }}); 118 119 new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), 120 new Point(4, 4), new Point(3, 3)).migrateWorkspace(); 121 122 // Column 2 and row 2 got removed. 123 verifyWorkspace(new long[][][] {{ 124 {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, 125 {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, 126 {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, 127 }}); 128 } 129 testWorkspace_new_screen_created()130 public void testWorkspace_new_screen_created() throws Exception { 131 long[][][] ids = createGrid(new int[][][]{{ 132 { 0, 0, 0, 1}, 133 { 3, 1, 0, 4}, 134 { -1, -1, -1, -1}, 135 { 5, 2, -1, 6}, 136 }}); 137 138 new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), 139 new Point(4, 4), new Point(3, 3)).migrateWorkspace(); 140 141 // Items in the second column get moved to new screen 142 verifyWorkspace(new long[][][] {{ 143 {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, 144 {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, 145 {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, 146 }, { 147 {ids[0][0][2], ids[0][1][2], -1}, 148 }}); 149 } 150 testWorkspace_items_merged_in_next_screen()151 public void testWorkspace_items_merged_in_next_screen() throws Exception { 152 long[][][] ids = createGrid(new int[][][]{{ 153 { 0, 0, 0, 1}, 154 { 3, 1, 0, 4}, 155 { -1, -1, -1, -1}, 156 { 5, 2, -1, 6}, 157 },{ 158 { 0, 0, -1, 1}, 159 { 3, 1, -1, 4}, 160 }}); 161 162 new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), 163 new Point(4, 4), new Point(3, 3)).migrateWorkspace(); 164 165 // Items in the second column of the first screen should get placed on the 3rd 166 // row of the second screen 167 verifyWorkspace(new long[][][] {{ 168 {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, 169 {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, 170 {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, 171 }, { 172 {ids[1][0][0], ids[1][0][1], ids[1][0][3]}, 173 {ids[1][1][0], ids[1][1][1], ids[1][1][3]}, 174 {ids[0][0][2], ids[0][1][2], -1}, 175 }}); 176 } 177 testWorkspace_items_not_merged_in_next_screen()178 public void testWorkspace_items_not_merged_in_next_screen() throws Exception { 179 // First screen has 2 items that need to be moved, but second screen has only one 180 // empty space after migration (top-left corner) 181 long[][][] ids = createGrid(new int[][][]{{ 182 { 0, 0, 0, 1}, 183 { 3, 1, 0, 4}, 184 { -1, -1, -1, -1}, 185 { 5, 2, -1, 6}, 186 },{ 187 { -1, 0, -1, 1}, 188 { 3, 1, -1, 4}, 189 { -1, -1, -1, -1}, 190 { 5, 2, -1, 6}, 191 }}); 192 193 new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(), 194 new Point(4, 4), new Point(3, 3)).migrateWorkspace(); 195 196 // Items in the second column of the first screen should get placed on a new screen. 197 verifyWorkspace(new long[][][] {{ 198 {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, 199 {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, 200 {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, 201 }, { 202 { -1, ids[1][0][1], ids[1][0][3]}, 203 {ids[1][1][0], ids[1][1][1], ids[1][1][3]}, 204 {ids[1][3][0], ids[1][3][1], ids[1][3][3]}, 205 }, { 206 {ids[0][0][2], ids[0][1][2], -1}, 207 }}); 208 } 209 210 /** 211 * Initializes the DB with dummy elements to represent the provided grid structure. 212 * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for 213 * type definitions. The first dimension represents the screens and the next 214 * two represent the workspace grid. 215 * @return the same grid representation where each entry is the corresponding item id. 216 */ createGrid(int[][][] typeArray)217 private long[][][] createGrid(int[][][] typeArray) throws Exception { 218 long[][][] ids = new long[typeArray.length][][]; 219 220 for (int i = 0; i < typeArray.length; i++) { 221 // Add screen to DB 222 long screenId = LauncherAppState.getLauncherProvider().generateNewScreenId(); 223 ContentValues v = new ContentValues(); 224 v.put(LauncherSettings.WorkspaceScreens._ID, screenId); 225 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); 226 getMockContentResolver().insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v); 227 228 ids[i] = new long[typeArray[i].length][]; 229 for (int y = 0; y < typeArray[i].length; y++) { 230 ids[i][y] = new long[typeArray[i][y].length]; 231 for (int x = 0; x < typeArray[i][y].length; x++) { 232 if (typeArray[i][y][x] < 0) { 233 // Empty cell 234 ids[i][y][x] = -1; 235 } else { 236 ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y); 237 } 238 } 239 } 240 } 241 return ids; 242 } 243 244 /** 245 * Verifies that the workspace items are arranged in the provided order. 246 * @param ids A 3d array where the first dimension represents the screen, and the rest two 247 * represent the workspace grid. 248 */ verifyWorkspace(long[][][] ids)249 private void verifyWorkspace(long[][][] ids) { 250 ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(getMockContext()); 251 assertEquals(ids.length, allScreens.size()); 252 int total = 0; 253 254 for (int i = 0; i < ids.length; i++) { 255 long screenId = allScreens.get(i); 256 for (int y = 0; y < ids[i].length; y++) { 257 for (int x = 0; x < ids[i][y].length; x++) { 258 long id = ids[i][y][x]; 259 260 Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, 261 new String[]{LauncherSettings.Favorites._ID}, 262 "container=-100 and screen=" + screenId + 263 " and cellX=" + x + " and cellY=" + y, null, null, null); 264 if (id == -1) { 265 assertEquals(0, c.getCount()); 266 } else { 267 assertEquals(1, c.getCount()); 268 c.moveToNext(); 269 assertEquals(id, c.getLong(0)); 270 total++; 271 } 272 c.close(); 273 } 274 } 275 } 276 277 // Verify that not other entry exist in the DB. 278 Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, 279 new String[]{LauncherSettings.Favorites._ID}, 280 "container=-100", null, null, null); 281 assertEquals(total, c.getCount()); 282 c.close(); 283 } 284 285 /** 286 * Adds a dummy item in the DB. 287 * @param type {@link #APPLICATION} or {@link #SHORTCUT} or >= 2 for 288 * folder (where the type represents the number of items in the folder). 289 */ addItem(int type, long screen, long container, int x, int y)290 private long addItem(int type, long screen, long container, int x, int y) throws Exception { 291 long id = LauncherAppState.getLauncherProvider().generateNewItemId(); 292 293 ContentValues values = new ContentValues(); 294 values.put(LauncherSettings.Favorites._ID, id); 295 values.put(LauncherSettings.Favorites.CONTAINER, container); 296 values.put(LauncherSettings.Favorites.SCREEN, screen); 297 values.put(LauncherSettings.Favorites.CELLX, x); 298 values.put(LauncherSettings.Favorites.CELLY, y); 299 values.put(LauncherSettings.Favorites.SPANX, 1); 300 values.put(LauncherSettings.Favorites.SPANY, 1); 301 302 if (type == APPLICATION || type == SHORTCUT) { 303 values.put(LauncherSettings.Favorites.ITEM_TYPE, type); 304 values.put(LauncherSettings.Favorites.INTENT, VALID_INTENT); 305 } else { 306 values.put(LauncherSettings.Favorites.ITEM_TYPE, 307 LauncherSettings.Favorites.ITEM_TYPE_FOLDER); 308 // Add folder items. 309 for (int i = 0; i < type; i++) { 310 addItem(APPLICATION, 0, id, 0, 0); 311 } 312 } 313 314 getMockContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values); 315 return id; 316 } 317 } 318