blob: c81517a348f27cf5f8ed3165856822abd1fcb8e7 [file] [log] [blame]
Amith Yamasani4f582632014-02-19 14:31:52 -08001/*
2 * Copyright (C) 2014 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
17package android.content.pm;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ILauncherApps;
23import android.content.pm.IOnAppsChangedListener;
Amith Yamasanie781c812014-05-28 15:28:18 -070024import android.content.pm.PackageManager.NameNotFoundException;
Amith Yamasani4f582632014-02-19 14:31:52 -080025import android.graphics.Rect;
26import android.os.Bundle;
Kenny Guyb42c89b2014-07-28 19:20:07 +010027import android.os.Handler;
28import android.os.Looper;
29import android.os.Message;
Amith Yamasani4f582632014-02-19 14:31:52 -080030import android.os.RemoteException;
31import android.os.UserHandle;
Amith Yamasanie781c812014-05-28 15:28:18 -070032import android.os.UserManager;
Amith Yamasani4f582632014-02-19 14:31:52 -080033import android.util.Log;
34
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.List;
38
39/**
40 * Class for retrieving a list of launchable activities for the current user and any associated
41 * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
42 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
43 * for package changes here.
Amith Yamasanie781c812014-05-28 15:28:18 -070044 * <p>
45 * To watch for managed profiles being added or removed, register for the following broadcasts:
46 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
47 * <p>
48 * You can retrieve the list of profiles associated with this user with
49 * {@link UserManager#getUserProfiles()}.
Amith Yamasani4f582632014-02-19 14:31:52 -080050 */
51public class LauncherApps {
52
53 static final String TAG = "LauncherApps";
54 static final boolean DEBUG = false;
55
56 private Context mContext;
57 private ILauncherApps mService;
Amith Yamasanie781c812014-05-28 15:28:18 -070058 private PackageManager mPm;
Amith Yamasani4f582632014-02-19 14:31:52 -080059
Kenny Guyb42c89b2014-07-28 19:20:07 +010060 private List<CallbackMessageHandler> mCallbacks
61 = new ArrayList<CallbackMessageHandler>();
Kenny Guyc01545372014-06-16 14:17:26 +010062
63 /**
64 * Callbacks for package changes to this and related managed profiles.
65 */
Kenny Guyf939dba2014-08-15 15:32:34 +010066 public static abstract class Callback {
Kenny Guyc01545372014-06-16 14:17:26 +010067 /**
68 * Indicates that a package was removed from the specified profile.
69 *
Kenny Guyb42c89b2014-07-28 19:20:07 +010070 * If a package is removed while being updated onPackageChanged will be
71 * called instead.
72 *
Kenny Guyc01545372014-06-16 14:17:26 +010073 * @param packageName The name of the package that was removed.
74 * @param user The UserHandle of the profile that generated the change.
75 */
76 abstract public void onPackageRemoved(String packageName, UserHandle user);
77
78 /**
79 * Indicates that a package was added to the specified profile.
80 *
Kenny Guyb42c89b2014-07-28 19:20:07 +010081 * If a package is added while being updated then onPackageChanged will be
82 * called instead.
83 *
Kenny Guyc01545372014-06-16 14:17:26 +010084 * @param packageName The name of the package that was added.
85 * @param user The UserHandle of the profile that generated the change.
86 */
87 abstract public void onPackageAdded(String packageName, UserHandle user);
88
89 /**
90 * Indicates that a package was modified in the specified profile.
Kenny Guyb42c89b2014-07-28 19:20:07 +010091 * This can happen, for example, when the package is updated or when
92 * one or more components are enabled or disabled.
Kenny Guyc01545372014-06-16 14:17:26 +010093 *
94 * @param packageName The name of the package that has changed.
95 * @param user The UserHandle of the profile that generated the change.
96 */
97 abstract public void onPackageChanged(String packageName, UserHandle user);
98
99 /**
100 * Indicates that one or more packages have become available. For
101 * example, this can happen when a removable storage card has
102 * reappeared.
103 *
104 * @param packageNames The names of the packages that have become
105 * available.
106 * @param user The UserHandle of the profile that generated the change.
107 * @param replacing Indicates whether these packages are replacing
108 * existing ones.
109 */
110 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
111 boolean replacing);
112
113 /**
114 * Indicates that one or more packages have become unavailable. For
115 * example, this can happen when a removable storage card has been
116 * removed.
117 *
118 * @param packageNames The names of the packages that have become
119 * unavailable.
120 * @param user The UserHandle of the profile that generated the change.
121 * @param replacing Indicates whether the packages are about to be
122 * replaced with new versions.
123 */
124 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
125 boolean replacing);
126 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800127
Amith Yamasani4f582632014-02-19 14:31:52 -0800128 /** @hide */
129 public LauncherApps(Context context, ILauncherApps service) {
130 mContext = context;
131 mService = service;
Amith Yamasanie781c812014-05-28 15:28:18 -0700132 mPm = context.getPackageManager();
Amith Yamasani4f582632014-02-19 14:31:52 -0800133 }
134
135 /**
136 * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
137 * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
138 *
139 * @param packageName The specific package to query. If null, it checks all installed packages
140 * in the profile.
141 * @param user The UserHandle of the profile.
142 * @return List of launchable activities. Can be an empty list but will not be null.
143 */
144 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
145 List<ResolveInfo> activities = null;
146 try {
147 activities = mService.getLauncherActivities(packageName, user);
148 } catch (RemoteException re) {
149 }
150 if (activities == null) {
151 return Collections.EMPTY_LIST;
152 }
153 ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
154 final int count = activities.size();
155 for (int i = 0; i < count; i++) {
156 ResolveInfo ri = activities.get(i);
Amith Yamasanie781c812014-05-28 15:28:18 -0700157 long firstInstallTime = 0;
158 try {
159 firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
160 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
161 } catch (NameNotFoundException nnfe) {
162 // Sorry, can't find package
163 }
164 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
165 firstInstallTime);
Amith Yamasani4f582632014-02-19 14:31:52 -0800166 if (DEBUG) {
167 Log.v(TAG, "Returning activity for profile " + user + " : "
168 + lai.getComponentName());
169 }
170 lais.add(lai);
171 }
172 return lais;
173 }
174
175 static ComponentName getComponentName(ResolveInfo ri) {
176 return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
177 }
178
179 /**
180 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
181 * returns null.
182 *
183 * @param intent The intent to find a match for.
184 * @param user The profile to look in for a match.
185 * @return An activity info object if there is a match.
186 */
187 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
188 try {
189 ResolveInfo ri = mService.resolveActivity(intent, user);
190 if (ri != null) {
Amith Yamasanie781c812014-05-28 15:28:18 -0700191 long firstInstallTime = 0;
192 try {
193 firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
194 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
195 } catch (NameNotFoundException nnfe) {
196 // Sorry, can't find package
197 }
198 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
199 firstInstallTime);
Amith Yamasani4f582632014-02-19 14:31:52 -0800200 return info;
201 }
202 } catch (RemoteException re) {
Amith Yamasaniad97b8b2014-07-29 16:25:17 -0700203 throw new RuntimeException("Failed to call LauncherAppsService");
Amith Yamasani4f582632014-02-19 14:31:52 -0800204 }
205 return null;
206 }
207
208 /**
Kenny Guyf939dba2014-08-15 15:32:34 +0100209 * Starts a Main activity in the specified profile.
Amith Yamasani4f582632014-02-19 14:31:52 -0800210 *
211 * @param component The ComponentName of the activity to launch
Amith Yamasanie781c812014-05-28 15:28:18 -0700212 * @param user The UserHandle of the profile
213 * @param sourceBounds The Rect containing the source bounds of the clicked icon
214 * @param opts Options to pass to startActivity
215 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100216 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
Amith Yamasanie781c812014-05-28 15:28:18 -0700217 Bundle opts) {
Amith Yamasani5abdbb62014-04-08 17:23:46 -0700218 if (DEBUG) {
Kenny Guyf939dba2014-08-15 15:32:34 +0100219 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
Amith Yamasani5abdbb62014-04-08 17:23:46 -0700220 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800221 try {
222 mService.startActivityAsUser(component, sourceBounds, opts, user);
223 } catch (RemoteException re) {
224 // Oops!
225 }
226 }
227
228 /**
Kenny Guy466d2032014-07-23 12:23:35 +0100229 * Starts the settings activity to show the application details for a
230 * package in the specified profile.
231 *
232 * @param component The ComponentName of the package to launch settings for.
233 * @param user The UserHandle of the profile
234 * @param sourceBounds The Rect containing the source bounds of the clicked icon
235 * @param opts Options to pass to startActivity
236 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100237 public void startAppDetailsActivity(ComponentName component, UserHandle user,
Kenny Guy466d2032014-07-23 12:23:35 +0100238 Rect sourceBounds, Bundle opts) {
239 try {
240 mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
241 } catch (RemoteException re) {
242 // Oops!
243 }
244 }
245
246 /**
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100247 * Checks if the package is installed and enabled for a profile.
248 *
249 * @param packageName The package to check.
250 * @param user The UserHandle of the profile.
251 *
252 * @return true if the package exists and is enabled.
253 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100254 public boolean isPackageEnabled(String packageName, UserHandle user) {
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100255 try {
256 return mService.isPackageEnabled(packageName, user);
257 } catch (RemoteException re) {
Amith Yamasaniad97b8b2014-07-29 16:25:17 -0700258 throw new RuntimeException("Failed to call LauncherAppsService");
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100259 }
260 }
261
262 /**
263 * Checks if the activity exists and it enabled for a profile.
264 *
265 * @param component The activity to check.
266 * @param user The UserHandle of the profile.
267 *
268 * @return true if the activity exists and is enabled.
269 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100270 public boolean isActivityEnabled(ComponentName component, UserHandle user) {
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100271 try {
272 return mService.isActivityEnabled(component, user);
273 } catch (RemoteException re) {
Amith Yamasaniad97b8b2014-07-29 16:25:17 -0700274 throw new RuntimeException("Failed to call LauncherAppsService");
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100275 }
276 }
277
278
279 /**
Kenny Guy10a574f2014-08-26 16:17:58 +0100280 * Registers a callback for changes to packages in current and managed profiles.
Kenny Guyc01545372014-06-16 14:17:26 +0100281 *
Kenny Guy10a574f2014-08-26 16:17:58 +0100282 * @param callback The callback to register.
Kenny Guyc01545372014-06-16 14:17:26 +0100283 */
Kenny Guy10a574f2014-08-26 16:17:58 +0100284 public void registerCallback(Callback callback) {
285 registerCallback(callback, null);
Kenny Guyb42c89b2014-07-28 19:20:07 +0100286 }
287
288 /**
Kenny Guy10a574f2014-08-26 16:17:58 +0100289 * Registers a callback for changes to packages in current and managed profiles.
Kenny Guyb42c89b2014-07-28 19:20:07 +0100290 *
Kenny Guy10a574f2014-08-26 16:17:58 +0100291 * @param callback The callback to register.
Kenny Guyb42c89b2014-07-28 19:20:07 +0100292 * @param handler that should be used to post callbacks on, may be null.
293 */
Kenny Guy10a574f2014-08-26 16:17:58 +0100294 public void registerCallback(Callback callback, Handler handler) {
Kenny Guyc01545372014-06-16 14:17:26 +0100295 synchronized (this) {
296 if (callback != null && !mCallbacks.contains(callback)) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100297 boolean addedFirstCallback = mCallbacks.size() == 0;
298 addCallbackLocked(callback, handler);
299 if (addedFirstCallback) {
Kenny Guyc01545372014-06-16 14:17:26 +0100300 try {
301 mService.addOnAppsChangedListener(mAppsChangedListener);
302 } catch (RemoteException re) {
303 }
304 }
305 }
306 }
307 }
308
309 /**
Kenny Guy10a574f2014-08-26 16:17:58 +0100310 * Unregisters a callback that was previously registered.
Kenny Guyc01545372014-06-16 14:17:26 +0100311 *
Kenny Guy10a574f2014-08-26 16:17:58 +0100312 * @param callback The callback to unregister.
313 * @see #registerCallback(Callback)
Kenny Guyc01545372014-06-16 14:17:26 +0100314 */
Kenny Guy10a574f2014-08-26 16:17:58 +0100315 public void unregisterCallback(Callback callback) {
Kenny Guyc01545372014-06-16 14:17:26 +0100316 synchronized (this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100317 removeCallbackLocked(callback);
Kenny Guy44b6dee2014-07-10 18:10:14 +0100318 if (mCallbacks.size() == 0) {
Amith Yamasanie781c812014-05-28 15:28:18 -0700319 try {
320 mService.removeOnAppsChangedListener(mAppsChangedListener);
321 } catch (RemoteException re) {
322 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800323 }
324 }
325 }
326
Kenny Guyf939dba2014-08-15 15:32:34 +0100327 private void removeCallbackLocked(Callback callback) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100328 if (callback == null) {
329 throw new IllegalArgumentException("Callback cannot be null");
330 }
331 final int size = mCallbacks.size();
332 for (int i = 0; i < size; ++i) {
333 if (mCallbacks.get(i).mCallback == callback) {
334 mCallbacks.remove(i);
335 return;
336 }
337 }
338 }
339
Kenny Guyf939dba2014-08-15 15:32:34 +0100340 private void addCallbackLocked(Callback callback, Handler handler) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100341 // Remove if already present.
342 removeCallbackLocked(callback);
343 if (handler == null) {
344 handler = new Handler();
345 }
346 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
347 mCallbacks.add(toAdd);
348 }
349
Amith Yamasani4f582632014-02-19 14:31:52 -0800350 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
351
352 @Override
Kenny Guyb42c89b2014-07-28 19:20:07 +0100353 public void onPackageRemoved(UserHandle user, String packageName)
354 throws RemoteException {
Amith Yamasani4f582632014-02-19 14:31:52 -0800355 if (DEBUG) {
356 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
357 }
358 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100359 for (CallbackMessageHandler callback : mCallbacks) {
360 callback.postOnPackageRemoved(packageName, user);
Kenny Guyc01545372014-06-16 14:17:26 +0100361 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800362 }
363 }
364
365 @Override
366 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
367 if (DEBUG) {
368 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
369 }
370 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100371 for (CallbackMessageHandler callback : mCallbacks) {
372 callback.postOnPackageChanged(packageName, user);
Kenny Guyc01545372014-06-16 14:17:26 +0100373 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800374 }
375 }
376
377 @Override
378 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
379 if (DEBUG) {
380 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
381 }
382 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100383 for (CallbackMessageHandler callback : mCallbacks) {
384 callback.postOnPackageAdded(packageName, user);
Kenny Guyc01545372014-06-16 14:17:26 +0100385 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800386 }
387 }
388
389 @Override
390 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
391 throws RemoteException {
392 if (DEBUG) {
393 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
394 }
395 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100396 for (CallbackMessageHandler callback : mCallbacks) {
397 callback.postOnPackagesAvailable(packageNames, user, replacing);
Kenny Guyc01545372014-06-16 14:17:26 +0100398 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800399 }
400 }
401
402 @Override
403 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
404 throws RemoteException {
405 if (DEBUG) {
406 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
407 }
408 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100409 for (CallbackMessageHandler callback : mCallbacks) {
410 callback.postOnPackagesUnavailable(packageNames, user, replacing);
Kenny Guyc01545372014-06-16 14:17:26 +0100411 }
412 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800413 }
414 };
Kenny Guyb42c89b2014-07-28 19:20:07 +0100415
416 private static class CallbackMessageHandler extends Handler {
417 private static final int MSG_ADDED = 1;
418 private static final int MSG_REMOVED = 2;
419 private static final int MSG_CHANGED = 3;
420 private static final int MSG_AVAILABLE = 4;
421 private static final int MSG_UNAVAILABLE = 5;
422
Kenny Guyf939dba2014-08-15 15:32:34 +0100423 private LauncherApps.Callback mCallback;
Kenny Guyb42c89b2014-07-28 19:20:07 +0100424
425 private static class CallbackInfo {
426 String[] packageNames;
427 String packageName;
428 boolean replacing;
429 UserHandle user;
430 }
431
Kenny Guyf939dba2014-08-15 15:32:34 +0100432 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100433 super(looper, null, true);
434 mCallback = callback;
435 }
436
437 @Override
438 public void handleMessage(Message msg) {
439 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
440 return;
441 }
442 CallbackInfo info = (CallbackInfo) msg.obj;
443 switch (msg.what) {
444 case MSG_ADDED:
445 mCallback.onPackageAdded(info.packageName, info.user);
446 break;
447 case MSG_REMOVED:
448 mCallback.onPackageRemoved(info.packageName, info.user);
449 break;
450 case MSG_CHANGED:
451 mCallback.onPackageChanged(info.packageName, info.user);
452 break;
453 case MSG_AVAILABLE:
454 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
455 break;
456 case MSG_UNAVAILABLE:
457 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
458 break;
459 }
460 }
461
462 public void postOnPackageAdded(String packageName, UserHandle user) {
463 CallbackInfo info = new CallbackInfo();
464 info.packageName = packageName;
465 info.user = user;
466 obtainMessage(MSG_ADDED, info).sendToTarget();
467 }
468
469 public void postOnPackageRemoved(String packageName, UserHandle user) {
470 CallbackInfo info = new CallbackInfo();
471 info.packageName = packageName;
472 info.user = user;
473 obtainMessage(MSG_REMOVED, info).sendToTarget();
474 }
475
476 public void postOnPackageChanged(String packageName, UserHandle user) {
477 CallbackInfo info = new CallbackInfo();
478 info.packageName = packageName;
479 info.user = user;
480 obtainMessage(MSG_CHANGED, info).sendToTarget();
481 }
482
483 public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
484 boolean replacing) {
485 CallbackInfo info = new CallbackInfo();
486 info.packageNames = packageNames;
487 info.replacing = replacing;
488 info.user = user;
489 obtainMessage(MSG_AVAILABLE, info).sendToTarget();
490 }
491
492 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
493 boolean replacing) {
494 CallbackInfo info = new CallbackInfo();
495 info.packageNames = packageNames;
496 info.replacing = replacing;
497 info.user = user;
498 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
499 }
500 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800501}