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>&lt;Picture android:priority="6"/&gt;</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