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