Home | History | Annotate | Download | only in compat
      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 
     51     LauncherAppsCompatV16(Context context) {
     52         mPm = context.getPackageManager();
     53         mContext = context;
     54         mPackageMonitor = new PackageMonitor();
     55    }
     56 
     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 
     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 
     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 
     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 
     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 
    107     public synchronized void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
    108         mCallbacks.remove(callback);
    109         if (mCallbacks.size() == 0) {
    110             unregisterForPackageIntents();
    111         }
    112     }
    113 
    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 
    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 
    132     private void unregisterForPackageIntents() {
    133         mContext.unregisterReceiver(mPackageMonitor);
    134     }
    135 
    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 
    148     private synchronized List<OnAppsChangedCallbackCompat> getCallbacks() {
    149         return new ArrayList<OnAppsChangedCallbackCompat>(mCallbacks);
    150     }
    151 
    152     private class PackageMonitor extends BroadcastReceiver {
    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