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