2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.launcher3.compat;
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;
34 import com.android.launcher3.util.Thunk;
36 import java.util.ArrayList;
37 import java.util.List;
40 * Version of {@link LauncherAppsCompat} for devices with API level 16.
41 * Devices Pre-L don't support multiple profiles in one launcher so
42 * user parameters are ignored and all methods operate on the current user.
44 public class LauncherAppsCompatV16 extends LauncherAppsCompat {
46 private PackageManager mPm;
47 private Context mContext;
48 private List<OnAppsChangedCallbackCompat> mCallbacks
49 = new ArrayList<OnAppsChangedCallbackCompat>();
50 private PackageMonitor mPackageMonitor;
52 LauncherAppsCompatV16(Context context) {
53 mPm = context.getPackageManager();
55 mPackageMonitor = new PackageMonitor();
58 public List<LauncherActivityInfoCompat> getActivityList(String packageName,
59 UserHandleCompat user) {
60 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
61 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
62 mainIntent.setPackage(packageName);
63 List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0);
64 List<LauncherActivityInfoCompat> list =
65 new ArrayList<LauncherActivityInfoCompat>(infos.size());
66 for (ResolveInfo info : infos) {
67 list.add(new LauncherActivityInfoCompatV16(mContext, info));
72 public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) {
73 ResolveInfo info = mPm.resolveActivity(intent, 0);
75 return new LauncherActivityInfoCompatV16(mContext, info);
80 public void startActivityForProfile(ComponentName component, UserHandleCompat user,
81 Rect sourceBounds, Bundle opts) {
82 Intent launchIntent = new Intent(Intent.ACTION_MAIN);
83 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
84 launchIntent.setComponent(component);
85 launchIntent.setSourceBounds(sourceBounds);
86 launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
87 mContext.startActivity(launchIntent, opts);
90 public void showAppDetailsForProfile(ComponentName component, UserHandleCompat user) {
91 String packageName = component.getPackageName();
92 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
93 Uri.fromParts("package", packageName, null));
94 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
95 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
96 mContext.startActivity(intent, null);
99 public synchronized void addOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
100 if (callback != null && !mCallbacks.contains(callback)) {
101 mCallbacks.add(callback);
102 if (mCallbacks.size() == 1) {
103 registerForPackageIntents();
108 public synchronized void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
109 mCallbacks.remove(callback);
110 if (mCallbacks.size() == 0) {
111 unregisterForPackageIntents();
115 public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
116 return isAppEnabled(mPm, packageName, 0);
119 public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
121 ActivityInfo info = mPm.getActivityInfo(component, 0);
122 return info != null && info.isEnabled();
123 } catch (NameNotFoundException e) {
128 private void unregisterForPackageIntents() {
129 mContext.unregisterReceiver(mPackageMonitor);
132 private void registerForPackageIntents() {
133 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
134 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
135 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
136 filter.addDataScheme("package");
137 mContext.registerReceiver(mPackageMonitor, filter);
138 filter = new IntentFilter();
139 filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
140 filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
141 mContext.registerReceiver(mPackageMonitor, filter);
144 @Thunk synchronized List<OnAppsChangedCallbackCompat> getCallbacks() {
145 return new ArrayList<OnAppsChangedCallbackCompat>(mCallbacks);
148 @Thunk class PackageMonitor extends BroadcastReceiver {
149 public void onReceive(Context context, Intent intent) {
150 final String action = intent.getAction();
151 final UserHandleCompat user = UserHandleCompat.myUserHandle();
153 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
154 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
155 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
156 final String packageName = intent.getData().getSchemeSpecificPart();
157 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
159 if (packageName == null || packageName.length() == 0) {
160 // they sent us a bad intent
163 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
164 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
165 callback.onPackageChanged(packageName, user);
167 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
169 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
170 callback.onPackageRemoved(packageName, user);
173 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
174 // later, we will update the package at this time
175 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
177 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
178 callback.onPackageAdded(packageName, user);
181 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
182 callback.onPackageChanged(packageName, user);
186 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
187 // EXTRA_REPLACING is available Kitkat onwards. For lower devices, it is broadcasted
188 // when moving a package or mounting/un-mounting external storage. Assume that
189 // it is a replacing operation.
190 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
191 Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT);
192 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
193 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
194 callback.onPackagesAvailable(packages, user, replacing);
196 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
197 // This intent is broadcasted when moving a package or mounting/un-mounting
199 // However on Kitkat this is also sent when a package is being updated, and
200 // contains an extra Intent.EXTRA_REPLACING=true for that case.
201 // Using false as default for Intent.EXTRA_REPLACING gives correct value on
202 // lower devices as the intent is not sent when the app is updating/replacing.
203 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
204 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
205 for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
206 callback.onPackagesUnavailable(packages, user, replacing);