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