• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.launcher3.compat;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.LauncherApps;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.ResolveInfo;
29 import android.graphics.Rect;
30 import android.graphics.drawable.Drawable;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.provider.Settings;
34 
35 import com.android.launcher3.Utilities;
36 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
37 import com.android.launcher3.util.PackageManagerHelper;
38 import com.android.launcher3.util.Thunk;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 
43 /**
44  * Version of {@link LauncherAppsCompat} for devices with API level 16.
45  * Devices Pre-L don't support multiple profiles in one launcher so
46  * user parameters are ignored and all methods operate on the current user.
47  */
48 public class LauncherAppsCompatV16 extends LauncherAppsCompat {
49 
50     private PackageManager mPm;
51     private Context mContext;
52     private List<OnAppsChangedCallbackCompat> mCallbacks
53             = new ArrayList<OnAppsChangedCallbackCompat>();
54     private PackageMonitor mPackageMonitor;
55 
LauncherAppsCompatV16(Context context)56     LauncherAppsCompatV16(Context context) {
57         mPm = context.getPackageManager();
58         mContext = context;
59         mPackageMonitor = new PackageMonitor();
60    }
61 
getActivityList(String packageName, UserHandleCompat user)62     public List<LauncherActivityInfoCompat> getActivityList(String packageName,
63             UserHandleCompat user) {
64         final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
65         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
66         mainIntent.setPackage(packageName);
67         List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0);
68         List<LauncherActivityInfoCompat> list =
69                 new ArrayList<LauncherActivityInfoCompat>(infos.size());
70         for (ResolveInfo info : infos) {
71             list.add(new LauncherActivityInfoCompatV16(mContext, info));
72         }
73         return list;
74     }
75 
resolveActivity(Intent intent, UserHandleCompat user)76     public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) {
77         ResolveInfo info = mPm.resolveActivity(intent, 0);
78         if (info != null) {
79             return new LauncherActivityInfoCompatV16(mContext, info);
80         }
81         return null;
82     }
83 
startActivityForProfile(ComponentName component, UserHandleCompat user, Rect sourceBounds, Bundle opts)84     public void startActivityForProfile(ComponentName component, UserHandleCompat user,
85             Rect sourceBounds, Bundle opts) {
86         Intent launchIntent = new Intent(Intent.ACTION_MAIN);
87         launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
88         launchIntent.setComponent(component);
89         launchIntent.setSourceBounds(sourceBounds);
90         launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
91         mContext.startActivity(launchIntent, opts);
92     }
93 
showAppDetailsForProfile(ComponentName component, UserHandleCompat user)94     public void showAppDetailsForProfile(ComponentName component, UserHandleCompat user) {
95         String packageName = component.getPackageName();
96         Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
97                 Uri.fromParts("package", packageName, null));
98         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
99                 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
100         mContext.startActivity(intent, null);
101     }
102 
addOnAppsChangedCallback(OnAppsChangedCallbackCompat callback)103     public synchronized void addOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
104         if (callback != null && !mCallbacks.contains(callback)) {
105             mCallbacks.add(callback);
106             if (mCallbacks.size() == 1) {
107                 registerForPackageIntents();
108             }
109         }
110     }
111 
removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback)112     public synchronized void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
113         mCallbacks.remove(callback);
114         if (mCallbacks.size() == 0) {
115             unregisterForPackageIntents();
116         }
117     }
118 
isPackageEnabledForProfile(String packageName, UserHandleCompat user)119     public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
120         return PackageManagerHelper.isAppEnabled(mPm, packageName);
121     }
122 
isActivityEnabledForProfile(ComponentName component, UserHandleCompat user)123     public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
124         try {
125             ActivityInfo info = mPm.getActivityInfo(component, 0);
126             return info != null && info.isEnabled();
127         } catch (NameNotFoundException e) {
128             return false;
129         }
130     }
131 
isPackageSuspendedForProfile(String packageName, UserHandleCompat user)132     public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
133         return false;
134     }
135 
unregisterForPackageIntents()136     private void unregisterForPackageIntents() {
137         mContext.unregisterReceiver(mPackageMonitor);
138     }
139 
registerForPackageIntents()140     private void registerForPackageIntents() {
141         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
142         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
143         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
144         filter.addDataScheme("package");
145         mContext.registerReceiver(mPackageMonitor, filter);
146         filter = new IntentFilter();
147         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
148         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
149         mContext.registerReceiver(mPackageMonitor, filter);
150     }
151 
getCallbacks()152     @Thunk synchronized List<OnAppsChangedCallbackCompat> getCallbacks() {
153         return new ArrayList<OnAppsChangedCallbackCompat>(mCallbacks);
154     }
155 
156     @Thunk class PackageMonitor extends BroadcastReceiver {
onReceive(Context context, Intent intent)157         public void onReceive(Context context, Intent intent) {
158             final String action = intent.getAction();
159             final UserHandleCompat user = UserHandleCompat.myUserHandle();
160 
161             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
162                     || Intent.ACTION_PACKAGE_REMOVED.equals(action)
163                     || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
164                 final String packageName = intent.getData().getSchemeSpecificPart();
165                 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
166 
167                 if (packageName == null || packageName.length() == 0) {
168                     // they sent us a bad intent
169                     return;
170                 }
171                 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
172                     for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
173                         callback.onPackageChanged(packageName, user);
174                     }
175                 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
176                     if (!replacing) {
177                         for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
178                             callback.onPackageRemoved(packageName, user);
179                         }
180                     }
181                     // else, we are replacing the package, so a PACKAGE_ADDED will be sent
182                     // later, we will update the package at this time
183                 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
184                     if (!replacing) {
185                         for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
186                             callback.onPackageAdded(packageName, user);
187                         }
188                     } else {
189                         for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
190                             callback.onPackageChanged(packageName, user);
191                         }
192                     }
193                 }
194             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
195                 // EXTRA_REPLACING is available Kitkat onwards. For lower devices, it is broadcasted
196                 // when moving a package or mounting/un-mounting external storage. Assume that
197                 // it is a replacing operation.
198                 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
199                         !Utilities.ATLEAST_KITKAT);
200                 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
201                 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
202                     callback.onPackagesAvailable(packages, user, replacing);
203                 }
204             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
205                 // This intent is broadcasted when moving a package or mounting/un-mounting
206                 // external storage.
207                 // However on Kitkat this is also sent when a package is being updated, and
208                 // contains an extra Intent.EXTRA_REPLACING=true for that case.
209                 // Using false as default for Intent.EXTRA_REPLACING gives correct value on
210                 // lower devices as the intent is not sent when the app is updating/replacing.
211                 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
212                 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
213                 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
214                     callback.onPackagesUnavailable(packages, user, replacing);
215                 }
216             }
217         }
218     }
219 }
220