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