1 /* 2 * Copyright (C) 2018 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.dialer.i18n; 18 19 import android.support.annotation.Nullable; 20 import android.telephony.PhoneNumberUtils; 21 import android.text.SpannableStringBuilder; 22 import android.text.SpannedString; 23 import android.text.TextUtils; 24 import android.util.Patterns; 25 import java.util.regex.Matcher; 26 27 /** A formatter that applies bidirectional formatting to phone numbers in text. */ 28 public final class DialerBidiFormatter { 29 30 /** Unicode "Left-To-Right Embedding" (LRE) character. */ 31 private static final char LRE = '\u202A'; 32 33 /** Unicode "Pop Directional Formatting" (PDF) character. */ 34 private static final char PDF = '\u202C'; 35 DialerBidiFormatter()36 private DialerBidiFormatter() {} 37 38 /** 39 * Divides the given text into segments, applies LTR formatting and adds TTS span to segments that 40 * are phone numbers, then reassembles the text. 41 * 42 * <p>Formatted phone numbers usually contain one or more whitespaces (e.g., "+1 650-253-0000", 43 * "(650) 253-0000", etc). The system mistakes such a number for tokens separated by whitespaces. 44 * Therefore, these numbers can't be correctly shown in a RTL context (e.g., "+1 650-253-0000" 45 * would be shown as "650-253-0000 1+".) 46 * 47 * <p>This method wraps phone numbers with Unicode formatting characters LRE & PDF to ensure phone 48 * numbers are always shown as LTR strings. 49 * 50 * <p>Note that the regex used to find phone numbers ({@link Patterns#PHONE}) will also match any 51 * number. As this method also adds TTS span to segments that match {@link Patterns#PHONE}, extra 52 * actions need to be taken if you don't want a number to be read as a phone number by TalkBack. 53 */ format(@ullable CharSequence text)54 public static CharSequence format(@Nullable CharSequence text) { 55 if (TextUtils.isEmpty(text)) { 56 return text; 57 } 58 59 SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); 60 61 // Find the start index and the end index of each segment matching the phone number pattern. 62 Matcher matcher = Patterns.PHONE.matcher(text.toString()); 63 64 int currIndex = 0; 65 while (matcher.find()) { 66 int start = matcher.start(); 67 int end = matcher.end(); 68 69 // Handle the case where the input text doesn't start with a phone number. 70 if (currIndex < start) { 71 spannableStringBuilder.append(text.subSequence(currIndex, start)); 72 } 73 74 // For a phone number, wrap it with Unicode characters LRE & PDF so that it will always be 75 // shown as a LTR string. 76 spannableStringBuilder.append( 77 PhoneNumberUtils.createTtsSpannable( 78 TextUtils.concat( 79 String.valueOf(LRE), text.subSequence(start, end), String.valueOf(PDF)))); 80 81 currIndex = end; 82 } 83 84 // Handle the case where the input doesn't end with a phone number. 85 if (currIndex < text.length()) { 86 spannableStringBuilder.append(text.subSequence(currIndex, text.length())); 87 } 88 89 return new SpannedString(spannableStringBuilder); 90 } 91 } 92