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