Merge "Change blocked id cache to static." into ub-contactsdialer-a-dev
diff --git a/Android.mk b/Android.mk
index 0d7754c..bd0c9de 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,6 +7,12 @@
 contacts_common_dir := ../ContactsCommon
 phone_common_dir := ../PhoneCommon
 
+ifeq ($(TARGET_BUILD_APPS),)
+support_library_root_dir := frameworks/support
+else
+support_library_root_dir := prebuilts/sdk/current/support
+endif
+
 src_dirs := src \
     $(incallui_dir)/src \
     $(contacts_common_dir)/src \
@@ -19,10 +25,10 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
 LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) \
-    frameworks/support/v7/appcompat/res \
-    frameworks/support/v7/cardview/res \
-    frameworks/support/v7/recyclerview/res \
-    frameworks/support/design/res
+    $(support_library_root_dir)/v7/cardview/res \
+    $(support_library_root_dir)/v7/recyclerview/res \
+    $(support_library_root_dir)/v7/appcompat/res \
+    $(support_library_root_dir)/design/res
 
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
diff --git a/res/drawable/blocked_number_item_background.xml b/res/drawable/ic_blocked_numbers_settings_add.xml
similarity index 63%
rename from res/drawable/blocked_number_item_background.xml
rename to res/drawable/ic_blocked_numbers_settings_add.xml
index acfea20..adcf906 100644
--- a/res/drawable/blocked_number_item_background.xml
+++ b/res/drawable/ic_blocked_numbers_settings_add.xml
@@ -15,15 +15,6 @@
      limitations under the License.
 -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <shape android:shape="rectangle">
-            <solid android:color="@color/divider_line_color" />
-        </shape>
-    </item>
-    <item android:bottom="1dp">
-        <shape android:shape="rectangle">
-            <solid android:color="@android:color/white" />
-        </shape>
-    </item>
-</layer-list>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_add_24dp"
+    android:tint="@color/blocked_number_icon_tint" />
diff --git a/res/layout/blocked_number_header.xml b/res/layout/blocked_number_header.xml
index 29a05bd..50a3dc8 100644
--- a/res/layout/blocked_number_header.xml
+++ b/res/layout/blocked_number_header.xml
@@ -45,41 +45,6 @@
 
     </LinearLayout>
 
-
-    <android.support.v7.widget.CardView
-        android:id="@+id/hide_blocked_calls_setting"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="8dp"
-        card_view:cardCornerRadius="0dp">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:gravity="center_vertical"
-            android:padding="16dp"
-            android:paddingEnd="8dp"
-            android:background="@android:color/white"
-            android:focusable="true">
-
-            <TextView
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:text="@string/blocked_call_settings_hide_setting"
-                style="@style/BlockedNumbersDescriptionTextStyle" />
-
-            <Switch android:id="@+id/hide_blocked_calls_switch"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginLeft="40dp"
-                android:focusable="false" />
-
-        </LinearLayout>
-
-    </android.support.v7.widget.CardView>
-
     <android.support.v7.widget.CardView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -136,18 +101,29 @@
                     android:layout_height="1dp"
                     android:layout_below="@id/import_button"
                     android:background="@color/divider_line_color"
-                    android:layout_marginTop="8dp"
-                    android:layout_marginBottom="8dp" />
+                    android:layout_marginTop="8dp" />
 
             </RelativeLayout>
 
-            <Button android:id="@+id/add_number_button"
-                android:layout_width="wrap_content"
+            <TextView android:id="@+id/add_number_textview"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:layout_marginEnd="@dimen/blocked_number_container_padding"
-                android:layout_marginBottom="8dp"
-                android:text="@string/blockNumber" />
+                android:background="?android:attr/selectableItemBackground"
+                android:textColor="@color/blocked_number_primary_text_color"
+                android:textSize="@dimen/blocked_number_primary_text_size"
+                android:paddingTop="24dp"
+                android:paddingBottom="24dp"
+                android:paddingEnd="@dimen/blocked_number_container_padding"
+                android:paddingStart="24dp"
+                android:drawableStart="@drawable/ic_blocked_numbers_settings_add"
+                android:drawablePadding="24dp"
+                android:text="@string/addBlockedNumber" />
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:layout_marginStart="72dp"
+                android:background="@color/divider_line_color" />
 
         </LinearLayout>
 
diff --git a/res/layout/blocked_number_item.xml b/res/layout/blocked_number_item.xml
index 79ca42e..82e2207 100644
--- a/res/layout/blocked_number_item.xml
+++ b/res/layout/blocked_number_item.xml
@@ -25,7 +25,7 @@
     android:gravity="center_vertical"
     android:orientation="horizontal"
     android:focusable="true"
-    android:background="@drawable/blocked_number_item_background">
+    android:background="@android:color/white">
 
     <QuickContactBadge
         android:id="@+id/quick_contact_photo"
@@ -69,7 +69,7 @@
         android:background="?android:attr/selectableItemBackgroundBorderless"
         android:src="@drawable/ic_remove"
         android:scaleType="center"
-        android:tint="@color/delete_icon_tint"
+        android:tint="@color/blocked_number_icon_tint"
         android:contentDescription="@string/description_blocked_number_list_delete" />
 
 </LinearLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 76bc093..2ca697f 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -127,7 +127,7 @@
     <color name="blocked_contact_background">#afafaf</color>
     <color name="blocked_number_primary_text_color">@color/dialtacts_primary_text_color</color>
     <color name="blocked_number_secondary_text_color">@color/dialtacts_secondary_text_color</color>
-    <color name="delete_icon_tint">#6D6D6D</color>
+    <color name="blocked_number_icon_tint">#616161</color>
     <color name="blocked_number_background">#E0E0E0</color>
     <color name="blocked_number_accent_color">#42A5F5</color>
     <color name="blocked_number_block_color">#F44336</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f74d2ea..4d4fb1e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -855,12 +855,6 @@
         You previously marked some callers to be automatically sent to voicemail via other apps.
     </string>
 
-    <!-- Text for a setting to hide blocked calls and automatically delete new voicemail from block
-         numbers. [CHAR LIMIT=NONE] -->
-    <string name="blocked_call_settings_hide_setting">
-        Hide calls and automatically delete new voicemails from blocked numbers.
-    </string>
-
     <!-- Label for button to view numbers of contacts previous marked to be sent to voicemail.
          [CHAR_LIMIT=20] -->
     <string name="blocked_call_settings_view_numbers_button">View Numbers</string>
@@ -878,7 +872,7 @@
     <string name="description_blocked_number_list_delete">Unblock number</string>
 
     <!-- Button to bring up UI to add a number to the blocked call list. [CHAR LIMIT=40] -->
-    <string name="blockNumber">Add number</string>
+    <string name="addBlockedNumber">Add number</string>
 
     <!-- Heading for the block list in the "Spam and blocked cal)ls" settings. [CHAR LIMIT=64] -->
     <string name="blockList">Block list</string>
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index 2fffaff..7771563 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -46,7 +46,7 @@
     public enum Tasks {
         DELETE_VOICEMAIL,
         DELETE_CALL,
-        MARK_BLOCKED,
+        DELETE_BLOCKED_CALL,
         MARK_VOICEMAIL_READ,
         GET_CALL_DETAILS,
     }
@@ -81,7 +81,7 @@
         static final int TRANSCRIPTION_COLUMN_INDEX = 11;
     }
 
-    private static class CallLogMarkBlockedQuery {
+    private static class CallLogDeleteBlockedCallQuery {
         static final String[] PROJECTION = new String[] {
             CallLog.Calls._ID,
             CallLog.Calls.DATE
@@ -104,7 +104,7 @@
     // Try to identify if a call log entry corresponds to a number which was blocked. We match by
     // by comparing its creation time to the time it was added in the InCallUi and seeing if they
     // fall within a certain threshold.
-    private static final int MATCH_BLOCKED_CALL_THRESHOLD_MS = 1500;
+    private static final int MATCH_BLOCKED_CALL_THRESHOLD_MS = 3000;
 
     private static AsyncTaskExecutor sAsyncTaskExecutor;
 
@@ -256,15 +256,16 @@
     }
 
     /**
-     * Marks last call made by the number the call type of the specified call as BLOCKED in the call log.
+     * Deletes the last call made by the number within a threshold of the call time added in the
+     * call log, assuming it is a blocked call for which no entry should be shown.
      *
      * @param context The context.
-     * @param number Number of which to mark the most recent call as BLOCKED.
+     * @param number Number of blocked call, for which to delete the call log entry.
      * @param timeAddedMs The time the number was added to InCall, in milliseconds.
      * @param listener The listener to invoke after looking up for a call log entry matching the
      *     number and time added.
      */
-    public static void markCallAsBlocked(
+    public static void deleteBlockedCall(
             final Context context,
             final String number,
             final long timeAddedMs,
@@ -273,23 +274,30 @@
             initTaskExecutor();
         }
 
-        sAsyncTaskExecutor.submit(Tasks.MARK_BLOCKED, new AsyncTask<Void, Void, Long>() {
+        sAsyncTaskExecutor.submit(Tasks.DELETE_BLOCKED_CALL, new AsyncTask<Void, Void, Long>() {
             @Override
             public Long doInBackground(Void... params) {
                 // First, lookup the call log entry of the most recent call with this number.
                 Cursor cursor = context.getContentResolver().query(
                         TelecomUtil.getCallLogUri(context),
-                        CallLogMarkBlockedQuery.PROJECTION,
+                        CallLogDeleteBlockedCallQuery.PROJECTION,
                         CallLog.Calls.NUMBER + "= ?",
                         new String[] { number },
                         CallLog.Calls.DATE + " DESC LIMIT 1");
 
-                // If found, return the call log entry id.
+                // If match is found, delete this call log entry and return the call log entry id.
                 if (cursor.moveToFirst()) {
-                    long creationTime = cursor.getLong(CallLogMarkBlockedQuery.DATE_COLUMN_INDEX);
+                    long creationTime =
+                            cursor.getLong(CallLogDeleteBlockedCallQuery.DATE_COLUMN_INDEX);
                     if (timeAddedMs > creationTime
                             && timeAddedMs - creationTime < MATCH_BLOCKED_CALL_THRESHOLD_MS) {
-                        return cursor.getLong(CallLogMarkBlockedQuery.ID_COLUMN_INDEX);
+                        long callLogEntryId =
+                                cursor.getLong(CallLogDeleteBlockedCallQuery.ID_COLUMN_INDEX);
+                        context.getContentResolver().delete(
+                                TelecomUtil.getCallLogUri(context),
+                                CallLog.Calls._ID + " IN (" + callLogEntryId + ")",
+                                null);
+                        return callLogEntryId;
                     }
                 }
                 return (long) -1;
@@ -300,20 +308,6 @@
                 if (listener != null) {
                     listener.onQueryFinished(callLogEntryId >= 0);
                 }
-
-                if (callLogEntryId < 0) {
-                    return;
-                }
-
-                // Then, update that call log entry to have type BLOCKED.
-                final ContentValues values = new ContentValues();
-                values.put(CallLog.Calls.TYPE, AppCompatConstants.CALLS_BLOCKED_TYPE);
-
-                context.getContentResolver().update(
-                        TelecomUtil.getCallLogUri(context),
-                        values,
-                        CallLog.Calls._ID + "= ?",
-                        new String[] { Long.toString(callLogEntryId) });
             }
         });
     }
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index f84ffd5..3cf58bd 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -125,7 +125,6 @@
     private boolean mRefreshDataRequired = true;
 
     private boolean mHasReadCallLogPermission = false;
-    private boolean mShouldHideBlockedCalls = false;
 
     // Exactly same variable is in Fragment as a package private.
     private boolean mMenuVisible = true;
@@ -211,8 +210,6 @@
         resolver.registerContentObserver(Status.CONTENT_URI, true, mVoicemailStatusObserver);
         setHasOptionsMenu(true);
 
-        mShouldHideBlockedCalls = FilteredNumbersUtil.shouldHideBlockedCalls(getActivity());
-
         if (mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
             mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter
                     .getInstance(activity, state);
@@ -342,10 +339,6 @@
             mRefreshDataRequired = true;
             updateEmptyMessage(mCallTypeFilter);
         }
-        if (mShouldHideBlockedCalls != FilteredNumbersUtil.shouldHideBlockedCalls(getActivity())) {
-            mShouldHideBlockedCalls = !mShouldHideBlockedCalls;
-            mRefreshDataRequired = true;
-        }
 
         mHasReadCallLogPermission = hasReadCallLogPermission;
         refreshData();
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 01489c1..3efdce7 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -191,13 +191,10 @@
             selectionArgs.add(Long.toString(newerThan));
         }
 
-        final boolean shouldHideBlockedCalls =
-                FilteredNumbersUtil.shouldHideBlockedCalls(mContext);
-        if (shouldHideBlockedCalls) {
-            where.append(" AND ");
-            where.append("(" + Calls.TYPE + " != ?)");
-            selectionArgs.add(Integer.toString(AppCompatConstants.CALLS_BLOCKED_TYPE));
-        }
+        // Always hide blocked calls.
+        where.append(" AND ");
+        where.append("(" + Calls.TYPE + " != ?)");
+        selectionArgs.add(Integer.toString(AppCompatConstants.CALLS_BLOCKED_TYPE));
 
         final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
         final String selection = where.length() > 0 ? where.toString() : null;
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
index 1987b80..635e874 100644
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
@@ -135,9 +135,8 @@
                     mContext, newCall.number, newCall.countryIso, newCall.dateMs)) {
                 itr.remove();
 
-                if (FilteredNumbersUtil.shouldHideBlockedCalls(mContext)) {
-                    mContext.getContentResolver().delete(newCall.voicemailUri, null, null);
-                }
+                // Delete the voicemail.
+                mContext.getContentResolver().delete(newCall.voicemailUri, null, null);
                 continue;
             }
 
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java b/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
index 853076c..a2a716f 100644
--- a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
+++ b/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
@@ -36,7 +36,6 @@
 import android.widget.Switch;
 
 import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogQueryHandler;
 import com.android.dialer.database.FilteredNumberContract;
 import com.android.dialer.filterednumber.FilteredNumbersUtil.CheckForSendToVoicemailContactListener;
 import com.android.dialer.filterednumber.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
@@ -44,14 +43,11 @@
 import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
 
 public class BlockedNumbersFragment extends ListFragment
-        implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener,
-                CallLogQueryHandler.Listener {
+        implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener {
 
     private BlockedNumbersAdapter mAdapter;
-    private CallLogQueryHandler mCallLogQueryHandler;
     private VoicemailStatusHelper mVoicemailStatusHelper;
 
-    private Switch mHideSettingSwitch;
     private View mImportSettings;
     private View mBlockedNumbersDisabledForEmergency;
 
@@ -69,32 +65,15 @@
         }
         setListAdapter(mAdapter);
 
-        mCallLogQueryHandler
-                = new CallLogQueryHandler(getContext(), getContext().getContentResolver(), this);
         mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
 
-        mHideSettingSwitch = (Switch) getActivity().findViewById(R.id.hide_blocked_calls_switch);
-        mHideSettingSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                FilteredNumbersUtil.setShouldHideBlockedCalls(getActivity(), isChecked);
-            }
-        });
-        getActivity().findViewById(R.id.hide_blocked_calls_setting).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(final View view) {
-                        mHideSettingSwitch.toggle();
-                    }
-                });
-
         mImportSettings = getActivity().findViewById(R.id.import_settings);
         mBlockedNumbersDisabledForEmergency =
                 getActivity().findViewById(R.id.blocked_numbers_disabled_for_emergency);
 
         getActivity().findViewById(R.id.import_button).setOnClickListener(this);;
         getActivity().findViewById(R.id.view_numbers_button).setOnClickListener(this);
-        getActivity().findViewById(R.id.add_number_button).setOnClickListener(this);
+        getActivity().findViewById(R.id.add_number_textview).setOnClickListener(this);
     }
 
     @Override
@@ -132,9 +111,6 @@
                     }
                 });
 
-        mHideSettingSwitch.setChecked(FilteredNumbersUtil.shouldHideBlockedCalls(getContext()));
-        mCallLogQueryHandler.fetchVoicemailStatus();
-
         if (FilteredNumbersUtil.hasRecentEmergencyCall(getContext())) {
             mBlockedNumbersDisabledForEmergency.setVisibility(View.VISIBLE);
         } else {
@@ -182,7 +158,7 @@
         }
 
         switch (view.getId()) {
-            case R.id.add_number_button:
+            case R.id.add_number_textview:
                 activity.showSearchUi();
                 break;
             case R.id.view_numbers_button:
@@ -199,37 +175,4 @@
                 break;
         }
     }
-
-    @Override
-    public void onVoicemailStatusFetched(Cursor cursor) {
-        Activity activity = getActivity();
-        if (activity == null) {
-            return;
-        }
-
-        final View hideSetting = activity.findViewById(R.id.hide_blocked_calls_setting);
-        if (cursor == null) {
-            hideSetting.setVisibility(View.GONE);
-            return;
-        }
-
-        final boolean hasVisualVoicemailSource =
-                mVoicemailStatusHelper.getNumberActivityVoicemailSources(cursor) > 0;
-        if (hasVisualVoicemailSource) {
-            hideSetting.setVisibility(View.VISIBLE);
-        } else {
-            hideSetting.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    public void onVoicemailUnreadCountFetched(Cursor cursor) {
-        // Do nothing.
-    }
-
-    @Override
-    public boolean onCallsFetched(Cursor cursor) {
-        // Do nothing.
-        return false;
-    }
 }
diff --git a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java b/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
index 8a0d792..fa6aa7e 100644
--- a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
+++ b/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
@@ -52,7 +52,6 @@
     // Disable incoming call blocking if there was a call within the past 2 days.
     private static final long RECENT_EMERGENCY_CALL_THRESHOLD_MS = 1000 * 60 * 60 * 24 * 2;
 
-    private static final String HIDE_BLOCKED_CALLS_PREF_KEY = "hide_blocked_calls";
     // Pref key for storing the time of end of the last emergency call in milliseconds after epoch.
     private static final String LAST_EMERGENCY_CALL_MS_PREF_KEY = "last_emergency_call_ms";
 
@@ -235,7 +234,7 @@
      * Use {@code FilteredNumberAsyncQueryHandler} to asynchronously check if a number is blocked.
      */
     public static boolean shouldBlockVoicemail(
-            Context context, String number, String countryIso, long dateMs) {
+            Context context, String number, String countryIso, long voicemailDateMs) {
         final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
         if (TextUtils.isEmpty(normalizedNumber)) {
             return false;
@@ -256,7 +255,8 @@
                 cursor.moveToFirst();
 
                 // Block if number is found and it was added before this voicemail was received.
-                shouldBlock = cursor.getCount() > 0 && dateMs > cursor.getLong(0);
+                final long numberBlockedTimeMs = cursor.getLong(0);
+                shouldBlock = cursor.getCount() > 0 && voicemailDateMs > numberBlockedTimeMs;
             } finally {
                 cursor.close();
             }
@@ -265,24 +265,6 @@
         return shouldBlock;
     }
 
-    public static boolean shouldHideBlockedCalls(Context context) {
-        if (context == null) {
-            return false;
-        }
-        return PreferenceManager.getDefaultSharedPreferences(context)
-                .getBoolean(HIDE_BLOCKED_CALLS_PREF_KEY, false);
-    }
-
-    public static void setShouldHideBlockedCalls(Context context, boolean shouldHide) {
-        if (context == null) {
-            return;
-        }
-        PreferenceManager.getDefaultSharedPreferences(context)
-                .edit()
-                .putBoolean(HIDE_BLOCKED_CALLS_PREF_KEY, shouldHide)
-                .apply();
-    }
-
     public static boolean hasRecentEmergencyCall(Context context) {
         if (context == null) {
             return false;
@@ -336,7 +318,8 @@
                         .setContentTitle(context.getString(
                                 R.string.call_blocking_disabled_notification_title))
                         .setContentText(context.getString(
-                                R.string.call_blocking_disabled_notification_text));
+                                R.string.call_blocking_disabled_notification_text))
+                        .setAutoCancel(true);
 
                 final Intent contentIntent =
                         new Intent(context, BlockedNumbersSettingsActivity.class);
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);
         }
     }
 }