Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2010 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.providers.contacts;
     18 
     19 import android.accounts.AccountManager;
     20 import android.accounts.AuthenticatorDescription;
     21 import android.content.Context;
     22 import android.content.pm.PackageInfo;
     23 import android.content.pm.PackageManager;
     24 import android.content.pm.PackageManager.NameNotFoundException;
     25 import android.content.pm.ServiceInfo;
     26 import android.content.res.XmlResourceParser;
     27 import android.util.Log;
     28 
     29 import com.android.internal.util.XmlUtils;
     30 import com.google.android.collect.Maps;
     31 
     32 import org.xmlpull.v1.XmlPullParser;
     33 import org.xmlpull.v1.XmlPullParserException;
     34 
     35 import java.io.IOException;
     36 import java.util.HashMap;
     37 
     38 /**
     39  * Maintains a cache of photo priority per account type.  During contact aggregation
     40  * photo with a higher priority is chosen for the the entire contact, barring an
     41  * explicit override by the user, which is captured as the is_superprimary flag
     42  * on the photo itself.
     43  */
     44 public class PhotoPriorityResolver  {
     45     private static final String TAG = "PhotoPriorityResolver";
     46 
     47     public static final int DEFAULT_PRIORITY = 7;
     48 
     49     /**
     50      * Meta-data key for the contacts configuration associated with a sync service.
     51      */
     52     private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
     53 
     54     /**
     55      * The XML tag capturing the picture priority. The syntax is:
     56      * <code>&lt;Picture android:priority="6"/&gt;</code>
     57      */
     58     private static final String PICTURE_TAG = "Picture";
     59 
     60     /**
     61      * Name of the attribute of the Picture tag capturing the priority itself.
     62      */
     63     private static final String PRIORITY_ATTR = "priority";
     64 
     65     private Context mContext;
     66     private HashMap<String, Integer> mPhotoPriorities = Maps.newHashMap();
     67 
     68     public PhotoPriorityResolver(Context context) {
     69         mContext = context;
     70     }
     71 
     72     /**
     73      * Returns the photo priority for the specified account type.  Maintains cache
     74      * of photo priorities.
     75      */
     76     public synchronized int getPhotoPriority(String accountType) {
     77         if (accountType == null) {
     78             return DEFAULT_PRIORITY;
     79         }
     80 
     81         Integer priority = mPhotoPriorities.get(accountType);
     82         if (priority == null) {
     83             priority = resolvePhotoPriority(accountType);
     84             mPhotoPriorities.put(accountType, priority);
     85         }
     86         return priority;
     87      }
     88 
     89     /**
     90      * Finds photo priority for the specified account type.
     91      */
     92     private int resolvePhotoPriority(String accountType) {
     93         final AccountManager am = AccountManager.get(mContext);
     94 
     95         for (AuthenticatorDescription auth : am.getAuthenticatorTypes()) {
     96             if (accountType.equals(auth.type)) {
     97                 return resolvePhotoPriorityFromMetaData(auth.packageName);
     98             }
     99         }
    100 
    101         return DEFAULT_PRIORITY;
    102     }
    103 
    104     /**
    105      * Finds the meta-data XML containing the contacts configuration and
    106      * reads the picture priority from that file.
    107      */
    108     /* package */ int resolvePhotoPriorityFromMetaData(String packageName) {
    109         final PackageManager pm = mContext.getPackageManager();
    110         try {
    111             PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES
    112                     | PackageManager.GET_META_DATA);
    113             if (pi != null && pi.services != null) {
    114                 for (ServiceInfo si : pi.services) {
    115                     final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS);
    116                     if (parser != null) {
    117                         return loadPhotoPriorityFromXml(mContext, parser);
    118                     }
    119                 }
    120             }
    121         } catch (NameNotFoundException e) {
    122             Log.w(TAG, "Problem loading photo priorities: " + e.toString());
    123         }
    124         return DEFAULT_PRIORITY;
    125     }
    126 
    127     private int loadPhotoPriorityFromXml(Context context, XmlPullParser parser) {
    128         int priority = DEFAULT_PRIORITY;
    129         try {
    130             int type;
    131             while ((type = parser.next()) != XmlPullParser.START_TAG
    132                     && type != XmlPullParser.END_DOCUMENT) {
    133                 // Drain comments and whitespace
    134             }
    135 
    136             if (type != XmlPullParser.START_TAG) {
    137                 throw new IllegalStateException("No start tag found");
    138             }
    139 
    140             final int depth = parser.getDepth();
    141             while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
    142                     && type != XmlPullParser.END_DOCUMENT) {
    143                 String name = parser.getName();
    144                 if (type == XmlPullParser.START_TAG && PICTURE_TAG.equals(name)) {
    145                     int attributeCount = parser.getAttributeCount();
    146                     for (int i = 0; i < attributeCount; i++) {
    147                         String attr = parser.getAttributeName(i);
    148                         if (PRIORITY_ATTR.equals(attr)) {
    149                             priority = XmlUtils.convertValueToInt(parser.getAttributeValue(i),
    150                                     DEFAULT_PRIORITY);
    151                         } else {
    152                             throw new IllegalStateException("Unsupported attribute " + attr);
    153                         }
    154                     }
    155                 }
    156             }
    157         } catch (XmlPullParserException e) {
    158             throw new IllegalStateException("Problem reading XML", e);
    159         } catch (IOException e) {
    160             throw new IllegalStateException("Problem reading XML", e);
    161         }
    162 
    163         return priority;
    164     }
    165 }
    166