summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/i18n/DialerBidiFormatter.java
blob: 440db17832dfce3a0929cdf99e06c0f65346d0f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
 * 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);
  }
}