1 /*
2  * Copyright (C) 2011 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 package com.android.contacts.common.util;
17 
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.net.Uri;
23 import android.net.Uri.Builder;
24 import android.provider.ContactsContract;
25 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
26 import android.text.TextUtils;
27 
28 import com.android.contacts.common.model.dataitem.StructuredNameDataItem;
29 
30 import java.util.Map;
31 import java.util.TreeMap;
32 
33 /**
34  * Utility class for converting between a display name and structured name (and vice-versa), via
35  * calls to the contact provider.
36  */
37 public class NameConverter {
38 
39     /**
40      * The array of fields that comprise a structured name.
41      */
42     public static final String[] STRUCTURED_NAME_FIELDS = new String[] {
43             StructuredName.PREFIX,
44             StructuredName.GIVEN_NAME,
45             StructuredName.MIDDLE_NAME,
46             StructuredName.FAMILY_NAME,
47             StructuredName.SUFFIX
48     };
49 
50     /**
51      * Converts the given structured name (provided as a map from {@link StructuredName} fields to
52      * corresponding values) into a display name string.
53      * <p>
54      * Note that this operates via a call back to the ContactProvider, but it does not access the
55      * database, so it should be safe to call from the UI thread.  See
56      * ContactsProvider2.completeName() for the underlying method call.
57      * @param context Activity context.
58      * @param structuredName The structured name map to convert.
59      * @return The display name computed from the structured name map.
60      */
structuredNameToDisplayName(Context context, Map<String, String> structuredName)61     public static String structuredNameToDisplayName(Context context,
62             Map<String, String> structuredName) {
63         Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
64         for (String key : STRUCTURED_NAME_FIELDS) {
65             if (structuredName.containsKey(key)) {
66                 appendQueryParameter(builder, key, structuredName.get(key));
67             }
68         }
69         return fetchDisplayName(context, builder.build());
70     }
71 
72     /**
73      * Converts the given structured name (provided as ContentValues) into a display name string.
74      * @param context Activity context.
75      * @param values The content values containing values comprising the structured name.
76      * @return
77      */
structuredNameToDisplayName(Context context, ContentValues values)78     public static String structuredNameToDisplayName(Context context, ContentValues values) {
79         Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
80         for (String key : STRUCTURED_NAME_FIELDS) {
81             if (values.containsKey(key)) {
82                 appendQueryParameter(builder, key, values.getAsString(key));
83             }
84         }
85         return fetchDisplayName(context, builder.build());
86     }
87 
88     /**
89      * Helper method for fetching the display name via the given URI.
90      */
fetchDisplayName(Context context, Uri uri)91     private static String fetchDisplayName(Context context, Uri uri) {
92         String displayName = null;
93         Cursor cursor = context.getContentResolver().query(uri, new String[]{
94                 StructuredName.DISPLAY_NAME,
95         }, null, null, null);
96 
97         if (cursor != null) {
98             try {
99                 if (cursor.moveToFirst()) {
100                     displayName = cursor.getString(0);
101                 }
102             } finally {
103                 cursor.close();
104             }
105         }
106         return displayName;
107     }
108 
109     /**
110      * Converts the given display name string into a structured name (as a map from
111      * {@link StructuredName} fields to corresponding values).
112      * <p>
113      * Note that this operates via a call back to the ContactProvider, but it does not access the
114      * database, so it should be safe to call from the UI thread.
115      * @param context Activity context.
116      * @param displayName The display name to convert.
117      * @return The structured name map computed from the display name.
118      */
displayNameToStructuredName(Context context, String displayName)119     public static Map<String, String> displayNameToStructuredName(Context context,
120             String displayName) {
121         Map<String, String> structuredName = new TreeMap<String, String>();
122         Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
123 
124         appendQueryParameter(builder, StructuredName.DISPLAY_NAME, displayName);
125         Cursor cursor = context.getContentResolver().query(builder.build(), STRUCTURED_NAME_FIELDS,
126                 null, null, null);
127 
128         if (cursor != null) {
129             try {
130                 if (cursor.moveToFirst()) {
131                     for (int i = 0; i < STRUCTURED_NAME_FIELDS.length; i++) {
132                         structuredName.put(STRUCTURED_NAME_FIELDS[i], cursor.getString(i));
133                     }
134                 }
135             } finally {
136                 cursor.close();
137             }
138         }
139         return structuredName;
140     }
141 
142     /**
143      * Converts the given display name string into a structured name (inserting the structured
144      * values into a new or existing ContentValues object).
145      * <p>
146      * Note that this operates via a call back to the ContactProvider, but it does not access the
147      * database, so it should be safe to call from the UI thread.
148      * @param context Activity context.
149      * @param displayName The display name to convert.
150      * @param contentValues The content values object to place the structured name values into.  If
151      *     null, a new one will be created and returned.
152      * @return The ContentValues object containing the structured name fields derived from the
153      *     display name.
154      */
displayNameToStructuredName(Context context, String displayName, ContentValues contentValues)155     public static ContentValues displayNameToStructuredName(Context context, String displayName,
156             ContentValues contentValues) {
157         if (contentValues == null) {
158             contentValues = new ContentValues();
159         }
160         Map<String, String> mapValues = displayNameToStructuredName(context, displayName);
161         for (String key : mapValues.keySet()) {
162             contentValues.put(key, mapValues.get(key));
163         }
164         return contentValues;
165     }
166 
appendQueryParameter(Builder builder, String field, String value)167     private static void appendQueryParameter(Builder builder, String field, String value) {
168         if (!TextUtils.isEmpty(value)) {
169             builder.appendQueryParameter(field, value);
170         }
171     }
172 
173     /**
174      * Parses phonetic name and returns parsed data (family, middle, given) as ContentValues.
175      * Parsed data should be {@link StructuredName#PHONETIC_FAMILY_NAME},
176      * {@link StructuredName#PHONETIC_MIDDLE_NAME}, and
177      * {@link StructuredName#PHONETIC_GIVEN_NAME}.
178      * If this method cannot parse given phoneticName, null values will be stored.
179      *
180      * @param phoneticName Phonetic name to be parsed
181      * @param values ContentValues to be used for storing data. If null, new instance will be
182      * created.
183      * @return ContentValues with parsed data. Those data can be null.
184      */
parsePhoneticName(String phoneticName, StructuredNameDataItem item)185     public static StructuredNameDataItem parsePhoneticName(String phoneticName,
186             StructuredNameDataItem item) {
187         String family = null;
188         String middle = null;
189         String given = null;
190 
191         if (!TextUtils.isEmpty(phoneticName)) {
192             String[] strings = phoneticName.split(" ", 3);
193             switch (strings.length) {
194                 case 1:
195                     family = strings[0];
196                     break;
197                 case 2:
198                     family = strings[0];
199                     given = strings[1];
200                     break;
201                 case 3:
202                     family = strings[0];
203                     middle = strings[1];
204                     given = strings[2];
205                     break;
206             }
207         }
208 
209         if (item == null) {
210             item = new StructuredNameDataItem();
211         }
212         item.setPhoneticFamilyName(family);
213         item.setPhoneticMiddleName(middle);
214         item.setPhoneticGivenName(given);
215         return item;
216     }
217 
218     /**
219      * Constructs and returns a phonetic full name from given parts.
220      */
buildPhoneticName(String family, String middle, String given)221     public static String buildPhoneticName(String family, String middle, String given) {
222         if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
223                 || !TextUtils.isEmpty(given)) {
224             StringBuilder sb = new StringBuilder();
225             if (!TextUtils.isEmpty(family)) {
226                 sb.append(family.trim()).append(' ');
227             }
228             if (!TextUtils.isEmpty(middle)) {
229                 sb.append(middle.trim()).append(' ');
230             }
231             if (!TextUtils.isEmpty(given)) {
232                 sb.append(given.trim()).append(' ');
233             }
234             sb.setLength(sb.length() - 1); // Yank the last space
235             return sb.toString();
236         } else {
237             return null;
238         }
239     }
240 }
241