Speed dial respects display order prefs

- Speed dial names change based on whether user wants first name first
  or last name first
- Sort order preferece is respected if ContactEntries have conflicting
  pinned positions
- Added tests for PhoneFavoritesTileAdapter.arrangeContactsByPinnedPosition
  method

Bug:19364093
Change-Id: I81214abce572e297cc21fcb4f5a901ecad958380
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
index 05780c6..c12bed7 100644
--- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
@@ -95,6 +95,11 @@
         setMeasuredDimension(width, height);
     }
 
+    @Override
+    protected String getNameForView(ContactEntry contactEntry) {
+        return contactEntry.getPreferredDisplayName();
+    }
+
     public ContactEntry getContactEntry() {
         return mContactEntry;
     }
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index e957c83..cd4c10b 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -38,13 +38,13 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
 
 import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.ContactTileLoaderFactory;
 import com.android.contacts.common.list.ContactEntry;
 import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
 import com.android.contacts.common.list.ContactTileView;
+import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.dialer.R;
 
 import java.util.ArrayList;
@@ -70,6 +70,7 @@
 
     private Context mContext;
     private Resources mResources;
+    private ContactsPreferences mContactsPreferences;
 
     /** Contact data stored in cache. This is used to populate the associated view. */
     protected ArrayList<ContactEntry> mContactEntries = null;
@@ -92,7 +93,8 @@
     protected int mIdIndex;
     protected int mLookupIndex;
     protected int mPhotoUriIndex;
-    protected int mNameIndex;
+    protected int mNamePrimaryIndex;
+    protected int mNameAlternativeIndex;
     protected int mPresenceIndex;
     protected int mStatusIndex;
 
@@ -124,9 +126,17 @@
         public int compare(ContactEntry lhs, ContactEntry rhs) {
             return ComparisonChain.start()
                     .compare(lhs.pinned, rhs.pinned)
-                    .compare(lhs.name, rhs.name)
+                    .compare(getPreferredSortName(lhs), getPreferredSortName(rhs))
                     .result();
         }
+
+        private String getPreferredSortName(ContactEntry contactEntry) {
+            if (mContactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY
+                    || TextUtils.isEmpty(contactEntry.nameAlternative)) {
+                return contactEntry.namePrimary;
+            }
+            return contactEntry.nameAlternative;
+        }
     };
 
     public interface OnDataSetChangedForAnimationListener {
@@ -140,6 +150,7 @@
         mListener = listener;
         mContext = context;
         mResources = context.getResources();
+        mContactsPreferences = new ContactsPreferences(mContext);
         mNumFrequents = 0;
         mContactEntries = new ArrayList<ContactEntry>();
 
@@ -172,19 +183,26 @@
      */
     protected void bindColumnIndices() {
         mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
-        mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
-        mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
-        mNameIndex = ContactTileLoaderFactory.DISPLAY_NAME;
+        mNamePrimaryIndex = ContactTileLoaderFactory.DISPLAY_NAME;
+        mNameAlternativeIndex = ContactTileLoaderFactory.DISPLAY_NAME_ALTERNATIVE;
         mStarredIndex = ContactTileLoaderFactory.STARRED;
-        mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
-        mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
-
+        mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
+        mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
         mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
         mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
         mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
-        mIsDefaultNumberIndex = ContactTileLoaderFactory.IS_DEFAULT_NUMBER;
         mPinnedIndex = ContactTileLoaderFactory.PINNED;
-        mContactIdIndex = ContactTileLoaderFactory.CONTACT_ID_FOR_DATA;
+        mContactIdIndex = ContactTileLoaderFactory.CONTACT_ID;
+
+
+        mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
+        mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
+        mIsDefaultNumberIndex = ContactTileLoaderFactory.IS_DEFAULT_NUMBER;
+    }
+
+    public void refreshContactsPreferences() {
+        mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+        mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
     }
 
     /**
@@ -261,15 +279,19 @@
             final String photoUri = cursor.getString(mPhotoUriIndex);
             final String lookupKey = cursor.getString(mLookupIndex);
             final int pinned = cursor.getInt(mPinnedIndex);
-            final String name = cursor.getString(mNameIndex);
+            final String name = cursor.getString(mNamePrimaryIndex);
+            final String nameAlternative = cursor.getString(mNameAlternativeIndex);
             final boolean isStarred = cursor.getInt(mStarredIndex) > 0;
             final boolean isDefaultNumber = cursor.getInt(mIsDefaultNumberIndex) > 0;
 
             final ContactEntry contact = new ContactEntry();
 
             contact.id = id;
-            contact.name = (!TextUtils.isEmpty(name)) ? name :
+            contact.namePrimary = (!TextUtils.isEmpty(name)) ? name :
                     mResources.getString(R.string.missing_name);
+            contact.nameAlternative = (!TextUtils.isEmpty(nameAlternative)) ? nameAlternative :
+                    mResources.getString(R.string.missing_name);
+            contact.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
             contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
             contact.lookupKey = lookupKey;
             contact.lookupUri = ContentUris.withAppendedId(
diff --git a/src/com/android/dialer/list/SpeedDialFragment.java b/src/com/android/dialer/list/SpeedDialFragment.java
index 64129cd..e30f406 100644
--- a/src/com/android/dialer/list/SpeedDialFragment.java
+++ b/src/com/android/dialer/list/SpeedDialFragment.java
@@ -202,7 +202,9 @@
     public void onResume() {
         Trace.beginSection(TAG + " onResume");
         super.onResume();
-
+        if (mContactTileAdapter != null) {
+            mContactTileAdapter.refreshContactsPreferences();
+        }
         if (PermissionsUtil.hasContactsPermissions(getActivity())) {
             if (getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE) == null) {
                 getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null,
diff --git a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
index d0547bd..8819384 100644
--- a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
+++ b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
@@ -1,5 +1,8 @@
 package com.android.dialer.list;
 
+import com.google.common.collect.Lists;
+
+import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.provider.ContactsContract.PinnedPositions;
@@ -8,15 +11,18 @@
 
 import com.android.contacts.common.ContactTileLoaderFactory;
 import com.android.contacts.common.list.ContactEntry;
+import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.dialer.list.PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener;
 
+import junit.framework.Assert;
+
 import java.util.ArrayList;
 
 @SmallTest
 public class PhoneFavoritesTileAdapterTest extends AndroidTestCase {
-    private PhoneFavoritesTileAdapter mAdapter;
-    private static final OnDataSetChangedForAnimationListener
-            sOnDataSetChangedForAnimationListener = new OnDataSetChangedForAnimationListener() {
+
+    private static final OnDataSetChangedForAnimationListener NOOP_ANIMATION_LISTENER =
+            new OnDataSetChangedForAnimationListener() {
                 @Override
                 public void onDataSetChangedForAnimation(long... idsInPlace) {}
 
@@ -24,18 +30,161 @@
                 public void cacheOffsetsForDatasetChange() {}
             };
 
-    /**
-     * TODO: Add tests
-     *
-     * Test cases (various combinations of):
-     * No pinned contacts
-     * One pinned contact
-     * Multiple pinned contacts with differing pinned positions
-     * Multiple pinned contacts with conflicting pinned positions
-     * Pinned contacts with pinned positions at the start, middle, end, and outside the list
-     */
-    public void testArrangeContactsByPinnedPosition() {
+    private PhoneFavoritesTileAdapter mAdapter;
 
+    @Override
+    public void setUp() {
+        this.mAdapter = new PhoneFavoritesTileAdapter(getContext(), null, NOOP_ANIMATION_LISTENER);
+    }
+
+    /**
+     * For all arrangeContactsByPinnedPosition tests, the id for a particular ContactEntry
+     * represents the index at which it should be located after calling
+     * arrangeContactsByPinnedPosition
+     */
+
+    public void testArrangeContactsByPinnedPosition_NoPinned() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
+                getTestContactEntry(1), getTestContactEntry(2));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_NoPinned_RemoveDemoted() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
+                getTestContactEntry(-1, PinnedPositions.DEMOTED), getTestContactEntry(1));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 2);
+    }
+
+    public void testArrangeContactsByPinnedPosition_OnePinned_Beginning() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1),
+                getTestContactEntry(0, 1), getTestContactEntry(2));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_OnePinned_Middle() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
+                getTestContactEntry(1, 2), getTestContactEntry(2));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_OnePinned_End() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
+                getTestContactEntry(2, 3), getTestContactEntry(1));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_OnePinned_Outside() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
+                getTestContactEntry(2, 5), getTestContactEntry(1));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_OnePinned_RemoveDemoted() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
+                getTestContactEntry(-1, PinnedPositions.DEMOTED), getTestContactEntry(0));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 2);
+    }
+
+    public void testArrangeContactsByPinnedPosition_TwoPinned_Split() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0, 1),
+                getTestContactEntry(1), getTestContactEntry(2, 3));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_TwoPinned_Adjacent() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
+                getTestContactEntry(0), getTestContactEntry(2, 3));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_TwoPinned_Conflict_UnpinnedBefore() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
+                getTestContactEntry(0), getTestContactEntry(2, 2));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_TwoPinned_Conflict_UnpinnedAfter() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0, 1),
+                getTestContactEntry(2), getTestContactEntry(1, 1));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_TwoPinned_Conflict_RemoveDemoted() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
+                getTestContactEntry(-1, PinnedPositions.DEMOTED), getTestContactEntry(0, 2));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 2);
+    }
+
+    public void testArrangeContactsByPinnedPosition_AllPinned() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
+                getTestContactEntry(0, 1), getTestContactEntry(2, 3));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_AllPinned_TwoConflicts_ConflictsFirst() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
+                getTestContactEntry(0, 2), getTestContactEntry(2, 3));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_AllPinned_TwoConflicts_ConflictsLast() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0, 2),
+                getTestContactEntry(1, 3), getTestContactEntry(2, 3));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_AllPinned_AllConflicts() {
+        ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(2, 3),
+                getTestContactEntry(1, 3), getTestContactEntry(0, 3));
+        mAdapter.arrangeContactsByPinnedPosition(toArrange);
+
+        assertContactEntryListPositionsMatchId(toArrange, 3);
+    }
+
+    public void testArrangeContactsByPinnedPosition_All_Pinned_AllConflicts_SortNameAlternative() {
+        Context context  = getContext();
+        context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit()
+                .putInt(ContactsPreferences.SORT_ORDER_KEY,
+                        ContactsPreferences.SORT_ORDER_ALTERNATIVE)
+                .commit();
+        ArrayList<ContactEntry> actual = Lists.newArrayList(
+                getTestContactEntry(1, 3, "2", "1"),
+                getTestContactEntry(2, 3, "0", "2"),
+                getTestContactEntry(0, 3, "1", "0")
+        );
+        mAdapter.arrangeContactsByPinnedPosition(actual);
+
+        assertContactEntryListPositionsMatchId(actual, 3);
     }
 
     /**
@@ -55,6 +204,45 @@
 
     }
 
+    public void testSetContactCursor_DisplayNameOrder_Primary() {
+        setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_PRIMARY);
+        Cursor testCursor = getCursorForTest(1, 0);
+        mAdapter.setContactCursor(testCursor);
+        Assert.assertEquals(1, mAdapter.mContactEntries.size());
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
+                mAdapter.mContactEntries.get(0).nameDisplayOrder);
+    }
+
+    public void testSetContactCursor_DisplayNameOrder_Alternative() {
+        setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
+        Cursor testCursor = getCursorForTest(1, 0);
+        mAdapter.setContactCursor(testCursor);
+        Assert.assertEquals(1, mAdapter.mContactEntries.size());
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE,
+                mAdapter.mContactEntries.get(0).nameDisplayOrder);
+    }
+
+    public void testSetContactCursor_DisplayNameOrder_Changed() {
+        setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_PRIMARY);
+        Cursor testCursor = getCursorForTest(1, 0);
+        mAdapter.setContactCursor(testCursor);
+        Assert.assertEquals(1, mAdapter.mContactEntries.size());
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
+                mAdapter.mContactEntries.get(0).nameDisplayOrder);
+
+        setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
+        mAdapter.refreshContactsPreferences();
+        mAdapter.setContactCursor(testCursor);
+        Assert.assertEquals(1, mAdapter.mContactEntries.size());
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE,
+                mAdapter.mContactEntries.get(0).nameDisplayOrder);
+    }
+
+    private void setNameDisplayOrder(Context context, int displayOrder) {
+        context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit().putInt(
+                ContactsPreferences.DISPLAY_ORDER_KEY, displayOrder).commit();
+    }
+
     /**
      * Returns a cursor containing starred and frequent contacts for test purposes.
      *
@@ -72,38 +260,42 @@
         // The only field that really matters for testing is the contact id.
         for (int i = 0; i < numStarred; i++) {
             c.addRow(new Object[] {countId, null, 1, null, null, 0, 0, null, 0,
-                    PinnedPositions.UNPINNED, countId});
+                    PinnedPositions.UNPINNED, countId, null});
             countId++;
         }
 
         // Add frequent contact entries. These entries have the starred field set to 0 (false).
         for (int i = 0; i < numFrequents; i++) {
             c.addRow(new Object[] {countId, null, 0, null, null, 0, 0, null, 0,
-                    PinnedPositions.UNPINNED, countId});
+                    PinnedPositions.UNPINNED, countId, null});
             countId++;
         }
         return c;
     }
 
-    /**
-     * Returns a ContactEntry with test data corresponding to the provided contact Id
-     *
-     * @param id Non-negative id
-     * @return ContactEntry item used for testing
-     */
-    private ContactEntry getTestContactEntry(int id, boolean isFavorite) {
+    private ContactEntry getTestContactEntry(int id) {
+        return getTestContactEntry(id, PinnedPositions.UNPINNED);
+    }
+
+    private ContactEntry getTestContactEntry(int id, int pinned) {
+        return getTestContactEntry(id, pinned, String.valueOf(id), String.valueOf(id));
+    }
+
+    private ContactEntry getTestContactEntry(int id, int pinned, String namePrimaryAppend,
+            String nameAlternativeAppend) {
         ContactEntry contactEntry = new ContactEntry();
         contactEntry.id = id;
-        contactEntry.isFavorite = isFavorite;
+        contactEntry.pinned = pinned;
+        contactEntry.namePrimary = namePrimaryAppend;
+        contactEntry.nameAlternative = nameAlternativeAppend;
         return contactEntry;
     }
 
-    private void assertContactEntryRowsEqual(ArrayList<ContactEntry> expected,
-            ArrayList<ContactEntry> actual) {
-        assertEquals(expected.size(), actual.size());
-        for (int i = 0; i < actual.size(); i++) {
-            assertEquals(expected.get(i).id, actual.get(i).id);
-            assertEquals(expected.get(i).isFavorite, actual.get(i).isFavorite);
+    private void assertContactEntryListPositionsMatchId(ArrayList<ContactEntry> contactEntries,
+            int expectedSize) {
+        Assert.assertEquals(expectedSize, contactEntries.size());
+        for (int i = 0; i < expectedSize; ++i) {
+            Assert.assertEquals(i, contactEntries.get(i).id);
         }
     }
 }