LauncherApps.java revision c0154537b0b7926ce6a3c778597b3c2735ca5497
1/*
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.app.AppGlobals;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.ILauncherApps;
24import android.content.pm.IOnAppsChangedListener;
25import android.content.pm.PackageManager.NameNotFoundException;
26import android.graphics.Rect;
27import android.os.Bundle;
28import android.os.RemoteException;
29import android.os.UserHandle;
30import android.os.UserManager;
31import android.util.Log;
32
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.List;
36
37/**
38 * Class for retrieving a list of launchable activities for the current user and any associated
39 * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
40 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
41 * for package changes here.
42 * <p>
43 * To watch for managed profiles being added or removed, register for the following broadcasts:
44 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
45 * <p>
46 * You can retrieve the list of profiles associated with this user with
47 * {@link UserManager#getUserProfiles()}.
48 */
49public class LauncherApps {
50
51    static final String TAG = "LauncherApps";
52    static final boolean DEBUG = false;
53
54    private Context mContext;
55    private ILauncherApps mService;
56    private PackageManager mPm;
57
58    private List<OnAppsChangedListener> mListeners
59            = new ArrayList<OnAppsChangedListener>();
60    private List<OnAppsChangedCallback> mCallbacks
61            = new ArrayList<OnAppsChangedCallback>();
62
63    /**
64     * Callbacks for package changes to this and related managed profiles.
65     */
66    public static abstract class OnAppsChangedCallback {
67        /**
68         * Indicates that a package was removed from the specified profile.
69         *
70         * @param packageName The name of the package that was removed.
71         * @param user The UserHandle of the profile that generated the change.
72         */
73        abstract public void onPackageRemoved(String packageName, UserHandle user);
74
75        /**
76         * Indicates that a package was added to the specified profile.
77         *
78         * @param packageName The name of the package that was added.
79         * @param user The UserHandle of the profile that generated the change.
80         */
81        abstract public void onPackageAdded(String packageName, UserHandle user);
82
83        /**
84         * Indicates that a package was modified in the specified profile.
85         *
86         * @param packageName The name of the package that has changed.
87         * @param user The UserHandle of the profile that generated the change.
88         */
89        abstract public void onPackageChanged(String packageName, UserHandle user);
90
91        /**
92         * Indicates that one or more packages have become available. For
93         * example, this can happen when a removable storage card has
94         * reappeared.
95         *
96         * @param packageNames The names of the packages that have become
97         *            available.
98         * @param user The UserHandle of the profile that generated the change.
99         * @param replacing Indicates whether these packages are replacing
100         *            existing ones.
101         */
102        abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
103                boolean replacing);
104
105        /**
106         * Indicates that one or more packages have become unavailable. For
107         * example, this can happen when a removable storage card has been
108         * removed.
109         *
110         * @param packageNames The names of the packages that have become
111         *            unavailable.
112         * @param user The UserHandle of the profile that generated the change.
113         * @param replacing Indicates whether the packages are about to be
114         *            replaced with new versions.
115         */
116        abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
117                boolean replacing);
118    }
119
120    /**
121     * Callbacks for package changes to this and related managed profiles.
122     */
123    public interface OnAppsChangedListener {
124        /**
125         * Indicates that a package was removed from the specified profile.
126         *
127         * @param user The UserHandle of the profile that generated the change.
128         * @param packageName The name of the package that was removed.
129         */
130        void onPackageRemoved(UserHandle user, String packageName);
131
132        /**
133         * Indicates that a package was added to the specified profile.
134         *
135         * @param user The UserHandle of the profile that generated the change.
136         * @param packageName The name of the package that was added.
137         */
138        void onPackageAdded(UserHandle user, String packageName);
139
140        /**
141         * Indicates that a package was modified in the specified profile.
142         *
143         * @param user The UserHandle of the profile that generated the change.
144         * @param packageName The name of the package that has changed.
145         */
146        void onPackageChanged(UserHandle user, String packageName);
147
148        /**
149         * Indicates that one or more packages have become available. For
150         * example, this can happen when a removable storage card has
151         * reappeared.
152         *
153         * @param user The UserHandle of the profile that generated the change.
154         * @param packageNames The names of the packages that have become
155         *            available.
156         * @param replacing Indicates whether these packages are replacing
157         *            existing ones.
158         */
159        void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing);
160
161        /**
162         * Indicates that one or more packages have become unavailable. For
163         * example, this can happen when a removable storage card has been
164         * removed.
165         *
166         * @param user The UserHandle of the profile that generated the change.
167         * @param packageNames The names of the packages that have become
168         *            unavailable.
169         * @param replacing Indicates whether the packages are about to be
170         *            replaced with new versions.
171         */
172        void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing);
173
174    }
175
176    /** @hide */
177    public LauncherApps(Context context, ILauncherApps service) {
178        mContext = context;
179        mService = service;
180        mPm = context.getPackageManager();
181    }
182
183    /**
184     * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
185     * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
186     *
187     * @param packageName The specific package to query. If null, it checks all installed packages
188     *            in the profile.
189     * @param user The UserHandle of the profile.
190     * @return List of launchable activities. Can be an empty list but will not be null.
191     */
192    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
193        List<ResolveInfo> activities = null;
194        try {
195            activities = mService.getLauncherActivities(packageName, user);
196        } catch (RemoteException re) {
197        }
198        if (activities == null) {
199            return Collections.EMPTY_LIST;
200        }
201        ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
202        final int count = activities.size();
203        for (int i = 0; i < count; i++) {
204            ResolveInfo ri = activities.get(i);
205            long firstInstallTime = 0;
206            try {
207                firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
208                    PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
209            } catch (NameNotFoundException nnfe) {
210                // Sorry, can't find package
211            }
212            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
213                    firstInstallTime);
214            if (DEBUG) {
215                Log.v(TAG, "Returning activity for profile " + user + " : "
216                        + lai.getComponentName());
217            }
218            lais.add(lai);
219        }
220        return lais;
221    }
222
223    static ComponentName getComponentName(ResolveInfo ri) {
224        return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
225    }
226
227    /**
228     * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
229     * returns null.
230     *
231     * @param intent The intent to find a match for.
232     * @param user The profile to look in for a match.
233     * @return An activity info object if there is a match.
234     */
235    public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
236        try {
237            ResolveInfo ri = mService.resolveActivity(intent, user);
238            if (ri != null) {
239                long firstInstallTime = 0;
240                try {
241                    firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
242                            PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
243                } catch (NameNotFoundException nnfe) {
244                    // Sorry, can't find package
245                }
246                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
247                        firstInstallTime);
248                return info;
249            }
250        } catch (RemoteException re) {
251            return null;
252        }
253        return null;
254    }
255
256    /**
257     * Starts an activity in the specified profile.
258     *
259     * @param component The ComponentName of the activity to launch
260     * @param sourceBounds The Rect containing the source bounds of the clicked icon
261     * @param opts Options to pass to startActivity
262     * @param user The UserHandle of the profile
263     * @hide remove before ship
264     */
265    public void startActivityForProfile(ComponentName component, Rect sourceBounds,
266            Bundle opts, UserHandle user) {
267        startActivityForProfile(component, user, sourceBounds, opts);
268    }
269
270    /**
271     * Starts an activity in the specified profile.
272     *
273     * @param component The ComponentName of the activity to launch
274     * @param user The UserHandle of the profile
275     * @param sourceBounds The Rect containing the source bounds of the clicked icon
276     * @param opts Options to pass to startActivity
277     */
278    public void startActivityForProfile(ComponentName component, UserHandle user, Rect sourceBounds,
279            Bundle opts) {
280        if (DEBUG) {
281            Log.i(TAG, "StartActivityForProfile " + component + " " + user.getIdentifier());
282        }
283        try {
284            mService.startActivityAsUser(component, sourceBounds, opts, user);
285        } catch (RemoteException re) {
286            // Oops!
287        }
288    }
289
290    /**
291     * Checks if the package is installed and enabled for a profile.
292     *
293     * @param packageName The package to check.
294     * @param user The UserHandle of the profile.
295     *
296     * @return true if the package exists and is enabled.
297     */
298    public boolean isPackageEnabledForProfile(String packageName, UserHandle user) {
299        try {
300            return mService.isPackageEnabled(packageName, user);
301        } catch (RemoteException re) {
302            return false;
303        }
304    }
305
306    /**
307     * Checks if the activity exists and it enabled for a profile.
308     *
309     * @param component The activity to check.
310     * @param user The UserHandle of the profile.
311     *
312     * @return true if the activity exists and is enabled.
313     */
314    public boolean isActivityEnabledForProfile(ComponentName component, UserHandle user) {
315        try {
316            return mService.isActivityEnabled(component, user);
317        } catch (RemoteException re) {
318            return false;
319        }
320    }
321
322
323    /**
324     * Adds a listener for changes to packages in current and managed profiles.
325     *
326     * @param listener The listener to add.
327     */
328    public void addOnAppsChangedListener(OnAppsChangedListener listener) {
329        synchronized (this) {
330            if (listener != null && !mListeners.contains(listener)) {
331                mListeners.add(listener);
332                if (mListeners.size() == 1 && mCallbacks.size() == 0) {
333                    try {
334                        mService.addOnAppsChangedListener(mAppsChangedListener);
335                    } catch (RemoteException re) {
336                    }
337                }
338            }
339        }
340    }
341
342    /**
343     * Removes a listener that was previously added.
344     *
345     * @param listener The listener to remove.
346     * @see #addOnAppsChangedListener(OnAppsChangedListener)
347     */
348    public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
349        synchronized (this) {
350            mListeners.remove(listener);
351            if (mListeners.size() == 0 && mCallbacks.size() == 0) {
352                try {
353                    mService.removeOnAppsChangedListener(mAppsChangedListener);
354                } catch (RemoteException re) {
355                }
356            }
357        }
358    }
359
360    /**
361     * Adds a callback for changes to packages in current and managed profiles.
362     *
363     * @param callback The callback to add.
364     */
365    public void addOnAppsChangedCallback(OnAppsChangedCallback callback) {
366        synchronized (this) {
367            if (callback != null && !mCallbacks.contains(callback)) {
368                mCallbacks.add(callback);
369                if (mCallbacks.size() == 1 && mListeners.size() == 0) {
370                    try {
371                        mService.addOnAppsChangedListener(mAppsChangedListener);
372                    } catch (RemoteException re) {
373                    }
374                }
375            }
376        }
377    }
378
379    /**
380     * Removes a callback that was previously added.
381     *
382     * @param callback The callback to remove.
383     * @see #addOnAppsChangedListener(OnAppsChangedCallback)
384     */
385    public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) {
386        synchronized (this) {
387            mListeners.remove(callback);
388            if (mListeners.size() == 0 && mCallbacks.size() == 0) {
389                try {
390                    mService.removeOnAppsChangedListener(mAppsChangedListener);
391                } catch (RemoteException re) {
392                }
393            }
394        }
395    }
396
397    private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
398
399        @Override
400        public void onPackageRemoved(UserHandle user, String packageName) throws RemoteException {
401            if (DEBUG) {
402                Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
403            }
404            synchronized (LauncherApps.this) {
405                for (OnAppsChangedListener listener : mListeners) {
406                    listener.onPackageRemoved(user, packageName);
407                }
408                for (OnAppsChangedCallback callback : mCallbacks) {
409                    callback.onPackageRemoved(packageName, user);
410                }
411            }
412        }
413
414        @Override
415        public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
416            if (DEBUG) {
417                Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
418            }
419            synchronized (LauncherApps.this) {
420                for (OnAppsChangedListener listener : mListeners) {
421                    listener.onPackageChanged(user, packageName);
422                }
423                for (OnAppsChangedCallback callback : mCallbacks) {
424                    callback.onPackageChanged(packageName, user);
425                }
426            }
427        }
428
429        @Override
430        public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
431            if (DEBUG) {
432                Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
433            }
434            synchronized (LauncherApps.this) {
435                for (OnAppsChangedListener listener : mListeners) {
436                    listener.onPackageAdded(user, packageName);
437                }
438                for (OnAppsChangedCallback callback : mCallbacks) {
439                    callback.onPackageAdded(packageName, user);
440                }
441            }
442        }
443
444        @Override
445        public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
446                throws RemoteException {
447            if (DEBUG) {
448                Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
449            }
450            synchronized (LauncherApps.this) {
451                for (OnAppsChangedListener listener : mListeners) {
452                    listener.onPackagesAvailable(user, packageNames, replacing);
453                }
454                for (OnAppsChangedCallback callback : mCallbacks) {
455                    callback.onPackagesAvailable(packageNames, user, replacing);
456                }
457            }
458        }
459
460        @Override
461        public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
462                throws RemoteException {
463            if (DEBUG) {
464                Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
465            }
466            synchronized (LauncherApps.this) {
467                for (OnAppsChangedListener listener : mListeners) {
468                    listener.onPackagesUnavailable(user, packageNames, replacing);
469                }
470                for (OnAppsChangedCallback callback : mCallbacks) {
471                    callback.onPackagesUnavailable(packageNames, user, replacing);
472                }
473           }
474        }
475    };
476}
477