2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.content.pm;
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.TestApi;
23 import android.content.ActivityNotFoundException;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager.ApplicationInfoFlags;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.res.Resources;
30 import android.graphics.Bitmap;
31 import android.graphics.BitmapFactory;
32 import android.graphics.Rect;
33 import android.graphics.drawable.BitmapDrawable;
34 import android.graphics.drawable.Drawable;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.ParcelFileDescriptor;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.os.UserHandle;
43 import android.os.UserManager;
44 import android.util.DisplayMetrics;
45 import android.util.Log;
47 import java.io.IOException;
48 import java.lang.annotation.Retention;
49 import java.lang.annotation.RetentionPolicy;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collections;
53 import java.util.List;
56 * Class for retrieving a list of launchable activities for the current user and any associated
57 * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
58 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
59 * for package changes here.
61 * To watch for managed profiles being added or removed, register for the following broadcasts:
62 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
64 * You can retrieve the list of profiles associated with this user with
65 * {@link UserManager#getUserProfiles()}.
67 public class LauncherApps {
69 static final String TAG = "LauncherApps";
70 static final boolean DEBUG = false;
72 private Context mContext;
73 private ILauncherApps mService;
74 private PackageManager mPm;
76 private List<CallbackMessageHandler> mCallbacks
77 = new ArrayList<CallbackMessageHandler>();
80 * Callbacks for package changes to this and related managed profiles.
82 public static abstract class Callback {
84 * Indicates that a package was removed from the specified profile.
86 * If a package is removed while being updated onPackageChanged will be
89 * @param packageName The name of the package that was removed.
90 * @param user The UserHandle of the profile that generated the change.
92 abstract public void onPackageRemoved(String packageName, UserHandle user);
95 * Indicates that a package was added to the specified profile.
97 * If a package is added while being updated then onPackageChanged will be
100 * @param packageName The name of the package that was added.
101 * @param user The UserHandle of the profile that generated the change.
103 abstract public void onPackageAdded(String packageName, UserHandle user);
106 * Indicates that a package was modified in the specified profile.
107 * This can happen, for example, when the package is updated or when
108 * one or more components are enabled or disabled.
110 * @param packageName The name of the package that has changed.
111 * @param user The UserHandle of the profile that generated the change.
113 abstract public void onPackageChanged(String packageName, UserHandle user);
116 * Indicates that one or more packages have become available. For
117 * example, this can happen when a removable storage card has
120 * @param packageNames The names of the packages that have become
122 * @param user The UserHandle of the profile that generated the change.
123 * @param replacing Indicates whether these packages are replacing
126 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
130 * Indicates that one or more packages have become unavailable. For
131 * example, this can happen when a removable storage card has been
134 * @param packageNames The names of the packages that have become
136 * @param user The UserHandle of the profile that generated the change.
137 * @param replacing Indicates whether the packages are about to be
138 * replaced with new versions.
140 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
144 * Indicates that one or more packages have been suspended. For
145 * example, this can happen when a Device Administrator suspends
148 * @param packageNames The names of the packages that have just been
150 * @param user The UserHandle of the profile that generated the change.
152 public void onPackagesSuspended(String[] packageNames, UserHandle user) {
156 * Indicates that one or more packages have been unsuspended. For
157 * example, this can happen when a Device Administrator unsuspends
160 * @param packageNames The names of the packages that have just been
162 * @param user The UserHandle of the profile that generated the change.
164 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
168 * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest)
169 * have been added, updated or removed.
171 * <p>Only the applications that are allowed to access the shortcut information,
172 * as defined in {@link #hasShortcutHostPermission()}, will receive it.
174 * @param packageName The name of the package that has the shortcuts.
175 * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned).
176 * Only "key" information will be provided, as defined in
177 * {@link ShortcutInfo#hasKeyFieldsOnly()}.
178 * @param user The UserHandle of the profile that generated the change.
180 * @see ShortcutManager
182 public void onShortcutsChanged(@NonNull String packageName,
183 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
188 * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
190 public static class ShortcutQuery {
192 * Include dynamic shortcuts in the result.
194 public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
196 /** @hide kept for unit tests */
198 public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC;
201 * Include pinned shortcuts in the result.
203 public static final int FLAG_MATCH_PINNED = 1 << 1;
205 /** @hide kept for unit tests */
207 public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED;
210 * Include manifest shortcuts in the result.
212 public static final int FLAG_MATCH_MANIFEST = 1 << 3;
214 /** @hide kept for unit tests */
216 public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
219 public static final int FLAG_MATCH_ALL_KINDS =
220 FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
222 /** @hide kept for unit tests */
224 public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS;
227 * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to
228 * see which fields fields "key".
229 * This allows quicker access to shortcut information in order to
230 * determine whether the caller's in-memory cache needs to be updated.
232 * <p>Typically, launcher applications cache all or most shortcut information
233 * in memory in order to show shortcuts without a delay.
235 * When a given launcher application wants to update its cache, such as when its process
236 * restarts, it can fetch shortcut information with this flag.
237 * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each
238 * shortcut, fetching a shortcut's non-key information only if that shortcut has been
241 * @see ShortcutManager
243 public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
251 FLAG_GET_KEY_FIELDS_ONLY,
253 @Retention(RetentionPolicy.SOURCE)
254 public @interface QueryFlags {}
262 List<String> mShortcutIds;
265 ComponentName mActivity;
270 public ShortcutQuery() {
274 * If non-zero, returns only shortcuts that have been added or updated
275 * since the given timestamp, expressed in milliseconds since the Epoch—see
276 * {@link System#currentTimeMillis()}.
278 public ShortcutQuery setChangedSince(long changedSince) {
279 mChangedSince = changedSince;
284 * If non-null, returns only shortcuts from the package.
286 public ShortcutQuery setPackage(@Nullable String packageName) {
287 mPackage = packageName;
292 * If non-null, return only the specified shortcuts by ID. When setting this field,
293 * a package name must also be set with {@link #setPackage}.
295 public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
296 mShortcutIds = shortcutIds;
301 * If non-null, returns only shortcuts associated with the activity; i.e.
302 * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
303 * to {@code activity}.
305 public ShortcutQuery setActivity(@Nullable ComponentName activity) {
306 mActivity = activity;
311 * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise,
312 * no shortcuts will be returned.
315 * <li>{@link #FLAG_MATCH_DYNAMIC}
316 * <li>{@link #FLAG_MATCH_PINNED}
317 * <li>{@link #FLAG_MATCH_MANIFEST}
318 * <li>{@link #FLAG_GET_KEY_FIELDS_ONLY}
321 public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
322 mQueryFlags = queryFlags;
328 public LauncherApps(Context context, ILauncherApps service) {
331 mPm = context.getPackageManager();
336 public LauncherApps(Context context) {
337 this(context, ILauncherApps.Stub.asInterface(
338 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
342 * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
343 * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
345 * @param packageName The specific package to query. If null, it checks all installed packages
347 * @param user The UserHandle of the profile.
348 * @return List of launchable activities. Can be an empty list but will not be null.
350 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
351 ParceledListSlice<ResolveInfo> activities = null;
353 activities = mService.getLauncherActivities(packageName, user);
354 } catch (RemoteException re) {
355 throw re.rethrowFromSystemServer();
357 if (activities == null) {
358 return Collections.EMPTY_LIST;
360 ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
361 for (ResolveInfo ri : activities.getList()) {
362 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
364 Log.v(TAG, "Returning activity for profile " + user + " : "
365 + lai.getComponentName());
373 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
376 * @param intent The intent to find a match for.
377 * @param user The profile to look in for a match.
378 * @return An activity info object if there is a match.
380 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
382 ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user);
384 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
387 } catch (RemoteException re) {
388 throw re.rethrowFromSystemServer();
394 * Starts a Main activity in the specified profile.
396 * @param component The ComponentName of the activity to launch
397 * @param user The UserHandle of the profile
398 * @param sourceBounds The Rect containing the source bounds of the clicked icon
399 * @param opts Options to pass to startActivity
401 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
404 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
407 mService.startActivityAsUser(component, sourceBounds, opts, user);
408 } catch (RemoteException re) {
409 throw re.rethrowFromSystemServer();
414 * Starts the settings activity to show the application details for a
415 * package in the specified profile.
417 * @param component The ComponentName of the package to launch settings for.
418 * @param user The UserHandle of the profile
419 * @param sourceBounds The Rect containing the source bounds of the clicked icon
420 * @param opts Options to pass to startActivity
422 public void startAppDetailsActivity(ComponentName component, UserHandle user,
423 Rect sourceBounds, Bundle opts) {
425 mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
426 } catch (RemoteException re) {
427 throw re.rethrowFromSystemServer();
432 * Checks if the package is installed and enabled for a profile.
434 * @param packageName The package to check.
435 * @param user The UserHandle of the profile.
437 * @return true if the package exists and is enabled.
439 public boolean isPackageEnabled(String packageName, UserHandle user) {
441 return mService.isPackageEnabled(packageName, user);
442 } catch (RemoteException re) {
443 throw re.rethrowFromSystemServer();
448 * Retrieve all of the information we know about a particular package / application.
450 * @param packageName The package of the application
451 * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
452 * @param user The UserHandle of the profile.
454 * @return An {@link ApplicationInfo} containing information about the package or
455 * null of the package isn't found.
458 public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
461 return mService.getApplicationInfo(packageName, flags, user);
462 } catch (RemoteException re) {
463 throw re.rethrowFromSystemServer();
468 * Checks if the activity exists and it enabled for a profile.
470 * @param component The activity to check.
471 * @param user The UserHandle of the profile.
473 * @return true if the activity exists and is enabled.
475 public boolean isActivityEnabled(ComponentName component, UserHandle user) {
477 return mService.isActivityEnabled(component, user);
478 } catch (RemoteException re) {
479 throw re.rethrowFromSystemServer();
484 * Returns whether the caller can access the shortcut information.
486 * <p>Only the default launcher can access the shortcut information.
488 * <p>Note when this method returns {@code false}, it may be a temporary situation because
489 * the user is trying a new launcher application. The user may decide to change the default
490 * launcher back to the calling application again, so even if a launcher application loses
491 * this permission, it does <b>not</b> have to purge pinned shortcut information.
492 * If the calling launcher application contains pinned shortcuts, they will still work,
493 * even though the caller no longer has the shortcut host permission.
495 * <p>Returns {@code false} when the user is locked.
497 * @see ShortcutManager
499 public boolean hasShortcutHostPermission() {
501 return mService.hasShortcutHostPermission(mContext.getPackageName());
502 } catch (RemoteException re) {
503 throw re.rethrowFromSystemServer();
508 * Returns {@link ShortcutInfo}s that match {@code query}.
510 * <p>Callers must be allowed to access the shortcut information, as defined in {@link
511 * #hasShortcutHostPermission()}.
513 * <p>Returns am empty list when the user is locked, or when the {@code user} user
514 * is locked or not running.
516 * @param query result includes shortcuts matching this query.
517 * @param user The UserHandle of the profile.
519 * @return the IDs of {@link ShortcutInfo}s that match the query.
521 * @see ShortcutManager
524 public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
525 @NonNull UserHandle user) {
527 return mService.getShortcuts(mContext.getPackageName(),
528 query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
529 query.mQueryFlags, user)
531 } catch (RemoteException e) {
532 throw e.rethrowFromSystemServer();
537 * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests.
541 public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
542 @NonNull List<String> ids, @NonNull UserHandle user) {
543 final ShortcutQuery q = new ShortcutQuery();
544 q.setPackage(packageName);
545 q.setShortcutIds(ids);
546 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
547 return getShortcuts(q, user);
551 * Pin shortcuts on a package.
553 * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
554 * However, different launchers may have different set of pinned shortcuts.
556 * <p>The calling launcher application must be allowed to access the shortcut information,
557 * as defined in {@link #hasShortcutHostPermission()}.
559 * <p>Call will be ignored when the user is locked, or when the {@code user} user
560 * is locked or not running.
562 * @param packageName The target package name.
563 * @param shortcutIds The IDs of the shortcut to be pinned.
564 * @param user The UserHandle of the profile.
566 * @see ShortcutManager
568 public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
569 @NonNull UserHandle user) {
571 mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
572 } catch (RemoteException e) {
573 throw e.rethrowFromSystemServer();
578 * @hide kept for testing.
581 public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
582 return shortcut.getIconResourceId();
586 * @hide kept for testing.
589 public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
590 @NonNull UserHandle user) {
591 final ShortcutQuery q = new ShortcutQuery();
592 q.setPackage(packageName);
593 q.setShortcutIds(Arrays.asList(shortcutId));
594 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
595 final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
597 return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
601 * @hide internal/unit tests only
603 public ParcelFileDescriptor getShortcutIconFd(
604 @NonNull ShortcutInfo shortcut) {
605 return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
606 shortcut.getUserId());
610 * @hide internal/unit tests only
612 public ParcelFileDescriptor getShortcutIconFd(
613 @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
614 return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
617 private ParcelFileDescriptor getShortcutIconFd(
618 @NonNull String packageName, @NonNull String shortcutId, int userId) {
620 return mService.getShortcutIconFd(mContext.getPackageName(),
621 packageName, shortcutId, userId);
622 } catch (RemoteException e) {
623 throw e.rethrowFromSystemServer();
628 * Returns the icon for this shortcut, without any badging for the profile.
630 * <p>The calling launcher application must be allowed to access the shortcut information,
631 * as defined in {@link #hasShortcutHostPermission()}.
633 * <p>Returns {@code null} when the user is locked, or when the user owning the shortcut
634 * is locked or not running.
636 * @param density The preferred density of the icon, zero for default density. Use
637 * density DPI values from {@link DisplayMetrics}.
639 * @return The drawable associated with the shortcut.
641 * @see ShortcutManager
642 * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
643 * @see DisplayMetrics
645 public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
646 if (shortcut.hasIconFile()) {
647 final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
652 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
653 return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp);
657 } catch (IOException ignore) {
660 } else if (shortcut.hasIconResource()) {
662 final int resId = shortcut.getIconResourceId();
664 return null; // Shouldn't happen but just in case.
666 final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(),
667 /* flags =*/ 0, shortcut.getUserHandle());
668 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
669 return res.getDrawableForDensity(resId, density);
670 } catch (NameNotFoundException | Resources.NotFoundException e) {
674 return null; // Has no icon.
679 * Returns the shortcut icon with badging appropriate for the profile.
681 * <p>The calling launcher application must be allowed to access the shortcut information,
682 * as defined in {@link #hasShortcutHostPermission()}.
684 * <p>Returns {@code 0} when the user is locked, or when the user owning the shortcut
685 * is locked or not running.
687 * @param density Optional density for the icon, or 0 to use the default density. Use
688 * @return A badged icon for the shortcut.
690 * @see ShortcutManager
691 * @see #getShortcutIconDrawable(ShortcutInfo, int)
692 * @see DisplayMetrics
694 public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
695 final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
697 return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
698 originalIcon, shortcut.getUserHandle());
704 * <p>The calling launcher application must be allowed to access the shortcut information,
705 * as defined in {@link #hasShortcutHostPermission()}.
707 * <p>Throws {@link android.content.ActivityNotFoundException}
708 * when the user is locked, or when the {@code user} user
709 * is locked or not running.
711 * @param packageName The target shortcut package name.
712 * @param shortcutId The target shortcut ID.
713 * @param sourceBounds The Rect containing the source bounds of the clicked icon.
714 * @param startActivityOptions Options to pass to startActivity.
715 * @param user The UserHandle of the profile.
717 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
718 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
720 public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
721 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
722 @NonNull UserHandle user) {
723 startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
724 user.getIdentifier());
728 * Launches a shortcut.
730 * <p>The calling launcher application must be allowed to access the shortcut information,
731 * as defined in {@link #hasShortcutHostPermission()}.
733 * <p>Throws {@link android.content.ActivityNotFoundException}
734 * when the user is locked, or when the user owning the shortcut
735 * is locked or not running.
737 * @param shortcut The target shortcut.
738 * @param sourceBounds The Rect containing the source bounds of the clicked icon.
739 * @param startActivityOptions Options to pass to startActivity.
741 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
742 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
744 public void startShortcut(@NonNull ShortcutInfo shortcut,
745 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
746 startShortcut(shortcut.getPackage(), shortcut.getId(),
747 sourceBounds, startActivityOptions,
748 shortcut.getUserId());
751 private void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
752 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
755 final boolean success =
756 mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
757 sourceBounds, startActivityOptions, userId);
759 throw new ActivityNotFoundException("Shortcut could not be started");
761 } catch (RemoteException e) {
762 throw e.rethrowFromSystemServer();
767 * Registers a callback for changes to packages in current and managed profiles.
769 * @param callback The callback to register.
771 public void registerCallback(Callback callback) {
772 registerCallback(callback, null);
776 * Registers a callback for changes to packages in current and managed profiles.
778 * @param callback The callback to register.
779 * @param handler that should be used to post callbacks on, may be null.
781 public void registerCallback(Callback callback, Handler handler) {
782 synchronized (this) {
783 if (callback != null && findCallbackLocked(callback) < 0) {
784 boolean addedFirstCallback = mCallbacks.size() == 0;
785 addCallbackLocked(callback, handler);
786 if (addedFirstCallback) {
788 mService.addOnAppsChangedListener(mContext.getPackageName(),
789 mAppsChangedListener);
790 } catch (RemoteException re) {
791 throw re.rethrowFromSystemServer();
799 * Unregisters a callback that was previously registered.
801 * @param callback The callback to unregister.
802 * @see #registerCallback(Callback)
804 public void unregisterCallback(Callback callback) {
805 synchronized (this) {
806 removeCallbackLocked(callback);
807 if (mCallbacks.size() == 0) {
809 mService.removeOnAppsChangedListener(mAppsChangedListener);
810 } catch (RemoteException re) {
811 throw re.rethrowFromSystemServer();
817 /** @return position in mCallbacks for callback or -1 if not present. */
818 private int findCallbackLocked(Callback callback) {
819 if (callback == null) {
820 throw new IllegalArgumentException("Callback cannot be null");
822 final int size = mCallbacks.size();
823 for (int i = 0; i < size; ++i) {
824 if (mCallbacks.get(i).mCallback == callback) {
831 private void removeCallbackLocked(Callback callback) {
832 int pos = findCallbackLocked(callback);
834 mCallbacks.remove(pos);
838 private void addCallbackLocked(Callback callback, Handler handler) {
839 // Remove if already present.
840 removeCallbackLocked(callback);
841 if (handler == null) {
842 handler = new Handler();
844 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
845 mCallbacks.add(toAdd);
848 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
851 public void onPackageRemoved(UserHandle user, String packageName)
852 throws RemoteException {
854 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
856 synchronized (LauncherApps.this) {
857 for (CallbackMessageHandler callback : mCallbacks) {
858 callback.postOnPackageRemoved(packageName, user);
864 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
866 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
868 synchronized (LauncherApps.this) {
869 for (CallbackMessageHandler callback : mCallbacks) {
870 callback.postOnPackageChanged(packageName, user);
876 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
878 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
880 synchronized (LauncherApps.this) {
881 for (CallbackMessageHandler callback : mCallbacks) {
882 callback.postOnPackageAdded(packageName, user);
888 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
889 throws RemoteException {
891 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
893 synchronized (LauncherApps.this) {
894 for (CallbackMessageHandler callback : mCallbacks) {
895 callback.postOnPackagesAvailable(packageNames, user, replacing);
901 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
902 throws RemoteException {
904 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
906 synchronized (LauncherApps.this) {
907 for (CallbackMessageHandler callback : mCallbacks) {
908 callback.postOnPackagesUnavailable(packageNames, user, replacing);
914 public void onPackagesSuspended(UserHandle user, String[] packageNames)
915 throws RemoteException {
917 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
919 synchronized (LauncherApps.this) {
920 for (CallbackMessageHandler callback : mCallbacks) {
921 callback.postOnPackagesSuspended(packageNames, user);
927 public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
928 throws RemoteException {
930 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
932 synchronized (LauncherApps.this) {
933 for (CallbackMessageHandler callback : mCallbacks) {
934 callback.postOnPackagesUnsuspended(packageNames, user);
940 public void onShortcutChanged(UserHandle user, String packageName,
941 ParceledListSlice shortcuts) {
943 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
945 final List<ShortcutInfo> list = shortcuts.getList();
946 synchronized (LauncherApps.this) {
947 for (CallbackMessageHandler callback : mCallbacks) {
948 callback.postOnShortcutChanged(packageName, user, list);
954 private static class CallbackMessageHandler extends Handler {
955 private static final int MSG_ADDED = 1;
956 private static final int MSG_REMOVED = 2;
957 private static final int MSG_CHANGED = 3;
958 private static final int MSG_AVAILABLE = 4;
959 private static final int MSG_UNAVAILABLE = 5;
960 private static final int MSG_SUSPENDED = 6;
961 private static final int MSG_UNSUSPENDED = 7;
962 private static final int MSG_SHORTCUT_CHANGED = 8;
964 private LauncherApps.Callback mCallback;
966 private static class CallbackInfo {
967 String[] packageNames;
971 List<ShortcutInfo> shortcuts;
974 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
975 super(looper, null, true);
976 mCallback = callback;
980 public void handleMessage(Message msg) {
981 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
984 CallbackInfo info = (CallbackInfo) msg.obj;
987 mCallback.onPackageAdded(info.packageName, info.user);
990 mCallback.onPackageRemoved(info.packageName, info.user);
993 mCallback.onPackageChanged(info.packageName, info.user);
996 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
998 case MSG_UNAVAILABLE:
999 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
1002 mCallback.onPackagesSuspended(info.packageNames, info.user);
1004 case MSG_UNSUSPENDED:
1005 mCallback.onPackagesUnsuspended(info.packageNames, info.user);
1007 case MSG_SHORTCUT_CHANGED:
1008 mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
1013 public void postOnPackageAdded(String packageName, UserHandle user) {
1014 CallbackInfo info = new CallbackInfo();
1015 info.packageName = packageName;
1017 obtainMessage(MSG_ADDED, info).sendToTarget();
1020 public void postOnPackageRemoved(String packageName, UserHandle user) {
1021 CallbackInfo info = new CallbackInfo();
1022 info.packageName = packageName;
1024 obtainMessage(MSG_REMOVED, info).sendToTarget();
1027 public void postOnPackageChanged(String packageName, UserHandle user) {
1028 CallbackInfo info = new CallbackInfo();
1029 info.packageName = packageName;
1031 obtainMessage(MSG_CHANGED, info).sendToTarget();
1034 public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
1035 boolean replacing) {
1036 CallbackInfo info = new CallbackInfo();
1037 info.packageNames = packageNames;
1038 info.replacing = replacing;
1040 obtainMessage(MSG_AVAILABLE, info).sendToTarget();
1043 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
1044 boolean replacing) {
1045 CallbackInfo info = new CallbackInfo();
1046 info.packageNames = packageNames;
1047 info.replacing = replacing;
1049 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
1052 public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
1053 CallbackInfo info = new CallbackInfo();
1054 info.packageNames = packageNames;
1056 obtainMessage(MSG_SUSPENDED, info).sendToTarget();
1059 public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) {
1060 CallbackInfo info = new CallbackInfo();
1061 info.packageNames = packageNames;
1063 obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
1066 public void postOnShortcutChanged(String packageName, UserHandle user,
1067 List<ShortcutInfo> shortcuts) {
1068 CallbackInfo info = new CallbackInfo();
1069 info.packageName = packageName;
1071 info.shortcuts = shortcuts;
1072 obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();