Fixes highlighting of prefix.
Highlighting of a prefix (from a search) in contacts name was not done
correctly when the name was also set to be in bold. This commit changes
the logic to separate the two aspects and combines them in a way that
makes things easier to use.
Change-Id: Ibdf12d8478b7ab44ca0be82353c43ed9118d29cd
diff --git a/src/com/android/contacts/format/DisplayNameFormatter.java b/src/com/android/contacts/format/DisplayNameFormatter.java
index 05698f8..07577f2 100644
--- a/src/com/android/contacts/format/DisplayNameFormatter.java
+++ b/src/com/android/contacts/format/DisplayNameFormatter.java
@@ -25,8 +25,6 @@
import android.text.Spannable;
import android.widget.TextView;
-import java.util.Arrays;
-
/**
* Sets the content of the given text view, to contain the formatted display name, with a
* prefix if necessary.
@@ -62,6 +60,11 @@
public void setDisplayName(TextView view, int displayOrder,
boolean highlightingEnabled, char[] highlightedPrefix) {
+ view.setText(getDisplayName(displayOrder, highlightingEnabled, highlightedPrefix));
+ }
+
+ public CharSequence getDisplayName(int displayOrder, boolean highlightingEnabled,
+ char[] highlightedPrefix) {
// Compute the point at which name and alternate name overlap (for bolding).
int overlapPoint = FormatUtils.overlapPoint(mNameBuffer, mAlternateNameBuffer);
int boldStart = 0;
@@ -72,35 +75,29 @@
}
int size = mNameBuffer.sizeCopied;
- if (size != 0) {
- if (highlightedPrefix != null) {
- mPrefixHighlighter.setText(view, mNameBuffer, highlightedPrefix);
- } else if (highlightingEnabled) {
- if (mTextWithHighlighting == null) {
- mTextWithHighlighting =
- mTextWithHighlightingFactory.createTextWithHighlighting();
- }
- mTextWithHighlighting.setText(mNameBuffer, mAlternateNameBuffer);
- if (overlapPoint > 0) {
- // Bold the first name.
- view.setText(FormatUtils.applyStyleToSpan(Typeface.BOLD,
- mTextWithHighlighting, boldStart, boldEnd,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
- } else {
- view.setText(mTextWithHighlighting);
- }
- } else {
- if (overlapPoint > 0) {
- // Bold the first name.
- view.setText(FormatUtils.applyStyleToSpan(Typeface.BOLD,
- new String(Arrays.copyOfRange(mNameBuffer.data, 0, size)),
- boldStart, boldEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
- } else {
- view.setText(mNameBuffer.data, 0, size);
- }
- }
- } else {
- view.setText(mUnknownNameText);
+ if (size == 0) {
+ return mUnknownNameText;
}
+
+ CharSequence text;
+ if (highlightingEnabled) {
+ if (mTextWithHighlighting == null) {
+ mTextWithHighlighting =
+ mTextWithHighlightingFactory.createTextWithHighlighting();
+ }
+ mTextWithHighlighting.setText(mNameBuffer, mAlternateNameBuffer);
+ text = mTextWithHighlighting;
+ } else {
+ text = FormatUtils.charArrayBufferToString(mNameBuffer);
+ }
+ if (highlightedPrefix != null) {
+ text = mPrefixHighlighter.apply(text, highlightedPrefix);
+ }
+ if (overlapPoint > 0) {
+ // Bold the first or last name.
+ text = FormatUtils.applyStyleToSpan(Typeface.BOLD, text, boldStart, boldEnd,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ return text;
}
}
diff --git a/src/com/android/contacts/format/FormatUtils.java b/src/com/android/contacts/format/FormatUtils.java
index 757e235..34a6078 100644
--- a/src/com/android/contacts/format/FormatUtils.java
+++ b/src/com/android/contacts/format/FormatUtils.java
@@ -120,45 +120,51 @@
}
}
+ /** Returns a String that represents the content of the given {@link CharArrayBuffer}. */
+ public static String charArrayBufferToString(CharArrayBuffer buffer) {
+ return new String(buffer.data, 0, buffer.sizeCopied);
+ }
+
/**
* Finds the index of the first word that starts with the given prefix.
* <p>
* If not found, returns -1.
+ *
+ * @param text the text in which to search for the prefix
+ * @param prefix the text to find, in upper case letters
*/
- public static int indexOfWordPrefix(CharArrayBuffer buffer, char[] prefix) {
- if (prefix == null || prefix.length == 0) {
+ public static int indexOfWordPrefix(CharSequence text, char[] prefix) {
+ int textLength = text.length();
+ int prefixLength = prefix.length;
+
+ if (prefix == null || prefixLength == 0 || textLength < prefixLength) {
return -1;
}
- char[] string1 = buffer.data;
- int bufferSize = buffer.sizeCopied;
- int prefixSize = prefix.length;
-
int i = 0;
- while (i < bufferSize) {
-
+ while (i < textLength) {
// Skip non-word characters
- while (i < bufferSize && !Character.isLetterOrDigit(string1[i])) {
+ while (i < textLength && !Character.isLetterOrDigit(text.charAt(i))) {
i++;
}
- if (i + prefixSize > bufferSize) {
+ if (i + prefixLength > textLength) {
return -1;
}
// Compare the prefixes
int j;
- for (j = 0; j < prefixSize; j++) {
- if (Character.toUpperCase(string1[i+j]) != prefix[j]) {
+ for (j = 0; j < prefixLength; j++) {
+ if (Character.toUpperCase(text.charAt(i + j)) != prefix[j]) {
break;
}
}
- if (j == prefixSize) {
+ if (j == prefixLength) {
return i;
}
// Skip this word
- while (i < bufferSize && Character.isLetterOrDigit(string1[i])) {
+ while (i < textLength && Character.isLetterOrDigit(text.charAt(i))) {
i++;
}
}
diff --git a/src/com/android/contacts/format/PrefixHighlighter.java b/src/com/android/contacts/format/PrefixHighlighter.java
index fce1d4b..943dcb4 100644
--- a/src/com/android/contacts/format/PrefixHighlighter.java
+++ b/src/com/android/contacts/format/PrefixHighlighter.java
@@ -17,9 +17,7 @@
package com.android.contacts.format;
import android.database.CharArrayBuffer;
-import android.graphics.Typeface;
import android.text.SpannableString;
-import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.widget.TextView;
@@ -27,7 +25,6 @@
* Highlights the text in a text field.
*/
public class PrefixHighlighter {
- private final CharArrayBuffer mBuffer = new CharArrayBuffer(128);
private final int mPrefixHighlightColor;
private ForegroundColorSpan mPrefixColorSpan;
@@ -44,8 +41,7 @@
* @param prefix the prefix to look for
*/
public void setText(TextView view, String text, char[] prefix) {
- FormatUtils.copyToCharArrayBuffer(text, mBuffer);
- setText(view, mBuffer, prefix);
+ view.setText(apply(text, prefix));
}
/**
@@ -56,20 +52,27 @@
* @param prefix the prefix to look for
*/
public void setText(TextView view, CharArrayBuffer text, char[] prefix) {
+ setText(view, FormatUtils.charArrayBufferToString(text), prefix);
+ }
+
+ /**
+ * Returns a CharSequence which highlights the given prefix if found in the given text.
+ *
+ * @param text the text to which to apply the highlight
+ * @param prefix the prefix to look for
+ */
+ public CharSequence apply(CharSequence text, char[] prefix) {
int index = FormatUtils.indexOfWordPrefix(text, prefix);
if (index != -1) {
if (mPrefixColorSpan == null) {
mPrefixColorSpan = new ForegroundColorSpan(mPrefixHighlightColor);
}
- String string = new String(text.data, 0, text.sizeCopied);
- SpannableString name = new SpannableString(
- FormatUtils.applyStyleToSpan(Typeface.BOLD, string, 0, index,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE));
- name.setSpan(mPrefixColorSpan, index, index + prefix.length, 0 /* flags */);
- view.setText(name);
+ SpannableString result = new SpannableString(text);
+ result.setSpan(mPrefixColorSpan, index, index + prefix.length, 0 /* flags */);
+ return result;
} else {
- view.setText(text.data, 0, text.sizeCopied);
+ return text;
}
}
}
diff --git a/tests/src/com/android/contacts/format/DisplayNameFormatterTest.java b/tests/src/com/android/contacts/format/DisplayNameFormatterTest.java
index a7843c1..fb834c0 100644
--- a/tests/src/com/android/contacts/format/DisplayNameFormatterTest.java
+++ b/tests/src/com/android/contacts/format/DisplayNameFormatterTest.java
@@ -28,6 +28,10 @@
@SmallTest
public class DisplayNameFormatterTest extends AndroidTestCase {
private static final int TEST_PREFIX_HIGHLIGHT_COLOR = 0xFF0000;
+ /** The HTML code used to mark the start of the highlighted part. */
+ private static final String START = "<font color =\"#1ff0000\">";
+ /** The HTML code used to mark the end of the highlighted part. */
+ private static final String END = "</font>";
private PrefixHighlighter mPrefixHighlighter;
/** The object under test. */
@@ -86,7 +90,26 @@
public void testSetDisplayName_Prefix() {
setNames("John Doe", "Doe John");
setDisplayNameWithPrefix("DO");
- SpannedTestUtils.checkHtmlText("<b>John </b><font color =\"#1ff0000\">Do</font>e", mView);
+ SpannedTestUtils.checkHtmlText("<b>John </b>" + START + "Do" + END + "e", mView);
+ }
+
+ public void testSetDisplayName_PrefixFirstName() {
+ setNames("John Doe", "Doe John");
+ setDisplayNameWithPrefix("JO");
+ SpannedTestUtils.checkHtmlText(START + "<b>Jo</b>" + END + "<b>hn </b>Doe", mView);
+ }
+
+ public void testSetDisplayName_PrefixMiddleName() {
+ setNames("John Paul Doe", "Doe John Paul");
+ setDisplayNameWithPrefix("PAU");
+ SpannedTestUtils.checkHtmlText("<b>John </b>" + START + "<b>Pau</b>" + END + "<b>l </b>Doe",
+ mView);
+ }
+
+ public void testSetDisplayName_ReversedPrefix() {
+ setNames("John Doe", "Doe John");
+ setDisplayNameReversedWithPrefix("DO");
+ SpannedTestUtils.checkHtmlText("John " + START + "<b>Do</b>" + END + "<b>e</b>", mView);
}
public void testSetDisplayName_Empty() {
@@ -153,6 +176,15 @@
}
/**
+ * Sets the display name reversed on the text view with prefix highlighting enabled.
+ */
+ private void setDisplayNameReversedWithPrefix(String prefix) {
+ mDisplayNameFormatter.setDisplayName(mView,
+ ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE, false,
+ prefix.toCharArray());
+ }
+
+ /**
* Sets the display name on the text view with highlighting enabled.
*/
private void setDisplayNameWithHighlighting() {
diff --git a/tests/src/com/android/contacts/format/FormatUtilsTests.java b/tests/src/com/android/contacts/format/FormatUtilsTests.java
index 0c1c925..42e2d53 100644
--- a/tests/src/com/android/contacts/format/FormatUtilsTests.java
+++ b/tests/src/com/android/contacts/format/FormatUtilsTests.java
@@ -47,6 +47,19 @@
checkCopyToCharArrayBuffer(charArrayBuffer, "test test test test test", 24);
}
+ public void testCharArrayBufferToString() {
+ checkCharArrayBufferToString("");
+ checkCharArrayBufferToString("test");
+ checkCharArrayBufferToString("test test test test test");
+ }
+
+ /** Checks that copying a string into a {@link CharArrayBuffer} and back works correctly. */
+ private void checkCharArrayBufferToString(String text) {
+ CharArrayBuffer buffer = new CharArrayBuffer(20);
+ FormatUtils.copyToCharArrayBuffer(text, buffer);
+ assertEquals(text, FormatUtils.charArrayBufferToString(buffer));
+ }
+
/**
* Checks that copying into the char array buffer copies the values correctly.
*/
@@ -88,9 +101,6 @@
* @param expectedIndex the expected value to be returned by the function
*/
private void checkIndexOfWordPrefix(String text, String wordPrefix, int expectedIndex) {
- CharArrayBuffer buffer = new CharArrayBuffer(text.length());
- FormatUtils.copyToCharArrayBuffer(text, buffer);
- assertEquals(expectedIndex,
- FormatUtils.indexOfWordPrefix(buffer, wordPrefix.toCharArray()));
+ assertEquals(expectedIndex, FormatUtils.indexOfWordPrefix(text, wordPrefix.toCharArray()));
}
}
diff --git a/tests/src/com/android/contacts/format/PrefixHighligherTest.java b/tests/src/com/android/contacts/format/PrefixHighligherTest.java
index 9ec1c48..5500abf 100644
--- a/tests/src/com/android/contacts/format/PrefixHighligherTest.java
+++ b/tests/src/com/android/contacts/format/PrefixHighligherTest.java
@@ -17,11 +17,7 @@
package com.android.contacts.format;
import android.database.CharArrayBuffer;
-import android.graphics.Typeface;
import android.test.AndroidTestCase;
-import android.text.Spannable;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StyleSpan;
import android.widget.TextView;
/**
@@ -29,6 +25,10 @@
*/
public class PrefixHighligherTest extends AndroidTestCase {
private static final int TEST_PREFIX_HIGHLIGHT_COLOR = 0xFF0000;
+ /** The HTML code used to mark the start of the highlighted part. */
+ private static final String START = "<font color =\"#1ff0000\">";
+ /** The HTML code used to mark the end of the highlighted part. */
+ private static final String END = "</font>";
/** The object under test. */
private PrefixHighlighter mPrefixHighlighter;
@@ -47,39 +47,46 @@
public void testSetText_EmptyPrefix() {
mPrefixHighlighter.setText(mView, "", new char[0]);
- checkTextAndNoSpans("");
+ SpannedTestUtils.checkHtmlText("", mView);
+
mPrefixHighlighter.setText(mView, "test", new char[0]);
- checkTextAndNoSpans("test");
+ SpannedTestUtils.checkHtmlText("test", mView);
}
public void testSetText_MatchingPrefix() {
mPrefixHighlighter.setText(mView, "test", "TE".toCharArray());
- checkTextAndSpan("test", 0, 2);
+ SpannedTestUtils.checkHtmlText(START + "te" + END + "st", mView);
+
mPrefixHighlighter.setText(mView, "Test", "TE".toCharArray());
- checkTextAndSpan("Test", 0, 2);
+ SpannedTestUtils.checkHtmlText(START + "Te" + END + "st", mView);
+
mPrefixHighlighter.setText(mView, "TEst", "TE".toCharArray());
- checkTextAndSpan("TEst", 0, 2);
+ SpannedTestUtils.checkHtmlText(START + "TE" + END + "st", mView);
+
mPrefixHighlighter.setText(mView, "a test", "TE".toCharArray());
- checkTextAndSpan("a test", 2, 4);
+ SpannedTestUtils.checkHtmlText("a " + START + "te" + END + "st", mView);
}
public void testSetText_NotMatchingPrefix() {
mPrefixHighlighter.setText(mView, "test", "TA".toCharArray());
- checkTextAndNoSpans("test");
+ SpannedTestUtils.checkHtmlText("test", mView);
}
public void testSetText_FirstMatch() {
mPrefixHighlighter.setText(mView, "a test's tests are not tests", "TE".toCharArray());
- checkTextAndSpan("a test's tests are not tests", 2, 4);
+ SpannedTestUtils.checkHtmlText("a " +START + "te" + END + "st's tests are not tests",
+ mView);
}
public void testSetText_NoMatchingMiddleOfWord() {
mPrefixHighlighter.setText(mView, "atest", "TE".toCharArray());
- checkTextAndNoSpans("atest");
+ SpannedTestUtils.checkHtmlText("atest", mView);
+
mPrefixHighlighter.setText(mView, "atest otest", "TE".toCharArray());
- checkTextAndNoSpans("atest otest");
+ SpannedTestUtils.checkHtmlText("atest otest", mView);
+
mPrefixHighlighter.setText(mView, "atest test", "TE".toCharArray());
- checkTextAndSpan("atest test", 6, 8);
+ SpannedTestUtils.checkHtmlText("atest " + START + "te" + END + "st", mView);
}
public void testSetText_CharArrayBuffer() {
@@ -87,74 +94,14 @@
FormatUtils.copyToCharArrayBuffer("test", buffer);
mPrefixHighlighter.setText(mView, buffer, new char[0]);
- checkTextAndNoSpans("test");
+ SpannedTestUtils.checkHtmlText("test", mView);
FormatUtils.copyToCharArrayBuffer("a test", buffer);
mPrefixHighlighter.setText(mView, buffer, "TE".toCharArray());
- checkTextAndSpan("a test", 2, 4);
+ SpannedTestUtils.checkHtmlText("a " + START + "te" + END + "st", mView);
FormatUtils.copyToCharArrayBuffer("test", buffer);
mPrefixHighlighter.setText(mView, buffer, "TA".toCharArray());
- checkTextAndNoSpans("test");
- }
-
- /**
- * Checks that the text view contains the given text and there is no highlighted prefix.
- *
- * @param expectedText the text expected to be in the view
- */
- private void checkTextAndNoSpans(String expectedText) {
- checkTextAndOptionalSpan(expectedText, false, 0, 0);
- }
-
- /**
- * Checks that the text view contains the given text and the prefix is highlighted at the given
- * position.
- *
- * @param expectedText the text expected to be in the view
- * @param expectedStart the expect start of the highlighted prefix
- * @param expectedEnd the expect end of the highlighted prefix
- */
- private void checkTextAndSpan(String expectedText, int expectedStart, int expectedEnd) {
- checkTextAndOptionalSpan(expectedText, true, expectedStart, expectedEnd);
- }
-
- /**
- * Checks that the text view contains the given text and the prefix is highlighted if expected.
- *
- * @param expectedText the text expected to be in the view
- * @param expectedHighlighted whether the prefix should be highlighted in the view
- * @param expectedStart the expect start of the highlighted prefix
- * @param expectedEnd the expect end of the highlighted prefix
- */
- private void checkTextAndOptionalSpan(String expectedText, boolean expectedHighlighted,
- int expectedStart, int expectedEnd) {
- // First check that the text is correct.
- assertEquals(expectedText, mView.getText().toString());
- // Get the spannable stored in the text view.
- Spannable actualText = (Spannable) mView.getText();
- // Get the style and color spans applied to the text.
- StyleSpan[] styleSpans = actualText.getSpans(0, expectedText.length(), StyleSpan.class);
- ForegroundColorSpan[] foregroundColorSpans =
- actualText.getSpans(0, expectedText.length(), ForegroundColorSpan.class);
- if (!expectedHighlighted) {
- // There should be no bold or colored text.
- assertEquals(0, styleSpans.length);
- assertEquals(0, foregroundColorSpans.length);
- } else {
- // The text up to the found prefix is bold.
- assertEquals(1, styleSpans.length);
- StyleSpan boldSpan = styleSpans[0];
- assertEquals(Typeface.BOLD, boldSpan.getStyle());
- assertEquals(0, actualText.getSpanStart(boldSpan));
- assertEquals(expectedStart, actualText.getSpanEnd(boldSpan));
-
- // The prefix itself is in the highlight color.
- assertEquals(1, foregroundColorSpans.length);
- ForegroundColorSpan foregroundColorSpan = foregroundColorSpans[0];
- assertEquals(TEST_PREFIX_HIGHLIGHT_COLOR, foregroundColorSpan.getForegroundColor());
- assertEquals(expectedStart, actualText.getSpanStart(foregroundColorSpan));
- assertEquals(expectedEnd, actualText.getSpanEnd(foregroundColorSpan));
- }
+ SpannedTestUtils.checkHtmlText("test", mView);
}
}
diff --git a/tests/src/com/android/contacts/list/ContactListItemViewTest.java b/tests/src/com/android/contacts/list/ContactListItemViewTest.java
index 81d8151..07ea814 100644
--- a/tests/src/com/android/contacts/list/ContactListItemViewTest.java
+++ b/tests/src/com/android/contacts/list/ContactListItemViewTest.java
@@ -35,6 +35,11 @@
*/
@LargeTest
public class ContactListItemViewTest extends ActivityInstrumentationTestCase2<DialtactsActivity> {
+ /** The HTML code used to mark the start of the highlighted part. */
+ private static final String START = "<font color =\"#729a27\">";
+ /** The HTML code used to mark the end of the highlighted part. */
+ private static final String END = "</font>";
+
public ContactListItemViewTest() {
super(DialtactsActivity.class);
}
@@ -68,7 +73,19 @@
view.showDisplayName(cursor, 0, 1, false,
ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY);
- SpannedTestUtils.checkHtmlText("<b>John </b><font color =\"#729a27\">Doe</font>",
+ SpannedTestUtils.checkHtmlText("<b>John </b>" + START + "Doe" + END,
+ view.getNameTextView());
+ }
+
+ public void testShowDisplayName_WithPrefixReversed() {
+ Cursor cursor = createCursor("John Doe", "Doe John");
+ ContactListItemView view = createView();
+
+ view.setHighlightedPrefix("DOE".toCharArray());
+ view.showDisplayName(cursor, 0, 1, false,
+ ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE);
+
+ SpannedTestUtils.checkHtmlText("John " + START + "<b>Doe</b>" + END,
view.getNameTextView());
}
@@ -88,7 +105,7 @@
ContactListItemView view = createView();
view.setHighlightedPrefix("TEST".toCharArray());
view.setSnippet("This is a test");
- SpannedTestUtils.checkHtmlText("<b>This is a </b><font color =\"#729a27\">test</font>",
+ SpannedTestUtils.checkHtmlText("This is a " + START + "test" + END,
view.getSnippetView());
}