1767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov/* 2767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 3767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * 4767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 5767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * you may not use this file except in compliance with the License. 6767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * You may obtain a copy of the License at 7767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * 8767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 9767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * 10767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 11767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 12767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * See the License for the specific language governing permissions and 14767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * limitations under the License 15767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 16767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 17767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovpackage com.android.providers.contacts; 18767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 19767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.accounts.AccountManager; 20767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.accounts.AuthenticatorDescription; 21767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.content.Context; 22767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.content.pm.PackageInfo; 23767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.content.pm.PackageManager; 24767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.content.pm.PackageManager.NameNotFoundException; 2538210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport android.content.pm.ServiceInfo; 26767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.content.res.XmlResourceParser; 27767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport android.util.Log; 28767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 2938210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.internal.util.XmlUtils; 3038210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.google.android.collect.Maps; 3138210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki 3238210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport org.xmlpull.v1.XmlPullParser; 3338210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport org.xmlpull.v1.XmlPullParserException; 3438210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki 35767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport java.io.IOException; 36767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovimport java.util.HashMap; 37767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 38767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov/** 39767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Maintains a cache of photo priority per account type. During contact aggregation 40767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * photo with a higher priority is chosen for the the entire contact, barring an 41767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * explicit override by the user, which is captured as the is_superprimary flag 42767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * on the photo itself. 43767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 44767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikovpublic class PhotoPriorityResolver { 45767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private static final String TAG = "PhotoPriorityResolver"; 46767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 47767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov public static final int DEFAULT_PRIORITY = 7; 48767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 49767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /** 50767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Meta-data key for the contacts configuration associated with a sync service. 51767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 52767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE"; 53767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 54767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /** 55767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * The XML tag capturing the picture priority. The syntax is: 56767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * <code><Picture android:priority="6"/></code> 57767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 58767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private static final String PICTURE_TAG = "Picture"; 59767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 60767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /** 61767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Name of the attribute of the Picture tag capturing the priority itself. 62767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 63767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private static final String PRIORITY_ATTR = "priority"; 64767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 65767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private Context mContext; 66767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private HashMap<String, Integer> mPhotoPriorities = Maps.newHashMap(); 67767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 68767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov public PhotoPriorityResolver(Context context) { 69767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov mContext = context; 70767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 71767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 72767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /** 73767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Returns the photo priority for the specified account type. Maintains cache 74767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * of photo priorities. 75767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 76767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov public synchronized int getPhotoPriority(String accountType) { 77767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (accountType == null) { 78767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return DEFAULT_PRIORITY; 79767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 80767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 81767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov Integer priority = mPhotoPriorities.get(accountType); 82767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (priority == null) { 83767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov priority = resolvePhotoPriority(accountType); 84767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov mPhotoPriorities.put(accountType, priority); 85767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 86767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return priority; 87767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 88767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 89767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /** 90767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Finds photo priority for the specified account type. 91767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 92767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private int resolvePhotoPriority(String accountType) { 93767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov final AccountManager am = AccountManager.get(mContext); 94767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 95767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov for (AuthenticatorDescription auth : am.getAuthenticatorTypes()) { 96767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (accountType.equals(auth.type)) { 97767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return resolvePhotoPriorityFromMetaData(auth.packageName); 98767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 99767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 100767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 101767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return DEFAULT_PRIORITY; 102767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 103767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 104767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /** 105767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * Finds the meta-data XML containing the contacts configuration and 106767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov * reads the picture priority from that file. 107767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov */ 108767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov /* package */ int resolvePhotoPriorityFromMetaData(String packageName) { 109767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov final PackageManager pm = mContext.getPackageManager(); 110767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov try { 111767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES 112767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov | PackageManager.GET_META_DATA); 113767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (pi != null && pi.services != null) { 114767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov for (ServiceInfo si : pi.services) { 115767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS); 116767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (parser != null) { 117767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return loadPhotoPriorityFromXml(mContext, parser); 118767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 119767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 120767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 121767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } catch (NameNotFoundException e) { 122767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov Log.w(TAG, "Problem loading photo priorities: " + e.toString()); 123767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 124767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return DEFAULT_PRIORITY; 125767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 126767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 127767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov private int loadPhotoPriorityFromXml(Context context, XmlPullParser parser) { 128767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov int priority = DEFAULT_PRIORITY; 129767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov try { 130767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov int type; 131767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov while ((type = parser.next()) != XmlPullParser.START_TAG 132767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov && type != XmlPullParser.END_DOCUMENT) { 133767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov // Drain comments and whitespace 134767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 135767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 136767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (type != XmlPullParser.START_TAG) { 137767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov throw new IllegalStateException("No start tag found"); 138767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 139767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 140767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov final int depth = parser.getDepth(); 141767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 142767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov && type != XmlPullParser.END_DOCUMENT) { 143767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov String name = parser.getName(); 144767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (type == XmlPullParser.START_TAG && PICTURE_TAG.equals(name)) { 145767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov int attributeCount = parser.getAttributeCount(); 146767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov for (int i = 0; i < attributeCount; i++) { 147767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov String attr = parser.getAttributeName(i); 148767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov if (PRIORITY_ATTR.equals(attr)) { 149767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov priority = XmlUtils.convertValueToInt(parser.getAttributeValue(i), 150767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov DEFAULT_PRIORITY); 151767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } else { 152767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov throw new IllegalStateException("Unsupported attribute " + attr); 153767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 154767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 155767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 156767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 157767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } catch (XmlPullParserException e) { 158767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov throw new IllegalStateException("Problem reading XML", e); 159767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } catch (IOException e) { 160767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov throw new IllegalStateException("Problem reading XML", e); 161767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 162767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov 163767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov return priority; 164767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov } 165767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov} 166