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