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