1 /* 2 * Copyright (C) 2019 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 17 package com.android.launcher3.model; 18 19 import static androidx.test.InstrumentationRegistry.getContext; 20 21 import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID; 22 import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER; 23 import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_SOURCE; 24 import static com.android.launcher3.LauncherSettings.Favorites.CELLX; 25 import static com.android.launcher3.LauncherSettings.Favorites.CELLY; 26 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER; 27 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; 28 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; 29 import static com.android.launcher3.LauncherSettings.Favorites.ICON; 30 import static com.android.launcher3.LauncherSettings.Favorites.INTENT; 31 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE; 32 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 33 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; 34 import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS; 35 import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID; 36 import static com.android.launcher3.LauncherSettings.Favorites.RANK; 37 import static com.android.launcher3.LauncherSettings.Favorites.RESTORED; 38 import static com.android.launcher3.LauncherSettings.Favorites.SCREEN; 39 import static com.android.launcher3.LauncherSettings.Favorites.SPANX; 40 import static com.android.launcher3.LauncherSettings.Favorites.SPANY; 41 import static com.android.launcher3.LauncherSettings.Favorites.TITLE; 42 import static com.android.launcher3.LauncherSettings.Favorites._ID; 43 import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY; 44 45 import static junit.framework.Assert.assertEquals; 46 import static junit.framework.Assert.assertFalse; 47 import static junit.framework.Assert.assertNotNull; 48 import static junit.framework.Assert.assertNull; 49 import static junit.framework.Assert.assertTrue; 50 51 import android.content.ComponentName; 52 import android.content.Context; 53 import android.content.Intent; 54 import android.database.MatrixCursor; 55 import android.os.Process; 56 57 import androidx.test.ext.junit.runners.AndroidJUnit4; 58 import androidx.test.filters.SmallTest; 59 60 import com.android.launcher3.InvariantDeviceProfile; 61 import com.android.launcher3.LauncherAppState; 62 import com.android.launcher3.model.data.ItemInfo; 63 import com.android.launcher3.model.data.WorkspaceItemInfo; 64 import com.android.launcher3.util.Executors; 65 import com.android.launcher3.util.LauncherModelHelper; 66 import com.android.launcher3.util.PackageManagerHelper; 67 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.Test; 71 import org.junit.runner.RunWith; 72 73 /** 74 * Tests for {@link LoaderCursor} 75 */ 76 @SmallTest 77 @RunWith(AndroidJUnit4.class) 78 public class LoaderCursorTest { 79 80 private LauncherModelHelper mModelHelper; 81 private LauncherAppState mApp; 82 private PackageManagerHelper mPmHelper; 83 84 private MatrixCursor mCursor; 85 private InvariantDeviceProfile mIDP; 86 private Context mContext; 87 88 private LoaderCursor mLoaderCursor; 89 90 @Before setup()91 public void setup() { 92 mModelHelper = new LauncherModelHelper(); 93 mContext = mModelHelper.sandboxContext; 94 mIDP = InvariantDeviceProfile.INSTANCE.get(mContext); 95 mApp = LauncherAppState.getInstance(mContext); 96 mPmHelper = PackageManagerHelper.INSTANCE.get(mContext); 97 98 mCursor = new MatrixCursor(new String[] { 99 ICON, TITLE, _ID, CONTAINER, ITEM_TYPE, 100 PROFILE_ID, SCREEN, CELLX, CELLY, RESTORED, 101 INTENT, APPWIDGET_ID, APPWIDGET_PROVIDER, 102 SPANX, SPANY, RANK, OPTIONS, APPWIDGET_SOURCE 103 }); 104 105 UserManagerState ums = new UserManagerState(); 106 mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, mPmHelper, null); 107 ums.allUsers.put(0, Process.myUserHandle()); 108 } 109 110 @After tearDown()111 public void tearDown() { 112 mCursor.close(); 113 mModelHelper.destroy(); 114 } 115 initCursor(int itemType, String title)116 private void initCursor(int itemType, String title) { 117 mCursor.newRow() 118 .add(_ID, 1) 119 .add(PROFILE_ID, 0) 120 .add(ITEM_TYPE, itemType) 121 .add(TITLE, title) 122 .add(CONTAINER, CONTAINER_DESKTOP); 123 } 124 125 @Test getAppShortcutInfo_dontAllowMissing_invalidComponent()126 public void getAppShortcutInfo_dontAllowMissing_invalidComponent() { 127 initCursor(ITEM_TYPE_APPLICATION, ""); 128 assertTrue(mLoaderCursor.moveToNext()); 129 ComponentName cn = new ComponentName(mContext.getPackageName(), "placeholder-do"); 130 assertNull(mLoaderCursor.getAppShortcutInfo( 131 new Intent().setComponent(cn), false /* allowMissingTarget */, true)); 132 } 133 134 @Test getAppShortcutInfo_dontAllowMissing_validComponent()135 public void getAppShortcutInfo_dontAllowMissing_validComponent() throws Exception { 136 ComponentName cn = new ComponentName(getContext(), TEST_ACTIVITY); 137 initCursor(ITEM_TYPE_APPLICATION, ""); 138 assertTrue(mLoaderCursor.moveToNext()); 139 140 WorkspaceItemInfo info = Executors.MODEL_EXECUTOR.submit(() -> 141 mLoaderCursor.getAppShortcutInfo( 142 new Intent().setComponent(cn), false /* allowMissingTarget */, true)) 143 .get(); 144 assertNotNull(info); 145 assertTrue(PackageManagerHelper.isLauncherAppTarget(info.getIntent())); 146 } 147 148 @Test getAppShortcutInfo_allowMissing_invalidComponent()149 public void getAppShortcutInfo_allowMissing_invalidComponent() throws Exception { 150 initCursor(ITEM_TYPE_APPLICATION, ""); 151 assertTrue(mLoaderCursor.moveToNext()); 152 153 ComponentName cn = new ComponentName(mContext.getPackageName(), "placeholder-do"); 154 WorkspaceItemInfo info = Executors.MODEL_EXECUTOR.submit(() -> 155 mLoaderCursor.getAppShortcutInfo( 156 new Intent().setComponent(cn), true /* allowMissingTarget */, true)) 157 .get(); 158 assertNotNull(info); 159 assertTrue(PackageManagerHelper.isLauncherAppTarget(info.getIntent())); 160 } 161 162 @Test loadSimpleShortcut()163 public void loadSimpleShortcut() { 164 initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut"); 165 assertTrue(mLoaderCursor.moveToNext()); 166 167 WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem(); 168 assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user)); 169 assertEquals("my-shortcut", info.title); 170 assertEquals(ITEM_TYPE_DEEP_SHORTCUT, info.itemType); 171 } 172 173 @Test checkItemPlacement_outsideBounds()174 public void checkItemPlacement_outsideBounds() { 175 mIDP.numRows = 4; 176 mIDP.numColumns = 4; 177 mIDP.numDatabaseHotseatIcons = 3; 178 179 // Item outside screen bounds are not placed 180 assertFalse(mLoaderCursor.checkItemPlacement( 181 newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1), true)); 182 } 183 184 @Test checkItemPlacement_overlappingItems()185 public void checkItemPlacement_overlappingItems() { 186 mIDP.numRows = 4; 187 mIDP.numColumns = 4; 188 mIDP.numDatabaseHotseatIcons = 3; 189 190 // Overlapping mItems are not placed 191 assertTrue(mLoaderCursor.checkItemPlacement( 192 newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), true)); 193 assertFalse(mLoaderCursor.checkItemPlacement( 194 newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), true)); 195 196 assertTrue(mLoaderCursor.checkItemPlacement( 197 newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), true)); 198 assertFalse(mLoaderCursor.checkItemPlacement( 199 newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), true)); 200 201 assertTrue(mLoaderCursor.checkItemPlacement( 202 newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1), true)); 203 assertTrue(mLoaderCursor.checkItemPlacement( 204 newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1), true)); 205 206 assertFalse(mLoaderCursor.checkItemPlacement( 207 newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1), true)); 208 } 209 210 @Test checkItemPlacement_hotseat()211 public void checkItemPlacement_hotseat() { 212 mIDP.numRows = 4; 213 mIDP.numColumns = 4; 214 mIDP.numDatabaseHotseatIcons = 3; 215 216 // Hotseat mItems are only placed based on screenId 217 assertTrue(mLoaderCursor.checkItemPlacement( 218 newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1), true)); 219 assertTrue(mLoaderCursor.checkItemPlacement( 220 newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2), true)); 221 222 assertFalse(mLoaderCursor.checkItemPlacement( 223 newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3), true)); 224 } 225 newItemInfo(int cellX, int cellY, int spanX, int spanY, int container, int screenId)226 private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY, 227 int container, int screenId) { 228 ItemInfo info = new ItemInfo(); 229 info.cellX = cellX; 230 info.cellY = cellY; 231 info.spanX = spanX; 232 info.spanY = spanY; 233 info.container = container; 234 info.screenId = screenId; 235 return info; 236 } 237 } 238