1 package com.android.launcher3.model; 2 3 import android.content.ComponentName; 4 import android.content.ContentProviderOperation; 5 import android.content.ContentValues; 6 import android.content.Context; 7 import android.content.Intent; 8 import android.graphics.Rect; 9 import android.net.Uri; 10 import android.util.Pair; 11 12 import com.android.launcher3.ItemInfo; 13 import com.android.launcher3.LauncherProvider; 14 import com.android.launcher3.LauncherSettings; 15 import com.android.launcher3.ShortcutInfo; 16 import com.android.launcher3.util.GridOccupancy; 17 import com.android.launcher3.util.LongArrayMap; 18 import com.android.launcher3.util.Provider; 19 20 import org.mockito.ArgumentCaptor; 21 22 import java.util.ArrayList; 23 import java.util.List; 24 25 import static org.mockito.Matchers.isNull; 26 import static org.mockito.Mockito.any; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 /** 31 * Tests for {@link AddWorkspaceItemsTask} 32 */ 33 public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { 34 35 private final ComponentName mComponent1 = new ComponentName("a", "b"); 36 private final ComponentName mComponent2 = new ComponentName("b", "b"); 37 38 private ArrayList<Long> existingScreens; 39 private ArrayList<Long> newScreens; 40 private LongArrayMap<GridOccupancy> screenOccupancy; 41 42 @Override 43 protected void setUp() throws Exception { 44 super.setUp(); 45 existingScreens = new ArrayList<>(); 46 screenOccupancy = new LongArrayMap<>(); 47 newScreens = new ArrayList<>(); 48 49 idp.numColumns = 5; 50 idp.numRows = 5; 51 } 52 53 private AddWorkspaceItemsTask newTask(ItemInfo... items) { 54 List<Pair<ItemInfo, Object>> list = new ArrayList<>(); 55 for (ItemInfo item : items) { 56 list.add(Pair.create(item, null)); 57 } 58 return new AddWorkspaceItemsTask(Provider.of(list)) { 59 60 @Override 61 protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { } 62 }; 63 } 64 65 public void testFindSpaceForItem_prefers_second() { 66 // First screen has only one hole of size 1 67 int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); 68 69 // Second screen has 2 holes of sizes 3x2 and 2x3 70 setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5)); 71 72 Pair<Long, int[]> spaceFound = newTask() 73 .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 1, 1); 74 assertEquals(2L, (long) spaceFound.first); 75 assertTrue(screenOccupancy.get(spaceFound.first) 76 .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 1, 1)); 77 78 // Find a larger space 79 spaceFound = newTask() 80 .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 2, 3); 81 assertEquals(2L, (long) spaceFound.first); 82 assertTrue(screenOccupancy.get(spaceFound.first) 83 .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 2, 3)); 84 } 85 86 public void testFindSpaceForItem_adds_new_screen() throws Exception { 87 // First screen has 2 holes of sizes 3x2 and 2x3 88 setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5)); 89 commitScreensToDb(); 90 91 when(appState.getContext()).thenReturn(getMockContext()); 92 93 ArrayList<Long> oldScreens = new ArrayList<>(existingScreens); 94 Pair<Long, int[]> spaceFound = newTask() 95 .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 3, 3); 96 assertFalse(oldScreens.contains(spaceFound.first)); 97 assertTrue(newScreens.contains(spaceFound.first)); 98 } 99 100 public void testAddItem_existing_item_ignored() throws Exception { 101 ShortcutInfo info = new ShortcutInfo(); 102 info.intent = new Intent().setComponent(mComponent1); 103 104 // Setup a screen with a hole 105 setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); 106 commitScreensToDb(); 107 108 when(appState.getContext()).thenReturn(getMockContext()); 109 110 // Nothing was added 111 assertTrue(executeTaskForTest(newTask(info)).isEmpty()); 112 } 113 114 public void testAddItem_some_items_added() throws Exception { 115 ShortcutInfo info = new ShortcutInfo(); 116 info.intent = new Intent().setComponent(mComponent1); 117 118 ShortcutInfo info2 = new ShortcutInfo(); 119 info2.intent = new Intent().setComponent(mComponent2); 120 121 // Setup a screen with a hole 122 setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); 123 commitScreensToDb(); 124 125 when(appState.getContext()).thenReturn(getMockContext()); 126 127 executeTaskForTest(newTask(info, info2)).get(0).run(); 128 ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class); 129 ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class); 130 131 // only info2 should be added because info was already added to the workspace 132 // in setupWorkspaceWithHoles() 133 verify(callbacks).bindAppsAdded(any(ArrayList.class), notAnimated.capture(), 134 animated.capture()); 135 assertTrue(notAnimated.getValue().isEmpty()); 136 137 assertEquals(1, animated.getValue().size()); 138 assertTrue(animated.getValue().contains(info2)); 139 } 140 141 private int setupWorkspaceWithHoles(int startId, long screenId, Rect... holes) { 142 GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows); 143 occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true); 144 for (Rect r : holes) { 145 occupancy.markCells(r, false); 146 } 147 148 existingScreens.add(screenId); 149 screenOccupancy.append(screenId, occupancy); 150 151 for (int x = 0; x < idp.numColumns; x++) { 152 for (int y = 0; y < idp.numRows; y++) { 153 if (!occupancy.cells[x][y]) { 154 continue; 155 } 156 157 ShortcutInfo info = new ShortcutInfo(); 158 info.intent = new Intent().setComponent(mComponent1); 159 info.id = startId++; 160 info.screenId = screenId; 161 info.cellX = x; 162 info.cellY = y; 163 info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; 164 bgDataModel.addItem(targetContext, info, false); 165 } 166 } 167 return startId; 168 } 169 170 private void commitScreensToDb() throws Exception { 171 LauncherSettings.Settings.call(getMockContentResolver(), 172 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); 173 174 Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; 175 ArrayList<ContentProviderOperation> ops = new ArrayList<>(); 176 // Clear the table 177 ops.add(ContentProviderOperation.newDelete(uri).build()); 178 int count = existingScreens.size(); 179 for (int i = 0; i < count; i++) { 180 ContentValues v = new ContentValues(); 181 long screenId = existingScreens.get(i); 182 v.put(LauncherSettings.WorkspaceScreens._ID, screenId); 183 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); 184 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build()); 185 } 186 getMockContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops); 187 } 188 } 189