Loading src/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contacts.common.compat; import android.os.Build; import android.text.Spannable; import android.text.style.TtsSpan; import android.telephony.PhoneNumberUtils; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.google.i18n.phonenumbers.ShortNumberUtil; /** * This class contains static utility methods extracted from PhoneNumberUtils, and the * methods were added in API level 23. In this way, we could enable the corresponding functionality * for pre-M devices. We need maintain this class and keep it synced with PhoneNumberUtils. * Another thing to keep in mind is that we use com.google.i18n rather than com.android.i18n in * here, so we need make sure the application behavior is preserved. */ public class PhoneNumberUtilsCompat { private PhoneNumberUtilsCompat() {} public static CharSequence createTtsSpannable(CharSequence phoneNumber) { if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M) { return PhoneNumberUtils.createTtsSpannable(phoneNumber); } else { return createTtsSpannablePrivate(phoneNumber); } } public static TtsSpan createTtsSpan(String phoneNumber) { if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M) { return PhoneNumberUtils.createTtsSpan(phoneNumber); } else { return createTtsSpanPrivate(phoneNumber); } } /** * Copied from {@link PhoneNumberUtils#createTtsSpannable} */ private static CharSequence createTtsSpannablePrivate(CharSequence phoneNumber) { if (phoneNumber == null) { return null; } Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber); addTtsSpan(spannable, 0, spannable.length()); return spannable; } /** * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location, * annotating that location as containing a phone number. * * @param s A {@code Spannable} to annotate. * @param start The starting character position of the phone number in {@code s}. * @param endExclusive The position after the ending character in the phone number {@code s}. */ private static void addTtsSpan(Spannable s, int start, int endExclusive) { s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()), start, endExclusive, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } /** * Copied from {@link PhoneNumberUtils#createTtsSpan} */ private static TtsSpan createTtsSpanPrivate(String phoneNumberString) { if (phoneNumberString == null) { return null; } // Parse the phone number final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); PhoneNumber phoneNumber = null; try { // Don't supply a defaultRegion so this fails for non-international numbers because // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already // present phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null); } catch (NumberParseException ignored) { } // Build a telephone tts span final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder(); if (phoneNumber == null) { // Strip separators otherwise TalkBack will be silent // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel) builder.setNumberParts(splitAtNonNumerics(phoneNumberString)); } else { if (phoneNumber.hasCountryCode()) { builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode())); } builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber())); } return builder.build(); } /** * Split a phone number using spaces, ignoring anything that is not a digit * @param number A {@code CharSequence} before splitting, e.g., "+20(123)-456#" * @return A {@code String} after splitting, e.g., "20 123 456". */ private static String splitAtNonNumerics(CharSequence number) { StringBuilder sb = new StringBuilder(number.length()); for (int i = 0; i < number.length(); i++) { sb.append(PhoneNumberUtils.isISODigit(number.charAt(i)) ? number.charAt(i) : " "); } // It is very important to remove extra spaces. At time of writing, any leading or trailing // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS // span to be non-functional! return sb.toString().replaceAll(" +", " ").trim(); } } src/com/android/contacts/common/list/ContactListItemView.java +4 −3 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.os.Bundle; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.support.v4.content.ContextCompat; import android.telephony.PhoneNumberUtils; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; Loading @@ -51,6 +50,7 @@ import android.widget.TextView; import com.android.contacts.common.ContactPresenceIconUtil; import com.android.contacts.common.ContactStatusUtil; import com.android.contacts.common.R; import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.format.TextHighlighter; import com.android.contacts.common.util.ContactDisplayUtils; import com.android.contacts.common.util.SearchUtil; Loading Loading @@ -1106,7 +1106,8 @@ public class ContactListItemView extends ViewGroup mSnippetView.setVisibility(VISIBLE); if (ContactDisplayUtils.isPossiblePhoneNumber(text)) { // Give the text-to-speech engine a hint that it's a phone number mSnippetView.setContentDescription(PhoneNumberUtils.createTtsSpannable(text)); mSnippetView.setContentDescription( PhoneNumberUtilsCompat.createTtsSpannable(text)); } else { mSnippetView.setContentDescription(null); } Loading Loading @@ -1227,7 +1228,7 @@ public class ContactListItemView extends ViewGroup // Give the text-to-speech engine a hint that it's a phone number mNameTextView.setTextDirection(View.TEXT_DIRECTION_LTR); mNameTextView.setContentDescription( PhoneNumberUtils.createTtsSpannable(name.toString())); PhoneNumberUtilsCompat.createTtsSpannable(name.toString())); } else { mNameTextView.setContentDescription(null); } Loading src/com/android/contacts/common/util/ContactDisplayUtils.java +2 −3 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import com.google.common.base.Preconditions; import android.content.Context; import android.content.res.Resources; import android.telephony.PhoneNumberUtils; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; Loading @@ -31,8 +30,8 @@ import android.util.Log; import android.util.Patterns; import com.android.contacts.common.R; import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.testing.NeededForTesting; /** * Methods for handling various contact data labels. Loading Loading @@ -218,7 +217,7 @@ public class ContactDisplayUtils { int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber); while (start >= 0) { final int end = start + phoneNumber.length(); final TtsSpan ttsSpan = PhoneNumberUtils.createTtsSpan(phoneNumber); final TtsSpan ttsSpan = PhoneNumberUtilsCompat.createTtsSpan(phoneNumber); spannable.setSpan(ttsSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // this is consistenly done in a misleading way.. start = message.indexOf(phoneNumber, end); } Loading Loading
src/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contacts.common.compat; import android.os.Build; import android.text.Spannable; import android.text.style.TtsSpan; import android.telephony.PhoneNumberUtils; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.google.i18n.phonenumbers.ShortNumberUtil; /** * This class contains static utility methods extracted from PhoneNumberUtils, and the * methods were added in API level 23. In this way, we could enable the corresponding functionality * for pre-M devices. We need maintain this class and keep it synced with PhoneNumberUtils. * Another thing to keep in mind is that we use com.google.i18n rather than com.android.i18n in * here, so we need make sure the application behavior is preserved. */ public class PhoneNumberUtilsCompat { private PhoneNumberUtilsCompat() {} public static CharSequence createTtsSpannable(CharSequence phoneNumber) { if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M) { return PhoneNumberUtils.createTtsSpannable(phoneNumber); } else { return createTtsSpannablePrivate(phoneNumber); } } public static TtsSpan createTtsSpan(String phoneNumber) { if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M) { return PhoneNumberUtils.createTtsSpan(phoneNumber); } else { return createTtsSpanPrivate(phoneNumber); } } /** * Copied from {@link PhoneNumberUtils#createTtsSpannable} */ private static CharSequence createTtsSpannablePrivate(CharSequence phoneNumber) { if (phoneNumber == null) { return null; } Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber); addTtsSpan(spannable, 0, spannable.length()); return spannable; } /** * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location, * annotating that location as containing a phone number. * * @param s A {@code Spannable} to annotate. * @param start The starting character position of the phone number in {@code s}. * @param endExclusive The position after the ending character in the phone number {@code s}. */ private static void addTtsSpan(Spannable s, int start, int endExclusive) { s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()), start, endExclusive, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } /** * Copied from {@link PhoneNumberUtils#createTtsSpan} */ private static TtsSpan createTtsSpanPrivate(String phoneNumberString) { if (phoneNumberString == null) { return null; } // Parse the phone number final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); PhoneNumber phoneNumber = null; try { // Don't supply a defaultRegion so this fails for non-international numbers because // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already // present phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null); } catch (NumberParseException ignored) { } // Build a telephone tts span final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder(); if (phoneNumber == null) { // Strip separators otherwise TalkBack will be silent // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel) builder.setNumberParts(splitAtNonNumerics(phoneNumberString)); } else { if (phoneNumber.hasCountryCode()) { builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode())); } builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber())); } return builder.build(); } /** * Split a phone number using spaces, ignoring anything that is not a digit * @param number A {@code CharSequence} before splitting, e.g., "+20(123)-456#" * @return A {@code String} after splitting, e.g., "20 123 456". */ private static String splitAtNonNumerics(CharSequence number) { StringBuilder sb = new StringBuilder(number.length()); for (int i = 0; i < number.length(); i++) { sb.append(PhoneNumberUtils.isISODigit(number.charAt(i)) ? number.charAt(i) : " "); } // It is very important to remove extra spaces. At time of writing, any leading or trailing // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS // span to be non-functional! return sb.toString().replaceAll(" +", " ").trim(); } }
src/com/android/contacts/common/list/ContactListItemView.java +4 −3 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.os.Bundle; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.support.v4.content.ContextCompat; import android.telephony.PhoneNumberUtils; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; Loading @@ -51,6 +50,7 @@ import android.widget.TextView; import com.android.contacts.common.ContactPresenceIconUtil; import com.android.contacts.common.ContactStatusUtil; import com.android.contacts.common.R; import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.format.TextHighlighter; import com.android.contacts.common.util.ContactDisplayUtils; import com.android.contacts.common.util.SearchUtil; Loading Loading @@ -1106,7 +1106,8 @@ public class ContactListItemView extends ViewGroup mSnippetView.setVisibility(VISIBLE); if (ContactDisplayUtils.isPossiblePhoneNumber(text)) { // Give the text-to-speech engine a hint that it's a phone number mSnippetView.setContentDescription(PhoneNumberUtils.createTtsSpannable(text)); mSnippetView.setContentDescription( PhoneNumberUtilsCompat.createTtsSpannable(text)); } else { mSnippetView.setContentDescription(null); } Loading Loading @@ -1227,7 +1228,7 @@ public class ContactListItemView extends ViewGroup // Give the text-to-speech engine a hint that it's a phone number mNameTextView.setTextDirection(View.TEXT_DIRECTION_LTR); mNameTextView.setContentDescription( PhoneNumberUtils.createTtsSpannable(name.toString())); PhoneNumberUtilsCompat.createTtsSpannable(name.toString())); } else { mNameTextView.setContentDescription(null); } Loading
src/com/android/contacts/common/util/ContactDisplayUtils.java +2 −3 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import com.google.common.base.Preconditions; import android.content.Context; import android.content.res.Resources; import android.telephony.PhoneNumberUtils; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; Loading @@ -31,8 +30,8 @@ import android.util.Log; import android.util.Patterns; import com.android.contacts.common.R; import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.testing.NeededForTesting; /** * Methods for handling various contact data labels. Loading Loading @@ -218,7 +217,7 @@ public class ContactDisplayUtils { int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber); while (start >= 0) { final int end = start + phoneNumber.length(); final TtsSpan ttsSpan = PhoneNumberUtils.createTtsSpan(phoneNumber); final TtsSpan ttsSpan = PhoneNumberUtilsCompat.createTtsSpan(phoneNumber); spannable.setSpan(ttsSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // this is consistenly done in a misleading way.. start = message.indexOf(phoneNumber, end); } Loading