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.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 
     53     LauncherAppsCompatV16(Context context) {
     54         mPm = context.getPackageManager();
     55         mContext = context;
     56         mPackageMonitor = new PackageMonitor();
     57    }
     58 
     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 
     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 
     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 
     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 
    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 
    109     public synchronized void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
    110         mCallbacks.remove(callback);
    111         if (mCallbacks.size() == 0) {
    112             unregisterForPackageIntents();
    113         }
    114     }
    115 
    116     public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
    117         return PackageManagerHelper.isAppEnabled(mPm, packageName);
    118     }
    119 
    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 
    129     public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
    130         return false;
    131     }
    132 
    133     private void unregisterForPackageIntents() {
    134         mContext.unregisterReceiver(mPackageMonitor);
    135     }
    136 
    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 
    149     @Thunk synchronized List<OnAppsChangedCallbackCompat> getCallbacks() {
    150         return new ArrayList<OnAppsChangedCallbackCompat>(mCallbacks);
    151     }
    152 
    153     @Thunk class PackageMonitor extends BroadcastReceiver {
    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