blob: 440db17832dfce3a0929cdf99e06c0f65346d0f1 [file] [log] [blame]
/*
* Copyright (C) 2018 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.dialer.i18n;
import android.support.annotation.Nullable;
import android.telephony.PhoneNumberUtils;
import android.text.SpannableStringBuilder;
import android.text.SpannedString;
import android.text.TextUtils;
import android.util.Patterns;
import java.util.regex.Matcher;
/** A formatter that applies bidirectional formatting to phone numbers in text. */
public final class DialerBidiFormatter {
/** Unicode "Left-To-Right Embedding" (LRE) character. */
private static final char LRE = '\u202A';
/** Unicode "Pop Directional Formatting" (PDF) character. */
private static final char PDF = '\u202C';
private DialerBidiFormatter() {}
/**
* Divides the given text into segments, applies LTR formatting and adds TTS span to segments that
* are phone numbers, then reassembles the text.
*
* <p>Formatted phone numbers usually contain one or more whitespaces (e.g., "+1 650-253-0000",
* "(650) 253-0000", etc). The system mistakes such a number for tokens separated by whitespaces.
* Therefore, these numbers can't be correctly shown in a RTL context (e.g., "+1 650-253-0000"
* would be shown as "650-253-0000 1+".)
*
* <p>This method wraps phone numbers with Unicode formatting characters LRE & PDF to ensure phone
* numbers are always shown as LTR strings.
*
* <p>Note that the regex used to find phone numbers ({@link Patterns#PHONE}) will also match any
* number. As this method also adds TTS span to segments that match {@link Patterns#PHONE}, extra
* actions need to be taken if you don't want a number to be read as a phone number by TalkBack.
*/
public static CharSequence format(@Nullable CharSequence text) {
if (TextUtils.isEmpty(text)) {
return text;
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
// Find the start index and the end index of each segment matching the phone number pattern.
Matcher matcher = Patterns.PHONE.matcher(text.toString());
int currIndex = 0;
while (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
// Handle the case where the input text doesn't start with a phone number.
if (currIndex < start) {
spannableStringBuilder.append(text.subSequence(currIndex, start));
}
// For a phone number, wrap it with Unicode characters LRE & PDF so that it will always be
// shown as a LTR string.
spannableStringBuilder.append(
PhoneNumberUtils.createTtsSpannable(
TextUtils.concat(
String.valueOf(LRE), text.subSequence(start, end), String.valueOf(PDF))));
currIndex = end;
}
// Handle the case where the input doesn't end with a phone number.
if (currIndex < text.length()) {
spannableStringBuilder.append(text.subSequence(currIndex, text.length()));
}
return new SpannedString(spannableStringBuilder);
}
}