QueryFilteringUtil.java revision 2f1c7586bcce334ca69022eb8dc6d8965ceb6a05
1/*
2 * Copyright (C) 2017 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
17package com.android.dialer.searchfragment.common;
18
19import android.support.annotation.NonNull;
20import android.telephony.PhoneNumberUtils;
21import android.text.TextUtils;
22import java.util.regex.Pattern;
23
24/** Utility class for filtering, comparing and handling strings and queries. */
25public class QueryFilteringUtil {
26
27  /** Matches strings with "-", "(", ")", 2-9 of at least length one. */
28  static final Pattern T9_PATTERN = Pattern.compile("[\\-()2-9]+");
29
30  /**
31   * @return true if the query is of T9 format and the name's T9 representation belongs to the
32   *     query; false otherwise.
33   */
34  public static boolean nameMatchesT9Query(String query, String name) {
35    if (!T9_PATTERN.matcher(query).matches()) {
36      return false;
37    }
38
39    // Substring
40    if (indexOfQueryNonDigitsIgnored(query, getT9Representation(name)) != -1) {
41      return true;
42    }
43
44    // Check matches initials
45    // TODO investigate faster implementation
46    query = digitsOnly(query);
47    int queryIndex = 0;
48
49    String[] names = name.toLowerCase().split("\\s");
50    for (int i = 0; i < names.length && queryIndex < query.length(); i++) {
51      if (TextUtils.isEmpty(names[i])) {
52        continue;
53      }
54
55      if (getDigit(names[i].charAt(0)) == query.charAt(queryIndex)) {
56        queryIndex++;
57      }
58    }
59
60    return queryIndex == query.length();
61  }
62
63  /** @return true if the number belongs to the query. */
64  public static boolean numberMatchesNumberQuery(String query, String number) {
65    return PhoneNumberUtils.isGlobalPhoneNumber(query)
66        && indexOfQueryNonDigitsIgnored(query, number) != -1;
67  }
68
69  /**
70   * Checks if query is contained in number while ignoring all characters in both that are not
71   * digits (i.e. {@link Character#isDigit(char)} returns false).
72   *
73   * @return index where query is found with all non-digits removed, -1 if it's not found.
74   */
75  static int indexOfQueryNonDigitsIgnored(@NonNull String query, @NonNull String number) {
76    return digitsOnly(number).indexOf(digitsOnly(query));
77  }
78
79  // Returns string with letters replaced with their T9 representation.
80  static String getT9Representation(String s) {
81    StringBuilder builder = new StringBuilder(s.length());
82    for (char c : s.toLowerCase().toCharArray()) {
83      builder.append(getDigit(c));
84    }
85    return builder.toString();
86  }
87
88  /** @return String s with only digits recognized by Character#isDigit() remaining */
89  public static String digitsOnly(String s) {
90    StringBuilder sb = new StringBuilder();
91    for (int i = 0; i < s.length(); i++) {
92      char c = s.charAt(i);
93      if (Character.isDigit(c)) {
94        sb.append(c);
95      }
96    }
97    return sb.toString();
98  }
99
100  // Returns the T9 representation of a lower case character, otherwise returns the character.
101  static char getDigit(char c) {
102    switch (c) {
103      case 'a':
104      case 'b':
105      case 'c':
106        return '2';
107      case 'd':
108      case 'e':
109      case 'f':
110        return '3';
111      case 'g':
112      case 'h':
113      case 'i':
114        return '4';
115      case 'j':
116      case 'k':
117      case 'l':
118        return '5';
119      case 'm':
120      case 'n':
121      case 'o':
122        return '6';
123      case 'p':
124      case 'q':
125      case 'r':
126      case 's':
127        return '7';
128      case 't':
129      case 'u':
130      case 'v':
131        return '8';
132      case 'w':
133      case 'x':
134      case 'y':
135      case 'z':
136        return '9';
137      default:
138        return c;
139    }
140  }
141}
142