1 /* 2 * Copyright (C) 2011 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.contacts.common.format; 18 19 import android.text.SpannableString; 20 import android.text.style.CharacterStyle; 21 import android.text.style.StyleSpan; 22 import android.widget.TextView; 23 24 /** Highlights the text in a text field. */ 25 public class TextHighlighter { 26 27 private int mTextStyle; 28 29 private CharacterStyle mTextStyleSpan; 30 TextHighlighter(int textStyle)31 public TextHighlighter(int textStyle) { 32 mTextStyle = textStyle; 33 mTextStyleSpan = getStyleSpan(); 34 } 35 36 /** 37 * Sets the text on the given text view, highlighting the word that matches the given prefix. 38 * 39 * @param view the view on which to set the text 40 * @param text the string to use as the text 41 * @param prefix the prefix to look for 42 */ setPrefixText(TextView view, String text, String prefix)43 public void setPrefixText(TextView view, String text, String prefix) { 44 view.setText(applyPrefixHighlight(text, prefix)); 45 } 46 getStyleSpan()47 private CharacterStyle getStyleSpan() { 48 return new StyleSpan(mTextStyle); 49 } 50 51 /** 52 * Applies highlight span to the text. 53 * 54 * @param text Text sequence to be highlighted. 55 * @param start Start position of the highlight sequence. 56 * @param end End position of the highlight sequence. 57 */ applyMaskingHighlight(SpannableString text, int start, int end)58 public void applyMaskingHighlight(SpannableString text, int start, int end) { 59 /** Sets text color of the masked locations to be highlighted. */ 60 text.setSpan(getStyleSpan(), start, end, 0); 61 } 62 63 /** 64 * Returns a CharSequence which highlights the given prefix if found in the given text. 65 * 66 * @param text the text to which to apply the highlight 67 * @param prefix the prefix to look for 68 */ applyPrefixHighlight(CharSequence text, String prefix)69 public CharSequence applyPrefixHighlight(CharSequence text, String prefix) { 70 if (prefix == null) { 71 return text; 72 } 73 74 // Skip non-word characters at the beginning of prefix. 75 int prefixStart = 0; 76 while (prefixStart < prefix.length() 77 && !Character.isLetterOrDigit(prefix.charAt(prefixStart))) { 78 prefixStart++; 79 } 80 final String trimmedPrefix = prefix.substring(prefixStart); 81 82 int index = indexOfWordPrefix(text, trimmedPrefix); 83 if (index != -1) { 84 final SpannableString result = new SpannableString(text); 85 result.setSpan(mTextStyleSpan, index, index + trimmedPrefix.length(), 0 /* flags */); 86 return result; 87 } else { 88 return text; 89 } 90 } 91 92 /** 93 * Finds the index of the first word that starts with the given prefix. 94 * 95 * <p>If not found, returns -1. 96 * 97 * @param text the text in which to search for the prefix 98 * @param prefix the text to find, in upper case letters 99 */ indexOfWordPrefix(CharSequence text, String prefix)100 public static int indexOfWordPrefix(CharSequence text, String prefix) { 101 if (prefix == null || text == null) { 102 return -1; 103 } 104 105 int textLength = text.length(); 106 int prefixLength = prefix.length(); 107 108 if (prefixLength == 0 || textLength < prefixLength) { 109 return -1; 110 } 111 112 int i = 0; 113 while (i < textLength) { 114 // Skip non-word characters 115 while (i < textLength && !Character.isLetterOrDigit(text.charAt(i))) { 116 i++; 117 } 118 119 if (i + prefixLength > textLength) { 120 return -1; 121 } 122 123 // Compare the prefixes 124 int j; 125 for (j = 0; j < prefixLength; j++) { 126 if (Character.toUpperCase(text.charAt(i + j)) != prefix.charAt(j)) { 127 break; 128 } 129 } 130 if (j == prefixLength) { 131 return i; 132 } 133 134 // Skip this word 135 while (i < textLength && Character.isLetterOrDigit(text.charAt(i))) { 136 i++; 137 } 138 } 139 140 return -1; 141 } 142 } 143