summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/i18n
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2018-03-22 17:39:29 -0700
committerCopybara-Service <copybara-piper@google.com>2018-03-26 22:16:16 -0700
commitf4b484485a4519a99d797bd9c0c1cc902cfc7414 (patch)
treede75b03528aae36b06fc402fa39d081250671048 /java/com/android/dialer/i18n
parent009695e477f02b13d563f17a8de0e179d7715bf9 (diff)
Correctly display phone numbers containing whitespaces in RTL context.
Bug: 74421656 Test: DialerBidiFormatterTest PiperOrigin-RevId: 190154072 Change-Id: Ic7cb3be702dd28b07b6e5e1e6d89f75f0bb12655
Diffstat (limited to 'java/com/android/dialer/i18n')
-rw-r--r--java/com/android/dialer/i18n/DialerBidiFormatter.java123
1 files changed, 123 insertions, 0 deletions
diff --git a/java/com/android/dialer/i18n/DialerBidiFormatter.java b/java/com/android/dialer/i18n/DialerBidiFormatter.java
new file mode 100644
index 000000000..4ebaa666c
--- /dev/null
+++ b/java/com/android/dialer/i18n/DialerBidiFormatter.java
@@ -0,0 +1,123 @@
+/*
+ * 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.support.annotation.VisibleForTesting;
+import android.support.v4.text.BidiFormatter;
+import android.text.TextUtils;
+import android.util.Patterns;
+import com.android.dialer.common.Assert;
+import com.google.auto.value.AutoValue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+
+/**
+ * An enhanced version of {@link BidiFormatter} that can recognize a formatted phone number
+ * containing whitespaces.
+ *
+ * <p>Formatted phone numbers usually contain one or more whitespaces (e.g., "+1 650-253-0000",
+ * "(650) 253-0000", etc). {@link BidiFormatter} 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+".)
+ */
+public final class DialerBidiFormatter {
+
+ private DialerBidiFormatter() {}
+
+ /**
+ * Divides the given text into segments, applies {@link BidiFormatter#unicodeWrap(CharSequence)}
+ * to each segment, and then reassembles the text.
+ *
+ * <p>A segment of the text is either a substring matching {@link Patterns#PHONE} or one that does
+ * not.
+ *
+ * @see BidiFormatter#unicodeWrap(CharSequence)
+ */
+ public static CharSequence unicodeWrap(@Nullable CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
+ return text;
+ }
+
+ List<CharSequence> segments = segmentText(text);
+
+ StringBuilder formattedText = new StringBuilder();
+ for (CharSequence segment : segments) {
+ formattedText.append(BidiFormatter.getInstance().unicodeWrap(segment));
+ }
+
+ return formattedText.toString();
+ }
+
+ /**
+ * Segments the given text using {@link Patterns#PHONE}.
+ *
+ * <p>For example, "Mobile, +1 650-253-0000, 20 seconds" will be segmented into {"Mobile, ", "+1
+ * 650-253-0000", ", 20 seconds"}.
+ */
+ @VisibleForTesting
+ static List<CharSequence> segmentText(CharSequence text) {
+ Assert.checkArgument(!TextUtils.isEmpty(text));
+
+ List<CharSequence> segments = new ArrayList<>();
+
+ // Find the start index and the end index of each segment matching the phone number pattern.
+ Matcher matcher = Patterns.PHONE.matcher(text.toString());
+ List<Range> segmentRanges = new ArrayList<>();
+ while (matcher.find()) {
+ segmentRanges.add(Range.newBuilder().setStart(matcher.start()).setEnd(matcher.end()).build());
+ }
+
+ // Segment the text.
+ int currIndex = 0;
+ for (Range segmentRange : segmentRanges) {
+ if (currIndex < segmentRange.getStart()) {
+ segments.add(text.subSequence(currIndex, segmentRange.getStart()));
+ }
+
+ segments.add(text.subSequence(segmentRange.getStart(), segmentRange.getEnd()));
+ currIndex = segmentRange.getEnd();
+ }
+ if (currIndex < text.length()) {
+ segments.add(text.subSequence(currIndex, text.length()));
+ }
+
+ return segments;
+ }
+
+ /** Represents the start index (inclusive) and the end index (exclusive) of a text segment. */
+ @AutoValue
+ abstract static class Range {
+ static Builder newBuilder() {
+ return new AutoValue_DialerBidiFormatter_Range.Builder();
+ }
+
+ abstract int getStart();
+
+ abstract int getEnd();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setStart(int start);
+
+ abstract Builder setEnd(int end);
+
+ abstract Range build();
+ }
+ }
+}