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