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 static android.provider.ContactsContract.CommonDataKinds.Phone; 20 21 import android.content.Context; 22 import android.telephony.PhoneNumberUtils; 23 import android.text.Spannable; 24 import android.text.SpannableString; 25 import android.text.style.TtsSpan; 26 import android.util.Log; 27 import android.util.Patterns; 28 29 import com.android.contacts.common.R; 30 31 import com.android.i18n.phonenumbers.NumberParseException; 32 import com.android.i18n.phonenumbers.PhoneNumberUtil; 33 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; 34 import com.google.common.base.Preconditions; 35 36 /** 37 * Methods for handling various contact data labels. 38 */ 39 public class ContactDisplayUtils { 40 41 private static final String TAG = ContactDisplayUtils.class.getSimpleName(); 42 43 public static final int INTERACTION_CALL = 1; 44 public static final int INTERACTION_SMS = 2; 45 46 /** 47 * Checks if the given data type is a custom type. 48 * 49 * @param type Phone data type. 50 * @return {@literal true} if the type is custom. {@literal false} if not. 51 */ 52 public static boolean isCustomPhoneType(Integer type) { 53 return type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT; 54 } 55 56 /** 57 * Gets a display label for a given phone type. 58 * 59 * @param type The type of number. 60 * @param customLabel A custom label to use if the phone is determined to be of custom type 61 * determined by {@link #isCustomPhoneType(Integer))} 62 * @param interactionType whether this is a call or sms. Either {@link #INTERACTION_CALL} or 63 * {@link #INTERACTION_SMS}. 64 * @param context The application context. 65 * @return An appropriate string label 66 */ 67 public static CharSequence getLabelForCallOrSms(Integer type, CharSequence customLabel, 68 int interactionType, Context context) { 69 Preconditions.checkNotNull(context); 70 71 if (isCustomPhoneType(type)) { 72 return (customLabel == null) ? "" : customLabel; 73 } else { 74 int resId; 75 if (interactionType == INTERACTION_SMS) { 76 resId = getSmsLabelResourceId(type); 77 } else { 78 resId = getPhoneLabelResourceId(type); 79 if (interactionType != INTERACTION_CALL) { 80 Log.e(TAG, "Un-recognized interaction type: " + interactionType + 81 ". Defaulting to ContactDisplayUtils.INTERACTION_CALL."); 82 } 83 } 84 85 return context.getResources().getText(resId); 86 } 87 } 88 89 /** 90 * Find a label for calling. 91 * 92 * @param type The type of number. 93 * @return An appropriate string label. 94 */ 95 public static int getPhoneLabelResourceId(Integer type) { 96 if (type == null) return R.string.call_other; 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 /** 145 * Find a label for sending an sms. 146 * 147 * @param type The type of number. 148 * @return An appropriate string label. 149 */ 150 public static int getSmsLabelResourceId(Integer type) { 151 if (type == null) return R.string.sms_other; 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 * Note this will miss many things that are legitimate phone numbers, for example, 202 * phone numbers with letters. 203 */ 204 public static boolean isPossiblePhoneNumber(CharSequence text) { 205 return text == null ? false : Patterns.PHONE.matcher(text.toString()).matches(); 206 } 207 208 /** 209 * Returns a Spannable for the given phone number with a telephone {@link TtsSpan} set over 210 * the entire length of the given phone number. 211 */ 212 public static Spannable getTelephoneTtsSpannable(String phoneNumber) { 213 if (phoneNumber == null) { 214 return null; 215 } 216 final Spannable spannable = new SpannableString(phoneNumber); 217 final TtsSpan ttsSpan = getTelephoneTtsSpan(phoneNumber); 218 spannable.setSpan(ttsSpan, 0, phoneNumber.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 219 return spannable; 220 } 221 222 /** 223 * Returns a Spannable for the given message with a telephone {@link TtsSpan} set for 224 * the given phone number text wherever it is found within the message. 225 */ 226 public static Spannable getTelephoneTtsSpannable(String message, String phoneNumber) { 227 if (message == null) { 228 return null; 229 } 230 final Spannable spannable = new SpannableString(message); 231 int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber); 232 while (start >= 0) { 233 final int end = start + phoneNumber.length(); 234 final TtsSpan ttsSpan = getTelephoneTtsSpan(phoneNumber); 235 spannable.setSpan(ttsSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 236 start = message.indexOf(phoneNumber, end); 237 } 238 return spannable; 239 } 240 241 /** 242 * Returns a telephone {@link TtsSpan} for the given phone number. 243 */ 244 public static TtsSpan getTelephoneTtsSpan(String phoneNumberString) { 245 if (phoneNumberString == null) { 246 throw new NullPointerException(); 247 } 248 249 // Parse the phone number 250 final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); 251 PhoneNumber phoneNumber = null; 252 try { 253 // Don't supply a defaultRegion so this fails for non-international numbers because 254 // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already 255 // present 256 phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null); 257 } catch (NumberParseException ignored) { 258 } 259 260 // Build a telephone tts span 261 final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder(); 262 if (phoneNumber == null) { 263 // Strip separators otherwise TalkBack will be silent 264 // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel) 265 builder.setNumberParts(PhoneNumberUtils.stripSeparators(phoneNumberString)); 266 } else { 267 if (phoneNumber.hasCountryCode()) { 268 builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode())); 269 } 270 builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber())); 271 } 272 return builder.build(); 273 } 274 } 275