Set correct marquee effect.

We need to specify Spanned with MARQUEE to let TextView
enable MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS for marquee fade mode.

Bug: 5265348
Change-Id: I46a86239d76db1c330379b9097babdfbe734ce7d
diff --git a/res/layout/contact_tile_frequent_phone.xml b/res/layout/contact_tile_frequent_phone.xml
index 1cee5c6..aa2d9a8 100644
--- a/res/layout/contact_tile_frequent_phone.xml
+++ b/res/layout/contact_tile_frequent_phone.xml
@@ -69,6 +69,7 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="?attr/list_item_data_width_weight"
                 android:textSize="14sp"
+                android:ellipsize="marquee"
                 android:textColor="@color/dialtacts_secondary_text_color"
                 android:layout_marginLeft="8dip"
                 android:singleLine="true"
@@ -80,7 +81,7 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="?attr/list_item_label_width_weight"
                 android:textSize="12sp"
-                android:ellipsize="end"
+                android:ellipsize="marquee"
                 android:singleLine="true"
                 android:textAllCaps="true"
                 android:textColor="@color/dialtacts_secondary_text_color"
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 18c4d29..5d549b8 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -35,6 +35,8 @@
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
@@ -176,7 +178,7 @@
 
     private int mNameTextViewHeight;
     private int mPhoneticNameTextViewHeight;
-    private int mLabelTextViewHeight;
+    private int mLabelViewHeight;
     private int mDataViewHeight;
     private int mSnippetTextViewHeight;
     private int mStatusTextViewHeight;
@@ -326,7 +328,7 @@
 
         mNameTextViewHeight = 0;
         mPhoneticNameTextViewHeight = 0;
-        mLabelTextViewHeight = 0;
+        mLabelViewHeight = 0;
         mDataViewHeight = 0;
         mLabelAndDataViewMaxHeight = 0;
         mSnippetTextViewHeight = 0;
@@ -394,9 +396,9 @@
         if (isVisible(mLabelView)) {
             mLabelView.measure(MeasureSpec.makeMeasureSpec(labelWidth, MeasureSpec.AT_MOST),
                     MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-            mLabelTextViewHeight = mLabelView.getMeasuredHeight();
+            mLabelViewHeight = mLabelView.getMeasuredHeight();
         }
-        mLabelAndDataViewMaxHeight = Math.max(mLabelTextViewHeight, mDataViewHeight);
+        mLabelAndDataViewMaxHeight = Math.max(mLabelViewHeight, mDataViewHeight);
 
         if (isVisible(mSnippetView)) {
             mSnippetView.measure(
@@ -600,14 +602,15 @@
             if (mPhotoPosition == PhotoPosition.LEFT) {
                 // When photo is on left, label is placed on the right edge of the list item.
                 mLabelView.layout(rightBound - mLabelView.getMeasuredWidth(),
-                        textTopBound + mLabelAndDataViewMaxHeight - mLabelTextViewHeight,
+                        textTopBound + mLabelAndDataViewMaxHeight - mLabelViewHeight,
                         rightBound,
                         textTopBound + mLabelAndDataViewMaxHeight);
+                rightBound -= mLabelView.getMeasuredWidth();
             } else {
                 // When photo is on right, label is placed on the left of data view.
                 dataLeftBound = leftBound + mLabelView.getMeasuredWidth();
                 mLabelView.layout(leftBound,
-                        textTopBound + mLabelAndDataViewMaxHeight - mLabelTextViewHeight,
+                        textTopBound + mLabelAndDataViewMaxHeight - mLabelViewHeight,
                         dataLeftBound,
                         textTopBound + mLabelAndDataViewMaxHeight);
                 dataLeftBound += mGapBetweenLabelAndData;
@@ -783,7 +786,7 @@
                 mHeaderDivider.setBackgroundColor(mHeaderUnderlineColor);
                 addView(mHeaderDivider);
             }
-            mHeaderTextView.setText(title);
+            mHeaderTextView.setText(getMarqueeText(title));
             mHeaderTextView.setVisibility(View.VISIBLE);
             mHeaderDivider.setVisibility(View.VISIBLE);
             mHeaderTextView.setAllCaps(true);
@@ -929,7 +932,7 @@
             }
         } else {
             getPhoneticNameTextView();
-            mPhoneticNameTextView.setText(text, 0, size);
+            mPhoneticNameTextView.setText(getMarqueeText(text, size));
             mPhoneticNameTextView.setVisibility(VISIBLE);
         }
     }
@@ -960,7 +963,7 @@
             }
         } else {
             getLabelView();
-            mLabelView.setText(text);
+            mLabelView.setText(getMarqueeText(text));
             mLabelView.setVisibility(VISIBLE);
         }
     }
@@ -975,7 +978,7 @@
             }
         } else {
             getLabelView();
-            mLabelView.setText(text, 0, size);
+            mLabelView.setText(getMarqueeText(text, size));
             mLabelView.setVisibility(VISIBLE);
         }
     }
@@ -992,6 +995,7 @@
             if (mPhotoPosition == PhotoPosition.LEFT) {
                 mLabelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mCountViewTextSize);
                 mLabelView.setAllCaps(true);
+                mLabelView.setGravity(Gravity.RIGHT);
             } else {
                 mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
             }
@@ -1012,11 +1016,28 @@
             return;
         } else {
             getDataView();
-            mDataView.setText(text, 0, size);
+            mDataView.setText(getMarqueeText(text, size));
             mDataView.setVisibility(VISIBLE);
         }
     }
 
+    private CharSequence getMarqueeText(char[] text, int size) {
+        return getMarqueeText(new String(text, 0, size));
+    }
+
+    private CharSequence getMarqueeText(CharSequence text) {
+        if (getTextEllipsis() == TruncateAt.MARQUEE) {
+            // To show MARQUEE correctly (with END effect during non-active state), we need
+            // to build Spanned with MARQUEE in addition to TextView's ellipsize setting.
+            final SpannableStringBuilder builder = new SpannableStringBuilder(text);
+            builder.setSpan(TruncateAt.MARQUEE, 0, builder.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            return builder;
+        } else {
+            return text;
+        }
+    }
+
     /**
      * Returns the text view for the data text, creating it if necessary.
      */
@@ -1103,7 +1124,7 @@
             }
         } else {
             getCountView();
-            mCountView.setText(text);
+            mCountView.setText(getMarqueeText(text));
             mCountView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCountViewTextSize);
             mCountView.setGravity(Gravity.CENTER_VERTICAL);
             mCountView.setTextColor(mContactsCountTextColor);
@@ -1121,7 +1142,7 @@
             }
         } else {
             getStatusView();
-            mStatusView.setText(text);
+            mStatusView.setText(getMarqueeText(text));
             mStatusView.setVisibility(VISIBLE);
         }
     }
@@ -1146,9 +1167,7 @@
     }
 
     private TruncateAt getTextEllipsis() {
-        // Note: If we want to choose MARQUEE here, we may need to manually trigger TextView's
-        // startStopMarquee(), which is unfortunately *private*. See also issue 5465510.
-        return TruncateAt.MIDDLE;
+        return TruncateAt.MARQUEE;
     }
 
     public void showDisplayName(Cursor cursor, int nameColumnIndex, int alternativeNameColumnIndex,
@@ -1158,8 +1177,10 @@
         cursor.copyStringToBuffer(alternativeNameColumnIndex,
                 mDisplayNameFormatter.getAlternateNameBuffer());
 
-        mDisplayNameFormatter.setDisplayName(
-                getNameTextView(), displayOrder, highlightingEnabled, mHighlightedPrefix);
+        CharSequence displayName = mDisplayNameFormatter.getDisplayName(
+                displayOrder, highlightingEnabled, mHighlightedPrefix);
+        getNameTextView().setText(getMarqueeText(displayName));
+
         // Since the quick contact content description is derived from the display name and there is
         // no guarantee that when the quick contact is initialized the display name is already set,
         // do it here too.