1/*
2 * Copyright (C) 2012 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.common.util;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.provider.ContactsContract.CommonDataKinds.Phone;
22import android.support.annotation.NonNull;
23import android.support.annotation.Nullable;
24import android.text.Spannable;
25import android.text.SpannableString;
26import android.text.TextUtils;
27import android.text.style.TtsSpan;
28import android.util.Patterns;
29import com.android.contacts.common.R;
30import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
31import com.android.contacts.common.preference.ContactsPreferences;
32import com.android.dialer.common.LogUtil;
33import java.util.Objects;
34
35/** Methods for handling various contact data labels. */
36public class ContactDisplayUtils {
37
38  public static final int INTERACTION_CALL = 1;
39  public static final int INTERACTION_SMS = 2;
40
41  /**
42   * Checks if the given data type is a custom type.
43   *
44   * @param type Phone data type.
45   * @return {@literal true} if the type is custom. {@literal false} if not.
46   */
47  public static boolean isCustomPhoneType(Integer type) {
48    return type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT;
49  }
50
51  /**
52   * Gets a display label for a given phone type.
53   *
54   * @param type The type of number.
55   * @param customLabel A custom label to use if the phone is determined to be of custom type
56   *     determined by {@link #isCustomPhoneType(Integer))}
57   * @param interactionType whether this is a call or sms. Either {@link #INTERACTION_CALL} or
58   *     {@link #INTERACTION_SMS}.
59   * @param context The application context.
60   * @return An appropriate string label
61   */
62  public static CharSequence getLabelForCallOrSms(
63      Integer type, CharSequence customLabel, int interactionType, @NonNull Context context) {
64    Objects.requireNonNull(context);
65
66    if (isCustomPhoneType(type)) {
67      return (customLabel == null) ? "" : customLabel;
68    } else {
69      int resId;
70      if (interactionType == INTERACTION_SMS) {
71        resId = getSmsLabelResourceId(type);
72      } else {
73        resId = getPhoneLabelResourceId(type);
74        if (interactionType != INTERACTION_CALL) {
75          LogUtil.e(
76              "ContactDisplayUtils.getLabelForCallOrSms",
77              "un-recognized interaction type: "
78                  + interactionType
79                  + ". Defaulting to ContactDisplayUtils.INTERACTION_CALL.");
80        }
81      }
82
83      return context.getResources().getText(resId);
84    }
85  }
86
87  /**
88   * Find a label for calling.
89   *
90   * @param type The type of number.
91   * @return An appropriate string label.
92   */
93  public static int getPhoneLabelResourceId(Integer type) {
94    if (type == null) {
95      return R.string.call_other;
96    }
97    switch (type) {
98      case Phone.TYPE_HOME:
99        return R.string.call_home;
100      case Phone.TYPE_MOBILE:
101        return R.string.call_mobile;
102      case Phone.TYPE_WORK:
103        return R.string.call_work;
104      case Phone.TYPE_FAX_WORK:
105        return R.string.call_fax_work;
106      case Phone.TYPE_FAX_HOME:
107        return R.string.call_fax_home;
108      case Phone.TYPE_PAGER:
109        return R.string.call_pager;
110      case Phone.TYPE_OTHER:
111        return R.string.call_other;
112      case Phone.TYPE_CALLBACK:
113        return R.string.call_callback;
114      case Phone.TYPE_CAR:
115        return R.string.call_car;
116      case Phone.TYPE_COMPANY_MAIN:
117        return R.string.call_company_main;
118      case Phone.TYPE_ISDN:
119        return R.string.call_isdn;
120      case Phone.TYPE_MAIN:
121        return R.string.call_main;
122      case Phone.TYPE_OTHER_FAX:
123        return R.string.call_other_fax;
124      case Phone.TYPE_RADIO:
125        return R.string.call_radio;
126      case Phone.TYPE_TELEX:
127        return R.string.call_telex;
128      case Phone.TYPE_TTY_TDD:
129        return R.string.call_tty_tdd;
130      case Phone.TYPE_WORK_MOBILE:
131        return R.string.call_work_mobile;
132      case Phone.TYPE_WORK_PAGER:
133        return R.string.call_work_pager;
134      case Phone.TYPE_ASSISTANT:
135        return R.string.call_assistant;
136      case Phone.TYPE_MMS:
137        return R.string.call_mms;
138      default:
139        return R.string.call_custom;
140    }
141  }
142
143  /**
144   * Find a label for sending an sms.
145   *
146   * @param type The type of number.
147   * @return An appropriate string label.
148   */
149  public static int getSmsLabelResourceId(Integer type) {
150    if (type == null) {
151      return R.string.sms_other;
152    }
153    switch (type) {
154      case Phone.TYPE_HOME:
155        return R.string.sms_home;
156      case Phone.TYPE_MOBILE:
157        return R.string.sms_mobile;
158      case Phone.TYPE_WORK:
159        return R.string.sms_work;
160      case Phone.TYPE_FAX_WORK:
161        return R.string.sms_fax_work;
162      case Phone.TYPE_FAX_HOME:
163        return R.string.sms_fax_home;
164      case Phone.TYPE_PAGER:
165        return R.string.sms_pager;
166      case Phone.TYPE_OTHER:
167        return R.string.sms_other;
168      case Phone.TYPE_CALLBACK:
169        return R.string.sms_callback;
170      case Phone.TYPE_CAR:
171        return R.string.sms_car;
172      case Phone.TYPE_COMPANY_MAIN:
173        return R.string.sms_company_main;
174      case Phone.TYPE_ISDN:
175        return R.string.sms_isdn;
176      case Phone.TYPE_MAIN:
177        return R.string.sms_main;
178      case Phone.TYPE_OTHER_FAX:
179        return R.string.sms_other_fax;
180      case Phone.TYPE_RADIO:
181        return R.string.sms_radio;
182      case Phone.TYPE_TELEX:
183        return R.string.sms_telex;
184      case Phone.TYPE_TTY_TDD:
185        return R.string.sms_tty_tdd;
186      case Phone.TYPE_WORK_MOBILE:
187        return R.string.sms_work_mobile;
188      case Phone.TYPE_WORK_PAGER:
189        return R.string.sms_work_pager;
190      case Phone.TYPE_ASSISTANT:
191        return R.string.sms_assistant;
192      case Phone.TYPE_MMS:
193        return R.string.sms_mms;
194      default:
195        return R.string.sms_custom;
196    }
197  }
198
199  /**
200   * Whether the given text could be a phone number.
201   *
202   * <p>Note this will miss many things that are legitimate phone numbers, for example, phone
203   * numbers with letters.
204   */
205  public static boolean isPossiblePhoneNumber(CharSequence text) {
206    return text != null && Patterns.PHONE.matcher(text.toString()).matches();
207  }
208
209  /**
210   * Returns a Spannable for the given message with a telephone {@link TtsSpan} set for the given
211   * phone number text wherever it is found within the message.
212   */
213  public static Spannable getTelephoneTtsSpannable(
214      @Nullable String message, @Nullable String phoneNumber) {
215    if (message == null) {
216      return null;
217    }
218    final Spannable spannable = new SpannableString(message);
219    int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber);
220    while (start >= 0) {
221      final int end = start + phoneNumber.length();
222      final TtsSpan ttsSpan = PhoneNumberUtilsCompat.createTtsSpan(phoneNumber);
223      spannable.setSpan(
224          ttsSpan,
225          start,
226          end,
227          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // this is consistenly done in a misleading way..
228      start = message.indexOf(phoneNumber, end);
229    }
230    return spannable;
231  }
232
233  /**
234   * Retrieves a string from a string template that takes 1 phone number as argument, span the
235   * number with a telephone {@link TtsSpan}, and return the spanned string.
236   *
237   * @param resources to retrieve the string from
238   * @param stringId ID of the string
239   * @param number to pass in the template
240   * @return CharSequence with the phone number wrapped in a TtsSpan
241   */
242  public static CharSequence getTtsSpannedPhoneNumber(
243      Resources resources, int stringId, String number) {
244    String msg = resources.getString(stringId, number);
245    return ContactDisplayUtils.getTelephoneTtsSpannable(msg, number);
246  }
247
248  /**
249   * Returns either namePrimary or nameAlternative based on the {@link ContactsPreferences}.
250   * Defaults to the name that is non-null.
251   *
252   * @param namePrimary the primary name.
253   * @param nameAlternative the alternative name.
254   * @param contactsPreferences the ContactsPreferences used to determine the preferred display
255   *     name.
256   * @return namePrimary or nameAlternative depending on the value of displayOrderPreference.
257   */
258  public static String getPreferredDisplayName(
259      String namePrimary,
260      String nameAlternative,
261      @Nullable ContactsPreferences contactsPreferences) {
262    if (contactsPreferences == null) {
263      return namePrimary != null ? namePrimary : nameAlternative;
264    }
265    if (contactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
266      return namePrimary;
267    }
268
269    if (contactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE
270        && !TextUtils.isEmpty(nameAlternative)) {
271      return nameAlternative;
272    }
273
274    return namePrimary;
275  }
276
277  /**
278   * Returns either namePrimary or nameAlternative based on the {@link ContactsPreferences}.
279   * Defaults to the name that is non-null.
280   *
281   * @param namePrimary the primary name.
282   * @param nameAlternative the alternative name.
283   * @param contactsPreferences the ContactsPreferences used to determine the preferred sort order.
284   * @return namePrimary or nameAlternative depending on the value of displayOrderPreference.
285   */
286  public static String getPreferredSortName(
287      String namePrimary,
288      String nameAlternative,
289      @Nullable ContactsPreferences contactsPreferences) {
290    if (contactsPreferences == null) {
291      return namePrimary != null ? namePrimary : nameAlternative;
292    }
293
294    if (contactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
295      return namePrimary;
296    }
297
298    if (contactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_ALTERNATIVE
299        && !TextUtils.isEmpty(nameAlternative)) {
300      return nameAlternative;
301    }
302
303    return namePrimary;
304  }
305}
306