• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dialer.searchfragment.common;
18 
19 import android.graphics.Typeface;
20 import android.support.annotation.NonNull;
21 import android.support.annotation.Nullable;
22 import android.text.SpannableString;
23 import android.text.Spanned;
24 import android.text.TextUtils;
25 import android.text.style.StyleSpan;
26 
27 /** Utility class for handling bolding queries contained in string. */
28 public class QueryBoldingUtil {
29 
30   /**
31    * Compares a name and query and returns a {@link CharSequence} with bolded characters.
32    *
33    * <p>Some example:
34    *
35    * <ul>
36    *   <li>"query" would bold "John [query] Smith"
37    *   <li>"222" would bold "[AAA] Mom"
38    *   <li>"222" would bold "[A]llen [A]lex [A]aron"
39    * </ul>
40    *
41    * @param query containing any characters
42    * @param name of a contact/string that query will compare to
43    * @return name with query bolded if query can be found in the name.
44    */
getNameWithQueryBolded(@ullable String query, @NonNull String name)45   public static CharSequence getNameWithQueryBolded(@Nullable String query, @NonNull String name) {
46     if (TextUtils.isEmpty(query)) {
47       return name;
48     }
49 
50     int index = -1;
51     int numberOfBoldedCharacters = 0;
52 
53     if (QueryFilteringUtil.nameMatchesT9Query(query, name)) {
54       // Bold the characters that match the t9 query
55       String t9 = QueryFilteringUtil.getT9Representation(name);
56       index = QueryFilteringUtil.indexOfQueryNonDigitsIgnored(query, t9);
57       if (index == -1) {
58         return getNameWithInitialsBolded(query, name);
59       }
60       numberOfBoldedCharacters = query.length();
61 
62       for (int i = 0; i < query.length(); i++) {
63         char c = query.charAt(i);
64         if (!Character.isDigit(c)) {
65           numberOfBoldedCharacters--;
66         }
67       }
68 
69       for (int i = 0; i < index + numberOfBoldedCharacters; i++) {
70         if (!Character.isLetterOrDigit(name.charAt(i))) {
71           if (i < index) {
72             index++;
73           } else {
74             numberOfBoldedCharacters++;
75           }
76         }
77       }
78     }
79 
80     if (index == -1) {
81       // Bold the query as an exact match in the name
82       index = name.toLowerCase().indexOf(query);
83       numberOfBoldedCharacters = query.length();
84     }
85 
86     return index == -1 ? name : getBoldedString(name, index, numberOfBoldedCharacters);
87   }
88 
getNameWithInitialsBolded(String query, String name)89   private static CharSequence getNameWithInitialsBolded(String query, String name) {
90     SpannableString boldedInitials = new SpannableString(name);
91     name = name.toLowerCase();
92     int initialsBolded = 0;
93     int nameIndex = -1;
94 
95     while (++nameIndex < name.length() && initialsBolded < query.length()) {
96       if ((nameIndex == 0 || name.charAt(nameIndex - 1) == ' ')
97           && QueryFilteringUtil.getDigit(name.charAt(nameIndex)) == query.charAt(initialsBolded)) {
98         boldedInitials.setSpan(
99             new StyleSpan(Typeface.BOLD),
100             nameIndex,
101             nameIndex + 1,
102             Spanned.SPAN_INCLUSIVE_INCLUSIVE);
103         initialsBolded++;
104       }
105     }
106     return boldedInitials;
107   }
108 
109   /**
110    * Compares a number and a query and returns a {@link CharSequence} with bolded characters.
111    *
112    * <ul>
113    *   <li>"123" would bold "(650)34[1-23]24"
114    *   <li>"123" would bold "+1([123])111-2222
115    * </ul>
116    *
117    * @param query containing only numbers and phone number related characters "(", ")", "-", "+"
118    * @param number phone number of a contact that the query will compare to.
119    * @return number with query bolded if query can be found in the number.
120    */
getNumberWithQueryBolded( @ullable String query, @NonNull String number)121   public static CharSequence getNumberWithQueryBolded(
122       @Nullable String query, @NonNull String number) {
123     if (TextUtils.isEmpty(query) || !QueryFilteringUtil.numberMatchesNumberQuery(query, number)) {
124       return number;
125     }
126 
127     int index = QueryFilteringUtil.indexOfQueryNonDigitsIgnored(query, number);
128     int boldedCharacters = query.length();
129 
130     for (char c : query.toCharArray()) {
131       if (!Character.isDigit(c)) {
132         boldedCharacters--;
133       }
134     }
135 
136     for (int i = 0; i < index + boldedCharacters; i++) {
137       if (!Character.isDigit(number.charAt(i))) {
138         if (i <= index) {
139           index++;
140         } else {
141           boldedCharacters++;
142         }
143       }
144     }
145     return getBoldedString(number, index, boldedCharacters);
146   }
147 
getBoldedString(String s, int index, int numBolded)148   private static SpannableString getBoldedString(String s, int index, int numBolded) {
149     SpannableString span = new SpannableString(s);
150     span.setSpan(
151         new StyleSpan(Typeface.BOLD), index, index + numBolded, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
152     return span;
153   }
154 }
155