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 17package com.android.contacts.util; 18 19import org.xmlpull.v1.XmlPullParser; 20import org.xmlpull.v1.XmlPullParserException; 21 22import android.accounts.AccountManager; 23import android.accounts.AuthenticatorDescription; 24import android.content.Context; 25import android.content.pm.PackageInfo; 26import android.content.pm.PackageManager; 27import android.content.pm.ServiceInfo; 28import android.content.pm.PackageManager.NameNotFoundException; 29import android.content.res.Resources; 30import android.content.res.TypedArray; 31import android.content.res.XmlResourceParser; 32import android.content.res.Resources.NotFoundException; 33import android.util.AttributeSet; 34import android.util.Log; 35import android.util.Xml; 36 37import java.io.IOException; 38 39/** 40 * Retrieves localized names per account type. This allows customizing texts like 41 * "All Contacts" for certain account types, but e.g. "All Friends" or "All Connections" for others. 42 */ 43public class LocalizedNameResolver { 44 private static final String TAG = "LocalizedNameResolver"; 45 46 /** 47 * Meta-data key for the contacts configuration associated with a sync service. 48 */ 49 private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE"; 50 51 private static final String CONTACTS_DATA_KIND = "ContactsDataKind"; 52 53 /** 54 * Returns the name for All Contacts for the specified account type. 55 */ 56 public static String getAllContactsName(Context context, String accountType) { 57 if (context == null) throw new IllegalArgumentException("Context must not be null"); 58 if (accountType == null) return null; 59 60 return resolveAllContactsName(context, accountType); 61 } 62 63 /** 64 * Finds "All Contacts"-Name for the specified account type. 65 */ 66 private static String resolveAllContactsName(Context context, String accountType) { 67 final AccountManager am = AccountManager.get(context); 68 69 for (AuthenticatorDescription auth : am.getAuthenticatorTypes()) { 70 if (accountType.equals(auth.type)) { 71 return resolveAllContactsNameFromMetaData(context, auth.packageName); 72 } 73 } 74 75 return null; 76 } 77 78 /** 79 * Finds the meta-data XML containing the contacts configuration and 80 * reads the picture priority from that file. 81 */ 82 private static String resolveAllContactsNameFromMetaData(Context context, String packageName) { 83 final PackageManager pm = context.getPackageManager(); 84 try { 85 PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES 86 | PackageManager.GET_META_DATA); 87 if (pi != null && pi.services != null) { 88 for (ServiceInfo si : pi.services) { 89 final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS); 90 if (parser != null) { 91 return loadAllContactsNameFromXml(context, parser, packageName); 92 } 93 } 94 } 95 } catch (NameNotFoundException e) { 96 Log.w(TAG, "Problem loading \"All Contacts\"-name: " + e.toString()); 97 } 98 return null; 99 } 100 101 private static String loadAllContactsNameFromXml(Context context, XmlPullParser parser, 102 String packageName) { 103 try { 104 final AttributeSet attrs = Xml.asAttributeSet(parser); 105 int type; 106 while ((type = parser.next()) != XmlPullParser.START_TAG 107 && type != XmlPullParser.END_DOCUMENT) { 108 // Drain comments and whitespace 109 } 110 111 if (type != XmlPullParser.START_TAG) { 112 throw new IllegalStateException("No start tag found"); 113 } 114 115 final int depth = parser.getDepth(); 116 while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 117 && type != XmlPullParser.END_DOCUMENT) { 118 String name = parser.getName(); 119 if (type == XmlPullParser.START_TAG && CONTACTS_DATA_KIND.equals(name)) { 120 final TypedArray typedArray = context.obtainStyledAttributes(attrs, 121 android.R.styleable.ContactsDataKind); 122 try { 123 // See if a string has been hardcoded directly into the xml 124 final String nonResourceString = typedArray.getNonResourceString( 125 android.R.styleable.ContactsDataKind_allContactsName); 126 if (nonResourceString != null) { 127 return nonResourceString; 128 } 129 130 // See if a resource is referenced. We can't rely on getString 131 // to automatically resolve it as the resource lives in a different package 132 int id = typedArray.getResourceId( 133 android.R.styleable.ContactsDataKind_allContactsName, 0); 134 if (id == 0) return null; 135 136 // Resolve the resource Id 137 final PackageManager packageManager = context.getPackageManager(); 138 final Resources resources; 139 try { 140 resources = packageManager.getResourcesForApplication(packageName); 141 } catch (NameNotFoundException e) { 142 return null; 143 } 144 try { 145 return resources.getString(id); 146 } catch (NotFoundException e) { 147 return null; 148 } 149 } finally { 150 typedArray.recycle(); 151 } 152 } 153 } 154 return null; 155 } catch (XmlPullParserException e) { 156 throw new IllegalStateException("Problem reading XML", e); 157 } catch (IOException e) { 158 throw new IllegalStateException("Problem reading XML", e); 159 } 160 } 161} 162