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 package com.android.bluetooth.map;
     16 
     17 import java.util.ArrayList;
     18 import java.util.LinkedHashMap;
     19 import java.util.Set;
     20 
     21 import android.content.BroadcastReceiver;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.pm.PackageInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.PackageManager.NameNotFoundException;
     29 import android.content.pm.ResolveInfo;
     30 import android.database.ContentObserver;
     31 import android.net.Uri;
     32 import android.os.Handler;
     33 import com.android.bluetooth.mapapi.BluetoothMapContract;
     34 import android.util.Log;
     35 
     36 /**
     37  * Class to construct content observers for for email applications on the system.
     38  *
     39  *
     40  */
     41 
     42 public class BluetoothMapEmailAppObserver{
     43 
     44     private static final String TAG = "BluetoothMapEmailAppObserver";
     45 
     46     private static final boolean D = BluetoothMapService.DEBUG;
     47     private static final boolean V = BluetoothMapService.VERBOSE;
     48     /*  */
     49     private LinkedHashMap<BluetoothMapEmailSettingsItem, ArrayList<BluetoothMapEmailSettingsItem>> mFullList;
     50     private LinkedHashMap<String,ContentObserver> mObserverMap = new LinkedHashMap<String,ContentObserver>();
     51     private ContentResolver mResolver;
     52     private Context mContext;
     53     private BroadcastReceiver mReceiver;
     54     private PackageManager mPackageManager = null;
     55     BluetoothMapEmailSettingsLoader mLoader;
     56     BluetoothMapService mMapService = null;
     57 
     58     public BluetoothMapEmailAppObserver(final Context context, BluetoothMapService mapService) {
     59         mContext  = context;
     60         mMapService = mapService;
     61         mResolver = context.getContentResolver();
     62         mLoader    = new BluetoothMapEmailSettingsLoader(mContext);
     63         mFullList = mLoader.parsePackages(false); /* Get the current list of apps */
     64         createReceiver();
     65         initObservers();
     66     }
     67 
     68 
     69     private BluetoothMapEmailSettingsItem getApp(String packageName) {
     70         if(V) Log.d(TAG, "getApp(): Looking for " + packageName);
     71         for(BluetoothMapEmailSettingsItem app:mFullList.keySet()){
     72             if(V) Log.d(TAG, "  Comparing: " + app.getPackageName());
     73             if(app.getPackageName().equals(packageName)) {
     74                 if(V) Log.d(TAG, "  found " + app.mBase_uri_no_account);
     75                 return app;
     76             }
     77         }
     78         if(V) Log.d(TAG, "  NOT FOUND!");
     79         return null;
     80     }
     81 
     82     private void handleAccountChanges(String packageNameWithProvider) {
     83 
     84         if(D)Log.d(TAG,"handleAccountChanges (packageNameWithProvider: "+packageNameWithProvider+"\n");
     85         String packageName = packageNameWithProvider.replaceFirst("\\.[^\\.]+$", "");
     86         BluetoothMapEmailSettingsItem app = getApp(packageName);
     87         if(app != null) {
     88             ArrayList<BluetoothMapEmailSettingsItem> newAccountList = mLoader.parseAccounts(app);
     89             ArrayList<BluetoothMapEmailSettingsItem> oldAccountList = mFullList.get(app);
     90             ArrayList<BluetoothMapEmailSettingsItem> addedAccountList =
     91                     (ArrayList<BluetoothMapEmailSettingsItem>)newAccountList.clone();
     92             ArrayList<BluetoothMapEmailSettingsItem> removedAccountList = mFullList.get(app); // Same as oldAccountList.clone
     93 
     94             mFullList.put(app, newAccountList);
     95             for(BluetoothMapEmailSettingsItem newAcc: newAccountList){
     96                 for(BluetoothMapEmailSettingsItem oldAcc: oldAccountList){
     97                     if(newAcc.getId() == oldAcc.getId()){
     98                         // For each match remove from both removed and added lists
     99                         removedAccountList.remove(oldAcc);
    100                         addedAccountList.remove(newAcc);
    101                         if(!newAcc.getName().equals(oldAcc.getName()) && newAcc.mIsChecked){
    102                             // Name Changed and the acc is visible - Change Name in SDP record
    103                             mMapService.updateMasInstances(BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_RENAMED);
    104                             if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_RENAMED");
    105                         }
    106                         if(newAcc.mIsChecked != oldAcc.mIsChecked) {
    107                             // Visibility changed
    108                             if(newAcc.mIsChecked){
    109                                 // account added - create SDP record
    110                                 mMapService.updateMasInstances(BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_ADDED);
    111                                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_ADDED isChecked changed");
    112                             } else {
    113                                 // account removed - remove SDP record
    114                                 mMapService.updateMasInstances(BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED);
    115                                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED isChecked changed");
    116                             }
    117                         }
    118                         break;
    119                     }
    120                 }
    121             }
    122             // Notify on any removed accounts
    123             for(BluetoothMapEmailSettingsItem removedAcc: removedAccountList){
    124                 mMapService.updateMasInstances(BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED);
    125                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED " + removedAcc);
    126             }
    127             // Notify on any new accounts
    128             for(BluetoothMapEmailSettingsItem addedAcc: addedAccountList){
    129                 mMapService.updateMasInstances(BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_ADDED);
    130                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_ADDED " + addedAcc);
    131             }
    132 
    133         } else {
    134             Log.e(TAG, "Received change notification on package not registered for notifications!");
    135 
    136         }
    137     }
    138 
    139     /**
    140      * Adds a new content observer to the list of content observers.
    141      * The key for the observer is the uri as string
    142      * @param uri uri for the package that supports MAP email
    143      */
    144 
    145     public void registerObserver(BluetoothMapEmailSettingsItem app) {
    146         Uri uri = BluetoothMapContract.buildAccountUri(app.getProviderAuthority());
    147         if (V) Log.d(TAG, "registerObserver for URI "+uri.toString()+"\n");
    148         ContentObserver observer = new ContentObserver(new Handler()) {
    149             @Override
    150             public void onChange(boolean selfChange) {
    151                 onChange(selfChange, null);
    152             }
    153 
    154             @Override
    155             public void onChange(boolean selfChange, Uri uri) {
    156                 if (V) Log.d(TAG, "onChange on thread: " + Thread.currentThread().getId()
    157                         + " Uri: " + uri + " selfchange: " + selfChange);
    158                 if(uri != null) {
    159                     handleAccountChanges(uri.getHost());
    160                 } else {
    161                     Log.e(TAG, "Unable to handle change as the URI is NULL!");
    162                 }
    163 
    164             }
    165         };
    166         mObserverMap.put(uri.toString(), observer);
    167         mResolver.registerContentObserver(uri, true, observer);
    168     }
    169 
    170     public void unregisterObserver(BluetoothMapEmailSettingsItem app) {
    171         Uri uri = BluetoothMapContract.buildAccountUri(app.getProviderAuthority());
    172         if (V) Log.d(TAG, "unregisterObserver("+uri.toString()+")\n");
    173         mResolver.unregisterContentObserver(mObserverMap.get(uri.toString()));
    174         mObserverMap.remove(uri.toString());
    175     }
    176 
    177     private void initObservers(){
    178         if(D)Log.d(TAG,"initObservers()");
    179         for(BluetoothMapEmailSettingsItem app: mFullList.keySet()){
    180             registerObserver(app);
    181         }
    182     }
    183 
    184     private void deinitObservers(){
    185         if(D)Log.d(TAG,"deinitObservers()");
    186         for(BluetoothMapEmailSettingsItem app: mFullList.keySet()){
    187             unregisterObserver(app);
    188         }
    189     }
    190 
    191     private void createReceiver(){
    192         if(D)Log.d(TAG,"createReceiver()\n");
    193         IntentFilter intentFilter = new IntentFilter();
    194         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    195         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    196         intentFilter.addDataScheme("package");
    197         mReceiver= new BroadcastReceiver() {
    198             @Override
    199             public void onReceive(Context context, Intent intent) {
    200                 if(D)Log.d(TAG,"onReceive\n");
    201                 String action = intent.getAction();
    202                 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
    203                     Uri data = intent.getData();
    204                     String packageName = data.getEncodedSchemeSpecificPart();
    205                     if(D)Log.d(TAG,"The installed package is: "+ packageName);
    206                   //  PackageInfo pInfo = getPackageInfo(packageName);
    207                     ResolveInfo rInfo = mPackageManager.resolveActivity(intent, 0);
    208                     BluetoothMapEmailSettingsItem app = mLoader.createAppItem(rInfo, false);
    209                     if(app != null) {
    210                         registerObserver(app);
    211                         // Add all accounts to mFullList
    212                         ArrayList<BluetoothMapEmailSettingsItem> newAccountList = mLoader.parseAccounts(app);
    213                         mFullList.put(app, newAccountList);
    214                     }
    215                 }
    216                 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
    217 
    218                     Uri data = intent.getData();
    219                     String packageName = data.getEncodedSchemeSpecificPart();
    220                     if(D)Log.d(TAG,"The removed package is: "+ packageName);
    221                     BluetoothMapEmailSettingsItem app = getApp(packageName);
    222                     /* Find the object and remove from fullList */
    223                     if(app != null) {
    224                         unregisterObserver(app);
    225                         mFullList.remove(app);
    226                     }
    227                 }
    228             }
    229         };
    230         mContext.registerReceiver(mReceiver,new IntentFilter(Intent.ACTION_PACKAGE_ADDED));
    231     }
    232     private void removeReceiver(){
    233         if(D)Log.d(TAG,"removeReceiver()\n");
    234         mContext.unregisterReceiver(mReceiver);
    235     }
    236     private PackageInfo getPackageInfo(String packageName){
    237         mPackageManager = mContext.getPackageManager();
    238         try {
    239             return mPackageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA|PackageManager.GET_SERVICES);
    240         } catch (NameNotFoundException e) {
    241             Log.e(TAG,"Error getting package metadata", e);
    242         }
    243         return null;
    244     }
    245 
    246     /**
    247      * Method to get a list of the accounts (across all apps) that are set to be shared
    248      * through MAP.
    249      * @return Arraylist<BluetoothMapEmailSettingsItem> containing all enabled accounts
    250      */
    251     public ArrayList<BluetoothMapEmailSettingsItem> getEnabledAccountItems(){
    252         if(D)Log.d(TAG,"getEnabledAccountItems()\n");
    253         ArrayList<BluetoothMapEmailSettingsItem> list = new ArrayList<BluetoothMapEmailSettingsItem>();
    254         for(BluetoothMapEmailSettingsItem app:mFullList.keySet()){
    255             ArrayList<BluetoothMapEmailSettingsItem> accountList = mFullList.get(app);
    256             for(BluetoothMapEmailSettingsItem acc: accountList){
    257                 if(acc.mIsChecked) {
    258                     list.add(acc);
    259                 }
    260             }
    261         }
    262         return list;
    263     }
    264 
    265     /**
    266      * Method to get a list of the accounts (across all apps).
    267      * @return Arraylist<BluetoothMapEmailSettingsItem> containing all accounts
    268      */
    269     public ArrayList<BluetoothMapEmailSettingsItem> getAllAccountItems(){
    270         if(D)Log.d(TAG,"getAllAccountItems()\n");
    271         ArrayList<BluetoothMapEmailSettingsItem> list = new ArrayList<BluetoothMapEmailSettingsItem>();
    272         for(BluetoothMapEmailSettingsItem app:mFullList.keySet()){
    273             ArrayList<BluetoothMapEmailSettingsItem> accountList = mFullList.get(app);
    274             list.addAll(accountList);
    275         }
    276         return list;
    277     }
    278 
    279 
    280     /**
    281      * Cleanup all resources - must be called to avoid leaks.
    282      */
    283     public void shutdown() {
    284         deinitObservers();
    285         removeReceiver();
    286     }
    287 }
    288