Merge "New People activity"
diff --git a/res/layout/call_log_action_call.xml b/res/layout/call_log_action_call.xml
index 53c343c..747daa7 100644
--- a/res/layout/call_log_action_call.xml
+++ b/res/layout/call_log_action_call.xml
@@ -24,7 +24,6 @@
         android:paddingRight="14dip"
         android:layout_alignParentRight="true"
         android:gravity="center_vertical"
-        android:background="@drawable/call_background"
         android:src="@android:drawable/sym_action_call"
     />
 
diff --git a/res/layout/call_log_action_group.xml b/res/layout/call_log_action_group.xml
index 0b130b6..fa6bb79 100644
--- a/res/layout/call_log_action_group.xml
+++ b/res/layout/call_log_action_group.xml
@@ -24,7 +24,6 @@
         android:paddingRight="13dip"
         android:layout_alignParentRight="true"
         android:gravity="center_vertical"
-        android:background="@drawable/call_background"
         android:src="@*android:drawable/expander_ic_minimized"
     />
 
diff --git a/res/layout/call_log_list_child_item.xml b/res/layout/call_log_list_child_item.xml
index 68e1360..10e09a4 100644
--- a/res/layout/call_log_list_child_item.xml
+++ b/res/layout/call_log_list_child_item.xml
@@ -18,7 +18,6 @@
     android:layout_width="match_parent"
     android:layout_height="?android:attr/listPreferredItemHeight"
     android:paddingLeft="7dip"
-    android:background="@drawable/list_item_background_secondary"
 >
 
     <include layout="@layout/call_log_action_call"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 48af4a6..2f9acb4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -709,14 +709,10 @@
     <string name="import_from_sim">Import from SIM card</string>
 
     <!-- Action string for selecting (USB) storage for importing contacts [CHAR LIMIT=25] -->
-    <string name="import_from_sdcard" product="nosdcard">Import from storage</string>
-    <!-- Action string for selecting SD Card for importing contacts -->
-    <string name="import_from_sdcard" product="default">Import from SD card</string>
+    <string name="import_from_sdcard" product="default">Import from storage</string>
 
     <!-- Action that exports all contacts to (USB) storage [CHAR LIMIT=25] -->
-    <string name="export_to_sdcard" product="nosdcard">Export to storage</string>
-    <!-- Action that exports all contacts to SD Card -->
-    <string name="export_to_sdcard" product="default">Export to SD card</string>
+    <string name="export_to_sdcard" product="default">Export to storage</string>
 
     <!-- Action that shares visible contacts -->
     <string name="share_visible_contacts">Share visible contacts</string>
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 961c134..13d2e6c 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -188,8 +188,6 @@
         private boolean mFirst;
         private Thread mCallerIdThread;
 
-        private CharSequence[] mLabelArray;
-
         private Drawable mDrawableIncoming;
         private Drawable mDrawableOutgoing;
         private Drawable mDrawableMissed;
@@ -253,7 +251,6 @@
                     R.drawable.ic_call_log_list_outgoing_call);
             mDrawableMissed = getResources().getDrawable(
                     R.drawable.ic_call_log_list_missed_call);
-            mLabelArray = getResources().getTextArray(com.android.internal.R.array.phoneTypes);
         }
 
         /**
@@ -692,8 +689,7 @@
                 // "type" and "label" are currently unused for SIP addresses.
                 CharSequence numberLabel = null;
                 if (!PhoneNumberUtils.isUriNumber(number)) {
-                    numberLabel = Phone.getDisplayLabel(context, ntype, label,
-                            mLabelArray);
+                    numberLabel = Phone.getTypeLabel(getResources(), ntype, label);
                 }
                 views.numberView.setVisibility(View.VISIBLE);
                 views.numberView.setText(formattedNumber);
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/activities/CallLogActivityTests.java b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
index eb0ec73..1dbd56f 100644
--- a/tests/src/com/android/contacts/activities/CallLogActivityTests.java
+++ b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
@@ -18,6 +18,7 @@
 
 import com.android.contacts.R;
 import com.android.contacts.calllog.CallLogFragment;
+import com.android.contacts.calllog.CallLogFragment.CallLogListItemViews;
 import com.android.internal.telephony.CallerInfo;
 
 import android.content.res.Resources;
@@ -25,6 +26,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -64,6 +66,11 @@
     static private final int RAND_DURATION = -1;
     static private final long NOW = -1L;
 
+    /** A phone number to be used in tests. */
+    private static final String TEST_PHONE_NUMBER = "12125551000";
+    /** The formatted version of {@link #TEST_PHONE_NUMBER}. */
+    private static final String TEST_FORMATTED_PHONE_NUMBER = "1 212-555-1000";
+
     // We get the call list activity and assign is a frame to build
     // its list.  mAdapter is an inner class of
     // CallLogActivity to build the rows (view) in the call
@@ -171,6 +178,95 @@
         assertNull(view.findViewById(R.id.groupIndicator));
     }
 
+    @MediumTest
+    public void testBindView_NumberOnly() {
+        mCursor.moveToFirst();
+        insert(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
+        View view = mAdapter.newStandAloneView(getActivity(), mParentView);
+        mAdapter.bindStandAloneView(view, getActivity(), mCursor);
+
+        CallLogListItemViews views = (CallLogListItemViews) view.getTag();
+        assertNameIs(views, TEST_FORMATTED_PHONE_NUMBER);
+        assertNumberLabelIsGone(views);
+        assertNumberIsGone(views);
+    }
+
+    @MediumTest
+    public void testBindView_WithCachedName() {
+        mCursor.moveToFirst();
+        insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+                "John Doe", Phone.TYPE_HOME, "");
+        View view = mAdapter.newStandAloneView(getActivity(), mParentView);
+        mAdapter.bindStandAloneView(view, getActivity(), mCursor);
+
+        CallLogListItemViews views = (CallLogListItemViews) view.getTag();
+        assertNameIs(views, "John Doe");
+        assertNumberLabelIsVisible(views);
+        assertNumberIs(views, TEST_FORMATTED_PHONE_NUMBER);
+    }
+
+    @MediumTest
+    public void testBindView_UriNumber() {
+        mCursor.moveToFirst();
+        insertWithCachedValues("sip:johndoe@gmail.com", NOW, 0, Calls.INCOMING_TYPE,
+                "John Doe", Phone.TYPE_HOME, "");
+        View view = mAdapter.newStandAloneView(getActivity(), mParentView);
+        mAdapter.bindStandAloneView(view, getActivity(), mCursor);
+
+        CallLogListItemViews views = (CallLogListItemViews) view.getTag();
+        assertNameIs(views, "John Doe");
+        assertNumberLabelIsInvisible(views);
+        assertNumberIs(views, "sip:johndoe@gmail.com");
+    }
+
+    @MediumTest
+    public void testBindView_HomeLabel() {
+        mCursor.moveToFirst();
+        insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+                "John Doe", Phone.TYPE_HOME, "");
+        View view = mAdapter.newStandAloneView(getActivity(), mParentView);
+        mAdapter.bindStandAloneView(view, getActivity(), mCursor);
+
+        CallLogListItemViews views = (CallLogListItemViews) view.getTag();
+        assertNameIs(views, "John Doe");
+        assertNumberLabelIs(views, getTypeLabel(Phone.TYPE_HOME));
+        assertNumberIsVisible(views);
+    }
+
+    @MediumTest
+    public void testBindView_WorkLabel() {
+        mCursor.moveToFirst();
+        insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+                "John Doe", Phone.TYPE_WORK, "");
+        View view = mAdapter.newStandAloneView(getActivity(), mParentView);
+        mAdapter.bindStandAloneView(view, getActivity(), mCursor);
+
+        CallLogListItemViews views = (CallLogListItemViews) view.getTag();
+        assertNameIs(views, "John Doe");
+        assertNumberLabelIs(views, getTypeLabel(Phone.TYPE_WORK));
+        assertNumberIsVisible(views);
+    }
+
+    @MediumTest
+    public void testBindView_CustomLabel() {
+        mCursor.moveToFirst();
+        String numberLabel = "My label";
+        insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+                "John Doe", Phone.TYPE_CUSTOM, numberLabel);
+        View view = mAdapter.newStandAloneView(getActivity(), mParentView);
+        mAdapter.bindStandAloneView(view, getActivity(), mCursor);
+
+        CallLogListItemViews views = (CallLogListItemViews) view.getTag();
+        assertNameIs(views, "John Doe");
+        assertNumberLabelIs(views, numberLabel);
+        assertNumberIsVisible(views);
+    }
+
+    /** Returns the label associated with a given phone type. */
+    private CharSequence getTypeLabel(int phoneType) {
+        return Phone.getTypeLabel(getActivity().getResources(), phoneType, "");
+    }
+
     //
     // HELPERS to check conditions on the DB/views
     //
@@ -293,13 +389,20 @@
 
     /**
      * Insert a new call entry in the test DB.
+     *
+     * It includes the values for the cached contact associated with the number.
+     *
      * @param number The phone number. For unknown and private numbers,
      *               use CallerInfo.UNKNOWN_NUMBER or CallerInfo.PRIVATE_NUMBER.
      * @param date In millisec since epoch. Use NOW to use the current time.
      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
-     * @param type Eigher Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
+     * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
+     * @param cachedName the name of the contact with this number
+     * @param cachedNumberType the type of the number, from the contact with this number
+     * @param cachedNumberLabel the label of the number, from the contact with this number
      */
-    private void insert(String number, long date, int duration, int type) {
+    private void insertWithCachedValues(String number, long date, int duration, int type,
+            String cachedName, int cachedNumberType, String cachedNumberLabel) {
         MatrixCursor.RowBuilder row = mCursor.newRow();
         row.add(mIndex);
         mIndex ++;
@@ -317,13 +420,25 @@
             assertEquals(Calls.OUTGOING_TYPE, type);
         }
         row.add(type);  // type
-        row.add("");    // cached name
-        row.add(0);     // cached number type
-        row.add("");    // cached number label
+        row.add(cachedName);  // cached name
+        row.add(cachedNumberType);  // cached number type
+        row.add(cachedNumberLabel);  // cached number label
         row.add("US");  // country ISO
     }
 
     /**
+     * Insert a new call entry in the test DB.
+     * @param number The phone number. For unknown and private numbers,
+     *               use CallerInfo.UNKNOWN_NUMBER or CallerInfo.PRIVATE_NUMBER.
+     * @param date In millisec since epoch. Use NOW to use the current time.
+     * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
+     * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
+     */
+    private void insert(String number, long date, int duration, int type) {
+        insertWithCachedValues(number, date, duration, type, "", Phone.TYPE_HOME, "");
+    }
+
+    /**
      * Insert a new private call entry in the test DB.
      * @param date In millisec since epoch. Use NOW to use the current time.
      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
@@ -391,4 +506,47 @@
         }
         return privateOrUnknownOrVm;
     }
+
+    /** Asserts that the name text view is shown and contains the given text. */
+    private void assertNameIs(CallLogListItemViews views, String name) {
+        assertEquals(View.VISIBLE, views.line1View.getVisibility());
+        assertEquals(name, views.line1View.getText());
+    }
+
+    /** Asserts that the number label text view is shown and contains the given text. */
+    private void assertNumberLabelIs(CallLogListItemViews views, CharSequence numberLabel) {
+        assertNumberLabelIsVisible(views);
+        assertEquals(numberLabel, views.labelView.getText());
+    }
+
+    /** Asserts that the number label text view is shown. */
+    private void assertNumberLabelIsVisible(CallLogListItemViews views) {
+        assertEquals(View.VISIBLE, views.labelView.getVisibility());
+    }
+
+    /** Asserts that the number label text view is invisible. */
+    private void assertNumberLabelIsInvisible(CallLogListItemViews views) {
+        assertEquals(View.INVISIBLE, views.labelView.getVisibility());
+    }
+
+    /** Asserts that the number label text view is gone. */
+    private void assertNumberLabelIsGone(CallLogListItemViews views) {
+        assertEquals(View.GONE, views.labelView.getVisibility());
+    }
+
+    /** Asserts that the number text view is shown and contains the given text. */
+    private void assertNumberIs(CallLogListItemViews views, String number) {
+        assertNumberIsVisible(views);
+        assertEquals(number, views.numberView.getText());
+    }
+
+    /** Asserts that the number text view is shown. */
+    private void assertNumberIsVisible(CallLogListItemViews views) {
+        assertEquals(View.VISIBLE, views.numberView.getVisibility());
+    }
+
+    /** Asserts that the number text view is gone. */
+    private void assertNumberIsGone(CallLogListItemViews views) {
+        assertEquals(View.GONE, views.numberView.getVisibility());
+    }
 }
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());
     }