Home | History | Annotate | Download | only in map
      1 /*
      2 * Copyright (C) 2014 Samsung System LSI
      3 * Licensed under the Apache License, Version 2.0 (the "License");
      4 * you may not use this file except in compliance with the License.
      5 * You may obtain a copy of the License at
      6 *
      7 *      http://www.apache.org/licenses/LICENSE-2.0
      8 *
      9 * Unless required by applicable law or agreed to in writing, software
     10 * distributed under the License is distributed on an "AS IS" BASIS,
     11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 * See the License for the specific language governing permissions and
     13 * limitations under the License.
     14 */
     15 
     16 package com.android.bluetooth.map;
     17 
     18 import java.util.ArrayList;
     19 import java.util.LinkedHashMap;
     20 import java.util.List;
     21 
     22 import com.android.bluetooth.map.BluetoothMapEmailSettingsItem;
     23 
     24 
     25 
     26 import android.content.ContentProviderClient;
     27 import android.content.ContentResolver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.pm.ApplicationInfo;
     31 import android.content.pm.PackageInfo;
     32 import android.content.pm.PackageManager;
     33 import android.content.pm.PackageManager.NameNotFoundException;
     34 import android.content.pm.ResolveInfo;
     35 import android.database.Cursor;
     36 import android.net.Uri;
     37 import android.os.RemoteException;
     38 import com.android.bluetooth.mapapi.BluetoothMapContract;
     39 import android.text.format.DateUtils;
     40 import android.util.Log;
     41 
     42 
     43 public class BluetoothMapEmailSettingsLoader {
     44     private static final String TAG = "BluetoothMapEmailSettingsLoader";
     45     private static final boolean D = BluetoothMapService.DEBUG;
     46     private static final boolean V = BluetoothMapService.VERBOSE;
     47     private Context mContext = null;
     48     private PackageManager mPackageManager = null;
     49     private ContentResolver mResolver;
     50     private int mAccountsEnabledCount = 0;
     51     private ContentProviderClient mProviderClient = null;
     52     private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
     53 
     54     public BluetoothMapEmailSettingsLoader(Context ctx)
     55     {
     56         mContext = ctx;
     57     }
     58 
     59     /**
     60      * Method to look through all installed packages system-wide and find those that contain the
     61      * BTMAP meta-tag in their manifest file. For each app the list of accounts are fetched using
     62      * the method parseAccounts().
     63      * @return LinkedHashMap with the packages as keys(BluetoothMapEmailSettingsItem) and
     64      *          values as ArrayLists of BluetoothMapEmailSettingsItems.
     65      */
     66     public LinkedHashMap<BluetoothMapEmailSettingsItem,
     67                          ArrayList<BluetoothMapEmailSettingsItem>> parsePackages(boolean includeIcon) {
     68 
     69         LinkedHashMap<BluetoothMapEmailSettingsItem, ArrayList<BluetoothMapEmailSettingsItem>> groups =
     70                 new LinkedHashMap<BluetoothMapEmailSettingsItem,ArrayList<BluetoothMapEmailSettingsItem>>();
     71         Intent searchIntent = new Intent(BluetoothMapContract.PROVIDER_INTERFACE);
     72         // reset the counter every time this method is called.
     73         mAccountsEnabledCount=0;
     74         // find all installed packages and filter out those that do not support Map Email.
     75         // this is done by looking for a apps with content providers containing the intent-filter for
     76         // android.content.action.BTMAP_SHARE in the manifest file.
     77         mPackageManager = mContext.getPackageManager();
     78         List<ResolveInfo> resInfos = mPackageManager
     79                 .queryIntentContentProviders(searchIntent, 0);
     80 
     81         if (resInfos != null) {
     82             if(D) Log.d(TAG,"Found " + resInfos.size() + " applications");
     83             for (ResolveInfo rInfo : resInfos) {
     84                 // We cannot rely on apps that have been force-stopped in the application settings menu.
     85                 if ((rInfo.providerInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
     86                     BluetoothMapEmailSettingsItem app = createAppItem(rInfo, includeIcon);
     87                     if (app != null){
     88                         ArrayList<BluetoothMapEmailSettingsItem> accounts = parseAccounts(app);
     89                         // we do not want to list apps without accounts
     90                         if(accounts.size() >0)
     91                         {
     92                             // we need to make sure that the "select all" checkbox is checked if all accounts in the list are checked
     93                             app.mIsChecked = true;
     94                             for (BluetoothMapEmailSettingsItem acc: accounts)
     95                             {
     96                                 if(!acc.mIsChecked)
     97                                 {
     98                                     app.mIsChecked = false;
     99                                     break;
    100                                 }
    101                             }
    102                             groups.put(app, accounts);
    103                         }
    104                     }
    105                 } else {
    106                     if(D)Log.d(TAG,"Ignoring force-stopped authority "+ rInfo.providerInfo.authority +"\n");
    107                 }
    108             }
    109         }
    110         else {
    111             if(D) Log.d(TAG,"Found no applications");
    112         }
    113         return groups;
    114     }
    115 
    116     public BluetoothMapEmailSettingsItem createAppItem(ResolveInfo rInfo,
    117             boolean includeIcon) {
    118         String provider = rInfo.providerInfo.authority;
    119         if(provider != null) {
    120             String name = rInfo.loadLabel(mPackageManager).toString();
    121             if(D)Log.d(TAG,rInfo.providerInfo.packageName + " - " + name + " - meta-data(provider = " + provider+")\n");
    122             BluetoothMapEmailSettingsItem app = new BluetoothMapEmailSettingsItem(
    123                     "0",
    124                     name,
    125                     rInfo.providerInfo.packageName,
    126                     provider,
    127                     (includeIcon == false)? null : rInfo.loadIcon(mPackageManager));
    128             return app;
    129         }
    130 
    131         return null;
    132     }
    133 
    134 
    135     /**
    136      * Method for getting the accounts under a given contentprovider from a package.
    137      * This
    138      * @param app The parent app object
    139      * @return An ArrayList of BluetoothMapEmailSettingsItems containing all the accounts from the app
    140      */
    141     public ArrayList<BluetoothMapEmailSettingsItem> parseAccounts(BluetoothMapEmailSettingsItem app)  {
    142         Cursor c = null;
    143         if(D) Log.d(TAG,"Adding app "+app.getPackageName());
    144         ArrayList<BluetoothMapEmailSettingsItem> children = new ArrayList<BluetoothMapEmailSettingsItem>();
    145         // Get the list of accounts from the email apps content resolver (if possible
    146         mResolver = mContext.getContentResolver();
    147         try{
    148             mProviderClient = mResolver.acquireUnstableContentProviderClient(Uri.parse(app.mBase_uri_no_account));
    149              if (mProviderClient == null) {
    150                  throw new RemoteException("Failed to acquire provider for " + app.getPackageName());
    151              }
    152              mProviderClient.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
    153 
    154             Uri uri = Uri.parse(app.mBase_uri_no_account + "/" + BluetoothMapContract.TABLE_ACCOUNT);
    155             c = mProviderClient.query(uri, BluetoothMapContract.BT_ACCOUNT_PROJECTION, null, null,
    156                     BluetoothMapContract.AccountColumns._ID+" DESC");
    157             while (c != null && c.moveToNext()) {
    158                 BluetoothMapEmailSettingsItem child = new BluetoothMapEmailSettingsItem(
    159                     String.valueOf((c.getInt(c.getColumnIndex(BluetoothMapContract.AccountColumns._ID)))),
    160                     c.getString(c.getColumnIndex(BluetoothMapContract.AccountColumns.ACCOUNT_DISPLAY_NAME)) ,
    161                     app.getPackageName(),
    162                     app.getProviderAuthority(),
    163                     null);
    164 
    165                 child.mIsChecked = (c.getInt(c.getColumnIndex(BluetoothMapContract.AccountColumns.FLAG_EXPOSE))!=0);
    166                 /*update the account counter so we can make sure that not to many accounts are checked. */
    167                 if(child.mIsChecked)
    168                 {
    169                     mAccountsEnabledCount++;
    170                 }
    171                 children.add(child);
    172             }
    173         } catch (RemoteException e){
    174             if(D)Log.d(TAG,"Could not establish ContentProviderClient for "+app.getPackageName()+
    175                     " - returning empty account list" );
    176         } finally {
    177             if (c != null) c.close();
    178         }
    179         return children;
    180     }
    181     /**
    182      * Gets the number of enabled accounts in total across all supported apps.
    183      * NOTE that this method should not be called before the parsePackages method
    184      * has been successfully called.
    185      * @return number of enabled accounts
    186      */
    187     public int getAccountsEnabledCount() {
    188         if(D)Log.d(TAG,"Enabled Accounts count:"+ mAccountsEnabledCount);
    189         return mAccountsEnabledCount;
    190     }
    191 }
    192