16340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann/*
26340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * Copyright (C) 2010 The Android Open Source Project
36340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann *
46340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * Licensed under the Apache License, Version 2.0 (the "License");
56340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * you may not use this file except in compliance with the License.
66340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * You may obtain a copy of the License at
76340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann *
86340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann *      http://www.apache.org/licenses/LICENSE-2.0
96340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann *
106340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * Unless required by applicable law or agreed to in writing, software
116340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * distributed under the License is distributed on an "AS IS" BASIS,
126340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * See the License for the specific language governing permissions and
146340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * limitations under the License
156340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann */
166340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
176340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannpackage com.android.contacts.util;
186340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
196340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.accounts.AccountManager;
206340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.accounts.AuthenticatorDescription;
216340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.Context;
226340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.pm.PackageInfo;
236340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.pm.PackageManager;
246340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.pm.PackageManager.NameNotFoundException;
25e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport android.content.pm.ServiceInfo;
266340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.res.Resources;
27e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport android.content.res.Resources.NotFoundException;
286340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.res.TypedArray;
296340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.content.res.XmlResourceParser;
306340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.util.AttributeSet;
316340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.util.Log;
326340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport android.util.Xml;
336340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
34e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport org.xmlpull.v1.XmlPullParser;
35e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport org.xmlpull.v1.XmlPullParserException;
36e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
376340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannimport java.io.IOException;
386340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
396340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann/**
406340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * Retrieves localized names per account type. This allows customizing texts like
416340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann * "All Contacts" for certain account types, but e.g. "All Friends" or "All Connections" for others.
426340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann */
436340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmannpublic class LocalizedNameResolver  {
446340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    private static final String TAG = "LocalizedNameResolver";
456340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
466340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    /**
476340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     * Meta-data key for the contacts configuration associated with a sync service.
486340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     */
496340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
506340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
516340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    private static final String CONTACTS_DATA_KIND = "ContactsDataKind";
526340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
536340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    /**
546340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     * Returns the name for All Contacts for the specified account type.
556340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     */
566340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    public static String getAllContactsName(Context context, String accountType) {
576340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        if (context == null) throw new IllegalArgumentException("Context must not be null");
586340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        if (accountType == null) return null;
596340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
606340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        return resolveAllContactsName(context, accountType);
616340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     }
626340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
636340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    /**
646340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     * Finds "All Contacts"-Name for the specified account type.
656340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     */
666340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    private static String resolveAllContactsName(Context context, String accountType) {
676340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        final AccountManager am = AccountManager.get(context);
686340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
696340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        for (AuthenticatorDescription auth : am.getAuthenticatorTypes()) {
706340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            if (accountType.equals(auth.type)) {
716340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                return resolveAllContactsNameFromMetaData(context, auth.packageName);
726340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            }
736340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        }
746340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
756340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        return null;
766340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    }
776340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
786340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    /**
796340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     * Finds the meta-data XML containing the contacts configuration and
806340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     * reads the picture priority from that file.
816340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann     */
826340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    private static String resolveAllContactsNameFromMetaData(Context context, String packageName) {
836340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        final PackageManager pm = context.getPackageManager();
846340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        try {
856340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES
866340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    | PackageManager.GET_META_DATA);
876340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            if (pi != null && pi.services != null) {
886340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                for (ServiceInfo si : pi.services) {
896340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS);
906340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    if (parser != null) {
916340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        return loadAllContactsNameFromXml(context, parser, packageName);
926340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    }
936340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                }
946340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            }
956340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        } catch (NameNotFoundException e) {
966340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            Log.w(TAG, "Problem loading \"All Contacts\"-name: " + e.toString());
976340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        }
986340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        return null;
996340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    }
1006340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
1016340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    private static String loadAllContactsNameFromXml(Context context, XmlPullParser parser,
1026340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            String packageName) {
1036340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        try {
1046340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            final AttributeSet attrs = Xml.asAttributeSet(parser);
1056340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            int type;
1066340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            while ((type = parser.next()) != XmlPullParser.START_TAG
1076340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    && type != XmlPullParser.END_DOCUMENT) {
1086340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                // Drain comments and whitespace
1096340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            }
1106340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
1116340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            if (type != XmlPullParser.START_TAG) {
1126340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                throw new IllegalStateException("No start tag found");
1136340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            }
1146340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
1156340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            final int depth = parser.getDepth();
1166340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
1176340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    && type != XmlPullParser.END_DOCUMENT) {
1186340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                String name = parser.getName();
1196340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                if (type == XmlPullParser.START_TAG && CONTACTS_DATA_KIND.equals(name)) {
1206340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    final TypedArray typedArray = context.obtainStyledAttributes(attrs,
1216340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                            android.R.styleable.ContactsDataKind);
1226340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    try {
1236340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        // See if a string has been hardcoded directly into the xml
1246340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        final String nonResourceString = typedArray.getNonResourceString(
1256340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                                android.R.styleable.ContactsDataKind_allContactsName);
1266340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        if (nonResourceString != null) {
1276340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                            return nonResourceString;
1286340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        }
1296340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
1306340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        // See if a resource is referenced. We can't rely on getString
1316340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        // to automatically resolve it as the resource lives in a different package
1326340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        int id = typedArray.getResourceId(
1336340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                                android.R.styleable.ContactsDataKind_allContactsName, 0);
1346340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        if (id == 0) return null;
1356340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann
1366340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        // Resolve the resource Id
1376340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        final PackageManager packageManager = context.getPackageManager();
1386340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        final Resources resources;
1396340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        try {
1406340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                            resources = packageManager.getResourcesForApplication(packageName);
1416340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        } catch (NameNotFoundException e) {
1426340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                            return null;
1436340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        }
1446340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        try {
1456340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                            return resources.getString(id);
1466340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        } catch (NotFoundException e) {
1476340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                            return null;
1486340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        }
1496340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    } finally {
1506340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                        typedArray.recycle();
1516340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                    }
1526340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann                }
1536340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            }
1546340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            return null;
1556340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        } catch (XmlPullParserException e) {
1566340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            throw new IllegalStateException("Problem reading XML", e);
1576340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        } catch (IOException e) {
1586340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann            throw new IllegalStateException("Problem reading XML", e);
1596340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann        }
1606340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann    }
1616340038524b0b11cfdc07c553e1f5669812b48d1Daniel Lehmann}
162