summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/smartdial
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2017-12-19 11:28:51 -0800
committerCopybara-Service <copybara-piper@google.com>2017-12-19 14:00:02 -0800
commitab146531c0710ad46ac347d280b14c798f732a12 (patch)
tree4f28b116aee29fa60ad7fd1b6dbfaefdc54e56e4 /java/com/android/dialer/smartdial
parent0f76636bba4a2c01663c98fa4f3a88d3435e45d7 (diff)
Support dual alphabets in smart dial.
Bug: 30215380,70633239 Test: CompositeSmartDialMapTest, LatinSmartDialMapTest, RussianSmartDialMapTest, SmartDialNameMatcherTest PiperOrigin-RevId: 179580982 Change-Id: I5e4c3e61f0dfdc6ca1e80a93bb985ffec08dd8b0
Diffstat (limited to 'java/com/android/dialer/smartdial')
-rw-r--r--java/com/android/dialer/smartdial/CompositeSmartDialMap.java170
-rw-r--r--java/com/android/dialer/smartdial/LatinSmartDialMap.java794
-rw-r--r--java/com/android/dialer/smartdial/RussianSmartDialMap.java94
-rw-r--r--java/com/android/dialer/smartdial/SmartDialMap.java97
-rw-r--r--java/com/android/dialer/smartdial/SmartDialNameMatcher.java73
-rw-r--r--java/com/android/dialer/smartdial/SmartDialPrefix.java33
6 files changed, 787 insertions, 474 deletions
diff --git a/java/com/android/dialer/smartdial/CompositeSmartDialMap.java b/java/com/android/dialer/smartdial/CompositeSmartDialMap.java
new file mode 100644
index 000000000..d51e46f76
--- /dev/null
+++ b/java/com/android/dialer/smartdial/CompositeSmartDialMap.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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.smartdial;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.util.SimpleArrayMap;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.configprovider.ConfigProviderBindings;
+import com.google.common.base.Optional;
+
+/**
+ * A utility class that combines the functionality of two implementations of {@link SmartDialMap} so
+ * that we support smart dial for dual alphabets.
+ *
+ * <p>Of the two implementations of {@link SmartDialMap}, the default one always takes precedence.
+ * The second one is consulted only when the default one is unable to provide a valid result.
+ *
+ * <p>Note that the second implementation can be absent if it is not defined for the system's 1st
+ * language preference.
+ */
+@SuppressWarnings("Guava")
+public class CompositeSmartDialMap {
+ @VisibleForTesting
+ public static final String FLAG_ENABLE_DUAL_ALPHABETS = "enable_dual_alphabets_on_t9";
+
+ private static final SmartDialMap DEFAULT_MAP = LatinSmartDialMap.getInstance();
+
+ // A map in which each key is an ISO 639-2 language code and the corresponding value is a
+ // SmartDialMap
+ private static final SimpleArrayMap<String, SmartDialMap> EXTRA_MAPS = new SimpleArrayMap<>();
+
+ static {
+ EXTRA_MAPS.put("rus", RussianSmartDialMap.getInstance());
+ }
+
+ private CompositeSmartDialMap() {}
+
+ /**
+ * Returns true if the provided character can be mapped to a key on the dialpad.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ */
+ static boolean isValidDialpadCharacter(Context context, char ch) {
+ if (DEFAULT_MAP.isValidDialpadCharacter(ch)) {
+ return true;
+ }
+
+ Optional<SmartDialMap> extraMap = getExtraMap(context);
+ return extraMap.isPresent() && extraMap.get().isValidDialpadCharacter(ch);
+ }
+
+ /**
+ * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ */
+ static boolean isValidDialpadAlphabeticChar(Context context, char ch) {
+ if (DEFAULT_MAP.isValidDialpadAlphabeticChar(ch)) {
+ return true;
+ }
+
+ Optional<SmartDialMap> extraMap = getExtraMap(context);
+ return extraMap.isPresent() && extraMap.get().isValidDialpadAlphabeticChar(ch);
+ }
+
+ /**
+ * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad.
+ */
+ static boolean isValidDialpadNumericChar(Context context, char ch) {
+ if (DEFAULT_MAP.isValidDialpadNumericChar(ch)) {
+ return true;
+ }
+
+ Optional<SmartDialMap> extraMap = getExtraMap(context);
+ return extraMap.isPresent() && extraMap.get().isValidDialpadNumericChar(ch);
+ }
+
+ /**
+ * Get the index of the key on the dialpad which the character corresponds to.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ * <p>If the provided character can't be mapped to a key on the dialpad, return -1.
+ */
+ static byte getDialpadIndex(Context context, char ch) {
+ Optional<Byte> dialpadIndex = DEFAULT_MAP.getDialpadIndex(ch);
+ if (dialpadIndex.isPresent()) {
+ return dialpadIndex.get();
+ }
+
+ Optional<SmartDialMap> extraMap = getExtraMap(context);
+ if (extraMap.isPresent()) {
+ dialpadIndex = extraMap.get().getDialpadIndex(ch);
+ }
+
+ return dialpadIndex.isPresent() ? dialpadIndex.get() : -1;
+ }
+
+ /**
+ * Get the actual numeric character on the dialpad which the character corresponds to.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ * <p>If the provided character can't be mapped to a key on the dialpad, return the character.
+ */
+ static char getDialpadNumericCharacter(Context context, char ch) {
+ Optional<Character> dialpadNumericChar = DEFAULT_MAP.getDialpadNumericCharacter(ch);
+ if (dialpadNumericChar.isPresent()) {
+ return dialpadNumericChar.get();
+ }
+
+ Optional<SmartDialMap> extraMap = getExtraMap(context);
+ if (extraMap.isPresent()) {
+ dialpadNumericChar = extraMap.get().getDialpadNumericCharacter(ch);
+ }
+
+ return dialpadNumericChar.isPresent() ? dialpadNumericChar.get() : ch;
+ }
+
+ /**
+ * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
+ * from accented characters.
+ *
+ * <p>If the provided character can't be mapped to a key on the dialpad, return the character.
+ */
+ static char normalizeCharacter(Context context, char ch) {
+ Optional<Character> normalizedChar = DEFAULT_MAP.normalizeCharacter(ch);
+ if (normalizedChar.isPresent()) {
+ return normalizedChar.get();
+ }
+
+ Optional<SmartDialMap> extraMap = getExtraMap(context);
+ if (extraMap.isPresent()) {
+ normalizedChar = extraMap.get().normalizeCharacter(ch);
+ }
+
+ return normalizedChar.isPresent() ? normalizedChar.get() : ch;
+ }
+
+ @VisibleForTesting
+ static Optional<SmartDialMap> getExtraMap(Context context) {
+ if (!ConfigProviderBindings.get(context).getBoolean(FLAG_ENABLE_DUAL_ALPHABETS, false)) {
+ return Optional.absent();
+ }
+
+ String languageCode = CompatUtils.getLocale(context).getISO3Language();
+ return EXTRA_MAPS.containsKey(languageCode)
+ ? Optional.of(EXTRA_MAPS.get(languageCode))
+ : Optional.absent();
+ }
+}
diff --git a/java/com/android/dialer/smartdial/LatinSmartDialMap.java b/java/com/android/dialer/smartdial/LatinSmartDialMap.java
index 656fd12b2..b67901bbe 100644
--- a/java/com/android/dialer/smartdial/LatinSmartDialMap.java
+++ b/java/com/android/dialer/smartdial/LatinSmartDialMap.java
@@ -16,53 +16,63 @@
package com.android.dialer.smartdial;
-/** {@link SmartDialMap} for Latin based T9 dialpad searching. */
-public class LatinSmartDialMap implements SmartDialMap {
+import android.support.v4.util.SimpleArrayMap;
+import com.google.common.base.Optional;
- private static final char[] LATIN_LETTERS_TO_DIGITS = {
- '2',
- '2',
- '2', // A,B,C -> 2
- '3',
- '3',
- '3', // D,E,F -> 3
- '4',
- '4',
- '4', // G,H,I -> 4
- '5',
- '5',
- '5', // J,K,L -> 5
- '6',
- '6',
- '6', // M,N,O -> 6
- '7',
- '7',
- '7',
- '7', // P,Q,R,S -> 7
- '8',
- '8',
- '8', // T,U,V -> 8
- '9',
- '9',
- '9',
- '9' // W,X,Y,Z -> 9
- };
+/** A {@link SmartDialMap} for the Latin alphabet, which is for T9 dialpad searching. */
+@SuppressWarnings("Guava")
+final class LatinSmartDialMap extends SmartDialMap {
+ private static final SimpleArrayMap<Character, Character> CHAR_TO_KEY_MAP =
+ new SimpleArrayMap<>();
- @Override
- public boolean isValidDialpadAlphabeticChar(char ch) {
- return (ch >= 'a' && ch <= 'z');
- }
+ static {
+ CHAR_TO_KEY_MAP.put('a', '2');
+ CHAR_TO_KEY_MAP.put('b', '2');
+ CHAR_TO_KEY_MAP.put('c', '2');
- @Override
- public boolean isValidDialpadNumericChar(char ch) {
- return (ch >= '0' && ch <= '9');
+ CHAR_TO_KEY_MAP.put('d', '3');
+ CHAR_TO_KEY_MAP.put('e', '3');
+ CHAR_TO_KEY_MAP.put('f', '3');
+
+ CHAR_TO_KEY_MAP.put('g', '4');
+ CHAR_TO_KEY_MAP.put('h', '4');
+ CHAR_TO_KEY_MAP.put('i', '4');
+
+ CHAR_TO_KEY_MAP.put('j', '5');
+ CHAR_TO_KEY_MAP.put('k', '5');
+ CHAR_TO_KEY_MAP.put('l', '5');
+
+ CHAR_TO_KEY_MAP.put('m', '6');
+ CHAR_TO_KEY_MAP.put('n', '6');
+ CHAR_TO_KEY_MAP.put('o', '6');
+
+ CHAR_TO_KEY_MAP.put('p', '7');
+ CHAR_TO_KEY_MAP.put('q', '7');
+ CHAR_TO_KEY_MAP.put('r', '7');
+ CHAR_TO_KEY_MAP.put('s', '7');
+
+ CHAR_TO_KEY_MAP.put('t', '8');
+ CHAR_TO_KEY_MAP.put('u', '8');
+ CHAR_TO_KEY_MAP.put('v', '8');
+
+ CHAR_TO_KEY_MAP.put('w', '9');
+ CHAR_TO_KEY_MAP.put('x', '9');
+ CHAR_TO_KEY_MAP.put('y', '9');
+ CHAR_TO_KEY_MAP.put('z', '9');
}
- @Override
- public boolean isValidDialpadCharacter(char ch) {
- return (isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch));
+ private static LatinSmartDialMap instance;
+
+ static LatinSmartDialMap getInstance() {
+ if (instance == null) {
+ instance = new LatinSmartDialMap();
+ }
+
+ return instance;
}
+ private LatinSmartDialMap() {}
+
/*
* The switch statement in this function was generated using the python code:
* from unidecode import unidecode
@@ -72,7 +82,7 @@ public class LatinSmartDialMap implements SmartDialMap {
* # Unicode characters that decompose into multiple characters i.e.
* # into ss are not supported for now
* if (len(decoded) == 1 and decoded.isalpha()):
- * print "case '" + char + "': return '" + unidecode(char) + "';"
+ * print "case '" + char + "': return Optional.of('" + unidecode(char) + "');"
*
* This gives us a way to map characters containing accents/diacritics to their
* alphabetic equivalents. The unidecode library can be found at:
@@ -81,705 +91,695 @@ public class LatinSmartDialMap implements SmartDialMap {
* Also remaps all upper case latin characters to their lower case equivalents.
*/
@Override
- public char normalizeCharacter(char ch) {
+ Optional<Character> normalizeCharacter(char ch) {
+ if (isValidDialpadAlphabeticChar(ch)) {
+ return Optional.of(ch);
+ }
+
switch (ch) {
case 'À':
- return 'a';
+ return Optional.of('a');
case 'Á':
- return 'a';
+ return Optional.of('a');
case 'Â':
- return 'a';
+ return Optional.of('a');
case 'Ã':
- return 'a';
+ return Optional.of('a');
case 'Ä':
- return 'a';
+ return Optional.of('a');
case 'Å':
- return 'a';
+ return Optional.of('a');
case 'Ç':
- return 'c';
+ return Optional.of('c');
case 'È':
- return 'e';
+ return Optional.of('e');
case 'É':
- return 'e';
+ return Optional.of('e');
case 'Ê':
- return 'e';
+ return Optional.of('e');
case 'Ë':
- return 'e';
+ return Optional.of('e');
case 'Ì':
- return 'i';
+ return Optional.of('i');
case 'Í':
- return 'i';
+ return Optional.of('i');
case 'Î':
- return 'i';
+ return Optional.of('i');
case 'Ï':
- return 'i';
+ return Optional.of('i');
case 'Ð':
- return 'd';
+ return Optional.of('d');
case 'Ñ':
- return 'n';
+ return Optional.of('n');
case 'Ò':
- return 'o';
+ return Optional.of('o');
case 'Ó':
- return 'o';
+ return Optional.of('o');
case 'Ô':
- return 'o';
+ return Optional.of('o');
case 'Õ':
- return 'o';
+ return Optional.of('o');
case 'Ö':
- return 'o';
+ return Optional.of('o');
case '×':
- return 'x';
+ return Optional.of('x');
case 'Ø':
- return 'o';
+ return Optional.of('o');
case 'Ù':
- return 'u';
+ return Optional.of('u');
case 'Ú':
- return 'u';
+ return Optional.of('u');
case 'Û':
- return 'u';
+ return Optional.of('u');
case 'Ü':
- return 'u';
+ return Optional.of('u');
case 'Ý':
- return 'u';
+ return Optional.of('u');
case 'à':
- return 'a';
+ return Optional.of('a');
case 'á':
- return 'a';
+ return Optional.of('a');
case 'â':
- return 'a';
+ return Optional.of('a');
case 'ã':
- return 'a';
+ return Optional.of('a');
case 'ä':
- return 'a';
+ return Optional.of('a');
case 'å':
- return 'a';
+ return Optional.of('a');
case 'ç':
- return 'c';
+ return Optional.of('c');
case 'è':
- return 'e';
+ return Optional.of('e');
case 'é':
- return 'e';
+ return Optional.of('e');
case 'ê':
- return 'e';
+ return Optional.of('e');
case 'ë':
- return 'e';
+ return Optional.of('e');
case 'ì':
- return 'i';
+ return Optional.of('i');
case 'í':
- return 'i';
+ return Optional.of('i');
case 'î':
- return 'i';
+ return Optional.of('i');
case 'ï':
- return 'i';
+ return Optional.of('i');
case 'ð':
- return 'd';
+ return Optional.of('d');
case 'ñ':
- return 'n';
+ return Optional.of('n');
case 'ò':
- return 'o';
+ return Optional.of('o');
case 'ó':
- return 'o';
+ return Optional.of('o');
case 'ô':
- return 'o';
+ return Optional.of('o');
case 'õ':
- return 'o';
+ return Optional.of('o');
case 'ö':
- return 'o';
+ return Optional.of('o');
case 'ø':
- return 'o';
+ return Optional.of('o');
case 'ù':
- return 'u';
+ return Optional.of('u');
case 'ú':
- return 'u';
+ return Optional.of('u');
case 'û':
- return 'u';
+ return Optional.of('u');
case 'ü':
- return 'u';
+ return Optional.of('u');
case 'ý':
- return 'y';
+ return Optional.of('y');
case 'ÿ':
- return 'y';
+ return Optional.of('y');
case 'Ā':
- return 'a';
+ return Optional.of('a');
case 'ā':
- return 'a';
+ return Optional.of('a');
case 'Ă':
- return 'a';
+ return Optional.of('a');
case 'ă':
- return 'a';
+ return Optional.of('a');
case 'Ą':
- return 'a';
+ return Optional.of('a');
case 'ą':
- return 'a';
+ return Optional.of('a');
case 'Ć':
- return 'c';
+ return Optional.of('c');
case 'ć':
- return 'c';
+ return Optional.of('c');
case 'Ĉ':
- return 'c';
+ return Optional.of('c');
case 'ĉ':
- return 'c';
+ return Optional.of('c');
case 'Ċ':
- return 'c';
+ return Optional.of('c');
case 'ċ':
- return 'c';
+ return Optional.of('c');
case 'Č':
- return 'c';
+ return Optional.of('c');
case 'č':
- return 'c';
+ return Optional.of('c');
case 'Ď':
- return 'd';
+ return Optional.of('d');
case 'ď':
- return 'd';
+ return Optional.of('d');
case 'Đ':
- return 'd';
+ return Optional.of('d');
case 'đ':
- return 'd';
+ return Optional.of('d');
case 'Ē':
- return 'e';
+ return Optional.of('e');
case 'ē':
- return 'e';
+ return Optional.of('e');
case 'Ĕ':
- return 'e';
+ return Optional.of('e');
case 'ĕ':
- return 'e';
+ return Optional.of('e');
case 'Ė':
- return 'e';
+ return Optional.of('e');
case 'ė':
- return 'e';
+ return Optional.of('e');
case 'Ę':
- return 'e';
+ return Optional.of('e');
case 'ę':
- return 'e';
+ return Optional.of('e');
case 'Ě':
- return 'e';
+ return Optional.of('e');
case 'ě':
- return 'e';
+ return Optional.of('e');
case 'Ĝ':
- return 'g';
+ return Optional.of('g');
case 'ĝ':
- return 'g';
+ return Optional.of('g');
case 'Ğ':
- return 'g';
+ return Optional.of('g');
case 'ğ':
- return 'g';
+ return Optional.of('g');
case 'Ġ':
- return 'g';
+ return Optional.of('g');
case 'ġ':
- return 'g';
+ return Optional.of('g');
case 'Ģ':
- return 'g';
+ return Optional.of('g');
case 'ģ':
- return 'g';
+ return Optional.of('g');
case 'Ĥ':
- return 'h';
+ return Optional.of('h');
case 'ĥ':
- return 'h';
+ return Optional.of('h');
case 'Ħ':
- return 'h';
+ return Optional.of('h');
case 'ħ':
- return 'h';
+ return Optional.of('h');
case 'Ĩ':
- return 'i';
+ return Optional.of('i');
case 'ĩ':
- return 'i';
+ return Optional.of('i');
case 'Ī':
- return 'i';
+ return Optional.of('i');
case 'ī':
- return 'i';
+ return Optional.of('i');
case 'Ĭ':
- return 'i';
+ return Optional.of('i');
case 'ĭ':
- return 'i';
+ return Optional.of('i');
case 'Į':
- return 'i';
+ return Optional.of('i');
case 'į':
- return 'i';
+ return Optional.of('i');
case 'İ':
- return 'i';
+ return Optional.of('i');
case 'ı':
- return 'i';
+ return Optional.of('i');
case 'Ĵ':
- return 'j';
+ return Optional.of('j');
case 'ĵ':
- return 'j';
+ return Optional.of('j');
case 'Ķ':
- return 'k';
+ return Optional.of('k');
case 'ķ':
- return 'k';
+ return Optional.of('k');
case 'ĸ':
- return 'k';
+ return Optional.of('k');
case 'Ĺ':
- return 'l';
+ return Optional.of('l');
case 'ĺ':
- return 'l';
+ return Optional.of('l');
case 'Ļ':
- return 'l';
+ return Optional.of('l');
case 'ļ':
- return 'l';
+ return Optional.of('l');
case 'Ľ':
- return 'l';
+ return Optional.of('l');
case 'ľ':
- return 'l';
+ return Optional.of('l');
case 'Ŀ':
- return 'l';
+ return Optional.of('l');
case 'ŀ':
- return 'l';
+ return Optional.of('l');
case 'Ł':
- return 'l';
+ return Optional.of('l');
case 'ł':
- return 'l';
+ return Optional.of('l');
case 'Ń':
- return 'n';
+ return Optional.of('n');
case 'ń':
- return 'n';
+ return Optional.of('n');
case 'Ņ':
- return 'n';
+ return Optional.of('n');
case 'ņ':
- return 'n';
+ return Optional.of('n');
case 'Ň':
- return 'n';
+ return Optional.of('n');
case 'ň':
- return 'n';
+ return Optional.of('n');
case 'Ō':
- return 'o';
+ return Optional.of('o');
case 'ō':
- return 'o';
+ return Optional.of('o');
case 'Ŏ':
- return 'o';
+ return Optional.of('o');
case 'ŏ':
- return 'o';
+ return Optional.of('o');
case 'Ő':
- return 'o';
+ return Optional.of('o');
case 'ő':
- return 'o';
+ return Optional.of('o');
case 'Ŕ':
- return 'r';
+ return Optional.of('r');
case 'ŕ':
- return 'r';
+ return Optional.of('r');
case 'Ŗ':
- return 'r';
+ return Optional.of('r');
case 'ŗ':
- return 'r';
+ return Optional.of('r');
case 'Ř':
- return 'r';
+ return Optional.of('r');
case 'ř':
- return 'r';
+ return Optional.of('r');
case 'Ś':
- return 's';
+ return Optional.of('s');
case 'ś':
- return 's';
+ return Optional.of('s');
case 'Ŝ':
- return 's';
+ return Optional.of('s');
case 'ŝ':
- return 's';
+ return Optional.of('s');
case 'Ş':
- return 's';
+ return Optional.of('s');
case 'ş':
- return 's';
+ return Optional.of('s');
case 'Š':
- return 's';
+ return Optional.of('s');
case 'š':
- return 's';
+ return Optional.of('s');
case 'Ţ':
- return 't';
+ return Optional.of('t');
case 'ţ':
- return 't';
+ return Optional.of('t');
case 'Ť':
- return 't';
+ return Optional.of('t');
case 'ť':
- return 't';
+ return Optional.of('t');
case 'Ŧ':
- return 't';
+ return Optional.of('t');
case 'ŧ':
- return 't';
+ return Optional.of('t');
case 'Ũ':
- return 'u';
+ return Optional.of('u');
case 'ũ':
- return 'u';
+ return Optional.of('u');
case 'Ū':
- return 'u';
+ return Optional.of('u');
case 'ū':
- return 'u';
+ return Optional.of('u');
case 'Ŭ':
- return 'u';
+ return Optional.of('u');
case 'ŭ':
- return 'u';
+ return Optional.of('u');
case 'Ů':
- return 'u';
+ return Optional.of('u');
case 'ů':
- return 'u';
+ return Optional.of('u');
case 'Ű':
- return 'u';
+ return Optional.of('u');
case 'ű':
- return 'u';
+ return Optional.of('u');
case 'Ų':
- return 'u';
+ return Optional.of('u');
case 'ų':
- return 'u';
+ return Optional.of('u');
case 'Ŵ':
- return 'w';
+ return Optional.of('w');
case 'ŵ':
- return 'w';
+ return Optional.of('w');
case 'Ŷ':
- return 'y';
+ return Optional.of('y');
case 'ŷ':
- return 'y';
+ return Optional.of('y');
case 'Ÿ':
- return 'y';
+ return Optional.of('y');
case 'Ź':
- return 'z';
+ return Optional.of('z');
case 'ź':
- return 'z';
+ return Optional.of('z');
case 'Ż':
- return 'z';
+ return Optional.of('z');
case 'ż':
- return 'z';
+ return Optional.of('z');
case 'Ž':
- return 'z';
+ return Optional.of('z');
case 'ž':
- return 'z';
+ return Optional.of('z');
case 'ſ':
- return 's';
+ return Optional.of('s');
case 'ƀ':
- return 'b';
+ return Optional.of('b');
case 'Ɓ':
- return 'b';
+ return Optional.of('b');
case 'Ƃ':
- return 'b';
+ return Optional.of('b');
case 'ƃ':
- return 'b';
+ return Optional.of('b');
case 'Ɔ':
- return 'o';
+ return Optional.of('o');
case 'Ƈ':
- return 'c';
+ return Optional.of('c');
case 'ƈ':
- return 'c';
+ return Optional.of('c');
case 'Ɖ':
- return 'd';
+ return Optional.of('d');
case 'Ɗ':
- return 'd';
+ return Optional.of('d');
case 'Ƌ':
- return 'd';
+ return Optional.of('d');
case 'ƌ':
- return 'd';
+ return Optional.of('d');
case 'ƍ':
- return 'd';
+ return Optional.of('d');
case 'Ɛ':
- return 'e';
+ return Optional.of('e');
case 'Ƒ':
- return 'f';
+ return Optional.of('f');
case 'ƒ':
- return 'f';
+ return Optional.of('f');
case 'Ɠ':
- return 'g';
+ return Optional.of('g');
case 'Ɣ':
- return 'g';
+ return Optional.of('g');
case 'Ɩ':
- return 'i';
+ return Optional.of('i');
case 'Ɨ':
- return 'i';
+ return Optional.of('i');
case 'Ƙ':
- return 'k';
+ return Optional.of('k');
case 'ƙ':
- return 'k';
+ return Optional.of('k');
case 'ƚ':
- return 'l';
+ return Optional.of('l');
case 'ƛ':
- return 'l';
+ return Optional.of('l');
case 'Ɯ':
- return 'w';
+ return Optional.of('w');
case 'Ɲ':
- return 'n';
+ return Optional.of('n');
case 'ƞ':
- return 'n';
+ return Optional.of('n');
case 'Ɵ':
- return 'o';
+ return Optional.of('o');
case 'Ơ':
- return 'o';
+ return Optional.of('o');
case 'ơ':
- return 'o';
+ return Optional.of('o');
case 'Ƥ':
- return 'p';
+ return Optional.of('p');
case 'ƥ':
- return 'p';
+ return Optional.of('p');
case 'ƫ':
- return 't';
+ return Optional.of('t');
case 'Ƭ':
- return 't';
+ return Optional.of('t');
case 'ƭ':
- return 't';
+ return Optional.of('t');
case 'Ʈ':
- return 't';
+ return Optional.of('t');
case 'Ư':
- return 'u';
+ return Optional.of('u');
case 'ư':
- return 'u';
+ return Optional.of('u');
case 'Ʊ':
- return 'y';
+ return Optional.of('y');
case 'Ʋ':
- return 'v';
+ return Optional.of('v');
case 'Ƴ':
- return 'y';
+ return Optional.of('y');
case 'ƴ':
- return 'y';
+ return Optional.of('y');
case 'Ƶ':
- return 'z';
+ return Optional.of('z');
case 'ƶ':
- return 'z';
+ return Optional.of('z');
case 'ƿ':
- return 'w';
+ return Optional.of('w');
case 'Ǎ':
- return 'a';
+ return Optional.of('a');
case 'ǎ':
- return 'a';
+ return Optional.of('a');
case 'Ǐ':
- return 'i';
+ return Optional.of('i');
case 'ǐ':
- return 'i';
+ return Optional.of('i');
case 'Ǒ':
- return 'o';
+ return Optional.of('o');
case 'ǒ':
- return 'o';
+ return Optional.of('o');
case 'Ǔ':
- return 'u';
+ return Optional.of('u');
case 'ǔ':
- return 'u';
+ return Optional.of('u');
case 'Ǖ':
- return 'u';
+ return Optional.of('u');
case 'ǖ':
- return 'u';
+ return Optional.of('u');
case 'Ǘ':
- return 'u';
+ return Optional.of('u');
case 'ǘ':
- return 'u';
+ return Optional.of('u');
case 'Ǚ':
- return 'u';
+ return Optional.of('u');
case 'ǚ':
- return 'u';
+ return Optional.of('u');
case 'Ǜ':
- return 'u';
+ return Optional.of('u');
case 'ǜ':
- return 'u';
+ return Optional.of('u');
case 'Ǟ':
- return 'a';
+ return Optional.of('a');
case 'ǟ':
- return 'a';
+ return Optional.of('a');
case 'Ǡ':
- return 'a';
+ return Optional.of('a');
case 'ǡ':
- return 'a';
+ return Optional.of('a');
case 'Ǥ':
- return 'g';
+ return Optional.of('g');
case 'ǥ':
- return 'g';
+ return Optional.of('g');
case 'Ǧ':
- return 'g';
+ return Optional.of('g');
case 'ǧ':
- return 'g';
+ return Optional.of('g');
case 'Ǩ':
- return 'k';
+ return Optional.of('k');
case 'ǩ':
- return 'k';
+ return Optional.of('k');
case 'Ǫ':
- return 'o';
+ return Optional.of('o');
case 'ǫ':
- return 'o';
+ return Optional.of('o');
case 'Ǭ':
- return 'o';
+ return Optional.of('o');
case 'ǭ':
- return 'o';
+ return Optional.of('o');
case 'ǰ':
- return 'j';
+ return Optional.of('j');
case 'Dz':
- return 'd';
+ return Optional.of('d');
case 'Ǵ':
- return 'g';
+ return Optional.of('g');
case 'ǵ':
- return 'g';
+ return Optional.of('g');
case 'Ƿ':
- return 'w';
+ return Optional.of('w');
case 'Ǹ':
- return 'n';
+ return Optional.of('n');
case 'ǹ':
- return 'n';
+ return Optional.of('n');
case 'Ǻ':
- return 'a';
+ return Optional.of('a');
case 'ǻ':
- return 'a';
+ return Optional.of('a');
case 'Ǿ':
- return 'o';
+ return Optional.of('o');
case 'ǿ':
- return 'o';
+ return Optional.of('o');
case 'Ȁ':
- return 'a';
+ return Optional.of('a');
case 'ȁ':
- return 'a';
+ return Optional.of('a');
case 'Ȃ':
- return 'a';
+ return Optional.of('a');
case 'ȃ':
- return 'a';
+ return Optional.of('a');
case 'Ȅ':
- return 'e';
+ return Optional.of('e');
case 'ȅ':
- return 'e';
+ return Optional.of('e');
case 'Ȇ':
- return 'e';
+ return Optional.of('e');
case 'ȇ':
- return 'e';
+ return Optional.of('e');
case 'Ȉ':
- return 'i';
+ return Optional.of('i');
case 'ȉ':
- return 'i';
+ return Optional.of('i');
case 'Ȋ':
- return 'i';
+ return Optional.of('i');
case 'ȋ':
- return 'i';
+ return Optional.of('i');
case 'Ȍ':
- return 'o';
+ return Optional.of('o');
case 'ȍ':
- return 'o';
+ return Optional.of('o');
case 'Ȏ':
- return 'o';
+ return Optional.of('o');
case 'ȏ':
- return 'o';
+ return Optional.of('o');
case 'Ȑ':
- return 'r';
+ return Optional.of('r');
case 'ȑ':
- return 'r';
+ return Optional.of('r');
case 'Ȓ':
- return 'r';
+ return Optional.of('r');
case 'ȓ':
- return 'r';
+ return Optional.of('r');
case 'Ȕ':
- return 'u';
+ return Optional.of('u');
case 'ȕ':
- return 'u';
+ return Optional.of('u');
case 'Ȗ':
- return 'u';
+ return Optional.of('u');
case 'ȗ':
- return 'u';
+ return Optional.of('u');
case 'Ș':
- return 's';
+ return Optional.of('s');
case 'ș':
- return 's';
+ return Optional.of('s');
case 'Ț':
- return 't';
+ return Optional.of('t');
case 'ț':
- return 't';
+ return Optional.of('t');
case 'Ȝ':
- return 'y';
+ return Optional.of('y');
case 'ȝ':
- return 'y';
+ return Optional.of('y');
case 'Ȟ':
- return 'h';
+ return Optional.of('h');
case 'ȟ':
- return 'h';
+ return Optional.of('h');
case 'Ȥ':
- return 'z';
+ return Optional.of('z');
case 'ȥ':
- return 'z';
+ return Optional.of('z');
case 'Ȧ':
- return 'a';
+ return Optional.of('a');
case 'ȧ':
- return 'a';
+ return Optional.of('a');
case 'Ȩ':
- return 'e';
+ return Optional.of('e');
case 'ȩ':
- return 'e';
+ return Optional.of('e');
case 'Ȫ':
- return 'o';
+ return Optional.of('o');
case 'ȫ':
- return 'o';
+ return Optional.of('o');
case 'Ȭ':
- return 'o';
+ return Optional.of('o');
case 'ȭ':
- return 'o';
+ return Optional.of('o');
case 'Ȯ':
- return 'o';
+ return Optional.of('o');
case 'ȯ':
- return 'o';
+ return Optional.of('o');
case 'Ȱ':
- return 'o';
+ return Optional.of('o');
case 'ȱ':
- return 'o';
+ return Optional.of('o');
case 'Ȳ':
- return 'y';
+ return Optional.of('y');
case 'ȳ':
- return 'y';
+ return Optional.of('y');
case 'A':
- return 'a';
+ return Optional.of('a');
case 'B':
- return 'b';
+ return Optional.of('b');
case 'C':
- return 'c';
+ return Optional.of('c');
case 'D':
- return 'd';
+ return Optional.of('d');
case 'E':
- return 'e';
+ return Optional.of('e');
case 'F':
- return 'f';
+ return Optional.of('f');
case 'G':
- return 'g';
+ return Optional.of('g');
case 'H':
- return 'h';
+ return Optional.of('h');
case 'I':
- return 'i';
+ return Optional.of('i');
case 'J':
- return 'j';
+ return Optional.of('j');
case 'K':
- return 'k';
+ return Optional.of('k');
case 'L':
- return 'l';
+ return Optional.of('l');
case 'M':
- return 'm';
+ return Optional.of('m');
case 'N':
- return 'n';
+ return Optional.of('n');
case 'O':
- return 'o';
+ return Optional.of('o');
case 'P':
- return 'p';
+ return Optional.of('p');
case 'Q':
- return 'q';
+ return Optional.of('q');
case 'R':
- return 'r';
+ return Optional.of('r');
case 'S':
- return 's';
+ return Optional.of('s');
case 'T':
- return 't';
+ return Optional.of('t');
case 'U':
- return 'u';
+ return Optional.of('u');
case 'V':
- return 'v';
+ return Optional.of('v');
case 'W':
- return 'w';
+ return Optional.of('w');
case 'X':
- return 'x';
+ return Optional.of('x');
case 'Y':
- return 'y';
+ return Optional.of('y');
case 'Z':
- return 'z';
+ return Optional.of('z');
default:
- return ch;
+ return Optional.absent();
}
}
@Override
- public byte getDialpadIndex(char ch) {
- if (ch >= '0' && ch <= '9') {
- return (byte) (ch - '0');
- } else if (ch >= 'a' && ch <= 'z') {
- return (byte) (LATIN_LETTERS_TO_DIGITS[ch - 'a'] - '0');
- } else {
- return -1;
- }
- }
-
- @Override
- public char getDialpadNumericCharacter(char ch) {
- if (ch >= 'a' && ch <= 'z') {
- return LATIN_LETTERS_TO_DIGITS[ch - 'a'];
- }
- return ch;
+ SimpleArrayMap<Character, Character> getCharToKeyMap() {
+ return CHAR_TO_KEY_MAP;
}
}
diff --git a/java/com/android/dialer/smartdial/RussianSmartDialMap.java b/java/com/android/dialer/smartdial/RussianSmartDialMap.java
new file mode 100644
index 000000000..ada9182e1
--- /dev/null
+++ b/java/com/android/dialer/smartdial/RussianSmartDialMap.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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.smartdial;
+
+import android.support.v4.util.SimpleArrayMap;
+import com.google.common.base.Optional;
+
+/** A {@link SmartDialMap} for the Russian alphabet. */
+@SuppressWarnings("Guava")
+final class RussianSmartDialMap extends SmartDialMap {
+ private static final SimpleArrayMap<Character, Character> CHAR_TO_KEY_MAP =
+ new SimpleArrayMap<>();
+
+ // Reference: https://en.wikipedia.org/wiki/Russian_alphabet
+ static {
+ CHAR_TO_KEY_MAP.put('а', '2');
+ CHAR_TO_KEY_MAP.put('б', '2');
+ CHAR_TO_KEY_MAP.put('в', '2');
+ CHAR_TO_KEY_MAP.put('г', '2');
+
+ CHAR_TO_KEY_MAP.put('д', '3');
+ CHAR_TO_KEY_MAP.put('е', '3');
+ CHAR_TO_KEY_MAP.put('ё', '3');
+ CHAR_TO_KEY_MAP.put('ж', '3');
+ CHAR_TO_KEY_MAP.put('з', '3');
+
+ CHAR_TO_KEY_MAP.put('и', '4');
+ CHAR_TO_KEY_MAP.put('й', '4');
+ CHAR_TO_KEY_MAP.put('к', '4');
+ CHAR_TO_KEY_MAP.put('л', '4');
+
+ CHAR_TO_KEY_MAP.put('м', '5');
+ CHAR_TO_KEY_MAP.put('н', '5');
+ CHAR_TO_KEY_MAP.put('о', '5');
+ CHAR_TO_KEY_MAP.put('п', '5');
+
+ CHAR_TO_KEY_MAP.put('р', '6');
+ CHAR_TO_KEY_MAP.put('с', '6');
+ CHAR_TO_KEY_MAP.put('т', '6');
+ CHAR_TO_KEY_MAP.put('у', '6');
+
+ CHAR_TO_KEY_MAP.put('ф', '7');
+ CHAR_TO_KEY_MAP.put('х', '7');
+ CHAR_TO_KEY_MAP.put('ц', '7');
+ CHAR_TO_KEY_MAP.put('ч', '7');
+
+ CHAR_TO_KEY_MAP.put('ш', '8');
+ CHAR_TO_KEY_MAP.put('щ', '8');
+ CHAR_TO_KEY_MAP.put('ъ', '8');
+ CHAR_TO_KEY_MAP.put('ы', '8');
+
+ CHAR_TO_KEY_MAP.put('ь', '9');
+ CHAR_TO_KEY_MAP.put('э', '9');
+ CHAR_TO_KEY_MAP.put('ю', '9');
+ CHAR_TO_KEY_MAP.put('я', '9');
+ }
+
+ private static RussianSmartDialMap instance;
+
+ static RussianSmartDialMap getInstance() {
+ if (instance == null) {
+ instance = new RussianSmartDialMap();
+ }
+
+ return instance;
+ }
+
+ private RussianSmartDialMap() {}
+
+ @Override
+ Optional<Character> normalizeCharacter(char ch) {
+ ch = Character.toLowerCase(ch);
+ return isValidDialpadAlphabeticChar(ch) ? Optional.of(ch) : Optional.absent();
+ }
+
+ @Override
+ SimpleArrayMap<Character, Character> getCharToKeyMap() {
+ return CHAR_TO_KEY_MAP;
+ }
+}
diff --git a/java/com/android/dialer/smartdial/SmartDialMap.java b/java/com/android/dialer/smartdial/SmartDialMap.java
index 9638929a6..bc5c9ea72 100644
--- a/java/com/android/dialer/smartdial/SmartDialMap.java
+++ b/java/com/android/dialer/smartdial/SmartDialMap.java
@@ -16,45 +16,88 @@
package com.android.dialer.smartdial;
-/**
- * Note: These methods currently take characters as arguments. For future planned language support,
- * they will need to be changed to use codepoints instead of characters.
- *
- * <p>http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#codePointAt(int)
- *
- * <p>If/when this change is made, LatinSmartDialMap(which operates on chars) will continue to work
- * by simply casting from a codepoint to a character.
- */
-public interface SmartDialMap {
+import android.support.v4.util.SimpleArrayMap;
+import com.google.common.base.Optional;
+
+/** Definition for utilities that supports smart dial in different languages. */
+@SuppressWarnings("Guava")
+abstract class SmartDialMap {
- /*
- * Returns true if the provided character can be mapped to a key on the dialpad
+ /**
+ * Returns true if the provided character can be mapped to a key on the dialpad.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
*/
- boolean isValidDialpadCharacter(char ch);
+ protected boolean isValidDialpadCharacter(char ch) {
+ return isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch);
+ }
- /*
- * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad
+ /**
+ * Returns true if the provided character is a letter and can be mapped to a key on the dialpad.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
*/
- boolean isValidDialpadAlphabeticChar(char ch);
+ protected boolean isValidDialpadAlphabeticChar(char ch) {
+ return getCharToKeyMap().containsKey(ch);
+ }
- /*
- * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad
+ /**
+ * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad.
*/
- boolean isValidDialpadNumericChar(char ch);
+ protected boolean isValidDialpadNumericChar(char ch) {
+ return '0' <= ch && ch <= '9';
+ }
- /*
- * Get the index of the key on the dialpad which the character corresponds to
+ /**
+ * Get the index of the key on the dialpad which the character corresponds to.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ * <p>An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
+ * on the dialpad.
*/
- byte getDialpadIndex(char ch);
+ protected Optional<Byte> getDialpadIndex(char ch) {
+ if (isValidDialpadNumericChar(ch)) {
+ return Optional.of((byte) (ch - '0'));
+ }
- /*
- * Get the actual numeric character on the dialpad which the character corresponds to
+ if (isValidDialpadAlphabeticChar(ch)) {
+ return Optional.of((byte) (getCharToKeyMap().get(ch) - '0'));
+ }
+
+ return Optional.absent();
+ }
+
+ /**
+ * Get the actual numeric character on the dialpad which the character corresponds to.
+ *
+ * <p>The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ * <p>An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
+ * on the dialpad.
*/
- char getDialpadNumericCharacter(char ch);
+ protected Optional<Character> getDialpadNumericCharacter(char ch) {
+ return isValidDialpadAlphabeticChar(ch)
+ ? Optional.of(getCharToKeyMap().get(ch))
+ : Optional.absent();
+ }
- /*
+ /**
* Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
* from accented characters.
+ *
+ * <p>An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
+ * on the dialpad.
+ */
+ abstract Optional<Character> normalizeCharacter(char ch);
+
+ /**
+ * Returns a map in which each key is a normalized character and the corresponding value is a
+ * dialpad key.
*/
- char normalizeCharacter(char ch);
+ abstract SimpleArrayMap<Character, Character> getCharToKeyMap();
}
diff --git a/java/com/android/dialer/smartdial/SmartDialNameMatcher.java b/java/com/android/dialer/smartdial/SmartDialNameMatcher.java
index a9256ec3f..4e3e0cc3f 100644
--- a/java/com/android/dialer/smartdial/SmartDialNameMatcher.java
+++ b/java/com/android/dialer/smartdial/SmartDialNameMatcher.java
@@ -16,6 +16,7 @@
package com.android.dialer.smartdial;
+import android.content.Context;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.android.dialer.smartdial.SmartDialPrefix.PhoneNumberTokens;
@@ -29,8 +30,6 @@ import java.util.ArrayList;
* (J)ohn (S)mith.
*/
public class SmartDialNameMatcher {
-
- public static final SmartDialMap LATIN_SMART_DIAL_MAP = new LatinSmartDialMap();
// Whether or not we allow matches like 57 - (J)ohn (S)mith
private static final boolean ALLOW_INITIAL_MATCH = true;
@@ -39,15 +38,13 @@ public class SmartDialNameMatcher {
private static final int INITIAL_LENGTH_LIMIT = 1;
private final ArrayList<SmartDialMatchPosition> mMatchPositions = new ArrayList<>();
- private final SmartDialMap mMap;
private String mQuery;
// Controls whether to treat an empty query as a match (with anything).
private boolean mShouldMatchEmptyQuery = false;
- public SmartDialNameMatcher(String query, SmartDialMap map) {
+ public SmartDialNameMatcher(String query) {
mQuery = query;
- mMap = map;
}
/**
@@ -56,8 +53,8 @@ public class SmartDialNameMatcher {
* @param number Phone number we want to normalize
* @return Phone number consisting of digits from 0-9
*/
- public static String normalizeNumber(String number, SmartDialMap map) {
- return normalizeNumber(number, 0, map);
+ public static String normalizeNumber(Context context, String number) {
+ return normalizeNumber(context, number, /* offset = */ 0);
}
/**
@@ -67,11 +64,11 @@ public class SmartDialNameMatcher {
* @param offset Offset to start from
* @return Phone number consisting of digits from 0-9
*/
- public static String normalizeNumber(String number, int offset, SmartDialMap map) {
+ public static String normalizeNumber(Context context, String number, int offset) {
final StringBuilder s = new StringBuilder();
for (int i = offset; i < number.length(); i++) {
char ch = number.charAt(i);
- if (map.isValidDialpadNumericChar(ch)) {
+ if (CompositeSmartDialMap.isValidDialpadNumericChar(context, ch)) {
s.append(ch);
}
}
@@ -112,7 +109,7 @@ public class SmartDialNameMatcher {
* with the matching positions otherwise
*/
@Nullable
- public SmartDialMatchPosition matchesNumber(String phoneNumber, String query) {
+ public SmartDialMatchPosition matchesNumber(Context context, String phoneNumber, String query) {
if (TextUtils.isEmpty(phoneNumber)) {
return mShouldMatchEmptyQuery ? new SmartDialMatchPosition(0, 0) : null;
}
@@ -120,15 +117,19 @@ public class SmartDialNameMatcher {
constructEmptyMask(builder, phoneNumber.length());
// Try matching the number as is
- SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
+ SmartDialMatchPosition matchPos =
+ matchesNumberWithOffset(context, phoneNumber, query, /* offset = */ 0);
if (matchPos == null) {
- PhoneNumberTokens phoneNumberTokens = SmartDialPrefix.parsePhoneNumber(phoneNumber);
+ PhoneNumberTokens phoneNumberTokens = SmartDialPrefix.parsePhoneNumber(context, phoneNumber);
if (phoneNumberTokens.countryCodeOffset != 0) {
- matchPos = matchesNumberWithOffset(phoneNumber, query, phoneNumberTokens.countryCodeOffset);
+ matchPos =
+ matchesNumberWithOffset(
+ context, phoneNumber, query, phoneNumberTokens.countryCodeOffset);
}
if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0) {
- matchPos = matchesNumberWithOffset(phoneNumber, query, phoneNumberTokens.nanpCodeOffset);
+ matchPos =
+ matchesNumberWithOffset(context, phoneNumber, query, phoneNumberTokens.nanpCodeOffset);
}
}
if (matchPos != null) {
@@ -145,8 +146,8 @@ public class SmartDialNameMatcher {
* @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
* with the matching positions otherwise
*/
- public SmartDialMatchPosition matchesNumber(String phoneNumber) {
- return matchesNumber(phoneNumber, mQuery);
+ public SmartDialMatchPosition matchesNumber(Context context, String phoneNumber) {
+ return matchesNumber(context, phoneNumber, mQuery);
}
/**
@@ -160,7 +161,7 @@ public class SmartDialNameMatcher {
* with the matching positions otherwise
*/
private SmartDialMatchPosition matchesNumberWithOffset(
- String phoneNumber, String query, int offset) {
+ Context context, String phoneNumber, String query, int offset) {
if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
return mShouldMatchEmptyQuery ? new SmartDialMatchPosition(offset, offset) : null;
}
@@ -171,7 +172,7 @@ public class SmartDialNameMatcher {
break;
}
char ch = phoneNumber.charAt(i);
- if (mMap.isValidDialpadNumericChar(ch)) {
+ if (CompositeSmartDialMap.isValidDialpadNumericChar(context, ch)) {
if (ch != query.charAt(queryAt)) {
return null;
}
@@ -225,7 +226,10 @@ public class SmartDialNameMatcher {
* match positions (multiple matches correspond to initial matches).
*/
private boolean matchesCombination(
- String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) {
+ Context context,
+ String displayName,
+ String query,
+ ArrayList<SmartDialMatchPosition> matchList) {
StringBuilder builder = new StringBuilder();
constructEmptyMask(builder, displayName.length());
final int nameLength = displayName.length();
@@ -260,10 +264,10 @@ public class SmartDialNameMatcher {
while (nameStart < nameLength && queryStart < queryLength) {
char ch = displayName.charAt(nameStart);
// Strip diacritics from accented characters if any
- ch = mMap.normalizeCharacter(ch);
- if (mMap.isValidDialpadCharacter(ch)) {
- if (mMap.isValidDialpadAlphabeticChar(ch)) {
- ch = mMap.getDialpadNumericCharacter(ch);
+ ch = CompositeSmartDialMap.normalizeCharacter(context, ch);
+ if (CompositeSmartDialMap.isValidDialpadCharacter(context, ch)) {
+ if (CompositeSmartDialMap.isValidDialpadAlphabeticChar(context, ch)) {
+ ch = CompositeSmartDialMap.getDialpadNumericCharacter(context, ch);
}
if (ch != query.charAt(queryStart)) {
// Failed to match the current character in the query.
@@ -283,12 +287,16 @@ public class SmartDialNameMatcher {
// then skip to the end of the "Yoghurt" token.
if (queryStart == 0
- || mMap.isValidDialpadCharacter(
- mMap.normalizeCharacter(displayName.charAt(nameStart - 1)))) {
+ || CompositeSmartDialMap.isValidDialpadCharacter(
+ context,
+ CompositeSmartDialMap.normalizeCharacter(
+ context, displayName.charAt(nameStart - 1)))) {
// skip to the next token, in the case of 1 or 2.
while (nameStart < nameLength
- && mMap.isValidDialpadCharacter(
- mMap.normalizeCharacter(displayName.charAt(nameStart)))) {
+ && CompositeSmartDialMap.isValidDialpadCharacter(
+ context,
+ CompositeSmartDialMap.normalizeCharacter(
+ context, displayName.charAt(nameStart)))) {
nameStart++;
}
nameStart++;
@@ -316,7 +324,9 @@ public class SmartDialNameMatcher {
// find the next separator in the query string
int j;
for (j = nameStart; j < nameLength; j++) {
- if (!mMap.isValidDialpadCharacter(mMap.normalizeCharacter(displayName.charAt(j)))) {
+ if (!CompositeSmartDialMap.isValidDialpadCharacter(
+ context,
+ CompositeSmartDialMap.normalizeCharacter(context, displayName.charAt(j)))) {
break;
}
}
@@ -324,7 +334,8 @@ public class SmartDialNameMatcher {
if (j < nameLength - 1) {
final String remainder = displayName.substring(j + 1);
final ArrayList<SmartDialMatchPosition> partialTemp = new ArrayList<>();
- if (matchesCombination(remainder, query.substring(queryStart + 1), partialTemp)) {
+ if (matchesCombination(
+ context, remainder, query.substring(queryStart + 1), partialTemp)) {
// store the list of possible match positions
SmartDialMatchPosition.advanceMatchPositions(partialTemp, j + 1);
@@ -393,9 +404,9 @@ public class SmartDialNameMatcher {
* contained in query. If the function returns true, matchList will contain an ArrayList of
* match positions (multiple matches correspond to initial matches).
*/
- public boolean matches(String displayName) {
+ public boolean matches(Context context, String displayName) {
mMatchPositions.clear();
- return matchesCombination(displayName, mQuery, mMatchPositions);
+ return matchesCombination(context, displayName, mQuery, mMatchPositions);
}
public ArrayList<SmartDialMatchPosition> getMatchPositions() {
diff --git a/java/com/android/dialer/smartdial/SmartDialPrefix.java b/java/com/android/dialer/smartdial/SmartDialPrefix.java
index 36f174b33..b9c1f8c11 100644
--- a/java/com/android/dialer/smartdial/SmartDialPrefix.java
+++ b/java/com/android/dialer/smartdial/SmartDialPrefix.java
@@ -52,8 +52,6 @@ public class SmartDialPrefix {
"DialtactsActivity_user_sim_country_code";
private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
- /** Dialpad mapping. */
- private static final SmartDialMap mMap = new LatinSmartDialMap();
private static String sUserSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
/** Indicates whether user is in NANP regions. */
@@ -95,7 +93,7 @@ public class SmartDialPrefix {
* @param contactName Contact's name stored in string.
* @return A list of name tokens, for example separated first names, last name, etc.
*/
- public static ArrayList<String> parseToIndexTokens(String contactName) {
+ public static ArrayList<String> parseToIndexTokens(Context context, String contactName) {
final int length = contactName.length();
final ArrayList<String> result = new ArrayList<>();
char c;
@@ -106,10 +104,10 @@ public class SmartDialPrefix {
* " ", mark the current token as complete and add it to the list of tokens.
*/
for (int i = 0; i < length; i++) {
- c = mMap.normalizeCharacter(contactName.charAt(i));
- if (mMap.isValidDialpadCharacter(c)) {
+ c = CompositeSmartDialMap.normalizeCharacter(context, contactName.charAt(i));
+ if (CompositeSmartDialMap.isValidDialpadCharacter(context, c)) {
/** Converts a character into the number on dialpad that represents the character. */
- currentIndexToken.append(mMap.getDialpadIndex(c));
+ currentIndexToken.append(CompositeSmartDialMap.getDialpadIndex(context, c));
} else {
if (currentIndexToken.length() != 0) {
result.add(currentIndexToken.toString());
@@ -132,11 +130,11 @@ public class SmartDialPrefix {
* @param index The contact's name in string.
* @return A List of strings, whose prefix can be used to look up the contact.
*/
- public static ArrayList<String> generateNamePrefixes(String index) {
+ public static ArrayList<String> generateNamePrefixes(Context context, String index) {
final ArrayList<String> result = new ArrayList<>();
/** Parses the name into a list of tokens. */
- final ArrayList<String> indexTokens = parseToIndexTokens(index);
+ final ArrayList<String> indexTokens = parseToIndexTokens(context, index);
if (indexTokens.size() > 0) {
/**
@@ -198,13 +196,13 @@ public class SmartDialPrefix {
* @param number String of user's phone number.
* @return A list of strings where any prefix of any entry can be used to look up the number.
*/
- public static ArrayList<String> parseToNumberTokens(String number) {
+ public static ArrayList<String> parseToNumberTokens(Context context, String number) {
final ArrayList<String> result = new ArrayList<>();
if (!TextUtils.isEmpty(number)) {
/** Adds the full number to the list. */
- result.add(SmartDialNameMatcher.normalizeNumber(number, mMap));
+ result.add(SmartDialNameMatcher.normalizeNumber(context, number));
- final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(number);
+ final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(context, number);
if (phoneNumberTokens == null) {
return result;
}
@@ -212,12 +210,13 @@ public class SmartDialPrefix {
if (phoneNumberTokens.countryCodeOffset != 0) {
result.add(
SmartDialNameMatcher.normalizeNumber(
- number, phoneNumberTokens.countryCodeOffset, mMap));
+ context, number, phoneNumberTokens.countryCodeOffset));
}
if (phoneNumberTokens.nanpCodeOffset != 0) {
result.add(
- SmartDialNameMatcher.normalizeNumber(number, phoneNumberTokens.nanpCodeOffset, mMap));
+ SmartDialNameMatcher.normalizeNumber(
+ context, number, phoneNumberTokens.nanpCodeOffset));
}
}
return result;
@@ -229,13 +228,13 @@ public class SmartDialPrefix {
* @param number Raw phone number.
* @return a PhoneNumberToken instance with country code, NANP code information.
*/
- public static PhoneNumberTokens parsePhoneNumber(String number) {
+ public static PhoneNumberTokens parsePhoneNumber(Context context, String number) {
String countryCode = "";
int countryCodeOffset = 0;
int nanpNumberOffset = 0;
if (!TextUtils.isEmpty(number)) {
- String normalizedNumber = SmartDialNameMatcher.normalizeNumber(number, mMap);
+ String normalizedNumber = SmartDialNameMatcher.normalizeNumber(context, number);
if (number.charAt(0) == '+') {
/** If the number starts with '+', tries to find valid country code. */
for (int i = 1; i <= 1 + 3; i++) {
@@ -518,10 +517,6 @@ public class SmartDialPrefix {
return result;
}
- public static SmartDialMap getMap() {
- return mMap;
- }
-
/**
* Indicates whether the given country uses NANP numbers
*