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