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.app.AppGlobals;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ILauncherApps;
24 import android.content.pm.IOnAppsChangedListener;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.graphics.Rect;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.util.Log;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
41 * Class for retrieving a list of launchable activities for the current user and any associated
42 * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
43 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
44 * for package changes here.
46 * To watch for managed profiles being added or removed, register for the following broadcasts:
47 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
49 * You can retrieve the list of profiles associated with this user with
50 * {@link UserManager#getUserProfiles()}.
52 public class LauncherApps {
54 static final String TAG = "LauncherApps";
55 static final boolean DEBUG = false;
57 private Context mContext;
58 private ILauncherApps mService;
59 private PackageManager mPm;
61 private List<CallbackMessageHandler> mCallbacks
62 = new ArrayList<CallbackMessageHandler>();
65 * Callbacks for package changes to this and related managed profiles.
67 public static abstract class Callback {
69 * Indicates that a package was removed from the specified profile.
71 * If a package is removed while being updated onPackageChanged will be
74 * @param packageName The name of the package that was removed.
75 * @param user The UserHandle of the profile that generated the change.
77 abstract public void onPackageRemoved(String packageName, UserHandle user);
80 * Indicates that a package was added to the specified profile.
82 * If a package is added while being updated then onPackageChanged will be
85 * @param packageName The name of the package that was added.
86 * @param user The UserHandle of the profile that generated the change.
88 abstract public void onPackageAdded(String packageName, UserHandle user);
91 * Indicates that a package was modified in the specified profile.
92 * This can happen, for example, when the package is updated or when
93 * one or more components are enabled or disabled.
95 * @param packageName The name of the package that has changed.
96 * @param user The UserHandle of the profile that generated the change.
98 abstract public void onPackageChanged(String packageName, UserHandle user);
101 * Indicates that one or more packages have become available. For
102 * example, this can happen when a removable storage card has
105 * @param packageNames The names of the packages that have become
107 * @param user The UserHandle of the profile that generated the change.
108 * @param replacing Indicates whether these packages are replacing
111 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
115 * Indicates that one or more packages have become unavailable. For
116 * example, this can happen when a removable storage card has been
119 * @param packageNames The names of the packages that have become
121 * @param user The UserHandle of the profile that generated the change.
122 * @param replacing Indicates whether the packages are about to be
123 * replaced with new versions.
125 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
130 public LauncherApps(Context context, ILauncherApps service) {
133 mPm = context.getPackageManager();
137 * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
138 * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
140 * @param packageName The specific package to query. If null, it checks all installed packages
142 * @param user The UserHandle of the profile.
143 * @return List of launchable activities. Can be an empty list but will not be null.
145 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
146 List<ResolveInfo> activities = null;
148 activities = mService.getLauncherActivities(packageName, user);
149 } catch (RemoteException re) {
151 if (activities == null) {
152 return Collections.EMPTY_LIST;
154 ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
155 final int count = activities.size();
156 for (int i = 0; i < count; i++) {
157 ResolveInfo ri = activities.get(i);
158 long firstInstallTime = 0;
160 firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
161 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
162 } catch (NameNotFoundException nnfe) {
163 // Sorry, can't find package
165 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
168 Log.v(TAG, "Returning activity for profile " + user + " : "
169 + lai.getComponentName());
176 static ComponentName getComponentName(ResolveInfo ri) {
177 return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
181 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
184 * @param intent The intent to find a match for.
185 * @param user The profile to look in for a match.
186 * @return An activity info object if there is a match.
188 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
190 ResolveInfo ri = mService.resolveActivity(intent, user);
192 long firstInstallTime = 0;
194 firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
195 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
196 } catch (NameNotFoundException nnfe) {
197 // Sorry, can't find package
199 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
203 } catch (RemoteException re) {
204 throw new RuntimeException("Failed to call LauncherAppsService");
210 * Starts a Main activity in the specified profile.
212 * @param component The ComponentName of the activity to launch
213 * @param user The UserHandle of the profile
214 * @param sourceBounds The Rect containing the source bounds of the clicked icon
215 * @param opts Options to pass to startActivity
217 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
220 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
223 mService.startActivityAsUser(component, sourceBounds, opts, user);
224 } catch (RemoteException re) {
230 * Starts the settings activity to show the application details for a
231 * package in the specified profile.
233 * @param component The ComponentName of the package to launch settings for.
234 * @param user The UserHandle of the profile
235 * @param sourceBounds The Rect containing the source bounds of the clicked icon
236 * @param opts Options to pass to startActivity
238 public void startAppDetailsActivity(ComponentName component, UserHandle user,
239 Rect sourceBounds, Bundle opts) {
241 mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
242 } catch (RemoteException re) {
248 * Checks if the package is installed and enabled for a profile.
250 * @param packageName The package to check.
251 * @param user The UserHandle of the profile.
253 * @return true if the package exists and is enabled.
255 public boolean isPackageEnabled(String packageName, UserHandle user) {
257 return mService.isPackageEnabled(packageName, user);
258 } catch (RemoteException re) {
259 throw new RuntimeException("Failed to call LauncherAppsService");
264 * Checks if the activity exists and it enabled for a profile.
266 * @param component The activity to check.
267 * @param user The UserHandle of the profile.
269 * @return true if the activity exists and is enabled.
271 public boolean isActivityEnabled(ComponentName component, UserHandle user) {
273 return mService.isActivityEnabled(component, user);
274 } catch (RemoteException re) {
275 throw new RuntimeException("Failed to call LauncherAppsService");
281 * Registers a callback for changes to packages in current and managed profiles.
283 * @param callback The callback to register.
285 public void registerCallback(Callback callback) {
286 registerCallback(callback, null);
290 * Registers a callback for changes to packages in current and managed profiles.
292 * @param callback The callback to register.
293 * @param handler that should be used to post callbacks on, may be null.
295 public void registerCallback(Callback callback, Handler handler) {
296 synchronized (this) {
297 if (callback != null && !mCallbacks.contains(callback)) {
298 boolean addedFirstCallback = mCallbacks.size() == 0;
299 addCallbackLocked(callback, handler);
300 if (addedFirstCallback) {
302 mService.addOnAppsChangedListener(mAppsChangedListener);
303 } catch (RemoteException re) {
311 * Unregisters a callback that was previously registered.
313 * @param callback The callback to unregister.
314 * @see #registerCallback(Callback)
316 public void unregisterCallback(Callback callback) {
317 synchronized (this) {
318 removeCallbackLocked(callback);
319 if (mCallbacks.size() == 0) {
321 mService.removeOnAppsChangedListener(mAppsChangedListener);
322 } catch (RemoteException re) {
328 private void removeCallbackLocked(Callback callback) {
329 if (callback == null) {
330 throw new IllegalArgumentException("Callback cannot be null");
332 final int size = mCallbacks.size();
333 for (int i = 0; i < size; ++i) {
334 if (mCallbacks.get(i).mCallback == callback) {
335 mCallbacks.remove(i);
341 private void addCallbackLocked(Callback callback, Handler handler) {
342 // Remove if already present.
343 removeCallbackLocked(callback);
344 if (handler == null) {
345 handler = new Handler();
347 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
348 mCallbacks.add(toAdd);
351 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
354 public void onPackageRemoved(UserHandle user, String packageName)
355 throws RemoteException {
357 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
359 synchronized (LauncherApps.this) {
360 for (CallbackMessageHandler callback : mCallbacks) {
361 callback.postOnPackageRemoved(packageName, user);
367 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
369 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
371 synchronized (LauncherApps.this) {
372 for (CallbackMessageHandler callback : mCallbacks) {
373 callback.postOnPackageChanged(packageName, user);
379 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
381 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
383 synchronized (LauncherApps.this) {
384 for (CallbackMessageHandler callback : mCallbacks) {
385 callback.postOnPackageAdded(packageName, user);
391 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
392 throws RemoteException {
394 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
396 synchronized (LauncherApps.this) {
397 for (CallbackMessageHandler callback : mCallbacks) {
398 callback.postOnPackagesAvailable(packageNames, user, replacing);
404 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
405 throws RemoteException {
407 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
409 synchronized (LauncherApps.this) {
410 for (CallbackMessageHandler callback : mCallbacks) {
411 callback.postOnPackagesUnavailable(packageNames, user, replacing);
417 private static class CallbackMessageHandler extends Handler {
418 private static final int MSG_ADDED = 1;
419 private static final int MSG_REMOVED = 2;
420 private static final int MSG_CHANGED = 3;
421 private static final int MSG_AVAILABLE = 4;
422 private static final int MSG_UNAVAILABLE = 5;
424 private LauncherApps.Callback mCallback;
426 private static class CallbackInfo {
427 String[] packageNames;
433 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
434 super(looper, null, true);
435 mCallback = callback;
439 public void handleMessage(Message msg) {
440 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
443 CallbackInfo info = (CallbackInfo) msg.obj;
446 mCallback.onPackageAdded(info.packageName, info.user);
449 mCallback.onPackageRemoved(info.packageName, info.user);
452 mCallback.onPackageChanged(info.packageName, info.user);
455 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
457 case MSG_UNAVAILABLE:
458 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
463 public void postOnPackageAdded(String packageName, UserHandle user) {
464 CallbackInfo info = new CallbackInfo();
465 info.packageName = packageName;
467 obtainMessage(MSG_ADDED, info).sendToTarget();
470 public void postOnPackageRemoved(String packageName, UserHandle user) {
471 CallbackInfo info = new CallbackInfo();
472 info.packageName = packageName;
474 obtainMessage(MSG_REMOVED, info).sendToTarget();
477 public void postOnPackageChanged(String packageName, UserHandle user) {
478 CallbackInfo info = new CallbackInfo();
479 info.packageName = packageName;
481 obtainMessage(MSG_CHANGED, info).sendToTarget();
484 public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
486 CallbackInfo info = new CallbackInfo();
487 info.packageNames = packageNames;
488 info.replacing = replacing;
490 obtainMessage(MSG_AVAILABLE, info).sendToTarget();
493 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
495 CallbackInfo info = new CallbackInfo();
496 info.packageNames = packageNames;
497 info.replacing = replacing;
499 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
504 * TODO Remove after 2014-09-22
507 public void addCallback(Callback callback) {
508 registerCallback(callback);
512 * TODO Remove after 2014-09-22
515 public void removeCallback(Callback callback) {
516 unregisterCallback(callback);