| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.providers.contacts; |
| |
| import android.accounts.AccountManager; |
| import android.accounts.AuthenticatorDescription; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.content.res.XmlResourceParser; |
| import android.util.Log; |
| |
| import com.android.internal.util.XmlUtils; |
| import com.google.android.collect.Maps; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| /** |
| * Maintains a cache of photo priority per account type. During contact aggregation |
| * photo with a higher priority is chosen for the the entire contact, barring an |
| * explicit override by the user, which is captured as the is_superprimary flag |
| * on the photo itself. |
| */ |
| public class PhotoPriorityResolver { |
| private static final String TAG = "PhotoPriorityResolver"; |
| |
| public static final int DEFAULT_PRIORITY = 7; |
| |
| private static final String SYNC_META_DATA = "android.content.SyncAdapter"; |
| |
| /** |
| * The metadata name for so-called "contacts.xml". |
| * |
| * On LMP and later, we also accept the "alternate" name. |
| * This is to allow sync adapters to have a contacts.xml without making it visible on older |
| * platforms. If you modify this also update the matching list in |
| * ContactsCommon/ExternalAccountType. |
| */ |
| private static final String[] METADATA_CONTACTS_NAMES = new String[] { |
| "android.provider.ALTERNATE_CONTACTS_STRUCTURE", |
| "android.provider.CONTACTS_STRUCTURE" |
| }; |
| |
| |
| /** |
| * The XML tag capturing the picture priority. The syntax is: |
| * <code><Picture android:priority="6"/></code> |
| */ |
| private static final String PICTURE_TAG = "Picture"; |
| |
| /** |
| * Name of the attribute of the Picture tag capturing the priority itself. |
| */ |
| private static final String PRIORITY_ATTR = "priority"; |
| |
| private Context mContext; |
| private HashMap<String, Integer> mPhotoPriorities = Maps.newHashMap(); |
| |
| public PhotoPriorityResolver(Context context) { |
| mContext = context; |
| } |
| |
| /** |
| * Returns the photo priority for the specified account type. Maintains cache |
| * of photo priorities. |
| */ |
| public synchronized int getPhotoPriority(String accountType) { |
| if (accountType == null) { |
| return DEFAULT_PRIORITY; |
| } |
| |
| Integer priority = mPhotoPriorities.get(accountType); |
| if (priority == null) { |
| priority = resolvePhotoPriority(accountType); |
| mPhotoPriorities.put(accountType, priority); |
| } |
| return priority; |
| } |
| |
| /** |
| * Finds photo priority for the specified account type. |
| */ |
| private int resolvePhotoPriority(String accountType) { |
| final AccountManager am = AccountManager.get(mContext); |
| |
| for (AuthenticatorDescription auth : am.getAuthenticatorTypes()) { |
| if (accountType.equals(auth.type)) { |
| return resolvePhotoPriorityFromMetaData(auth.packageName); |
| } |
| } |
| |
| return DEFAULT_PRIORITY; |
| } |
| |
| /** |
| * Finds the meta-data XML containing the contacts configuration and |
| * reads the picture priority from that file. |
| */ |
| /* package */ int resolvePhotoPriorityFromMetaData(String packageName) { |
| final PackageManager pm = mContext.getPackageManager(); |
| final Intent intent = new Intent(SYNC_META_DATA).setPackage(packageName); |
| final List<ResolveInfo> intentServices = pm.queryIntentServices(intent, |
| PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); |
| |
| if (intentServices != null) { |
| for (final ResolveInfo resolveInfo : intentServices) { |
| final ServiceInfo serviceInfo = resolveInfo.serviceInfo; |
| if (serviceInfo == null) { |
| continue; |
| } |
| for (String metadataName : METADATA_CONTACTS_NAMES) { |
| final XmlResourceParser parser = serviceInfo.loadXmlMetaData( |
| pm, metadataName); |
| if (parser != null) { |
| return loadPhotoPriorityFromXml(mContext, parser); |
| } |
| } |
| } |
| } |
| return DEFAULT_PRIORITY; |
| } |
| |
| private int loadPhotoPriorityFromXml(Context context, XmlPullParser parser) { |
| int priority = DEFAULT_PRIORITY; |
| try { |
| int type; |
| while ((type = parser.next()) != XmlPullParser.START_TAG |
| && type != XmlPullParser.END_DOCUMENT) { |
| // Drain comments and whitespace |
| } |
| |
| if (type != XmlPullParser.START_TAG) { |
| throw new IllegalStateException("No start tag found"); |
| } |
| |
| final int depth = parser.getDepth(); |
| while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) |
| && type != XmlPullParser.END_DOCUMENT) { |
| String name = parser.getName(); |
| if (type == XmlPullParser.START_TAG && PICTURE_TAG.equals(name)) { |
| int attributeCount = parser.getAttributeCount(); |
| for (int i = 0; i < attributeCount; i++) { |
| String attr = parser.getAttributeName(i); |
| if (PRIORITY_ATTR.equals(attr)) { |
| priority = XmlUtils.convertValueToInt(parser.getAttributeValue(i), |
| DEFAULT_PRIORITY); |
| } else { |
| throw new IllegalStateException("Unsupported attribute " + attr); |
| } |
| } |
| } |
| } |
| } catch (XmlPullParserException e) { |
| throw new IllegalStateException("Problem reading XML", e); |
| } catch (IOException e) { |
| throw new IllegalStateException("Problem reading XML", e); |
| } |
| |
| return priority; |
| } |
| } |