blob: 5c21c8ee15699da5aef0e9125bdfdd748350bcfc [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) {
Sunny Goyal5892c9a2015-04-20 12:44:08 -0700149 throw new RuntimeException("Failed to call LauncherAppsService");
Amith Yamasani4f582632014-02-19 14:31:52 -0800150 }
151 if (activities == null) {
152 return Collections.EMPTY_LIST;
153 }
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);
Amith Yamasanie781c812014-05-28 15:28:18 -0700158 long firstInstallTime = 0;
159 try {
160 firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
161 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
162 } catch (NameNotFoundException nnfe) {
163 // Sorry, can't find package
164 }
165 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
166 firstInstallTime);
Amith Yamasani4f582632014-02-19 14:31:52 -0800167 if (DEBUG) {
168 Log.v(TAG, "Returning activity for profile " + user + " : "
169 + lai.getComponentName());
170 }
171 lais.add(lai);
172 }
173 return lais;
174 }
175
176 static ComponentName getComponentName(ResolveInfo ri) {
177 return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
178 }
179
180 /**
181 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
182 * returns null.
183 *
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.
187 */
188 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
189 try {
190 ResolveInfo ri = mService.resolveActivity(intent, user);
191 if (ri != null) {
Amith Yamasanie781c812014-05-28 15:28:18 -0700192 long firstInstallTime = 0;
193 try {
194 firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
195 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
196 } catch (NameNotFoundException nnfe) {
197 // Sorry, can't find package
198 }
199 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
200 firstInstallTime);
Amith Yamasani4f582632014-02-19 14:31:52 -0800201 return info;
202 }
203 } catch (RemoteException re) {
Amith Yamasaniad97b8b2014-07-29 16:25:17 -0700204 throw new RuntimeException("Failed to call LauncherAppsService");
Amith Yamasani4f582632014-02-19 14:31:52 -0800205 }
206 return null;
207 }
208
209 /**
Kenny Guyf939dba2014-08-15 15:32:34 +0100210 * Starts a Main activity in the specified profile.
Amith Yamasani4f582632014-02-19 14:31:52 -0800211 *
212 * @param component The ComponentName of the activity to launch
Amith Yamasanie781c812014-05-28 15:28:18 -0700213 * @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
216 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100217 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
Amith Yamasanie781c812014-05-28 15:28:18 -0700218 Bundle opts) {
Amith Yamasani5abdbb62014-04-08 17:23:46 -0700219 if (DEBUG) {
Kenny Guyf939dba2014-08-15 15:32:34 +0100220 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
Amith Yamasani5abdbb62014-04-08 17:23:46 -0700221 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800222 try {
223 mService.startActivityAsUser(component, sourceBounds, opts, user);
224 } catch (RemoteException re) {
225 // Oops!
226 }
227 }
228
229 /**
Kenny Guy466d2032014-07-23 12:23:35 +0100230 * Starts the settings activity to show the application details for a
231 * package in the specified profile.
232 *
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
237 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100238 public void startAppDetailsActivity(ComponentName component, UserHandle user,
Kenny Guy466d2032014-07-23 12:23:35 +0100239 Rect sourceBounds, Bundle opts) {
240 try {
241 mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
242 } catch (RemoteException re) {
243 // Oops!
244 }
245 }
246
247 /**
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100248 * Checks if the package is installed and enabled for a profile.
249 *
250 * @param packageName The package to check.
251 * @param user The UserHandle of the profile.
252 *
253 * @return true if the package exists and is enabled.
254 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100255 public boolean isPackageEnabled(String packageName, UserHandle user) {
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100256 try {
257 return mService.isPackageEnabled(packageName, user);
258 } catch (RemoteException re) {
Amith Yamasaniad97b8b2014-07-29 16:25:17 -0700259 throw new RuntimeException("Failed to call LauncherAppsService");
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100260 }
261 }
262
263 /**
264 * Checks if the activity exists and it enabled for a profile.
265 *
266 * @param component The activity to check.
267 * @param user The UserHandle of the profile.
268 *
269 * @return true if the activity exists and is enabled.
270 */
Kenny Guyf939dba2014-08-15 15:32:34 +0100271 public boolean isActivityEnabled(ComponentName component, UserHandle user) {
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100272 try {
273 return mService.isActivityEnabled(component, user);
274 } catch (RemoteException re) {
Amith Yamasaniad97b8b2014-07-29 16:25:17 -0700275 throw new RuntimeException("Failed to call LauncherAppsService");
Kenny Guy53fa4ec2014-04-29 14:24:18 +0100276 }
277 }
278
279
280 /**
Kenny Guy10a574f2014-08-26 16:17:58 +0100281 * Registers a callback for changes to packages in current and managed profiles.
Kenny Guyc01545372014-06-16 14:17:26 +0100282 *
Kenny Guy10a574f2014-08-26 16:17:58 +0100283 * @param callback The callback to register.
Kenny Guyc01545372014-06-16 14:17:26 +0100284 */
Kenny Guy10a574f2014-08-26 16:17:58 +0100285 public void registerCallback(Callback callback) {
286 registerCallback(callback, null);
Kenny Guyb42c89b2014-07-28 19:20:07 +0100287 }
288
289 /**
Kenny Guy10a574f2014-08-26 16:17:58 +0100290 * Registers a callback for changes to packages in current and managed profiles.
Kenny Guyb42c89b2014-07-28 19:20:07 +0100291 *
Kenny Guy10a574f2014-08-26 16:17:58 +0100292 * @param callback The callback to register.
Kenny Guyb42c89b2014-07-28 19:20:07 +0100293 * @param handler that should be used to post callbacks on, may be null.
294 */
Kenny Guy10a574f2014-08-26 16:17:58 +0100295 public void registerCallback(Callback callback, Handler handler) {
Kenny Guyc01545372014-06-16 14:17:26 +0100296 synchronized (this) {
297 if (callback != null && !mCallbacks.contains(callback)) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100298 boolean addedFirstCallback = mCallbacks.size() == 0;
299 addCallbackLocked(callback, handler);
300 if (addedFirstCallback) {
Kenny Guyc01545372014-06-16 14:17:26 +0100301 try {
302 mService.addOnAppsChangedListener(mAppsChangedListener);
303 } catch (RemoteException re) {
304 }
305 }
306 }
307 }
308 }
309
310 /**
Kenny Guy10a574f2014-08-26 16:17:58 +0100311 * Unregisters a callback that was previously registered.
Kenny Guyc01545372014-06-16 14:17:26 +0100312 *
Kenny Guy10a574f2014-08-26 16:17:58 +0100313 * @param callback The callback to unregister.
314 * @see #registerCallback(Callback)
Kenny Guyc01545372014-06-16 14:17:26 +0100315 */
Kenny Guy10a574f2014-08-26 16:17:58 +0100316 public void unregisterCallback(Callback callback) {
Kenny Guyc01545372014-06-16 14:17:26 +0100317 synchronized (this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100318 removeCallbackLocked(callback);
Kenny Guy44b6dee2014-07-10 18:10:14 +0100319 if (mCallbacks.size() == 0) {
Amith Yamasanie781c812014-05-28 15:28:18 -0700320 try {
321 mService.removeOnAppsChangedListener(mAppsChangedListener);
322 } catch (RemoteException re) {
323 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800324 }
325 }
326 }
327
Kenny Guyf939dba2014-08-15 15:32:34 +0100328 private void removeCallbackLocked(Callback callback) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100329 if (callback == null) {
330 throw new IllegalArgumentException("Callback cannot be null");
331 }
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);
336 return;
337 }
338 }
339 }
340
Kenny Guyf939dba2014-08-15 15:32:34 +0100341 private void addCallbackLocked(Callback callback, Handler handler) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100342 // Remove if already present.
343 removeCallbackLocked(callback);
344 if (handler == null) {
345 handler = new Handler();
346 }
347 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
348 mCallbacks.add(toAdd);
349 }
350
Amith Yamasani4f582632014-02-19 14:31:52 -0800351 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
352
353 @Override
Kenny Guyb42c89b2014-07-28 19:20:07 +0100354 public void onPackageRemoved(UserHandle user, String packageName)
355 throws RemoteException {
Amith Yamasani4f582632014-02-19 14:31:52 -0800356 if (DEBUG) {
357 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
358 }
359 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100360 for (CallbackMessageHandler callback : mCallbacks) {
361 callback.postOnPackageRemoved(packageName, user);
Kenny Guyc01545372014-06-16 14:17:26 +0100362 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800363 }
364 }
365
366 @Override
367 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
368 if (DEBUG) {
369 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
370 }
371 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100372 for (CallbackMessageHandler callback : mCallbacks) {
373 callback.postOnPackageChanged(packageName, user);
Kenny Guyc01545372014-06-16 14:17:26 +0100374 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800375 }
376 }
377
378 @Override
379 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
380 if (DEBUG) {
381 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
382 }
383 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100384 for (CallbackMessageHandler callback : mCallbacks) {
385 callback.postOnPackageAdded(packageName, user);
Kenny Guyc01545372014-06-16 14:17:26 +0100386 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800387 }
388 }
389
390 @Override
391 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
392 throws RemoteException {
393 if (DEBUG) {
394 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
395 }
396 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100397 for (CallbackMessageHandler callback : mCallbacks) {
398 callback.postOnPackagesAvailable(packageNames, user, replacing);
Kenny Guyc01545372014-06-16 14:17:26 +0100399 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800400 }
401 }
402
403 @Override
404 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
405 throws RemoteException {
406 if (DEBUG) {
407 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
408 }
409 synchronized (LauncherApps.this) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100410 for (CallbackMessageHandler callback : mCallbacks) {
411 callback.postOnPackagesUnavailable(packageNames, user, replacing);
Kenny Guyc01545372014-06-16 14:17:26 +0100412 }
413 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800414 }
415 };
Kenny Guyb42c89b2014-07-28 19:20:07 +0100416
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;
423
Kenny Guyf939dba2014-08-15 15:32:34 +0100424 private LauncherApps.Callback mCallback;
Kenny Guyb42c89b2014-07-28 19:20:07 +0100425
426 private static class CallbackInfo {
427 String[] packageNames;
428 String packageName;
429 boolean replacing;
430 UserHandle user;
431 }
432
Kenny Guyf939dba2014-08-15 15:32:34 +0100433 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
Kenny Guyb42c89b2014-07-28 19:20:07 +0100434 super(looper, null, true);
435 mCallback = callback;
436 }
437
438 @Override
439 public void handleMessage(Message msg) {
440 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
441 return;
442 }
443 CallbackInfo info = (CallbackInfo) msg.obj;
444 switch (msg.what) {
445 case MSG_ADDED:
446 mCallback.onPackageAdded(info.packageName, info.user);
447 break;
448 case MSG_REMOVED:
449 mCallback.onPackageRemoved(info.packageName, info.user);
450 break;
451 case MSG_CHANGED:
452 mCallback.onPackageChanged(info.packageName, info.user);
453 break;
454 case MSG_AVAILABLE:
455 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
456 break;
457 case MSG_UNAVAILABLE:
458 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
459 break;
460 }
461 }
462
463 public void postOnPackageAdded(String packageName, UserHandle user) {
464 CallbackInfo info = new CallbackInfo();
465 info.packageName = packageName;
466 info.user = user;
467 obtainMessage(MSG_ADDED, info).sendToTarget();
468 }
469
470 public void postOnPackageRemoved(String packageName, UserHandle user) {
471 CallbackInfo info = new CallbackInfo();
472 info.packageName = packageName;
473 info.user = user;
474 obtainMessage(MSG_REMOVED, info).sendToTarget();
475 }
476
477 public void postOnPackageChanged(String packageName, UserHandle user) {
478 CallbackInfo info = new CallbackInfo();
479 info.packageName = packageName;
480 info.user = user;
481 obtainMessage(MSG_CHANGED, info).sendToTarget();
482 }
483
484 public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
485 boolean replacing) {
486 CallbackInfo info = new CallbackInfo();
487 info.packageNames = packageNames;
488 info.replacing = replacing;
489 info.user = user;
490 obtainMessage(MSG_AVAILABLE, info).sendToTarget();
491 }
492
493 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
494 boolean replacing) {
495 CallbackInfo info = new CallbackInfo();
496 info.packageNames = packageNames;
497 info.replacing = replacing;
498 info.user = user;
499 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
500 }
501 }
Amith Yamasani4f582632014-02-19 14:31:52 -0800502}