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