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 */ 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 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 */ 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 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