• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.L;
4 import static android.os.Build.VERSION_CODES.M;
5 import static android.os.Build.VERSION_CODES.N;
6 import static android.os.Build.VERSION_CODES.O;
7 import static android.os.Build.VERSION_CODES.O_MR1;
8 import static android.os.Build.VERSION_CODES.P;
9 import static android.os.Build.VERSION_CODES.R;
10 import static android.os.Build.VERSION_CODES.TIRAMISU;
11 import static com.google.common.truth.Truth.assertThat;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertThrows;
14 import static org.junit.Assert.assertTrue;
15 import static org.robolectric.Shadows.shadowOf;
16 import static org.robolectric.util.reflector.Reflector.reflector;
17 
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.pm.ActivityInfo;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.IncrementalStatesInfo;
23 import android.content.pm.LauncherActivityInfo;
24 import android.content.pm.LauncherActivityInfoInternal;
25 import android.content.pm.LauncherApps;
26 import android.content.pm.LauncherApps.ShortcutQuery;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.ResolveInfo;
29 import android.content.pm.ShortcutInfo;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.HandlerThread;
33 import android.os.Looper;
34 import android.os.Process;
35 import android.os.UserHandle;
36 import androidx.test.core.app.ApplicationProvider;
37 import androidx.test.ext.junit.runners.AndroidJUnit4;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.concurrent.CountDownLatch;
41 import java.util.concurrent.TimeUnit;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.robolectric.RuntimeEnvironment;
46 import org.robolectric.annotation.Config;
47 import org.robolectric.shadow.api.Shadow;
48 import org.robolectric.util.ReflectionHelpers;
49 import org.robolectric.util.ReflectionHelpers.ClassParameter;
50 import org.robolectric.util.reflector.Accessor;
51 import org.robolectric.util.reflector.ForType;
52 
53 /** Robolectric test for {@link ShadowLauncherApps}. */
54 @RunWith(AndroidJUnit4.class)
55 @Config(minSdk = O_MR1)
56 public class ShadowLauncherAppsTest {
57   private static final String TEST_PACKAGE_NAME = "test-package";
58   private static final String TEST_PACKAGE_NAME_2 = "test-package2";
59   private static final String TEST_PACKAGE_NAME_3 = "test-package3";
60   private static final UserHandle USER_HANDLE = UserHandle.CURRENT;
61   private LauncherApps launcherApps;
62 
63   private static class DefaultCallback extends LauncherApps.Callback {
64     @Override
onPackageRemoved(String packageName, UserHandle user)65     public void onPackageRemoved(String packageName, UserHandle user) {}
66 
67     @Override
onPackageAdded(String packageName, UserHandle user)68     public void onPackageAdded(String packageName, UserHandle user) {}
69 
70     @Override
onPackageChanged(String packageName, UserHandle user)71     public void onPackageChanged(String packageName, UserHandle user) {}
72 
73     @Override
onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)74     public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {}
75 
76     @Override
onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)77     public void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing) {}
78   }
79 
80   @Before
setup()81   public void setup() {
82     launcherApps =
83         (LauncherApps)
84             ApplicationProvider.getApplicationContext()
85                 .getSystemService(Context.LAUNCHER_APPS_SERVICE);
86   }
87 
88   @ForType(ShortcutInfo.class)
89   private interface ReflectorShortcutInfo {
90     @Accessor("mPackageName")
setPackage(String packageName)91     void setPackage(String packageName);
92   }
93 
shadowLooper(Looper looper)94   private ShadowLooper shadowLooper(Looper looper) {
95     return Shadow.extract(looper);
96   }
97 
98   @Test
testIsPackageEnabled()99   public void testIsPackageEnabled() {
100     assertThat(launcherApps.isPackageEnabled(TEST_PACKAGE_NAME, USER_HANDLE)).isFalse();
101 
102     shadowOf(launcherApps).addEnabledPackage(USER_HANDLE, TEST_PACKAGE_NAME);
103     assertThat(launcherApps.isPackageEnabled(TEST_PACKAGE_NAME, USER_HANDLE)).isTrue();
104   }
105 
106   @Test
107   @Config(minSdk = O)
getShortcutConfigActivityList_getsShortcutsForPackageName()108   public void getShortcutConfigActivityList_getsShortcutsForPackageName() {
109     LauncherActivityInfo launcherActivityInfo1 =
110         createLauncherActivityInfo(TEST_PACKAGE_NAME, USER_HANDLE);
111     LauncherActivityInfo launcherActivityInfo2 =
112         createLauncherActivityInfo(TEST_PACKAGE_NAME_2, USER_HANDLE);
113     shadowOf(launcherApps).addShortcutConfigActivity(USER_HANDLE, launcherActivityInfo1);
114     shadowOf(launcherApps).addShortcutConfigActivity(USER_HANDLE, launcherActivityInfo2);
115 
116     assertThat(launcherApps.getShortcutConfigActivityList(TEST_PACKAGE_NAME, USER_HANDLE))
117         .contains(launcherActivityInfo1);
118   }
119 
120   @Test
121   @Config(minSdk = O)
getShortcutConfigActivityList_getsShortcutsForUserHandle()122   public void getShortcutConfigActivityList_getsShortcutsForUserHandle() {
123     LauncherActivityInfo launcherActivityInfo1 =
124         createLauncherActivityInfo(TEST_PACKAGE_NAME, USER_HANDLE);
125     LauncherActivityInfo launcherActivityInfo2 =
126         createLauncherActivityInfo(TEST_PACKAGE_NAME, UserHandle.of(10));
127     shadowOf(launcherApps).addShortcutConfigActivity(USER_HANDLE, launcherActivityInfo1);
128     shadowOf(launcherApps).addShortcutConfigActivity(UserHandle.of(10), launcherActivityInfo2);
129 
130     assertThat(launcherApps.getShortcutConfigActivityList(TEST_PACKAGE_NAME, UserHandle.of(10)))
131         .contains(launcherActivityInfo2);
132   }
133 
134   @Test
135   @Config(minSdk = O)
getShortcutConfigActivityList_packageNull_getsShortcutFromAllPackagesForUser()136   public void getShortcutConfigActivityList_packageNull_getsShortcutFromAllPackagesForUser() {
137     LauncherActivityInfo launcherActivityInfo1 =
138         createLauncherActivityInfo(TEST_PACKAGE_NAME, USER_HANDLE);
139     LauncherActivityInfo launcherActivityInfo2 =
140         createLauncherActivityInfo(TEST_PACKAGE_NAME_2, USER_HANDLE);
141     LauncherActivityInfo launcherActivityInfo3 =
142         createLauncherActivityInfo(TEST_PACKAGE_NAME_3, UserHandle.of(10));
143     shadowOf(launcherApps).addShortcutConfigActivity(USER_HANDLE, launcherActivityInfo1);
144     shadowOf(launcherApps).addShortcutConfigActivity(USER_HANDLE, launcherActivityInfo2);
145     shadowOf(launcherApps).addShortcutConfigActivity(UserHandle.of(10), launcherActivityInfo3);
146 
147     assertThat(launcherApps.getShortcutConfigActivityList(null, USER_HANDLE))
148         .containsExactly(launcherActivityInfo1, launcherActivityInfo2);
149   }
150 
151   @Test
152   @Config(minSdk = L, maxSdk = M)
testGetActivityListPreN()153   public void testGetActivityListPreN() {
154     assertThat(launcherApps.getActivityList(TEST_PACKAGE_NAME, USER_HANDLE)).isEmpty();
155 
156     ResolveInfo info =
157         ShadowResolveInfo.newResolveInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, TEST_PACKAGE_NAME);
158     LauncherActivityInfo launcherActivityInfo =
159         ReflectionHelpers.callConstructor(
160             LauncherActivityInfo.class,
161             ClassParameter.from(Context.class, ApplicationProvider.getApplicationContext()),
162             ClassParameter.from(ResolveInfo.class, info),
163             ClassParameter.from(UserHandle.class, USER_HANDLE),
164             ClassParameter.from(long.class, System.currentTimeMillis()));
165     shadowOf(launcherApps).addActivity(USER_HANDLE, launcherActivityInfo);
166     assertThat(launcherApps.getActivityList(TEST_PACKAGE_NAME, USER_HANDLE))
167         .contains(launcherActivityInfo);
168   }
169 
170   @Test
171   @Config(minSdk = N)
testGetActivityList()172   public void testGetActivityList() {
173     assertThat(launcherApps.getActivityList(TEST_PACKAGE_NAME, USER_HANDLE)).isEmpty();
174 
175     LauncherActivityInfo launcherActivityInfo1 =
176         createLauncherActivityInfo(TEST_PACKAGE_NAME, USER_HANDLE);
177 
178     shadowOf(launcherApps).addActivity(USER_HANDLE, launcherActivityInfo1);
179     assertThat(launcherApps.getActivityList(TEST_PACKAGE_NAME, USER_HANDLE))
180         .contains(launcherActivityInfo1);
181   }
182 
183   @Test
184   @Config(minSdk = N)
testGetActivityList_packageNull_getsActivitiesFromAllPackagesForUser()185   public void testGetActivityList_packageNull_getsActivitiesFromAllPackagesForUser() {
186     LauncherActivityInfo launcherActivityInfo1 =
187         createLauncherActivityInfo(TEST_PACKAGE_NAME, USER_HANDLE);
188     LauncherActivityInfo launcherActivityInfo2 =
189         createLauncherActivityInfo(TEST_PACKAGE_NAME_2, USER_HANDLE);
190     LauncherActivityInfo launcherActivityInfo3 =
191         createLauncherActivityInfo(TEST_PACKAGE_NAME_3, UserHandle.of(10));
192     shadowOf(launcherApps).addActivity(USER_HANDLE, launcherActivityInfo1);
193     shadowOf(launcherApps).addActivity(USER_HANDLE, launcherActivityInfo2);
194     shadowOf(launcherApps).addActivity(UserHandle.of(10), launcherActivityInfo3);
195 
196     assertThat(launcherApps.getActivityList(null, USER_HANDLE))
197         .containsExactly(launcherActivityInfo1, launcherActivityInfo2);
198   }
199 
200   @Test
201   @Config(minSdk = L)
testIsActivityEnabled()202   public void testIsActivityEnabled() {
203     ComponentName c1 = new ComponentName(ApplicationProvider.getApplicationContext(), "Activity1");
204     ComponentName c2 = new ComponentName(ApplicationProvider.getApplicationContext(), "Activity2");
205     ComponentName c3 = new ComponentName("other", "Activity1");
206     assertThat(launcherApps.isActivityEnabled(c1, USER_HANDLE)).isFalse();
207 
208     shadowOf(launcherApps).setActivityEnabled(USER_HANDLE, c1);
209     assertThat(launcherApps.isActivityEnabled(c1, USER_HANDLE)).isTrue();
210     assertThat(launcherApps.isActivityEnabled(c2, USER_HANDLE)).isFalse();
211     assertThat(launcherApps.isActivityEnabled(c3, USER_HANDLE)).isFalse();
212   }
213 
214   @Test
215   @Config(minSdk = O)
testGetApplicationInfo_packageNotFound()216   public void testGetApplicationInfo_packageNotFound() throws Exception {
217     Throwable throwable =
218         assertThrows(
219             NameNotFoundException.class,
220             () -> launcherApps.getApplicationInfo(TEST_PACKAGE_NAME, 0, USER_HANDLE));
221 
222     assertThat(throwable)
223         .hasMessageThat()
224         .isEqualTo(
225             "Package " + TEST_PACKAGE_NAME + " not found for user " + USER_HANDLE.getIdentifier());
226   }
227 
228   @Test
testGetApplicationInfo_incorrectPackage()229   public void testGetApplicationInfo_incorrectPackage() throws Exception {
230     ApplicationInfo applicationInfo = new ApplicationInfo();
231     applicationInfo.name = "Test app";
232     shadowOf(launcherApps).addApplicationInfo(USER_HANDLE, TEST_PACKAGE_NAME_2, applicationInfo);
233 
234     Throwable throwable =
235         assertThrows(
236             NameNotFoundException.class,
237             () -> launcherApps.getApplicationInfo(TEST_PACKAGE_NAME, 0, USER_HANDLE));
238 
239     assertThat(throwable)
240         .hasMessageThat()
241         .isEqualTo(
242             "Package " + TEST_PACKAGE_NAME + " not found for user " + USER_HANDLE.getIdentifier());
243   }
244 
245   @Test
testGetApplicationInfo_findsApplicationInfo()246   public void testGetApplicationInfo_findsApplicationInfo() throws Exception {
247     ApplicationInfo applicationInfo = new ApplicationInfo();
248     applicationInfo.name = "Test app";
249     shadowOf(launcherApps).addApplicationInfo(USER_HANDLE, TEST_PACKAGE_NAME, applicationInfo);
250 
251     assertThat(launcherApps.getApplicationInfo(TEST_PACKAGE_NAME, 0, USER_HANDLE))
252         .isEqualTo(applicationInfo);
253   }
254 
255   @Test
testCallbackFiresWhenShortcutAddedOrRemoved()256   public void testCallbackFiresWhenShortcutAddedOrRemoved() throws Exception {
257     final Boolean[] wasCalled = new Boolean[] {false};
258     final CountDownLatch latch1 = new CountDownLatch(1);
259     final CountDownLatch latch2 = new CountDownLatch(2);
260 
261     final String packageName = ApplicationProvider.getApplicationContext().getPackageName();
262 
263     HandlerThread handlerThread = new HandlerThread("test");
264     handlerThread.start();
265     try {
266       LauncherApps.Callback callback =
267           new DefaultCallback() {
268             @Override
269             public void onShortcutsChanged(
270                 String packageName, List<ShortcutInfo> shortcuts, UserHandle user) {
271               assertEquals(shortcuts.get(0).getPackage(), packageName);
272               wasCalled[0] = true;
273               latch1.countDown();
274               latch2.countDown();
275             }
276           };
277       launcherApps.registerCallback(callback, new Handler(handlerThread.getLooper()));
278       shadowOf(launcherApps)
279           .addDynamicShortcut(
280               new ShortcutInfo.Builder(ApplicationProvider.getApplicationContext(), "ID").build());
281       shadowLooper(handlerThread.getLooper()).idle();
282 
283       latch1.await(1, TimeUnit.SECONDS);
284       assertTrue(wasCalled[0]);
285 
286       wasCalled[0] = false;
287       launcherApps.pinShortcuts(packageName, new ArrayList<>(), Process.myUserHandle());
288       shadowLooper(handlerThread.getLooper()).idle();
289       latch2.await(1, TimeUnit.SECONDS);
290       assertTrue(wasCalled[0]);
291     } finally {
292       handlerThread.quit();
293     }
294   }
295 
296   @Test
testGetShortcuts()297   public void testGetShortcuts() {
298     final ShortcutInfo shortcut1 =
299         new ShortcutInfo.Builder(ApplicationProvider.getApplicationContext(), "ID1").build();
300     final ShortcutInfo shortcut2 =
301         new ShortcutInfo.Builder(ApplicationProvider.getApplicationContext(), "ID2").build();
302 
303     shadowOf(launcherApps).addDynamicShortcut(shortcut1);
304     shadowOf(launcherApps).addDynamicShortcut(shortcut2);
305 
306     assertThat(getPinnedShortcuts(null, null)).containsExactly(shortcut1, shortcut2);
307   }
308 
309   @Test
testGetShortcutsWithFilters()310   public void testGetShortcutsWithFilters() {
311     String myPackage = ApplicationProvider.getApplicationContext().getPackageName();
312     String otherPackage = "other";
313     ComponentName c1 = new ComponentName(ApplicationProvider.getApplicationContext(), "Activity1");
314     ComponentName c2 = new ComponentName(ApplicationProvider.getApplicationContext(), "Activity2");
315     ComponentName c3 = new ComponentName(otherPackage, "Activity1");
316 
317     final ShortcutInfo shortcut1 =
318         new ShortcutInfo.Builder(ApplicationProvider.getApplicationContext(), "ID1")
319             .setActivity(c1)
320             .build();
321     final ShortcutInfo shortcut2 =
322         new ShortcutInfo.Builder(ApplicationProvider.getApplicationContext(), "ID2")
323             .setActivity(c2)
324             .build();
325     final ShortcutInfo shortcut3 =
326         new ShortcutInfo.Builder(ApplicationProvider.getApplicationContext(), "ID3")
327             .setActivity(c3)
328             .build();
329     reflector(ReflectorShortcutInfo.class, shortcut3).setPackage(otherPackage);
330 
331     shadowOf(launcherApps).addDynamicShortcut(shortcut1);
332     shadowOf(launcherApps).addDynamicShortcut(shortcut2);
333     shadowOf(launcherApps).addDynamicShortcut(shortcut3);
334 
335     assertThat(getPinnedShortcuts(otherPackage, null)).containsExactly(shortcut3);
336     assertThat(getPinnedShortcuts(myPackage, null)).containsExactly(shortcut1, shortcut2);
337     assertThat(getPinnedShortcuts(null, c1)).containsExactly(shortcut1);
338     assertThat(getPinnedShortcuts(null, c2)).containsExactly(shortcut2);
339     assertThat(getPinnedShortcuts(null, c3)).containsExactly(shortcut3);
340   }
341 
342   @Test
testHasShortcutHostPermission()343   public void testHasShortcutHostPermission() {
344     shadowOf(launcherApps).setHasShortcutHostPermission(true);
345     assertThat(launcherApps.hasShortcutHostPermission()).isTrue();
346   }
347 
348   @Test
testHasShortcutHostPermission_returnsFalseByDefault()349   public void testHasShortcutHostPermission_returnsFalseByDefault() {
350     assertThat(launcherApps.hasShortcutHostPermission()).isFalse();
351   }
352 
353   @Test
354   @Config(minSdk = P)
getSuspendedPackageLauncherExtras_returnsBundle()355   public void getSuspendedPackageLauncherExtras_returnsBundle() {
356     Bundle bundle = new Bundle();
357     bundle.putInt("suspended_app", 5);
358     shadowOf(launcherApps)
359         .addSuspendedPackageLauncherExtras(USER_HANDLE, TEST_PACKAGE_NAME_2, bundle);
360 
361     assertThat(launcherApps.getSuspendedPackageLauncherExtras(TEST_PACKAGE_NAME_2, USER_HANDLE))
362         .isEqualTo(bundle);
363   }
364 
365   @Test
366   @Config(minSdk = P)
getSuspendedPackageLauncherExtras_returnsEmptyBundle()367   public void getSuspendedPackageLauncherExtras_returnsEmptyBundle() {
368     Throwable throwable =
369         assertThrows(
370             NameNotFoundException.class,
371             () -> launcherApps.getSuspendedPackageLauncherExtras(TEST_PACKAGE_NAME, USER_HANDLE));
372 
373     assertThat(throwable)
374         .hasMessageThat()
375         .isEqualTo(
376             "Suspended package extras for  "
377                 + TEST_PACKAGE_NAME
378                 + " not found for user "
379                 + USER_HANDLE.getIdentifier());
380   }
381 
getPinnedShortcuts(String packageName, ComponentName activity)382   private List<ShortcutInfo> getPinnedShortcuts(String packageName, ComponentName activity) {
383     ShortcutQuery query = new ShortcutQuery();
384     query.setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_PINNED);
385     query.setPackage(packageName);
386     query.setActivity(activity);
387     return launcherApps.getShortcuts(query, Process.myUserHandle());
388   }
389 
createLauncherActivityInfo( String packageName, UserHandle userHandle)390   private LauncherActivityInfo createLauncherActivityInfo(
391       String packageName, UserHandle userHandle) {
392     ActivityInfo info = new ActivityInfo();
393     info.packageName = packageName;
394     info.name = packageName;
395     info.nonLocalizedLabel = packageName;
396     if (RuntimeEnvironment.getApiLevel() <= R) {
397       return ReflectionHelpers.callConstructor(
398           LauncherActivityInfo.class,
399           ClassParameter.from(Context.class, ApplicationProvider.getApplicationContext()),
400           ClassParameter.from(ActivityInfo.class, info),
401           ClassParameter.from(UserHandle.class, userHandle));
402     } else if (RuntimeEnvironment.getApiLevel() <= TIRAMISU) {
403       LauncherActivityInfoInternal launcherActivityInfoInternal =
404           ReflectionHelpers.callConstructor(
405               LauncherActivityInfoInternal.class,
406               ClassParameter.from(ActivityInfo.class, info),
407               ClassParameter.from(IncrementalStatesInfo.class, null));
408 
409       return ReflectionHelpers.callConstructor(
410           LauncherActivityInfo.class,
411           ClassParameter.from(Context.class, ApplicationProvider.getApplicationContext()),
412           ClassParameter.from(UserHandle.class, userHandle),
413           ClassParameter.from(LauncherActivityInfoInternal.class, launcherActivityInfoInternal));
414     } else {
415 
416       LauncherActivityInfoInternal launcherActivityInfoInternal =
417           ReflectionHelpers.callConstructor(
418               LauncherActivityInfoInternal.class,
419               ClassParameter.from(ActivityInfo.class, info),
420               ClassParameter.from(IncrementalStatesInfo.class, null),
421               ClassParameter.from(UserHandle.class, userHandle));
422 
423       return ReflectionHelpers.callConstructor(
424           LauncherActivityInfo.class,
425           ClassParameter.from(Context.class, ApplicationProvider.getApplicationContext()),
426           ClassParameter.from(LauncherActivityInfoInternal.class, launcherActivityInfoInternal));
427     }
428   }
429 }
430