Home | History | Annotate | Download | only in common
      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