Merge "Fix VVM playback control buttons don’t support TALKBACK"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0f3285e..509960c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,7 +15,8 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.dialer">
+    package="com.android.dialer"
+    coreApp="true">
 
     <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -67,7 +68,7 @@
              dialpad screen. -->
         <activity android:name=".DialtactsActivity"
             android:label="@string/launcherDialer"
-            android:theme="@style/DialtactsTheme"
+            android:theme="@style/DialtactsThemeHiddenActionBar"
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:icon="@mipmap/ic_launcher_phone"
diff --git a/res/drawable-hdpi/dialer_recent_card_bg.9.png b/res/drawable-hdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index 2bfdbcb..0000000
--- a/res/drawable-hdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/divider.9.png b/res/drawable-hdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-hdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_back_arrow.png b/res/drawable-hdpi/ic_back_arrow.png
new file mode 100644
index 0000000..aad4f36
--- /dev/null
+++ b/res/drawable-hdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_info.png b/res/drawable-hdpi/ic_contact_info.png
deleted file mode 100644
index 9c23000..0000000
--- a/res/drawable-hdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contacts_tiles.9.png b/res/drawable-hdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..572fdb8
--- /dev/null
+++ b/res/drawable-hdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_dial_action_delete.png b/res/drawable-hdpi/ic_dial_action_delete.png
index 0bf8563..ebf692a 100644
--- a/res/drawable-hdpi/ic_dial_action_delete.png
+++ b/res/drawable-hdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_dialpad_lt.png b/res/drawable-hdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..bd0fe5d
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove.png b/res/drawable-hdpi/ic_remove.png
new file mode 100644
index 0000000..1ee6adf
--- /dev/null
+++ b/res/drawable-hdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove_highlight.png b/res/drawable-hdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..435ee36
--- /dev/null
+++ b/res/drawable-hdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_star_marked_as_fav.png b/res/drawable-hdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index 8a138c4..0000000
--- a/res/drawable-hdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-hdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..b5f50de
--- /dev/null
+++ b/res/drawable-hdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_voice_search.png b/res/drawable-hdpi/ic_voice_search.png
index 6caf4a4..9631d3e 100644
--- a/res/drawable-hdpi/ic_voice_search.png
+++ b/res/drawable-hdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-hdpi/overflow_thumbnail.png b/res/drawable-hdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..57db353
--- /dev/null
+++ b/res/drawable-hdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-hdpi/search_bg.9.png b/res/drawable-hdpi/search_bg.9.png
new file mode 100644
index 0000000..81c47be
--- /dev/null
+++ b/res/drawable-hdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/star_thumbnail.png b/res/drawable-hdpi/star_thumbnail.png
new file mode 100644
index 0000000..1d4d5e1
--- /dev/null
+++ b/res/drawable-hdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable-mdpi/dialer_recent_card_bg.9.png b/res/drawable-mdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index dff6a0b..0000000
--- a/res/drawable-mdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/divider.9.png b/res/drawable-mdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-mdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_back_arrow.png b/res/drawable-mdpi/ic_back_arrow.png
new file mode 100644
index 0000000..56eb887
--- /dev/null
+++ b/res/drawable-mdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_info.png b/res/drawable-mdpi/ic_contact_info.png
deleted file mode 100644
index 5d35ec5..0000000
--- a/res/drawable-mdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contacts_tiles.9.png b/res/drawable-mdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..343053a
--- /dev/null
+++ b/res/drawable-mdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_dial_action_delete.png b/res/drawable-mdpi/ic_dial_action_delete.png
index 98341e9..e1394c5 100644
--- a/res/drawable-mdpi/ic_dial_action_delete.png
+++ b/res/drawable-mdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_dialpad_lt.png b/res/drawable-mdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..14674ed
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove.png b/res/drawable-mdpi/ic_remove.png
new file mode 100644
index 0000000..2c134ea
--- /dev/null
+++ b/res/drawable-mdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove_highlight.png b/res/drawable-mdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..6a961cb
--- /dev/null
+++ b/res/drawable-mdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star_marked_as_fav.png b/res/drawable-mdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index ee1b5ec..0000000
--- a/res/drawable-mdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-mdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..e43b3ef
--- /dev/null
+++ b/res/drawable-mdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_voice_search.png b/res/drawable-mdpi/ic_voice_search.png
index e290f92..af58538 100644
--- a/res/drawable-mdpi/ic_voice_search.png
+++ b/res/drawable-mdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-mdpi/overflow_thumbnail.png b/res/drawable-mdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..c699374
--- /dev/null
+++ b/res/drawable-mdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-mdpi/search_bg.9.png b/res/drawable-mdpi/search_bg.9.png
new file mode 100644
index 0000000..879aeaa
--- /dev/null
+++ b/res/drawable-mdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/star_thumbnail.png b/res/drawable-mdpi/star_thumbnail.png
new file mode 100644
index 0000000..7b96272
--- /dev/null
+++ b/res/drawable-mdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable-xhdpi/dialer_recent_card_bg.9.png b/res/drawable-xhdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index 05d254d..0000000
--- a/res/drawable-xhdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/divider.9.png b/res/drawable-xhdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-xhdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_back_arrow.png b/res/drawable-xhdpi/ic_back_arrow.png
new file mode 100644
index 0000000..9d46e3d
--- /dev/null
+++ b/res/drawable-xhdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_info.png b/res/drawable-xhdpi/ic_contact_info.png
deleted file mode 100644
index 88d367b..0000000
--- a/res/drawable-xhdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contacts_tiles.9.png b/res/drawable-xhdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..5a56587
--- /dev/null
+++ b/res/drawable-xhdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dial_action_delete.png b/res/drawable-xhdpi/ic_dial_action_delete.png
index 989e8b1..6788669 100644
--- a/res/drawable-xhdpi/ic_dial_action_delete.png
+++ b/res/drawable-xhdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_dialpad_lt.png b/res/drawable-xhdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..345cf1a
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove.png b/res/drawable-xhdpi/ic_remove.png
new file mode 100644
index 0000000..be81592
--- /dev/null
+++ b/res/drawable-xhdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove_highlight.png b/res/drawable-xhdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..57949e3
--- /dev/null
+++ b/res/drawable-xhdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star_marked_as_fav.png b/res/drawable-xhdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index 372747a..0000000
--- a/res/drawable-xhdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-xhdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..ebfe897
--- /dev/null
+++ b/res/drawable-xhdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_voice_search.png b/res/drawable-xhdpi/ic_voice_search.png
index 6147581..6e5d811 100644
--- a/res/drawable-xhdpi/ic_voice_search.png
+++ b/res/drawable-xhdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-xhdpi/overflow_thumbnail.png b/res/drawable-xhdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..e538b98
--- /dev/null
+++ b/res/drawable-xhdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-xhdpi/search_bg.9.png b/res/drawable-xhdpi/search_bg.9.png
new file mode 100644
index 0000000..9d2d9ae
--- /dev/null
+++ b/res/drawable-xhdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/star_thumbnail.png b/res/drawable-xhdpi/star_thumbnail.png
new file mode 100644
index 0000000..a71262f
--- /dev/null
+++ b/res/drawable-xhdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable-xxhdpi/dialer_recent_card_bg.9.png b/res/drawable-xxhdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index 87d95f0..0000000
--- a/res/drawable-xxhdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/divider.9.png b/res/drawable-xxhdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-xxhdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_back_arrow.png b/res/drawable-xxhdpi/ic_back_arrow.png
new file mode 100644
index 0000000..66b6e35
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contact_info.png b/res/drawable-xxhdpi/ic_contact_info.png
deleted file mode 100644
index e5d2939..0000000
--- a/res/drawable-xxhdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contacts_tiles.9.png b/res/drawable-xxhdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..4e6872d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dial_action_delete.png b/res/drawable-xxhdpi/ic_dial_action_delete.png
index a7ff1b1..ca91a72 100644
--- a/res/drawable-xxhdpi/ic_dial_action_delete.png
+++ b/res/drawable-xxhdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_dialpad_lt.png b/res/drawable-xxhdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..45c1ab2
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove.png b/res/drawable-xxhdpi/ic_remove.png
new file mode 100644
index 0000000..2722f23
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove_highlight.png b/res/drawable-xxhdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..23ee8f6
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star_marked_as_fav.png b/res/drawable-xxhdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index 3eeff4c..0000000
--- a/res/drawable-xxhdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-xxhdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..0a99cdb
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_voice_search.png b/res/drawable-xxhdpi/ic_voice_search.png
index 4c17d2b..4e72f69 100644
--- a/res/drawable-xxhdpi/ic_voice_search.png
+++ b/res/drawable-xxhdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-xxhdpi/overflow_thumbnail.png b/res/drawable-xxhdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..7f3f733
--- /dev/null
+++ b/res/drawable-xxhdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-xxhdpi/search_bg.9.png b/res/drawable-xxhdpi/search_bg.9.png
new file mode 100644
index 0000000..2f4f4b6
--- /dev/null
+++ b/res/drawable-xxhdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/star_thumbnail.png b/res/drawable-xxhdpi/star_thumbnail.png
new file mode 100644
index 0000000..5f13fec
--- /dev/null
+++ b/res/drawable-xxhdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable/action_bar_tab.xml b/res/drawable/action_bar_tab.xml
index 35df053..3f31dba 100644
--- a/res/drawable/action_bar_tab.xml
+++ b/res/drawable/action_bar_tab.xml
@@ -16,11 +16,10 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
     <item android:drawable="@drawable/tab_selected_pressed" android:state_pressed="true" android:state_selected="true"/>
     <item android:drawable="@drawable/tab_selected_focused" android:state_focused="true" android:state_selected="true"/>
     <item android:drawable="@drawable/tab_selected" android:state_selected="true"/>
     <item android:drawable="@drawable/tab_unselected_pressed" android:state_pressed="true"/>
     <item android:drawable="@drawable/tab_unselected_focused" android:state_focused="true"/>
-
+    <item android:drawable="@drawable/tab_unselected" android:state_selected="false"/>
 </selector>
\ No newline at end of file
diff --git a/res/drawable/background_all_contacts.xml b/res/drawable/background_all_contacts.xml
new file mode 100644
index 0000000..0d3703f
--- /dev/null
+++ b/res/drawable/background_all_contacts.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="false">
+        <shape android:shape="rectangle" >
+            <solid android:color="@color/all_contacts_button_color" />
+        </shape>
+    </item>
+    <item android:state_pressed="true">
+        <shape android:shape="rectangle" >
+            <solid android:color="@color/all_contacts_button_pressed_color" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/call_history_actionbar_background.xml b/res/drawable/call_history_actionbar_background.xml
index f781f27..eabceac 100644
--- a/res/drawable/call_history_actionbar_background.xml
+++ b/res/drawable/call_history_actionbar_background.xml
@@ -16,10 +16,10 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
     <item>
         <shape android:shape="rectangle">
-            <solid android:color="#c6c6c6" />
+            <solid android:color="@color/actionbar_underline" />
         </shape>
     </item>
-    <item android:bottom="1dip">
+    <item android:bottom="2dp">
         <shape android:shape="rectangle">
             <solid android:color="@color/actionbar_background_color" />
         </shape>
diff --git a/res/drawable/dialpad_key_colors.xml b/res/drawable/dialpad_key_colors.xml
new file mode 100644
index 0000000..27b4d4f
--- /dev/null
+++ b/res/drawable/dialpad_key_colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_pressed="true"
+        android:drawable="@color/background_dialpad_pressed" />
+    <item android:drawable="@color/background_dialpad" />
+</selector>
diff --git a/res/drawable/shadow_fade_up.xml b/res/drawable/shadow_fade_up.xml
new file mode 100644
index 0000000..e2d9934
--- /dev/null
+++ b/res/drawable/shadow_fade_up.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <gradient
+        android:type="linear"
+        android:startColor="#66999999"
+        android:endColor="#00ffffff"
+        android:angle="90"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/tab_selected.xml b/res/drawable/tab_selected.xml
index 25d76fc..821f047 100644
--- a/res/drawable/tab_selected.xml
+++ b/res/drawable/tab_selected.xml
@@ -14,18 +14,26 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+    <!-- Tab underline -->
     <item>
-        <shape android:shape="rectangle" >
-            <solid android:color="#7d7d7d" />
+        <shape android:shape="rectangle">
+            <solid android:color="@color/tab_underline_selected_color"/>
         </shape>
     </item>
-    <item android:bottom="6dp">
+    <!-- Tab selection indicator -->
+    <item android:bottom="@dimen/tab_underline_height">
         <shape android:shape="rectangle" >
-            <solid android:color="@color/actionbar_background_color" />
+            <solid android:color="@color/tab_selected_color" />
         </shape>
     </item>
-
+    <!-- Tab background -->
+    <item android:bottom="@dimen/tab_selection_height">
+        <shape android:shape="rectangle" >
+            <gradient
+                android:startColor="@color/tab_background_gradient_start_color"
+                android:endColor="@color/tab_background_gradient_end_color"
+                android:angle="270" />
+        </shape>
+    </item>
 </layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_selected_focused.xml b/res/drawable/tab_selected_focused.xml
index 8efd7b3..7c0d35f 100644
--- a/res/drawable/tab_selected_focused.xml
+++ b/res/drawable/tab_selected_focused.xml
@@ -14,32 +14,33 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+    <!-- Tab underline -->
     <item>
-        <shape android:shape="rectangle" >
-            <solid android:color="#7d7d7d" />
+        <shape android:shape="rectangle">
+            <solid android:color="@color/tab_underline_selected_color"/>
         </shape>
     </item>
-    <item android:bottom="6dp">
+    <!-- Tab selection indicator -->
+    <item android:bottom="@dimen/tab_underline_height">
         <shape android:shape="rectangle" >
-            <solid android:color="@color/actionbar_background_color" />
+            <solid android:color="@color/tab_selected_color" />
+        </shape>
+    </item>
+    <!-- Tab background -->
+    <item android:bottom="@dimen/tab_selection_height">
+        <shape android:shape="rectangle" >
+            <gradient
+                android:startColor="@color/tab_background_gradient_start_color"
+                android:endColor="@color/tab_background_gradient_end_color"
+                android:angle="270" />
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle" >
-            <solid android:color="#27ffffff" />
-        </shape>
-    </item>
-    <item>
-        <shape android:shape="rectangle" >
-            <solid android:color="#00000000" />
-
             <stroke
-                android:width="2dp"
-                android:color="#27ffffff" />
+                android:width="4dp"
+                android:color="#44ff0000" />
         </shape>
     </item>
-
 </layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_selected_pressed.xml b/res/drawable/tab_selected_pressed.xml
index a38344e..32efb69 100644
--- a/res/drawable/tab_selected_pressed.xml
+++ b/res/drawable/tab_selected_pressed.xml
@@ -14,23 +14,26 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+    <!-- Tab underline -->
     <item>
-        <shape android:shape="rectangle" >
-            <solid android:color="#7d7d7d" />
+        <shape android:shape="rectangle">
+            <solid android:color="@color/tab_underline_selected_color"/>
         </shape>
     </item>
-    <item android:bottom="6dp">
+    <!-- Tab selection indicator -->
+    <item android:bottom="@dimen/tab_underline_height">
         <shape android:shape="rectangle" >
-            <solid android:color="@color/actionbar_background_color" />
+            <solid android:color="@color/tab_selected_color" />
         </shape>
     </item>
-    <item>
+    <!-- Tab background (in pressed state) -->
+    <item android:bottom="@dimen/tab_selection_height">
         <shape android:shape="rectangle" >
-            <solid android:color="#4dffffff" />
+            <gradient
+                android:startColor="@color/tab_background_gradient_start_pressed_color"
+                android:endColor="@color/tab_background_gradient_end_pressed_color"
+                android:angle="270" />
         </shape>
     </item>
-
 </layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected.xml b/res/drawable/tab_unselected.xml
new file mode 100644
index 0000000..3eee487
--- /dev/null
+++ b/res/drawable/tab_unselected.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <!-- Tab underline -->
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/tab_underline_color"/>
+        </shape>
+    </item>
+    <!-- Tab background -->
+    <item android:bottom="@dimen/tab_underline_height">
+        <shape android:shape="rectangle" >
+            <gradient
+                android:startColor="@color/tab_background_gradient_start_color"
+                android:endColor="@color/tab_background_gradient_end_color"
+                android:angle="270" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected_focused.xml b/res/drawable/tab_unselected_focused.xml
index 2aceb63..c854739 100644
--- a/res/drawable/tab_unselected_focused.xml
+++ b/res/drawable/tab_unselected_focused.xml
@@ -14,22 +14,27 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Tab underline -->
     <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/tab_underline_color"/>
+        </shape>
+    </item>
+    <!-- Tab background -->
+    <item android:bottom="@dimen/tab_underline_height">
         <shape android:shape="rectangle" >
-            <solid android:color="#27ffffff" />
+            <gradient
+                android:startColor="@color/tab_background_gradient_start_color"
+                android:endColor="@color/tab_background_gradient_end_color"
+                android:angle="270" />
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle" >
-            <solid android:color="#00000000" />
-
             <stroke
-                android:width="2dp"
-                android:color="#27ffffff" />
+                android:width="4dp"
+                android:color="#44ff0000" />
         </shape>
     </item>
-
 </layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected_pressed.xml b/res/drawable/tab_unselected_pressed.xml
index 2362b65..5d2af32 100644
--- a/res/drawable/tab_unselected_pressed.xml
+++ b/res/drawable/tab_unselected_pressed.xml
@@ -14,13 +14,20 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+    <!-- Tab underline -->
     <item>
-        <shape android:shape="rectangle" >
-            <solid android:color="#4dffffff" />
+        <shape android:shape="rectangle">
+            <solid android:color="@color/tab_underline_color"/>
         </shape>
     </item>
-
+    <!-- Tab background (in pressed state) -->
+    <item android:bottom="@dimen/tab_underline_height">
+        <shape android:shape="rectangle" >
+            <gradient
+                android:startColor="@color/tab_background_gradient_start_pressed_color"
+                android:endColor="@color/tab_background_gradient_end_pressed_color"
+                android:angle="270" />
+        </shape>
+    </item>
 </layer-list>
\ No newline at end of file
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index 1d368f6..e17dc27 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -34,9 +34,20 @@
         @id/call_log_item gone
     -->
 
+    <!-- Linear layout to separate the primary area containing the contact badge and caller
+         information and the secondary action (call details / play voicemail). -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        >
+
+        <!-- Primary area containing the contact badge and caller information -->
         <LinearLayout
             android:id="@+id/primary_action_view"
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
+            android:layout_weight="1"
             android:layout_height="wrap_content"
             android:layout_centerVertical="true"
             android:padding="@dimen/call_log_outer_margin"
@@ -46,7 +57,7 @@
             android:focusable="true"
             android:nextFocusRight="@+id/secondary_action_icon"
             android:nextFocusLeft="@+id/quick_contact_photo"
-        >
+            >
             <QuickContactBadge
                 android:id="@+id/quick_contact_photo"
                 android:layout_width="@dimen/call_log_list_contact_photo_size"
@@ -54,7 +65,7 @@
                 android:nextFocusRight="@id/primary_action_view"
                 android:layout_alignParentStart="true"
                 android:focusable="true"
-            />
+                />
             <LinearLayout
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
@@ -62,7 +73,7 @@
                 android:orientation="vertical"
                 android:gravity="center_vertical"
                 android:layout_marginStart="@dimen/call_log_inner_margin"
-            >
+                >
                 <TextView
                     android:id="@+id/name"
                     android:layout_width="wrap_content"
@@ -71,12 +82,12 @@
                     android:textColor="?attr/call_log_primary_text_color"
                     android:textSize="16sp"
                     android:singleLine="true"
-                />
+                    />
                 <LinearLayout
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:orientation="horizontal"
-                >
+                    >
                     <TextView
                         android:id="@+id/label"
                         android:layout_width="wrap_content"
@@ -87,13 +98,13 @@
                         android:singleLine="true"
                         android:ellipsize="marquee"
                         />
-                    </LinearLayout>
+                </LinearLayout>
                 <LinearLayout
                     android:id="@+id/call_type"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:orientation="horizontal"
-                >
+                    >
                     <view
                         class="com.android.dialer.calllog.CallTypeIconsView"
                         android:id="@+id/call_type_icons"
@@ -101,7 +112,7 @@
                         android:layout_height="wrap_content"
                         android:layout_marginEnd="@dimen/call_log_icon_margin"
                         android:layout_gravity="center_vertical"
-                    />
+                        />
                     <TextView
                         android:id="@+id/call_count_and_date"
                         android:layout_width="wrap_content"
@@ -111,23 +122,39 @@
                         android:textColor="?attr/call_log_secondary_text_color"
                         android:textSize="12sp"
                         android:singleLine="true"
-                    />
+                        />
                 </LinearLayout>
             </LinearLayout>
+        </LinearLayout>
+        <!-- Linear layout to house a vertical separator line and the secondary action button.
+             Used as a convenience to hide both the separator and action button at the same
+             time. -->
+        <LinearLayout
+            android:id="@+id/secondary_action_view"
+            android:layout_width="@dimen/call_log_call_action_width"
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            >
+            <!-- Thin vertical divider to visually separate the secondary action button -->
+            <View
+                android:id="@+id/vertical_divider"
+                android:layout_width="@dimen/call_log_list_item_vertical_divider_width"
+                android:layout_height="match_parent"
+                android:layout_marginTop="@dimen/call_log_list_item_vertical_divider_margin"
+                android:layout_marginBottom="@dimen/call_log_list_item_vertical_divider_margin"
+                android:background="?android:attr/dividerVertical"/>
+            <!-- The secondary action button; either play voicemail or call details. -->
             <ImageButton
                 android:id="@+id/secondary_action_icon"
-                android:layout_width="@dimen/call_log_call_action_width"
+                android:layout_width="fill_parent"
                 android:layout_height="match_parent"
-                android:paddingStart="@dimen/call_log_inner_margin"
-                android:paddingTop="@dimen/call_log_inner_margin"
-                android:paddingBottom="@dimen/call_log_inner_margin"
-                android:paddingEnd="@dimen/call_log_inner_margin"
                 android:scaleType="center"
                 android:background="?android:attr/selectableItemBackground"
                 android:nextFocusLeft="@id/primary_action_view"
-            />
+                />
         </LinearLayout>
-
+    </LinearLayout>
     <TextView
         android:id="@+id/call_log_header"
         style="@style/ContactListSeparatorTextViewStyle"
diff --git a/res/layout/dialpad.xml b/res/layout/dialpad.xml
index 4fc3e83..c13f525 100644
--- a/res/layout/dialpad.xml
+++ b/res/layout/dialpad.xml
@@ -19,42 +19,25 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/dialpad"
     android:layout_width="match_parent"
-    android:layout_height="0px"
-    android:layout_weight="@integer/dialpad_layout_weight_dialpad"
-    android:layout_gravity="center_horizontal"
-    android:layout_marginTop="@dimen/dialpad_vertical_margin"
-    android:paddingStart="5dip"
-    android:paddingEnd="5dip"
-    android:paddingBottom="10dip"
-    android:stretchColumns="0,1,2"
+    android:layout_height="wrap_content"
+    android:paddingLeft="5dp"
+    android:paddingRight="5dp"
+    android:stretchColumns="*"
     android:layoutDirection="ltr" >
 
-    <TableRow
-        android:layout_height="0px"
-        android:layout_weight="1">
+    <TableRow>
         <com.android.dialer.dialpad.DialpadKeyButton
-            xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/one"
-            style="@style/DialtactsDialpadButtonStyle"
-            android:clickable="true"
-            android:focusable="true" >
+            style="@style/DialpadKeyButtonStyle">
             <LinearLayout
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center" >
+                style="@style/DialpadKeyInternalLayoutStyle">
                 <TextView
                     android:id="@+id/dialpad_key_number"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:textColor="@color/dialpad_primary_text_color"
-                    android:textSize="@dimen/dialpad_key_numbers_size"
-                    android:fontFamily="sans-serif-light"/>
+                    style="@style/DialpadKeyNumberStyle"/>
                 <ImageView
                     android:id="@+id/dialpad_key_voicemail"
-                    android:layout_width="@dimen/dialpad_key_letters_width"
-                    android:layout_height="wrap_content"
+                    style="@style/DialpadKeyLettersStyle"
                     android:src="@drawable/ic_dial_action_vm"
-                    android:paddingLeft="11dp"
                     android:scaleType="fitStart"
                     android:baselineAlignBottom="true" />
             </LinearLayout>
@@ -63,57 +46,45 @@
         <include layout="@layout/dialpad_key" android:id="@+id/three"/>
     </TableRow>
 
-    <TableRow
-        android:layout_height="0px"
-        android:layout_weight="1">
+    <TableRow>
         <include layout="@layout/dialpad_key" android:id="@+id/four"/>
         <include layout="@layout/dialpad_key" android:id="@+id/five"/>
         <include layout="@layout/dialpad_key" android:id="@+id/six"/>
     </TableRow>
 
-    <TableRow
-        android:layout_height="0px"
-        android:layout_weight="1">
+    <TableRow>
         <include layout="@layout/dialpad_key" android:id="@+id/seven"/>
         <include layout="@layout/dialpad_key" android:id="@+id/eight"/>
         <include layout="@layout/dialpad_key" android:id="@+id/nine"/>
     </TableRow>
 
-    <TableRow
-        android:layout_height="0px"
-        android:layout_weight="1">
+    <TableRow>
         <com.android.dialer.dialpad.DialpadKeyButton
-            xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/star"
-            style="@style/DialtactsDialpadButtonStyle"
-            android:clickable="true"
-            android:focusable="true" >
-            <TextView
-                android:id="@id/dialpad_key_number"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textColor="@color/dialpad_secondary_text_color"
-                android:textSize="@dimen/dialpad_key_special_characters_size"
-                android:fontFamily="sans-serif-light"
-                android:paddingRight="@dimen/dialpad_key_letters_width"
-                android:layout_gravity="center" />
+            style="@style/DialpadKeyButtonStyle">
+            <LinearLayout
+                style="@style/DialpadKeyInternalLayoutStyle">
+                <TextView
+                    android:id="@id/dialpad_key_number"
+                    style="@style/DialpadKeyStarPoundStyle" />
+                <View
+                    android:layout_height="match_parent"
+                    android:layout_width="@dimen/dialpad_key_letters_width" />
+            </LinearLayout>
         </com.android.dialer.dialpad.DialpadKeyButton>
         <include layout="@layout/dialpad_key" android:id="@+id/zero"/>
         <com.android.dialer.dialpad.DialpadKeyButton
-            xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/pound"
-            style="@style/DialtactsDialpadButtonStyle"
-            android:clickable="true"
-            android:focusable="true" >
-            <TextView
-                android:id="@id/dialpad_key_number"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textColor="@color/dialpad_secondary_text_color"
-                android:textSize="@dimen/dialpad_key_special_characters_size"
-                android:fontFamily="sans-serif-light"
-                android:paddingRight="@dimen/dialpad_key_letters_width"
-                android:layout_gravity="center" />
+            style="@style/DialpadKeyButtonStyle">
+            <LinearLayout
+                style="@style/DialpadKeyInternalLayoutStyle">
+                <TextView
+                    android:id="@id/dialpad_key_number"
+                    style="@style/DialpadKeyStarPoundStyle" />
+                <View
+                    android:layout_height="match_parent"
+                    android:layout_width="@dimen/dialpad_key_letters_width" />
+            </LinearLayout>
         </com.android.dialer.dialpad.DialpadKeyButton>
     </TableRow>
 </TableLayout>
diff --git a/res/layout/dialpad_chooser_list_item.xml b/res/layout/dialpad_chooser_list_item.xml
index 5ba88ca..9a49036 100644
--- a/res/layout/dialpad_chooser_list_item.xml
+++ b/res/layout/dialpad_chooser_list_item.xml
@@ -27,7 +27,7 @@
 
     <TextView android:id="@+id/text"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="@color/dialpad_text_color"
+        android:textColor="@color/dialpad_primary_text_color"
         android:layout_gravity="center_vertical"
         android:layout_width="0dip"
         android:layout_weight="1"
diff --git a/res/layout/dialpad_digits.xml b/res/layout/dialpad_digits.xml
new file mode 100644
index 0000000..21638f0
--- /dev/null
+++ b/res/layout/dialpad_digits.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Text field and possibly soft menu button above the keypad where
+     the digits are displayed. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/digits_container"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/dialpad_digits_height"
+    android:orientation="horizontal">
+
+    <view class="com.android.dialer.dialpad.DigitsEditText"
+        android:id="@+id/digits"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:paddingLeft="@dimen/dialpad_digits_padding"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:background="@android:color/transparent"
+        android:maxLines="1"
+        android:textSize="@dimen/dialpad_digits_text_size"
+        android:freezesText="true"
+        android:focusableInTouchMode="true"
+        android:editable="true"
+        android:cursorVisible="false"
+        android:textColor="@color/dialpad_digits_text_color"
+        android:textCursorDrawable="@null"
+        android:fontFamily="sans-serif-light"
+        android:textStyle="normal" />
+
+    <ImageButton
+        android:id="@+id/deleteButton"
+        android:paddingLeft="@dimen/dialpad_digits_padding"
+        android:paddingRight="@dimen/dialpad_digits_padding"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:state_enabled="false"
+        android:background="@drawable/dialpad_key_colors"
+        android:contentDescription="@string/description_delete_button"
+        android:src="@drawable/ic_dial_action_delete" />
+</LinearLayout>
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index e673cea..dfe5c7c 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -16,103 +16,55 @@
 <view class="com.android.dialer.dialpad.DialpadFragment$DialpadSlidingLinearLayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     android:orientation="vertical" >
     <!-- spacer view -->
     <View
         android:id="@+id/spacer"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1" />
+        android:layout_weight="1"
+        android:background="#00000000" />
+    <!-- Dialpad shadow -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="3dp"
+        android:background="@drawable/shadow_fade_up" />
     <view class="com.android.dialer.dialpad.DialpadFragment$HoverIgnoringLinearLayout"
         android:id="@+id/top"
+        android:animateLayoutChanges="true"
+        android:layout_height="wrap_content"
         android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="2"
+        android:layout_gravity="bottom"
         android:orientation="vertical"
-        android:paddingStart="@dimen/dialpad_horizontal_margin"
-        android:paddingEnd="@dimen/dialpad_horizontal_margin"
         android:layoutDirection="ltr"
-        android:background="@color/background_dialpad" >
+        android:background="@color/background_dialpad">
 
-
-        <!-- Text field and possibly soft menu button above the keypad where
-             the digits are displayed. -->
-        <LinearLayout
-            android:id="@+id/digits_container"
+        <Space
             android:layout_width="match_parent"
-            android:layout_height="0px"
-            android:layout_weight="@integer/dialpad_layout_weight_digits"
-            android:layout_marginTop="@dimen/dialpad_vertical_margin"
-            android:gravity="center" >
+            android:layout_height="2dp"
+            android:background="@color/dialpad_separator_line_color" />
 
-            <com.android.dialer.dialpad.DigitsEditText
-                android:id="@+id/digits"
-                android:layout_width="0dip"
-                android:layout_weight="1"
-                android:layout_height="match_parent"
-                android:paddingLeft="10dp"
-                android:gravity="center"
-                android:scrollHorizontally="true"
-                android:singleLine="true"
-                android:textAppearance="@style/DialtactsDigitsTextAppearance"
-                android:textColor="@color/dialpad_text_color"
-                android:textCursorDrawable="@null"
-                android:fontFamily="sans-serif-light"
-                android:nextFocusRight="@+id/overflow_menu"
-                android:background="@android:color/transparent" />
+        <Space
+            android:layout_width="match_parent"
+            android:layout_height="10dp" />
 
-            <ImageButton
-                android:id="@+id/deleteButton"
-                android:layout_width="56dip"
-                android:layout_height="match_parent"
-                android:layout_gravity="center_vertical"
-                android:gravity="center"
-                android:state_enabled="false"
-                android:background="?android:attr/selectableItemBackground"
-                android:contentDescription="@string/description_delete_button"
-                android:src="@drawable/ic_dial_action_delete" />
-        </LinearLayout>
+        <include layout="@layout/dialpad_digits"/>
 
-        <!-- Keypad section -->
+        <Space
+            android:layout_width="match_parent"
+            android:layout_height="8dp" />
+
         <include layout="@layout/dialpad" />
 
-        <View style="@style/DialpadHorizontalSeparator"/>
-
-        <LinearLayout
-            android:id="@+id/dialButtonContainer"
+        <Space
             android:layout_width="match_parent"
-            android:layout_height="@dimen/fake_action_bar_height"
-            android:layout_gravity="center_horizontal"
-            android:background="@color/dialpad_primary_text_color"
-            android:layoutDirection="locale">
-            <ImageButton
-                android:id="@+id/call_history_on_dialpad_button"
-                android:layout_height="match_parent"
-                android:layout_width="@dimen/fake_menu_button_min_width"
-                android:layout_gravity="bottom|start"
-                android:background="@drawable/btn_call"
-                android:contentDescription="@string/action_menu_call_history_description"
-                android:src="@drawable/ic_menu_history_lt"/>
-            <ImageButton
-                android:id="@+id/dialButton"
-                android:layout_width="0dp"
-                android:layout_weight="1"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                android:state_enabled="false"
-                android:background="@drawable/btn_call"
-                android:contentDescription="@string/description_dial_button"
-                android:src="@drawable/ic_dial_action_call" />
-            <ImageButton
-                android:id="@+id/overflow_menu_on_dialpad"
-                android:layout_height="match_parent"
-                android:layout_width="@dimen/fake_menu_button_min_width"
-                android:layout_gravity="bottom|end"
-                android:background="@drawable/btn_call"
-                android:src="@drawable/ic_menu_overflow_lt"
-                android:contentDescription="@string/action_menu_overflow_description" />
-        </LinearLayout>
+            android:layout_height="8dp" />
+
+        <Space
+            android:layout_width="match_parent"
+            android:layout_height="2dp"
+            android:background="@color/dialpad_separator_line_color" />
 
         <!-- "Dialpad chooser" UI, shown only when the user brings up the
              Dialer while a call is already in progress.
@@ -120,9 +72,9 @@
              (the textfield/button and the dialpad) are hidden. -->
         <ListView android:id="@+id/dialpadChooser"
             android:layout_width="match_parent"
-            android:layout_height="1dip"
+            android:layout_height="wrap_content"
             android:layout_weight="1"
-        />
+            android:visibility="gone" />
 
     </view>
 </view>
diff --git a/res/layout/dialpad_key.xml b/res/layout/dialpad_key.xml
index c104f8a..5bf858c 100644
--- a/res/layout/dialpad_key.xml
+++ b/res/layout/dialpad_key.xml
@@ -17,29 +17,19 @@
 <!-- A layout representing a single key in the dialpad -->
 <com.android.dialer.dialpad.DialpadKeyButton
     xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/DialtactsDialpadButtonStyle"
-    android:clickable="true"
-    android:focusable="true" >
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center">
-      <TextView
-          android:id="@+id/dialpad_key_number"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:textColor="@color/dialpad_primary_text_color"
-          android:textSize="@dimen/dialpad_key_numbers_size"
-          android:fontFamily="sans-serif-light"
-          />
-      <TextView
-          android:id="@+id/dialpad_key_letters"
-          android:layout_width="@dimen/dialpad_key_letters_width"
-          android:layout_height="wrap_content"
-          android:textColor="@color/dialpad_secondary_text_color"
-          android:textSize="@dimen/dialpad_key_letters_size"
-          android:paddingLeft="11dp"
-          android:fontFamily="sans-serif-light"
-          />
+    style="@style/DialpadKeyButtonStyle">
+
+    <LinearLayout style="@style/DialpadKeyInternalLayoutStyle">
+
+        <!-- Note in the referenced styles that we assign hard widths to these components
+             because we want them to line up vertically when we arrange them in an MxN grid -->
+
+        <TextView
+            android:id="@+id/dialpad_key_number"
+            style="@style/DialpadKeyNumberStyle" />
+
+        <TextView
+            android:id="@+id/dialpad_key_letters"
+            style="@style/DialpadKeyLettersStyle" />
     </LinearLayout>
 </com.android.dialer.dialpad.DialpadKeyButton>
\ No newline at end of file
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index e2c3853..0f80564 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -13,113 +13,181 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:focusable="true"
+    android:orientation="vertical"
     android:focusableInTouchMode="true"
     android:clipChildren="false"
-    android:id="@+id/dialtacts_container"
     >
-    <!-- Overlapping dialpad fragment is inserted here -->
-    <LinearLayout
+
+    <FrameLayout
+        android:id="@+id/dialtacts_container"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:orientation="vertical" >
+        android:layout_height="0dp"
+        android:layout_weight="1">
+
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:id="@+id/search_view_container"
-            android:orientation="vertical"
-            >
-            <LinearLayout
+            android:layout_height="match_parent"
+            android:clipChildren="false"
+            android:orientation="vertical" >
+
+            <!-- Search entry box and remove view -->
+            <FrameLayout
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="horizontal"
-                android:paddingLeft="16dp"
-                android:paddingRight="23dp"
-                android:background="@color/searchbox_background_color"
-                android:gravity="center_vertical">
-                <EditText
-                    android:id="@+id/search_view"
-                    android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:id="@+id/search_and_remove_view_container"
+                >
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/search_box_height"
+                    android:id="@+id/search_view_container"
+                    android:orientation="horizontal"
+                    android:layout_marginTop="@dimen/search_top_margin"
+                    android:layout_marginBottom="@dimen/search_bottom_margin"
+                    android:layout_marginLeft="@dimen/search_margin_horizontal"
+                    android:layout_marginRight="@dimen/search_margin_horizontal"
+                    android:paddingLeft="@dimen/search_box_left_padding"
+                    android:paddingRight="@dimen/search_box_right_padding"
+                    android:background="@drawable/search_bg"
+                    android:gravity="center_vertical"
+                    >
+                    <EditText
+                        android:id="@+id/search_view"
+                        android:layout_width="match_parent"
+                        android:layout_height="@dimen/search_box_icon_size"
+                        android:layout_weight="1"
+                        android:layout_marginLeft="@dimen/search_box_text_left_margin"
+                        android:textSize="@dimen/search_text_size"
+                        android:fontFamily="@string/search_font_family"
+                        android:textColor="@color/searchbox_text_color"
+                        android:textColorHint="@color/searchbox_hint_text_color"
+                        android:inputType="textFilter"/>
+                    <ImageView
+                        android:id="@+id/search_close_button"
+                        android:layout_height="@dimen/search_box_icon_size"
+                        android:layout_width="@dimen/search_box_icon_size"
+                        android:padding="6dp"
+                        android:src="@drawable/ic_close_dk"
+                        android:clickable="true"
+                        android:background="?android:attr/selectableItemBackground"
+                        android:contentDescription="@string/description_clear_search"
+                        android:visibility="gone" />
+                    <ImageView
+                        android:id="@+id/voice_search_button"
+                        android:layout_height="@dimen/search_box_icon_size"
+                        android:layout_width="@dimen/search_box_icon_size"
+                        android:padding="@dimen/search_box_icon_padding"
+                        android:src="@drawable/ic_voice_search"
+                        android:clickable="true"
+                        android:contentDescription="@string/description_start_voice_search"
+                        android:background="?android:attr/selectableItemBackground" />
+                </LinearLayout>
+                <com.android.dialer.list.RemoveView
+                    android:layout_width="match_parent"
                     android:layout_height="56dp"
-                    android:layout_weight="1"
-                    android:textSize="@dimen/search_text_size"
-                    android:inputType="textFilter"/>
-                <ImageView
-                    android:id="@+id/search_close_button"
-                    android:layout_height="40dp"
-                    android:layout_width="40dp"
-                    android:padding="6dp"
-                    android:src="@drawable/ic_close_dk"
-                    android:clickable="true"
-                    android:background="?android:attr/selectableItemBackground"
-                    android:visibility="gone" />
-                <ImageView
-                    android:id="@+id/voice_search_button"
-                    android:layout_height="40dp"
-                    android:layout_width="40dp"
-                    android:padding="3dp"
-                    android:src="@drawable/ic_voice_search"
-                    android:clickable="true"
-                    android:contentDescription="@string/description_start_voice_search"
-                    android:background="?android:attr/selectableItemBackground" />
-            </LinearLayout>
-            <View
-                android:id="@+id/searchbox_divider"
-                android:layout_height="1dp"
+                    android:id="@+id/remove_view_container"
+                    android:orientation="horizontal"
+                    android:gravity="center"
+                    android:visibility="gone">
+                    <ImageView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:id="@+id/remove_view_icon"
+                        android:src="@drawable/ic_remove"
+                        android:contentDescription="@string/remove_contact"
+                        />
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:id="@+id/remove_view_text"
+                        android:textSize="@dimen/remove_text_size"
+                        android:textColor="@color/remove_text_color"
+                        android:text="@string/remove_contact"
+                        />
+                </com.android.dialer.list.RemoveView>
+            </FrameLayout>
+
+            <!-- Relative Layout is used to contain the main contacts grid and the thin translucent
+                 horizontal divider line at the bottom of the contacts grid above the menu bar. -->
+            <RelativeLayout android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:clipChildren="false"
+                >
+                <!-- The main contacts grid -->
+                <FrameLayout
+                    android:layout_height="match_parent"
+                    android:layout_width="match_parent"
+                    android:id="@+id/dialtacts_frame"
+                    android:clipChildren="false"
+                    android:layout_alignParentBottom="true"
+                    android:layout_alignParentTop="true"
+                    android:layout_alignParentLeft="true"
+                    android:layout_alignParentRight="true"
+                    >
+                </FrameLayout>
+                <!-- Thin translucent horizontal line at the bottom of the contacts grid. Floats
+                     above the contacts grid and has a translucent color. -->
+                <View
+                    android:layout_height="2dp"
+                    android:layout_width="match_parent"
+                    android:background="@color/contacts_grid_bottom_border_color"
+                    android:layout_alignParentBottom="true"
+                    android:layout_alignParentLeft="true"
+                    android:layout_alignParentRight="true"
+                    />
+            </RelativeLayout>
+            <Space
+                android:id="@+id/contact_tile_frame_spacer"
+                android:layout_height="wrap_content"
                 android:layout_width="match_parent"
-                android:background="@color/background_dialer_light" />
+                android:layout_alignParentBottom="true"
+                android:visibility="gone"/>
         </LinearLayout>
-        <FrameLayout
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:layout_width="match_parent"
-            android:id="@+id/dialtacts_frame"
-            android:clipChildren="false">
-        </FrameLayout>
-        <View
-            android:layout_height="2dp"
-            android:layout_width="match_parent"
-            android:background="#33999999"
-        />
-        <FrameLayout
-            android:layout_height="@dimen/fake_action_bar_height"
-            android:layout_width="match_parent"
-            android:id="@+id/fake_action_bar"
-            android:background="@color/actionbar_background_color">
-            <ImageButton
-                android:id="@+id/call_history_button"
-                android:layout_width="@dimen/fake_menu_button_min_width"
-                android:layout_height="match_parent"
-                android:layout_gravity="bottom|start"
-                android:background="?android:attr/selectableItemBackground"
-                android:contentDescription="@string/action_menu_call_history_description"
-                android:src="@drawable/ic_menu_history_dk"/>
-            <ImageButton
-                android:id="@+id/dialpad_button"
-                android:layout_width="@dimen/fake_menu_button_min_width"
-                android:layout_height="match_parent"
-                android:layout_gravity="bottom|center"
-                android:background="?android:attr/selectableItemBackground"
-                android:contentDescription="@string/action_menu_dialpad_button"
-                android:src="@drawable/ic_menu_dialpad_dk"/>
-            <ImageButton
-                 android:id="@+id/overflow_menu"
-                 android:layout_width="@dimen/fake_menu_button_min_width"
-                 android:layout_height="match_parent"
-                 android:layout_gravity="bottom|end"
-                 android:src="@drawable/ic_menu_overflow_dk"
-                 android:contentDescription="@string/action_menu_overflow_description"
-                 android:background="?android:attr/selectableItemBackground"/>
-        </FrameLayout>
-        <View
-            android:id="@+id/dialtacts_bottom_padding"
-            android:layout_width="match_parent"
-            android:layout_height="?android:attr/actionBarSize"
+    </FrameLayout>
+
+    <!-- Fake action bar -->
+    <FrameLayout
+        android:layout_height="@dimen/fake_action_bar_height"
+        android:layout_width="match_parent"
+        android:id="@+id/fake_action_bar"
+        android:background="@color/actionbar_background_color">
+        <ImageButton
+            android:id="@+id/call_history_button"
+            android:layout_width="@dimen/fake_menu_button_min_width"
+            android:layout_height="match_parent"
+            android:layout_gravity="bottom|start"
+            android:background="?android:attr/selectableItemBackground"
+            android:contentDescription="@string/action_menu_call_history_description"
+            android:src="@drawable/ic_menu_history_lt"/>
+        <ImageButton
+            android:id="@+id/dialpad_button"
+            android:layout_width="@dimen/fake_menu_button_min_width"
+            android:layout_height="match_parent"
+            android:layout_gravity="bottom|center"
+            android:background="?android:attr/selectableItemBackground"
+            android:contentDescription="@string/action_menu_dialpad_button"
+            android:src="@drawable/ic_menu_dialpad_lt"/>
+        <ImageButton
+            android:id="@+id/dial_button"
+            android:layout_width="@dimen/fake_menu_button_min_width"
+            android:layout_height="match_parent"
+            android:layout_gravity="bottom|center"
+            android:background="@drawable/btn_call"
+            android:contentDescription="@string/description_dial_button"
+            android:src="@drawable/ic_dial_action_call"
             android:visibility="gone" />
-    </LinearLayout>
-</FrameLayout>
+        <ImageButton
+            android:id="@+id/overflow_menu"
+            android:layout_width="@dimen/fake_menu_button_min_width"
+            android:layout_height="match_parent"
+            android:layout_gravity="bottom|end"
+            android:src="@drawable/ic_menu_overflow_lt"
+            android:contentDescription="@string/action_menu_overflow_description"
+            android:background="?android:attr/selectableItemBackground"/>
+    </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/phone_favorite_regular_row_view.xml b/res/layout/phone_favorite_regular_row_view.xml
index 012c9be..d046fdb 100644
--- a/res/layout/phone_favorite_regular_row_view.xml
+++ b/res/layout/phone_favorite_regular_row_view.xml
@@ -65,18 +65,6 @@
                 android:singleLine="true"
                 android:textColor="@color/dialtacts_secondary_text_color" />
         </LinearLayout>
-        <ImageView
-            android:id="@+id/contact_favorite_star"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignRight="@id/contact_tile_quick"
-            android:layout_alignEnd="@id/contact_tile_quick"
-            android:layout_alignBottom="@id/contact_tile_quick"
-            android:layout_marginRight="7dip"
-            android:layout_marginEnd="7dip"
-            android:layout_marginBottom="7dip"
-            android:src="@drawable/ic_star_marked_as_fav"
-            android:visibility="gone" />
 
     </RelativeLayout>
 
diff --git a/res/layout/phone_favorite_tile_view.xml b/res/layout/phone_favorite_tile_view.xml
index 8806d39..952bb2a 100644
--- a/res/layout/phone_favorite_tile_view.xml
+++ b/res/layout/phone_favorite_tile_view.xml
@@ -25,7 +25,8 @@
         android:id="@+id/contact_favorite_card"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:focusable="true">
+        android:focusable="true"
+        android:background="@drawable/ic_contacts_tiles">
 
         <com.android.contacts.common.widget.LayoutSuppressingImageView
             android:id="@+id/contact_tile_image"
@@ -42,11 +43,11 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingLeft="8dp"
-            android:paddingRight="@dimen/contact_tile_info_button_height_and_width"
-            android:paddingStart="8dp"
-            android:paddingEnd="@dimen/contact_tile_info_button_height_and_width"
-            android:paddingBottom="4dp"
+            android:paddingLeft="@dimen/contact_tile_text_side_padding"
+            android:paddingRight="@dimen/contact_tile_text_side_padding"
+            android:paddingStart="@dimen/contact_tile_text_side_padding"
+            android:paddingEnd="@dimen/contact_tile_text_side_padding"
+            android:paddingBottom="@dimen/contact_tile_text_bottom_padding"
             android:layout_alignParentBottom="true"
             android:orientation="vertical" >
             <TextView
@@ -57,7 +58,7 @@
                 android:textColor="@color/contact_tile_name_color"
                 android:fontFamily="sans-serif"
                 android:singleLine="true"
-                android:textSize="16sp"
+                android:textSize="15sp"
                 android:fadingEdge="horizontal"
                 android:fadingEdgeLength="3dip"
                 android:ellipsize="marquee"
@@ -70,8 +71,7 @@
                 android:textColor="@color/contact_tile_name_color"
                 android:fontFamily="sans-serif"
                 android:singleLine="true"
-                android:textSize="12sp"
-                android:paddingBottom="2dp"
+                android:textSize="11sp"
                 android:fadingEdge="horizontal"
                 android:fadingEdgeLength="3dip"
                 android:ellipsize="marquee"
@@ -85,40 +85,22 @@
             android:nextFocusRight="@+id/contact_tile_secondary_button"
             android:background="?android:attr/selectableItemBackground" />
 
-        <ImageView
-            android:id="@+id/contact_favorite_star"
-            android:background="?android:attr/selectableItemBackground"
-            android:layout_height="@dimen/contact_tile_info_button_height_and_width"
-            android:layout_width="@dimen/contact_tile_info_button_height_and_width"
-            android:paddingLeft="8dp"
-            android:paddingRight="8dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:paddingTop="8dp"
-            android:paddingBottom="8dp"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentEnd="true"
-            android:src="@drawable/ic_star_marked_as_fav"
-            android:scaleType="center"
-            android:visibility="gone" />
-
-
         <ImageButton
             android:id="@id/contact_tile_secondary_button"
-            android:src="@drawable/ic_contact_info"
+            android:src="@drawable/overflow_thumbnail"
             android:background="?android:attr/selectableItemBackground"
             android:layout_height="@dimen/contact_tile_info_button_height_and_width"
             android:layout_width="@dimen/contact_tile_info_button_height_and_width"
-            android:paddingLeft="8dp"
-            android:paddingRight="8dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:paddingTop="8dp"
-            android:paddingBottom="8dp"
-            android:layout_alignParentBottom="true"
+            android:paddingLeft="4dp"
+            android:paddingRight="4dp"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp"
+            android:layout_alignParentTop="true"
             android:layout_alignParentRight="true"
             android:layout_alignParentEnd="true"
+            android:scaleType="center"
             android:contentDescription="@string/description_view_contact_detail" />
 
     </RelativeLayout>
diff --git a/res/layout/phone_favorites_fragment.xml b/res/layout/phone_favorites_fragment.xml
index 363565a..d1ff3d7 100644
--- a/res/layout/phone_favorites_fragment.xml
+++ b/res/layout/phone_favorites_fragment.xml
@@ -22,7 +22,7 @@
     android:divider="?android:attr/dividerHorizontal"
     android:showDividers="end"
     android:clipChildren="false"
-    android:background="@color/background_dialer_light">
+    android:background="@color/background_dialer_list_items">
 
     <FrameLayout
         android:id="@+id/contact_tile_frame"
@@ -30,7 +30,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentTop="true"
         android:layout_alignParentLeft="true"
-        android:layout_alignParentStart="true">
+        >
 
         <com.android.dialer.list.PhoneFavoriteListView
             android:id="@+id/contact_tile_list"
@@ -58,5 +58,4 @@
         android:layout_alignParentBottom="true"
         layout="@layout/phone_no_favorites"
         android:visibility="gone"/>
-
 </RelativeLayout>
diff --git a/res/layout/phone_favorites_menu.xml b/res/layout/phone_favorites_menu.xml
new file mode 100644
index 0000000..387ea5b
--- /dev/null
+++ b/res/layout/phone_favorites_menu.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- The phone favorites menu appears on the main dialer screen above the favorite callers area,
+     and provides access to the All Contacts list. -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:id="@+id/phone_favorites_menu"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingLeft="@dimen/favorites_menu_padding_horizontal"
+                android:paddingRight="@dimen/favorites_menu_padding_horizontal"
+                android:paddingTop="@dimen/favorites_menu_padding_top"
+                android:paddingBottom="@dimen/favorites_menu_padding_bottom"
+                android:background="@color/favorites_menu_background_color"
+        >
+    <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/favorites_menu_speed_dial_height"
+            android:fontFamily="@string/favorites_menu_speed_dial_font_family"
+            android:text="@string/favorites_menu_speed_dial"
+            android:textSize="@dimen/favorites_menu_speed_dial_text_size"
+            android:textColor="@color/speed_dial_text_color"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:gravity="center"
+        />
+    <Button
+            android:id="@+id/all_contacts_button"
+            android:fontFamily="@string/favorites_menu_all_contacts_font_family"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/favorites_menu_all_contacts_height"
+            android:paddingLeft="@dimen/favorites_menu_padding_horizontal"
+            android:paddingRight="@dimen/favorites_menu_padding_horizontal"
+            android:text="@string/favorites_menu_all_contacts"
+            android:textSize="@dimen/favorites_menu_all_contacts_text_size"
+            android:background="@drawable/background_all_contacts"
+            android:textColor="@color/all_contacts_button_text_color"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:gravity="center"
+        />
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/phone_no_favorites.xml b/res/layout/phone_no_favorites.xml
index 1a0079f..b95afd1 100644
--- a/res/layout/phone_no_favorites.xml
+++ b/res/layout/phone_no_favorites.xml
@@ -18,7 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
     android:minHeight="?android:attr/listPreferredItemHeight">
 
     <include
@@ -33,23 +32,18 @@
     <LinearLayout
         android:id="@+id/nofavorite_frame"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:layout_gravity="center"
         android:gravity="center"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
-        android:layout_above="@id/show_all_contact_button_in_nofav">
-
+        android:layout_centerInParent="true">
         <ImageView
             android:id="@+id/nofavorite_image"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:gravity="center_horizontal"
             android:src="@drawable/no_favorites_banner"
-            android:layout_marginBottom="14dp"/>
-
+            android:layout_marginBottom="14dp"
+            android:contentDescription="@string/no_favorites"/>
         <TextView
             android:id="@+id/title"
             android:layout_width="match_parent"
diff --git a/res/layout/tile_interactions_teaser_view.xml b/res/layout/tile_interactions_teaser_view.xml
index 4104446..8204cb1 100644
--- a/res/layout/tile_interactions_teaser_view.xml
+++ b/res/layout/tile_interactions_teaser_view.xml
@@ -18,7 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/actionbar_background_color"
+    android:background="@color/background_dialer_list_items"
     android:paddingBottom="@dimen/favorites_row_bottom_padding"
     android:paddingTop="@dimen/favorites_row_top_padding">
 
diff --git a/res/values/colors.xml b/res/values/colors.xml
index a8ddf2b..e9730ea 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -12,14 +12,26 @@
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
-  -->
+-->
 
 <resources>
 
     <!-- Secondary text color in the Phone app -->
     <color name="dialtacts_primary_text_color">#000000</color>
     <color name="dialtacts_secondary_text_color">#888888</color>
-    <color name="dialpad_text_color">#ffffff</color>
+
+    <!-- Background color of dialpad -->
+    <color name="background_dialpad">#ffffff</color>
+    <!-- Pressed color of dialpad buttons -->
+    <color name="background_dialpad_pressed">#ececec</color>
+    <!-- Primary color of dialpad text, including the call button -->
+    <color name="dialpad_primary_text_color">#3B77E7</color>
+    <!-- Secondary color of dialpad text (used for the letters corresponding to each digit -->
+    <color name="dialpad_secondary_text_color">#8b8b8b</color>
+    <!-- Color of dialpad digits -->
+    <color name="dialpad_digits_text_color">#000000</color>
+    <!-- Color for dialpad separator lines -->
+    <color name="dialpad_separator_line_color">#dadada</color>
 
     <!-- Color of the text describing an unconsumed missed call. -->
     <color name="call_log_missed_call_highlight_color">#FF0000</color>
@@ -27,8 +39,10 @@
     <!-- Color of the text describing an unconsumed voicemail. -->
     <color name="call_log_voicemail_highlight_color">#33b5e5</color>
 
-    <!-- Colour of voicemail progress bar to the right of position indicator.
-         Same as the background color of the dialer -->
+    <!--
+         Colour of voicemail progress bar to the right of position indicator.
+         Same as the background color of the dialer
+    -->
     <color name="voicemail_playback_seek_bar_yet_to_play">#cecece</color>
 
     <!-- Colour of voicemail progress bar to the left of position indicator. -->
@@ -38,36 +52,87 @@
     <color name="item_selected">#660099cc</color>
 
     <!-- Background color of new dialer activity -->
-    <color name="background_dialer_light">#cecece</color>
+    <color name="background_dialer_light">#eeeeee</color>
 
     <!-- Background color of dialer list items (contacts, call log entries) -->
     <color name="background_dialer_list_items">#eeeeee</color>
 
-    <!-- Background color of new dialpad -->
-    <color name="background_dialpad">#f2020709</color>
+    <!-- Background color of action bars.  Ensure this stays in sync with packages/Telephony
+         actionbar_background_color. -->
+    <color name="actionbar_background_color">#3B77E7</color>
 
-    <!-- Primary color of dialpad text, including the call button -->
-    <color name="dialpad_primary_text_color">#33b5e5</color>
-    <!-- Secondary color of dialpad text (used for the letters corresponding to each digit -->
-    <color name="dialpad_secondary_text_color">#aaaaaa</color>
-
-    <!-- Background color of action bars -->
-    <color name="actionbar_background_color">#ffffff</color>
+    <!-- Underline color of action bars.  Ensure this stays in sync with packages/Telephony
+         actionbar_underline. -->
+    <color name="actionbar_underline">#3265C1</color>
 
     <!-- Color of the 1dp divider that separates favorites -->
     <color name="favorite_contacts_separator_color">#d0d0d0</color>
 
     <!-- Background color of the search box -->
     <color name="searchbox_background_color">#ffffff</color>
-    <!-- Text color of the search box -->
-    <color name="searchbox_text_color">#d3d3d3</color>
+    <!-- Text color of the search box text as entered by user  -->
+    <color name="searchbox_text_color">#000000</color>
+    <!-- Text color of the search box hint text  -->
+    <color name="searchbox_hint_text_color">#d3d3d3</color>
 
     <!-- Color of the contact name in favorite tiles -->
-    <color name="contact_tile_name_color">#f0f0f0</color>
+    <color name="contact_tile_name_color">#ffffff</color>
 
     <!-- Undo dialogue color -->
     <color name="undo_dialogue_text_color">#4d4d4d</color>
 
     <!-- Text color for no favorites message -->
     <color name="nofavorite_text_color">#777777</color>
+
+    <!-- Text color for the "Remove" text in its regular state -->
+    <color name="remove_text_color">#555555</color>
+
+    <!-- Text color for the "Remove" text when a contact is dragged on top of the remove view -->
+    <color name="remove_highlighted_text_color">#FF3F3B</color>
+
+    <!-- Text color for the "speed dial" label in the favorites menu. -->
+    <color name="speed_dial_text_color">#555555</color>
+
+    <!-- Background color for the "All Contacts" button in the favorites menu. -->
+    <color name="all_contacts_button_color">#999999</color>
+
+    <!-- Background color for the "All Contacts" button in the favorites menu when pressed. -->
+    <color name="all_contacts_button_pressed_color">#808080</color>
+
+    <!-- Background color for the favorites menu. -->
+    <color name="favorites_menu_background_color">#eeeeee</color>
+
+    <!-- Text color for the "All Contacts" button above the favorite callers -->
+    <color name="all_contacts_button_text_color">#ffffff</color>
+
+    <!-- Background gradient start color for tabs in an unpressed state. -->
+    <color name="tab_background_gradient_start_color">#d3d3d3</color>
+
+    <!-- Background gradient end color for tabs in an unpressed state. -->
+    <color name="tab_background_gradient_end_color">#ebebeb</color>
+
+    <!-- Background gradient start color for tabs in a pressed state. -->
+    <color name="tab_background_gradient_start_pressed_color">#c4c4c4</color>
+
+    <!-- Background gradient end color for tabs in a pressed state. -->
+    <color name="tab_background_gradient_end_pressed_color">#dedede</color>
+
+    <!-- Color of the underline for a tab which is selected. -->
+    <color name="tab_underline_selected_color">#3265c1</color>
+
+    <!-- Color of the underline for a tab which is not selected. -->
+    <color name="tab_underline_color">#c8c8c8</color>
+
+    <!-- Color of the selection indicator for a tab which is selected. -->
+    <color name="tab_selected_color">#3b77e7</color>
+
+    <!-- Color of tab text. -->
+    <color name="tab_text_color">#505050</color>
+
+    <!-- Color of the bottom border below the contacts grid on the main dialer screen. -->
+    <color name="contacts_grid_bottom_border_color">#16000000</color>
+
+    <!-- Color of action bar text. Ensure this stays in sync with packages/Telephony
+         phone_settings_actionbar_text_color-->
+    <color name="actionbar_text_color">#FFFFFF</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1743952..9490d8d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -13,28 +13,21 @@
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
-  -->
+-->
 <resources>
 
-    <!-- Height of edit text in dialpad fragment -->
-    <dimen name="dialpad_horizontal_margin">0dip</dimen>
-    <dimen name="dialpad_vertical_margin">2dip</dimen>
-    <dimen name="dialpad_digits_text_size">35sp</dimen>
-
-    <!-- Just used in landscape mode -->
-    <dimen name="dialpad_digits_height">0px</dimen>
-    <dimen name="dialpad_digits_margin_bottom">0px</dimen>
-    <dimen name="dialpad_center_margin">3dp</dimen>
-    <dimen name="dialpad_button_margin">2dp</dimen>
-    <!-- Match call_button_height to Phone's dimens/in_call_end_button_height -->
-    <dimen name="call_button_height">74dp</dimen>
-
     <!--  Search View -->
     <dimen name="search_text_size">14sp</dimen>
 
+    <!--
+          Drag to remove view (in dp because it is used in conjunction with a statically
+          sized icon
+    -->
+    <dimen name="remove_text_size">16dp</dimen>
+
     <!-- Call Log -->
     <dimen name="call_log_call_action_size">32dip</dimen>
-    <dimen name="call_log_call_action_width">48dip</dimen>
+    <dimen name="call_log_call_action_width">54dp</dimen>
     <dimen name="call_log_icon_margin">4dip</dimen>
     <dimen name="call_log_inner_margin">8dip</dimen>
     <dimen name="call_log_outer_margin">8dip</dimen>
@@ -43,30 +36,47 @@
     <dimen name="call_log_list_contact_photo_size">64dip</dimen>
     <dimen name="call_detail_contact_name_margin">24dip</dimen>
     <dimen name="call_detail_button_spacing">2dip</dimen>
+    <!-- Defines the vertical margin for the vertical separator between
+         the main area of a call log entry and the secondary action button. -->
+    <dimen name="call_log_list_item_vertical_divider_margin">17dp</dimen>
 
-    <!-- Layout weight values for dialpad screen. These layouts will be used in one
-         LinearLayout (dialpad_fragment.xml), configuring dialpad screen's vertical
-         ratio. -->
-    <integer name="dialpad_layout_weight_digits">15</integer>
-    <integer name="dialpad_layout_weight_dialpad">65</integer>
+    <!-- Defines the width of the vertical separator between
+         the main area of a call log entry and the secondary action button. -->
+    <dimen name="call_log_list_item_vertical_divider_width">1dp</dimen>
 
     <!-- Text dimensions for dialpad keys -->
-    <dimen name="dialpad_key_numbers_size">40dp</dimen>
-    <dimen name="dialpad_key_letters_size">11dp</dimen>
-    <dimen name="dialpad_key_plus_size">15dp</dimen>
-    <dimen name="dialpad_key_special_characters_size">25dp</dimen>
-    <dimen name="dialpad_key_letters_width">41dp</dimen>
-
+    <dimen name="dialpad_key_numbers_size">40sp</dimen>
+    <dimen name="dialpad_key_letters_size">13sp</dimen>
+    <dimen name="dialpad_key_star_pound_size">26sp</dimen>
+    <dimen name="dialpad_key_numbers_width">30dp</dimen>
+    <dimen name="dialpad_key_letters_width">50dp</dimen>
+    <dimen name="dialpad_key_height">56dp</dimen>
+    <dimen name="dialpad_key_plus_size">18sp</dimen>
+    <dimen name="dialpad_number_to_letters_padding">11dp</dimen>
+    <dimen name="dialpad_horizontal_padding">5dp</dimen>
+    <dimen name="dialpad_digits_text_size">36sp</dimen>
+    <dimen name="dialpad_digits_height">47dp</dimen>
+    <dimen name="dialpad_digits_padding">16dp</dimen>
+    <dimen name="dialpad_digits_margin_bottom">0px</dimen>
+    <dimen name="dialpad_center_margin">3dp</dimen>
+    <dimen name="dialpad_button_margin">2dp</dimen>
+    <!-- Match call_button_height to Phone's dimens/in_call_end_button_height -->
+    <dimen name="call_button_height">74dp</dimen>
 
     <dimen name="fake_action_bar_height">60dp</dimen>
     <!-- Min with of fake menu buttons, which should be same as ActionBar's one -->
     <dimen name="fake_menu_button_min_width">56dp</dimen>
 
     <!--  Favorites tile and recent call log padding -->
+    <dimen name="contact_tile_divider_width">12dp</dimen>
+    <!-- Favorites tile and recent call log padding -->
     <dimen name="contact_tile_divider_padding">3dp</dimen>
     <dimen name="contact_tile_info_button_height_and_width">36dp</dimen>
-    <dimen name="favorites_row_top_padding">8dp</dimen>
-    <dimen name="favorites_row_bottom_padding">8dp</dimen>
+    <item name="contact_tile_height_to_width_ratio" type="dimen">67%</item>
+    <dimen name="contact_tile_text_side_padding">10dp</dimen>
+    <dimen name="contact_tile_text_bottom_padding">8dp</dimen>
+    <dimen name="favorites_row_top_padding">6dp</dimen>
+    <dimen name="favorites_row_bottom_padding">6dp</dimen>
     <dimen name="favorites_row_start_padding">8dp</dimen>
     <dimen name="favorites_row_end_padding">8dp</dimen>
     <dimen name="favorites_row_undo_text_side_padding">32dp</dimen>
@@ -75,4 +85,49 @@
     <!-- Padding for the tooltip -->
     <dimen name="dismiss_button_padding_start">20dip</dimen>
     <dimen name="dismiss_button_padding_end">28dip</dimen>
+
+    <!-- Padding above the favorites menu. -->
+    <dimen name="favorites_menu_padding_top">6dp</dimen>
+    <!-- Padding below the favorites menu.. -->
+    <dimen name="favorites_menu_padding_bottom">3dp</dimen>
+    <!-- Horizontal padding in the favorites menu. -->
+    <dimen name="favorites_menu_padding_horizontal">8dp</dimen>
+    <!-- Text size for the "speed dial" text in the favorites menu. -->
+    <dimen name="favorites_menu_speed_dial_text_size">18sp</dimen>
+    <!-- Height of the speed dial TextView in the favorites menu. -->
+    <dimen name="favorites_menu_speed_dial_height">24dp</dimen>
+    <!-- Text size for the "All Contacts" text in the favorites menu. -->
+    <dimen name="favorites_menu_all_contacts_text_size">12sp</dimen>
+    <!-- Height of the all contacts Button in the favorites menu. -->
+    <dimen name="favorites_menu_all_contacts_height">24dp</dimen>
+
+    <!-- Margin to the left and right of the search box. -->
+    <dimen name="search_margin_horizontal">7dp</dimen>
+    <!-- Margin above the search box. -->
+    <dimen name="search_top_margin">10dp</dimen>
+    <!-- Margin below the search box. -->
+    <dimen name="search_bottom_margin">4dp</dimen>
+    <!-- Height of the search box. -->
+    <dimen name="search_box_height">41dp</dimen>
+    <!-- Search box text size -->
+    <dimen name="search_text_size">13.24sp</dimen>
+    <!-- Search box interior padding - left -->
+    <dimen name="search_box_left_padding">16dp</dimen>
+    <!-- Search box interior padding - right -->
+    <dimen name="search_box_right_padding">8dp</dimen>
+    <!-- Padding around the icon in the search box. -->
+    <dimen name="search_box_icon_padding">3dp</dimen>
+    <!-- Left margin of the text field in the search box. -->
+    <dimen name="search_box_text_left_margin">8dp</dimen>
+    <!-- Size of the icon (voice search, close search) in the search box. -->
+    <dimen name="search_box_icon_size">37dp</dimen>
+
+    <!-- Height of the underline of a tab. -->
+    <dimen name="tab_underline_height">2dp</dimen>
+    <!-- Height of the selection indicator of a tab. -->
+    <dimen name="tab_selection_height">7.7dp</dimen>
+    <!-- Padding above and below the divider line of a tab. -->
+    <dimen name="tab_divider_padding">12dp</dimen>
+    <!-- Size of text in tabs. -->
+    <dimen name="tab_text_size">12sp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5b1c293..847e0f3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -465,6 +465,86 @@
     -->
     <string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string>
 
+    <!-- String describing the button to access the contact details for a name or number.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_contact_details">Contact details for <xliff:g id="nameOrNumber">%1$s</xliff:g></string>
+
+    <!-- String describing the button to access call details in the call log.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_call_details">Call details</string>
+
+    <!-- String indicating a call log entry has an associated voicemail.
+
+    Note: AccessibilityServices use this attribute to announce what the view represents.
+          This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_new_voicemail">New voicemail.</string>
+
+    <!-- String indicating the number of calls to/from a caller in the call log.
+
+    Note: AccessibilityServices use this attribute to announce what the view represents.
+          This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_num_calls"><xliff:g id="numberOfCalls">%1$s</xliff:g> calls.</string>
+
+
+    <!-- Call history description for a missed call from a caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_return_missed_call">Return missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+
+    <!-- Call history description for an answered call for a caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_return_answered_call">Return answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+    <!-- Call history description for a missed call from an unknown caller.
+         Drops the "return" part of description_return_missed_call since it is not
+         possible to actually call an unknown number.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_unknown_missed_call">Missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+
+    <!-- Call history description for an answered call from an unknown caller.
+        Drops the "return" part of description_return_answered_call since it is not
+         possible to actually call an unknown number.
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_unknown_answered_call">Answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+    <!-- String describing an outgoing call entry in the call log.  Used to indicate that
+         a call will be made to the specified caller.  Used when there are multiple calls to/from
+         the caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_call_last_multiple">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>.  Last called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string>
+
+    <!-- String describing an outgoing call entry in the call log.  Used to indicate that
+         a call will be made to the specified caller.  Used when there is only a single call
+         related to/from the caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_call_last">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>.  Called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string>
+
 
     <!-- String describing the button to SMS a number or contact.
 
@@ -483,11 +563,15 @@
     -->
     <string name="description_call_log_unheard_voicemail">Unheard voicemail</string>
 
+    <!--  String describing the icon used to clear the search field -->
+    <string name="description_clear_search">Clear search</string>
+
     <!-- String describing the icon used to start a voice search -->
     <string name="description_start_voice_search">Start voice search</string>
 
-    <!-- The string used to represent an unknown location for a phone number in the call log [CHAR LIMIT=3] -->
-    <string name="call_log_empty_gecode">\u0020</string>
+    <!-- The string used to represent an unknown location for a phone number in the call log
+        Do not translate. -->
+    <string name="call_log_empty_geocode"></string>
 
     <!-- Menu item used to call a contact, containing the number of the contact to call -->
     <string name="menu_callNumber">Call <xliff:g id="number">%s</xliff:g></string>
@@ -549,6 +633,9 @@
     <!-- Message displayed when there is no application available to handle the add contact menu option. [CHAR LIMIT=NONE] -->
     <string name="add_contact_not_available">Re-enable the People application to use this feature.</string>
 
+    <!-- Message displayed when there is no application available to handle voice search. [CHAR LIMIT=NONE] -->
+    <string name="voice_search_not_available">Voice search is not available.</string>
+
     <!-- Hint displayed in dialer search box when there is no query that is currently typed.
          [CHAR LIMIT=30] -->
     <string name="dialer_hint_find_contact">Type a name or phone number</string>
@@ -665,4 +752,33 @@
 
     <!-- Content description for dismiss button on badge. [CHAR LIMIT=NONE] -->
     <string name="description_dismiss">Dismiss</string>
+
+    <!-- Remove button that shows up when contact is long-pressed. [CHAR LIMIT=NONE] -->
+    <string name="remove_contact">Remove</string>
+
+    <!-- Header text displayed on the main dialer screen above the list of favorite phone numbers.
+         [CHAR LIMIT=21] -->
+    <string name="favorites_menu_speed_dial">Speed Dial</string>
+
+    <!-- Button text for the "all contacts" button displayed on the main dialer screen above the
+         list of favorite phone numbers.  Navigates the user to the "All Contacts" list.
+         This text represents the same action as the text in string "menu_allContacts".
+         [CHAR LIMIT=21] -->
+    <string name="favorites_menu_all_contacts">ALL CONTACTS</string>
+
+    <!-- The font-family to use for the "speed dial" label on the favorites menu.
+         Do not translate. -->
+    <string name="favorites_menu_speed_dial_font_family">sans-serif-light</string>
+
+    <!-- The font-family to use for the "all contacts" label on the favorites menu.
+         Do not translate. -->
+    <string name="favorites_menu_all_contacts_font_family">sans-serif</string>
+
+    <!-- The font-family to use for the text inside the searchbox.
+         Do not translate. -->
+    <string name="search_font_family">sans-serif</string>
+
+    <!-- The font-family to use for tab text.
+         Do not translate. -->
+    <string name="tab_font_family">sans-serif</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 89678fc..1025d0f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -21,7 +21,16 @@
         <item name="android:textColorSecondary">@color/dialtacts_secondary_text_color</item>
         <item name="android:windowActionBarOverlay">false</item>
         <item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
+        <!-- Style for the tabs (for the tabs) -->
         <item name="android:actionBarTabStyle">@style/DialtactsActionBarTabStyle</item>
+        <!-- Style for the tab bar (for the divider between tabs) -->
+        <item name="android:actionBarTabBarStyle">@style/DialtactsActionBarTabBarStyle</item>
+        <!-- Style for the tab bar text (for text on tabs) -->
+        <item name="android:actionBarTabTextStyle">@style/DialtactsActionBarTabTextStyle</item>
+        <!-- Style for the overflow button in the actionbar. -->
+        <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+        <!-- The "Up" icon in the action bar. -->
+        <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
         <item name="android:windowContentOverlay">@null</item>
         <!--  Searchbox Style -->
         <item name="android:editTextStyle">@style/DialtactsSearchboxStyle</item>
@@ -68,6 +77,24 @@
         <item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
     </style>
 
+    <!-- An extension of the main DialtactsTheme used when the the launcher activity is shown.
+         Ensures that there is no actionbar showing during the load of the dialer app. -->
+    <style name="DialtactsThemeHiddenActionBar" parent="DialtactsTheme">
+        <item name="android:actionBarStyle">@style/DialtactsHiddenActionBarStyle</item>
+    </style>
+
+    <!-- A "hidden" action bar style.  Used when loading the launcher activity so that the
+         default actionbar is effectively hidden. -->
+    <style name="DialtactsHiddenActionBarStyle" parent="DialtactsActionBarStyle">
+        <item name="android:height">0dp</item>
+    </style>
+
+    <!-- Action bar overflow menu icon. -->
+    <style name="DialtactsActionBarOverflow"
+           parent="@android:style/Widget.Holo.ActionButton.Overflow">
+        <item name="android:src">@drawable/ic_menu_overflow_lt</item>
+    </style>
+
     <style name="DialpadTheme" parent="DialtactsTheme">
         <item name="android:textColorPrimary">#FFFFFF</item>
     </style>
@@ -80,6 +107,9 @@
         <item name="android:windowBackground">@color/background_dialer_list_items</item>
         <item name="android:gravity">top</item>
         <item name="android:listViewStyle">@style/ListViewStyle</item>
+        <item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
+        <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+        <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
         <!-- CallLog -->
         <item name="call_log_primary_text_color">#333333</item>
         <item name="call_log_primary_background_color">#FFFFFF</item>
@@ -93,44 +123,84 @@
         <item name="call_log_voicemail_status_action_text_color">#33b5e5</item>
     </style>
 
-    <style name="Theme">
+    <style name="DialpadKeyNumberStyle">
+        <item name="android:textColor">@color/actionbar_background_color</item>
+        <item name="android:textSize">@dimen/dialpad_key_numbers_size</item>
+        <item name="android:fontFamily">sans-serif-light</item>
+        <item name="android:layout_width">@dimen/dialpad_key_numbers_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
     </style>
 
-    <style name="DialtactsDigitsTextAppearance">
-        <item name="android:maxLines">1</item>
-        <item name="android:scrollHorizontally">true</item>
-        <item name="android:textSize">@dimen/dialpad_digits_text_size</item>
-        <item name="android:freezesText">true</item>
-        <item name="android:focusableInTouchMode">true</item>
-        <item name="android:editable">true</item>
-        <item name="android:cursorVisible">false</item>
-        <item name="android:layout_weight">0</item>
+    <style name="DialpadKeyStarPoundStyle">
+        <item name="android:textColor">@color/dialpad_secondary_text_color</item>
+        <item name="android:textSize">@dimen/dialpad_key_star_pound_size</item>
+        <item name="android:fontFamily">sans-serif-light</item>
+        <item name="android:layout_width">@dimen/dialpad_key_numbers_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
     </style>
 
-    <style name="DialtactsDialpadButtonStyle">
-        <item name="android:layout_width">0dip</item>
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_weight">1</item>
-        <item name="android:background">?android:attr/selectableItemBackground</item>
+    <style name="DialpadKeyLettersStyle">
+        <item name="android:textColor">@color/dialpad_secondary_text_color</item>
+        <item name="android:textSize">@dimen/dialpad_key_letters_size</item>
+        <item name="android:fontFamily">sans-serif-light</item>
+        <item name="android:layout_width">@dimen/dialpad_key_letters_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:paddingLeft">@dimen/dialpad_number_to_letters_padding</item>
+    </style>
+
+    <style name="DialpadKeyButtonStyle">
         <item name="android:soundEffectsEnabled">false</item>
+        <item name="android:clickable">true</item>
+        <item name="android:layout_width">fill_parent</item>
+        <item name="android:layout_height">@dimen/dialpad_key_height</item>
+        <item name="android:background">@drawable/dialpad_key_colors</item>
+        <item name="android:focusable">true</item>
     </style>
 
-    <style name="DialpadHorizontalSeparator">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">@dimen/dialpad_vertical_margin</item>
-        <item name="android:background">#66000000</item>
+    <style name="DialpadKeyInternalLayoutStyle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center</item>
     </style>
 
     <style name="DialtactsActionBarStyle" parent="android:Widget.Holo.ActionBar">
         <item name="android:background">@drawable/call_history_actionbar_background</item>
-        <item name="android:backgroundStacked">@color/actionbar_background_color</item>
+        <item name="android:backgroundStacked">#ffffff</item>
+        <item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
         <!-- Empty icon -->
         <item name="android:icon">@android:color/transparent</item>
-        <item name="android:displayOptions"></item>
     </style>
 
+    <!-- Text in the action bar at the top of the screen -->
+    <style name="DialtactsActionBarTitleText"
+           parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
+        <item name="android:textColor">@color/actionbar_text_color</item>
+    </style>
+
+    <!-- Styling for the tab bar; handles styling of the divider line. -->
+    <style name="DialtactsActionBarTabBarStyle"
+           parent="@android:style/Widget.Holo.ActionBar.TabBar">
+        <item name="android:divider">@drawable/divider</item>
+        <item name="android:showDividers">middle</item>
+        <item name="android:dividerPadding">@dimen/tab_divider_padding</item>
+        <item name="android:background">@drawable/action_bar_tab</item>
+    </style>
+
+    <!-- Styling for tabs. -->
     <style name="DialtactsActionBarTabStyle" parent="@android:style/Widget.Holo.ActionBar.TabView">
         <item name="android:background">@drawable/action_bar_tab</item>
+        <item name="android:showDividers">none</item>
+    </style>
+
+    <!-- Text style for tabs. -->
+    <style name="DialtactsActionBarTabTextStyle"
+           parent="android:style/Widget.Holo.Light.ActionBar.TabText">
+        <item name="android:textColor">@color/tab_text_color</item>
+        <item name="android:textSize">@dimen/tab_text_size</item>
+        <item name="android:fontFamily">@string/tab_font_family</item>
+        <item name="android:textStyle">bold</item>
     </style>
 
     <style name="ListViewStyle" parent="@android:style/Widget.Holo.Light.ListView">
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index a24940d..42f295c 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -31,6 +32,7 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.CallLog;
+import android.provider.ContactsContract;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Contacts;
@@ -56,6 +58,7 @@
 import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.ClipboardUtils;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.model.Contact;
 import com.android.contacts.common.model.ContactLoader;
@@ -65,7 +68,7 @@
 import com.android.dialer.calllog.CallTypeHelper;
 import com.android.dialer.calllog.ContactInfo;
 import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.calllog.PhoneNumberHelper;
+import com.android.dialer.calllog.PhoneNumberDisplayHelper;
 import com.android.dialer.calllog.PhoneNumberUtilsWrapper;
 import com.android.dialer.util.AsyncTaskExecutor;
 import com.android.dialer.util.AsyncTaskExecutors;
@@ -114,7 +117,7 @@
     public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
 
     private CallTypeHelper mCallTypeHelper;
-    private PhoneNumberHelper mPhoneNumberHelper;
+    private PhoneNumberDisplayHelper mPhoneNumberHelper;
     private PhoneCallDetailsHelper mPhoneCallDetailsHelper;
     private TextView mHeaderTextView;
     private View mHeaderOverlayView;
@@ -300,7 +303,7 @@
         mResources = getResources();
 
         mCallTypeHelper = new CallTypeHelper(getResources());
-        mPhoneNumberHelper = new PhoneNumberHelper(mResources);
+        mPhoneNumberHelper = new PhoneNumberDisplayHelper(mResources);
         mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(mResources, mCallTypeHelper,
                 new PhoneNumberUtilsWrapper());
         mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
@@ -544,14 +547,14 @@
                             mainActionDescription);
                 }
 
+                final CharSequence displayNumber =
+                        mPhoneNumberHelper.getDisplayNumber(
+                                firstDetails.number,
+                                firstDetails.numberPresentation,
+                                firstDetails.formattedNumber);
+
                 // This action allows to call the number that places the call.
                 if (canPlaceCallsTo) {
-                    final CharSequence displayNumber =
-                            mPhoneNumberHelper.getDisplayNumber(
-                                    firstDetails.number,
-                                    firstDetails.numberPresentation,
-                                    firstDetails.formattedNumber);
-
                     ViewEntry entry = new ViewEntry(
                             getString(R.string.menu_callNumber,
                                     forceLeftToRight(displayNumber)),
@@ -622,7 +625,20 @@
                             }
                         },
                         historyList);
-                loadContactPhotos(photoUri);
+
+                final String displayNameForDefaultImage = TextUtils.isEmpty(firstDetails.name) ?
+                        displayNumber.toString() : firstDetails.name.toString();
+
+                final String lookupKey = ContactInfoHelper.getLookupKeyFromUri(contactUri);
+
+                final boolean isBusiness = mContactInfoHelper.isBusiness(firstDetails.sourceType);
+
+                final int contactType =
+                        isVoicemailNumber? ContactPhotoManager.TYPE_VOICEMAIL :
+                        isBusiness ? ContactPhotoManager.TYPE_BUSINESS :
+                        ContactPhotoManager.TYPE_DEFAULT;
+
+                loadContactPhotos(photoUri, displayNameForDefaultImage, lookupKey, contactType);
                 findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
             }
         }
@@ -643,7 +659,13 @@
             mMainActionPushLayerView.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    startActivity(actionIntent);
+                    try {
+                        startActivity(actionIntent);
+                    } catch (ActivityNotFoundException e) {
+                        final Toast toast = Toast.makeText(CallDetailActivity.this,
+                                R.string.add_contact_not_available, Toast.LENGTH_SHORT);
+                        toast.show();
+                    }
                 }
             });
             mMainActionPushLayerView.setContentDescription(actionDescription);
@@ -683,6 +705,7 @@
             final CharSequence numberLabel;
             final Uri photoUri;
             final Uri lookupUri;
+            int sourceType;
             // If this is not a regular number, there is no point in looking it up in the contacts.
             ContactInfo info =
                     PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)
@@ -697,6 +720,7 @@
                 numberLabel = "";
                 photoUri = null;
                 lookupUri = null;
+                sourceType = 0;
             } else {
                 formattedNumber = info.formattedNumber;
                 nameText = info.name;
@@ -704,11 +728,12 @@
                 numberLabel = info.label;
                 photoUri = info.photoUri;
                 lookupUri = info.lookupUri;
+                sourceType = info.sourceType;
             }
             return new PhoneCallDetails(number, numberPresentation,
                     formattedNumber, countryIso, geocode,
                     new int[]{ callType }, date, duration,
-                    nameText, numberType, numberLabel, lookupUri, photoUri);
+                    nameText, numberType, numberLabel, lookupUri, photoUri, sourceType);
         } finally {
             if (callCursor != null) {
                 callCursor.close();
@@ -717,9 +742,12 @@
     }
 
     /** Load the contact photos and places them in the corresponding views. */
-    private void loadContactPhotos(Uri photoUri) {
+    private void loadContactPhotos(Uri photoUri, String displayName, String lookupKey,
+            int contactType) {
+        final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
+                contactType);
         mContactPhotoManager.loadPhoto(mContactBackgroundView, photoUri,
-                mContactBackgroundView.getWidth(), true);
+                mContactBackgroundView.getWidth(), true, request);
     }
 
     static final class ViewEntry {
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 04934c7..b78a7bc 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -22,12 +22,12 @@
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
-import android.app.FragmentManager.BackStackEntry;
 import android.app.FragmentTransaction;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.drawable.Drawable;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -39,19 +39,18 @@
 import android.speech.RecognizerIntent;
 import android.telephony.TelephonyManager;
 import android.text.Editable;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.TextWatcher;
-import android.text.style.ImageSpan;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.EditText;
+import android.widget.LinearLayout;
 import android.widget.PopupMenu;
 import android.widget.Toast;
 
@@ -67,15 +66,20 @@
 import com.android.dialer.dialpad.SmartDialPrefix;
 import com.android.dialer.interactions.PhoneNumberInteraction;
 import com.android.dialer.list.AllContactsActivity;
+import com.android.dialer.list.DragDropController;
+import com.android.dialer.list.OnDragDropListener;
 import com.android.dialer.list.OnListFragmentScrolledListener;
 import com.android.dialer.list.PhoneFavoriteFragment;
+import com.android.dialer.list.PhoneFavoriteTileView;
 import com.android.dialer.list.RegularSearchFragment;
+import com.android.dialer.list.RemoveView;
 import com.android.dialer.list.SearchFragment;
 import com.android.dialer.list.SmartDialSearchFragment;
 import com.android.dialerbind.DatabaseHelperManager;
 import com.android.internal.telephony.ITelephony;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The dialer tab's title is 'phone', a more common name (see strings.xml).
@@ -83,11 +87,13 @@
 public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
         DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
         OnListFragmentScrolledListener,
-        DialpadFragment.OnDialpadFragmentStartedListener,
-        PhoneFavoriteFragment.OnShowAllContactsListener {
+        DialpadFragment.HostInterface,
+        PhoneFavoriteFragment.OnShowAllContactsListener,
+        PhoneFavoriteFragment.HostInterface,
+        OnDragDropListener, View.OnLongClickListener {
     private static final String TAG = "DialtactsActivity";
 
-    public static final boolean DEBUG = false;
+    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
 
@@ -114,10 +120,10 @@
      */
     private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
 
-    private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
-
     private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
 
+    private static final int ANIMATION_DURATION = 200;
+
     private String mFilterText;
 
     /**
@@ -141,14 +147,17 @@
     private SmartDialSearchFragment mSmartDialSearchFragment;
 
     private View mMenuButton;
+    private View mFakeActionBar;
     private View mCallHistoryButton;
     private View mDialpadButton;
+    private View mDialButton;
     private PopupMenu mOverflowMenu;
+    private PopupMenu mDialpadOverflowMenu;
 
-    // Padding view used to shift the fragments up when the dialpad is shown.
-    private View mBottomPaddingView;
+    // Padding view used to shift the fragment frame up when the dialpad is shown so that
+    // the contents of the fragment frame continue to exist in a layout of the same height
+    private View mFragmentsSpacer;
     private View mFragmentsFrame;
-    private View mActionBar;
 
     private boolean mInDialpadSearch;
     private boolean mInRegularSearch;
@@ -164,6 +173,10 @@
      */
     private boolean mFirstLaunch;
     private View mSearchViewContainer;
+    private RemoveView mRemoveViewContainer;
+    // This view points to the Framelayout that houses both the search view and remove view
+    // containers.
+    private View mSearchAndRemoveViewContainer;
     private View mSearchViewCloseButton;
     private View mVoiceSearchButton;
     private EditText mSearchView;
@@ -292,12 +305,10 @@
         // Add the favorites fragment, and the dialpad fragment, but only if savedInstanceState
         // is null. Otherwise the fragment manager takes care of recreating these fragments.
         if (savedInstanceState == null) {
-            final PhoneFavoriteFragment phoneFavoriteFragment = new PhoneFavoriteFragment();
-
-            final FragmentTransaction ft = getFragmentManager().beginTransaction();
-            ft.add(R.id.dialtacts_frame, phoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
-            ft.add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
-            ft.commit();
+            getFragmentManager().beginTransaction()
+                    .add(R.id.dialtacts_frame, new PhoneFavoriteFragment(), TAG_FAVORITES_FRAGMENT)
+                    .add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT)
+                    .commit();
         } else {
             mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
             mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
@@ -305,9 +316,27 @@
             mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
         }
 
-        mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
         mFragmentsFrame = findViewById(R.id.dialtacts_frame);
-        mActionBar = findViewById(R.id.fake_action_bar);
+        mFragmentsSpacer = findViewById(R.id.contact_tile_frame_spacer);
+
+        mRemoveViewContainer = (RemoveView) findViewById(R.id.remove_view_container);
+        mSearchAndRemoveViewContainer = findViewById(R.id.search_and_remove_view_container);
+
+        // When the first global layout pass is completed (and mSearchAndRemoveViewContainer has
+        // been assigned a valid height), assign that height to mFragmentsSpacer as well.
+        mSearchAndRemoveViewContainer.getViewTreeObserver().addOnGlobalLayoutListener(
+                new OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        mSearchAndRemoveViewContainer.getViewTreeObserver()
+                                .removeOnGlobalLayoutListener(this);
+                        mFragmentsSpacer.setLayoutParams(
+                                new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                                        mSearchAndRemoveViewContainer.getHeight()));
+                    }
+                });
+
+
         prepareSearchView();
 
         if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
@@ -315,7 +344,7 @@
             setupFilterText(intent);
         }
 
-        setupFakeActionBarItems();
+        hideDialpadFragment(false, false);
 
         mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
         SmartDialPrefix.initializeNanpSettings(this);
@@ -324,13 +353,14 @@
     @Override
     protected void onResume() {
         super.onResume();
+        setupFakeActionBarItems();
         if (mFirstLaunch) {
             displayFragment(getIntent());
         } else if (!phoneIsInUse() && mInCallDialpadUp) {
             hideDialpadFragment(false, true);
             mInCallDialpadUp = false;
         }
-
+        prepareVoiceSearchButton();
         mFirstLaunch = false;
         mDialerDatabaseHelper.startSmartDialUpdateThread();
     }
@@ -364,10 +394,16 @@
             mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
             mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
                     mPhoneNumberPickerActionListener);
+            if (mFragmentsFrame != null) {
+                mFragmentsFrame.setAlpha(1.0f);
+            }
         } else if (fragment instanceof SearchFragment) {
             mRegularSearchFragment = (RegularSearchFragment) fragment;
             mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
                     mPhoneNumberPickerActionListener);
+            if (mFragmentsFrame != null) {
+                mFragmentsFrame.setAlpha(1.0f);
+            }
         } else if (fragment instanceof PhoneFavoriteFragment) {
             mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
             mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
@@ -421,7 +457,11 @@
     public void onClick(View view) {
         switch (view.getId()) {
             case R.id.overflow_menu: {
-                mOverflowMenu.show();
+                if (isDialpadShowing()) {
+                    mDialpadOverflowMenu.show();
+                } else {
+                    mOverflowMenu.show();
+                }
                 break;
             }
             case R.id.dialpad_button:
@@ -432,7 +472,10 @@
                 mInCallDialpadUp = false;
                 showDialpadFragment(true);
                 break;
-            case R.id.call_history_on_dialpad_button:
+            case R.id.dial_button:
+                // Dial button was pressed; tell the Dialpad fragment
+                mDialpadFragment.dialButtonPressed();
+                break;
             case R.id.call_history_button:
                 // Use explicit CallLogActivity intent instead of ACTION_VIEW +
                 // CONTENT_TYPE, so that we always open our call log from our dialer
@@ -447,8 +490,13 @@
                 }
                 break;
             case R.id.voice_search_button:
-                final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
-                startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+                try {
+                    startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
+                            ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+                } catch (ActivityNotFoundException e) {
+                    Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
+                            Toast.LENGTH_SHORT).show();
+                }
                 break;
             default: {
                 Log.wtf(TAG, "Unexpected onClick event from " + view);
@@ -458,6 +506,22 @@
     }
 
     @Override
+    public boolean onLongClick(View view) {
+        switch (view.getId()) {
+            case R.id.dial_button: {
+                // Dial button was pressed; tell the Dialpad fragment
+                mDialpadFragment.dialButtonPressed();
+                return true;  // Consume the event
+            }
+            default: {
+                Log.wtf(TAG, "Unexpected onClick event from " + view);
+                break;
+            }
+        }
+        return false;
+    }
+
+    @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
             if (resultCode == RESULT_OK) {
@@ -486,6 +550,9 @@
         }
         ft.show(mDialpadFragment);
         ft.commit();
+        mDialButton.setVisibility(shouldShowOnscreenDialButton() ? View.VISIBLE : View.GONE);
+        mDialpadButton.setVisibility(View.GONE);
+        mMenuButton.setOnTouchListener(mDialpadOverflowMenu.getDragToOpenListener());
     }
 
     public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
@@ -501,35 +568,38 @@
         }
         ft.hide(mDialpadFragment);
         ft.commit();
+        mDialButton.setVisibility(View.GONE);
+        mDialpadButton.setVisibility(View.VISIBLE);
+        mMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
     }
 
     private void prepareSearchView() {
         mSearchViewContainer = findViewById(R.id.search_view_container);
         mSearchViewCloseButton = findViewById(R.id.search_close_button);
         mSearchViewCloseButton.setOnClickListener(this);
-        mVoiceSearchButton = findViewById(R.id.voice_search_button);
-        mVoiceSearchButton.setOnClickListener(this);
+
         mSearchView = (EditText) findViewById(R.id.search_view);
         mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+        mSearchView.setHint(getString(R.string.dialer_hint_find_contact));
 
-        final String hintText = getString(R.string.dialer_hint_find_contact);
+        prepareVoiceSearchButton();
+    }
 
-        // The following code is used to insert an icon into a CharSequence (copied from
-        // SearchView)
-        final SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
-        ssb.append(hintText);
-        final Drawable searchIcon = getResources().getDrawable(R.drawable.ic_ab_search);
-        final int textSize = (int) (mSearchView.getTextSize() * 1.20);
-        searchIcon.setBounds(0, 0, textSize, textSize);
-        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
-        mSearchView.setHint(ssb);
+    private void prepareVoiceSearchButton() {
+        mVoiceSearchButton = findViewById(R.id.voice_search_button);
+        final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+        if (canIntentBeHandled(voiceIntent)) {
+            mVoiceSearchButton.setVisibility(View.VISIBLE);
+            mVoiceSearchButton.setOnClickListener(this);
+        } else {
+            mVoiceSearchButton.setVisibility(View.GONE);
+        }
     }
 
     final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
-            mSearchViewContainer.setVisibility(View.GONE);
+            mSearchAndRemoveViewContainer.setVisibility(View.GONE);
         }
     };
 
@@ -548,82 +618,95 @@
     }
 
     public void hideSearchBar() {
-       hideSearchBar(true);
-    }
+        final int height = mSearchAndRemoveViewContainer.getHeight();
 
-    public void hideSearchBar(boolean shiftView) {
-        if (shiftView) {
-            mSearchViewContainer.animate().cancel();
-            mSearchViewContainer.setAlpha(1);
-            mSearchViewContainer.setTranslationY(0);
-            mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight())
-                    .setDuration(200).setListener(mHideListener);
+        mSearchAndRemoveViewContainer.animate().cancel();
+        mSearchAndRemoveViewContainer.setAlpha(1);
+        mSearchAndRemoveViewContainer.setTranslationY(0);
+        mSearchAndRemoveViewContainer.animate().withLayer().alpha(0)
+                .translationY(-height).setDuration(ANIMATION_DURATION)
+                .setListener(mHideListener);
 
-            mFragmentsFrame.animate().withLayer()
-                    .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener(
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mBottomPaddingView.setVisibility(View.VISIBLE);
-                            mFragmentsFrame.setTranslationY(0);
-                            mActionBar.setVisibility(View.INVISIBLE);
-                        }
-                    });
-        } else {
-            mSearchViewContainer.setTranslationY(-mSearchView.getHeight());
-            mActionBar.setVisibility(View.INVISIBLE);
+        mFragmentsFrame.animate().withLayer()
+                .translationY(-height).setDuration(ANIMATION_DURATION).setListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mFragmentsFrame.setTranslationY(0);
+                        // Display the fragments spacer (which has the same height as the
+                        // search box) now that the search box is hidden, so that
+                        // mFragmentsFrame always retains the same height
+                        mFragmentsSpacer.setVisibility(View.VISIBLE);
+                    }
+                });
+
+        if (!mInDialpadSearch && !mInRegularSearch) {
+            // If the favorites fragment is showing, fade to blank.
+            mFragmentsFrame.animate().alpha(0.0f);
         }
     }
 
     public void showSearchBar() {
-        mSearchViewContainer.animate().cancel();
-        mSearchViewContainer.setAlpha(0);
-        mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight());
-        mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        mSearchViewContainer.setVisibility(View.VISIBLE);
-                        mActionBar.setVisibility(View.VISIBLE);
-                    }
-                });
+        final int height = mSearchAndRemoveViewContainer.getHeight();
+        mSearchAndRemoveViewContainer.animate().cancel();
+        mSearchAndRemoveViewContainer.setAlpha(0);
+        mSearchAndRemoveViewContainer.setTranslationY(-height);
+        mSearchAndRemoveViewContainer.animate().withLayer().alpha(1).translationY(0)
+                .setDuration(ANIMATION_DURATION).setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            mSearchAndRemoveViewContainer.setVisibility(View.VISIBLE);
+                        }
+                    });
 
-        mFragmentsFrame.setTranslationY(-mSearchViewContainer.getHeight());
-        mFragmentsFrame.animate().withLayer().translationY(0).setDuration(200)
+        mFragmentsFrame.setTranslationY(-height);
+        mFragmentsFrame.animate().withLayer().translationY(0).setDuration(ANIMATION_DURATION)
+                .alpha(1.0f)
                 .setListener(
                         new AnimatorListenerAdapter() {
                             @Override
                             public void onAnimationStart(Animator animation) {
-                                mBottomPaddingView.setVisibility(View.GONE);
+                                // Hide the fragment spacer now that the search box will
+                                // be displayed again
+                                mFragmentsSpacer.setVisibility(View.GONE);
                             }
                         });
     }
 
-
-    public void setupFakeActionBarItems() {
+    private void setupFakeActionBarItems() {
         mMenuButton = findViewById(R.id.overflow_menu);
         if (mMenuButton != null) {
             mMenuButton.setOnClickListener(this);
-
-            mOverflowMenu = new OverflowPopupMenu(DialtactsActivity.this, mMenuButton);
-            final Menu menu = mOverflowMenu.getMenu();
-            mOverflowMenu.inflate(R.menu.dialtacts_options);
-            mOverflowMenu.setOnMenuItemClickListener(this);
+            if (mOverflowMenu == null) {
+                mOverflowMenu = buildOptionsMenu(mMenuButton);
+            }
+            if (mDialpadOverflowMenu == null) {
+                mDialpadOverflowMenu = mDialpadFragment.buildOptionsMenu(mMenuButton);
+            }
+            // Initial state is with dialpad fragment not shown
             mMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
         }
 
+        mFakeActionBar = findViewById(R.id.fake_action_bar);
+
         mCallHistoryButton = findViewById(R.id.call_history_button);
         // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
         mCallHistoryButton.setOnClickListener(this);
 
+        mDialButton = findViewById(R.id.dial_button);
+        mDialButton.setOnClickListener(this);
+        mDialButton.setOnLongClickListener(this);
+
         mDialpadButton = findViewById(R.id.dialpad_button);
         // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
         mDialpadButton.setOnClickListener(this);
     }
 
-    public void setupFakeActionBarItemsForDialpadFragment() {
-        final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button);
-        callhistoryButton.setOnClickListener(this);
+    private PopupMenu buildOptionsMenu(View invoker) {
+        PopupMenu menu = new OverflowPopupMenu(this, invoker);
+        menu.inflate(R.menu.dialtacts_options);
+        menu.setOnMenuItemClickListener(this);
+        return menu;
     }
 
     private void fixIntent(Intent intent) {
@@ -824,7 +907,6 @@
         }
 
         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
-        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
 
         SearchFragment fragment;
         if (mInDialpadSearch) {
@@ -870,6 +952,10 @@
         // transitioned between search fragments
         getFragmentManager().popBackStack(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
         setNotInSearchUi();
+
+        if (isDialpadShowing()) {
+            mFragmentsFrame.setAlpha(0);
+        }
     }
 
     /** Returns an Intent to launch Call Settings screen */
@@ -887,12 +973,6 @@
         } else if (getInSearchUi()) {
             mSearchView.setText(null);
             mDialpadFragment.clearDialpad();
-        } else if (isTaskRoot()) {
-            // Instead of stopping, simply push this to the back of the stack.
-            // This is only done when running at the top of the stack;
-            // otherwise, we have been launched by someone else so need to
-            // allow the user to go back to the caller.
-            moveTaskToBack(false);
         } else {
             super.onBackPressed();
         }
@@ -927,8 +1007,15 @@
     }
 
     @Override
-    public void onDialpadFragmentStarted() {
-        setupFakeActionBarItemsForDialpadFragment();
+    public void setDialButtonEnabled(boolean enabled) {
+        if (mDialButton != null) {
+            mDialButton.setEnabled(enabled);
+        }
+    }
+
+    @Override
+    public void setDialButtonContainerVisible(boolean visible) {
+        mFakeActionBar.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private boolean phoneIsInUse() {
@@ -950,9 +1037,58 @@
         return intent;
     }
 
-    public static Intent getInsertContactWithNameIntent(CharSequence text) {
-        final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
-        intent.putExtra(Intents.Insert.NAME, text);
-        return intent;
+    private boolean canIntentBeHandled(Intent intent) {
+        final PackageManager packageManager = getPackageManager();
+        final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY);
+        return resolveInfo != null && resolveInfo.size() > 0;
+    }
+
+    @Override
+    public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view) {
+        crossfadeViews(mRemoveViewContainer, mSearchViewContainer, ANIMATION_DURATION);
+    }
+
+    @Override
+    public void onDragHovered(int itemIndex, int x, int y) {}
+
+    @Override
+    public void onDragFinished(int x, int y) {
+        crossfadeViews(mSearchViewContainer, mRemoveViewContainer, ANIMATION_DURATION);
+    }
+
+    @Override
+    public void onDroppedOnRemove() {}
+
+    /**
+     * Allows the PhoneFavoriteFragment to attach the drag controller to mRemoveViewContainer
+     * once it has been attached to the activity.
+     */
+    @Override
+    public void setDragDropController(DragDropController dragController) {
+        mRemoveViewContainer.setDragDropController(dragController);
+    }
+
+    /**
+     * Crossfades two views so that the first one appears while the other one is fading
+     * out of view.
+     */
+    private void crossfadeViews(final View fadeIn, final View fadeOut, int duration) {
+        fadeOut.animate().alpha(0).setDuration(duration)
+        .setListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                fadeOut.setVisibility(View.GONE);
+            }
+        });
+
+        fadeIn.setVisibility(View.VISIBLE);
+        fadeIn.setAlpha(0);
+        fadeIn.animate().alpha(1).setDuration(ANIMATION_DURATION)
+                .setListener(null);
+    }
+
+    private boolean shouldShowOnscreenDialButton() {
+        return getResources().getBoolean(R.bool.config_show_onscreen_dial_button);
     }
 }
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index c380b65..4e01ab5 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -59,13 +59,17 @@
      * This is meant to store the high-res photo only.
      */
     public final Uri photoUri;
+    /**
+     * The source type of the contact associated with this call.
+     */
+    public final int sourceType;
 
     /** Create the details for a call with a number not associated with a contact. */
     public PhoneCallDetails(CharSequence number, int numberPresentation,
             CharSequence formattedNumber, String countryIso, String geocode,
             int[] callTypes, long date, long duration) {
         this(number, numberPresentation, formattedNumber, countryIso, geocode,
-                callTypes, date, duration, "", 0, "", null, null);
+                callTypes, date, duration, "", 0, "", null, null, 0);
     }
 
     /** Create the details for a call with a number associated with a contact. */
@@ -73,7 +77,7 @@
             CharSequence formattedNumber, String countryIso, String geocode,
             int[] callTypes, long date, long duration, CharSequence name,
             int numberType, CharSequence numberLabel, Uri contactUri,
-            Uri photoUri) {
+            Uri photoUri, int sourceType) {
         this.number = number;
         this.numberPresentation = numberPresentation;
         this.formattedNumber = formattedNumber;
@@ -87,5 +91,6 @@
         this.numberLabel = numberLabel;
         this.contactUri = contactUri;
         this.photoUri = photoUri;
+        this.sourceType = sourceType;
     }
 }
diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java
index 340168e..aee5052 100644
--- a/src/com/android/dialer/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/PhoneCallDetailsHelper.java
@@ -33,7 +33,7 @@
 import com.android.contacts.common.test.NeededForTesting;
 import com.android.dialer.calllog.CallTypeHelper;
 import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.PhoneNumberHelper;
+import com.android.dialer.calllog.PhoneNumberDisplayHelper;
 import com.android.dialer.calllog.PhoneNumberUtilsWrapper;
 
 /**
@@ -48,7 +48,7 @@
     private Long mCurrentTimeMillisForTest;
     // Helper classes.
     private final CallTypeHelper mCallTypeHelper;
-    private final PhoneNumberHelper mPhoneNumberHelper;
+    private final PhoneNumberDisplayHelper mPhoneNumberHelper;
     private final PhoneNumberUtilsWrapper mPhoneNumberUtilsWrapper;
 
     /**
@@ -62,8 +62,8 @@
             PhoneNumberUtilsWrapper phoneUtils) {
         mResources = resources;
         mCallTypeHelper = callTypeHelper;
-        mPhoneNumberHelper = new PhoneNumberHelper(resources);
         mPhoneNumberUtilsWrapper = phoneUtils;
+        mPhoneNumberHelper = new PhoneNumberDisplayHelper(mPhoneNumberUtilsWrapper, resources);
     }
 
     /** Fills the call details views with content. */
@@ -90,26 +90,13 @@
                 isHighlighted ? mCallTypeHelper.getHighlightedColor(details.callTypes[0]) : null;
 
         // The date of this call, relative to the current time.
-        CharSequence dateText =
-            DateUtils.getRelativeTimeSpanString(details.date,
-                    getCurrentTimeMillis(),
-                    DateUtils.MINUTE_IN_MILLIS,
-                    DateUtils.FORMAT_ABBREV_RELATIVE);
+        CharSequence dateText = getCallDate(details);
 
         // Set the call count and date.
         setCallCountAndDate(views, callCount, dateText, highlightColor);
 
-        CharSequence numberFormattedLabel = null;
-        // Only show a label if the number is shown and it is not a SIP address.
-        if (!TextUtils.isEmpty(details.number)
-                && !PhoneNumberUtils.isUriNumber(details.number.toString())) {
-            if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) {
-                numberFormattedLabel = details.geocode;
-            } else {
-                numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
-                        details.numberLabel);
-            }
-        }
+        // Get type of call (ie mobile, home, etc) if known, or the caller's
+        CharSequence numberFormattedLabel = getCallTypeOrLocation(details);
 
         final CharSequence nameText;
         final CharSequence numberText;
@@ -121,7 +108,7 @@
             nameText = displayNumber;
             if (TextUtils.isEmpty(details.geocode)
                     || mPhoneNumberUtilsWrapper.isVoicemailNumber(details.number)) {
-                numberText = mResources.getString(R.string.call_log_empty_gecode);
+                numberText = mResources.getString(R.string.call_log_empty_geocode);
             } else {
                 numberText = details.geocode;
             }
@@ -136,11 +123,45 @@
         }
 
         views.nameView.setText(nameText);
-
         views.labelView.setText(labelText);
         views.labelView.setVisibility(TextUtils.isEmpty(labelText) ? View.GONE : View.VISIBLE);
     }
 
+    /**
+     * For a call, if there is an associated contact for the caller, return the known call type
+     * (e.g. mobile, home, work).  If there is no associated contact, attempt to use the caller's
+     * location if known.
+     * @param details Call details to use.
+     * @return Type of call (mobile/home) if known, or the location of the caller (if known).
+     */
+    public CharSequence getCallTypeOrLocation(PhoneCallDetails details) {
+        CharSequence numberFormattedLabel = null;
+        // Only show a label if the number is shown and it is not a SIP address.
+        if (!TextUtils.isEmpty(details.number)
+                && !PhoneNumberUtils.isUriNumber(details.number.toString())) {
+            if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) {
+                numberFormattedLabel = details.geocode;
+            } else {
+                numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
+                        details.numberLabel);
+            }
+        }
+        return numberFormattedLabel;
+    }
+
+    /**
+     * Get the call date/time of the call, relative to the current time.
+     * e.g. 3 minutes ago
+     * @param details Call details to use.
+     * @return String representing when the call occurred.
+     */
+    public CharSequence getCallDate(PhoneCallDetails details) {
+        return DateUtils.getRelativeTimeSpanString(details.date,
+                getCurrentTimeMillis(),
+                DateUtils.MINUTE_IN_MILLIS,
+                DateUtils.FORMAT_ABBREV_RELATIVE);
+    }
+
     /** Sets the text of the header view for the details page of a phone call. */
     public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) {
         final CharSequence nameText;
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index e4fd0d8..aee24ba 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -27,6 +27,7 @@
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.PhoneLookup;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -37,11 +38,13 @@
 
 import com.android.common.widget.GroupingListAdapter;
 import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.util.UriUtils;
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.PhoneCallDetailsHelper;
 import com.android.dialer.R;
 import com.android.dialer.util.ExpirableCache;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;
 
@@ -176,16 +179,18 @@
     /** Helper to set up contact photos. */
     private final ContactPhotoManager mContactPhotoManager;
     /** Helper to parse and process phone numbers. */
-    private PhoneNumberHelper mPhoneNumberHelper;
+    private PhoneNumberDisplayHelper mPhoneNumberHelper;
     /** Helper to group call log entries. */
     private final CallLogGroupBuilder mCallLogGroupBuilder;
 
     /** Can be set to true by tests to disable processing of requests. */
     private volatile boolean mRequestProcessingDisabled = false;
 
-    /** True if CallLogAdapter is created from the PhoneFavoriteFragment, where the primary
-     * action should be set to call a number instead of opening the detail page. */
-    private boolean mUseCallAsPrimaryAction = false;
+    /**
+     * Whether to show the secondary action button used to play voicemail or show call details.
+     * True if created from a CallLogFragment.
+     * False if created from the PhoneFavoriteFragment. */
+    private boolean mShowSecondaryActionButton = true;
 
     private boolean mIsCallLog = true;
     private int mNumMissedCalls = 0;
@@ -246,14 +251,14 @@
     };
 
     public CallLogAdapter(Context context, CallFetcher callFetcher,
-            ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction,
+            ContactInfoHelper contactInfoHelper, boolean showSecondaryActionButton,
             boolean isCallLog) {
         super(context);
 
         mContext = context;
         mCallFetcher = callFetcher;
         mContactInfoHelper = contactInfoHelper;
-        mUseCallAsPrimaryAction = useCallAsPrimaryAction;
+        mShowSecondaryActionButton = showSecondaryActionButton;
         mIsCallLog = isCallLog;
 
         mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
@@ -263,7 +268,7 @@
         CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
 
         mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
-        mPhoneNumberHelper = new PhoneNumberHelper(resources);
+        mPhoneNumberHelper = new PhoneNumberDisplayHelper(resources);
         PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
                 resources, callTypeHelper, new PhoneNumberUtilsWrapper());
         mCallLogViewsHelper =
@@ -508,7 +513,7 @@
         // Get the views to bind to.
         CallLogListItemViews views = CallLogListItemViews.fromView(view);
         views.primaryActionView.setOnClickListener(mActionListener);
-        views.secondaryActionView.setOnClickListener(mActionListener);
+        views.secondaryActionButtonView.setOnClickListener(mActionListener);
         view.setTag(views);
     }
 
@@ -535,31 +540,33 @@
 
         final ContactInfo cachedContactInfo = getContactInfoFromCallLog(c);
 
-        if (!mUseCallAsPrimaryAction) {
-            // Sets the primary action to open call detail page.
-            views.primaryActionView.setTag(
-                    IntentProvider.getCallDetailIntentProvider(
-                            getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
-        } else if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) {
+        final boolean isVoicemailNumber =
+                PhoneNumberUtilsWrapper.INSTANCE.isVoicemailNumber(number);
+
+        // Primary action is always to call, if possible.
+        if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) {
             // Sets the primary action to call the number.
             views.primaryActionView.setTag(IntentProvider.getReturnCallIntentProvider(number));
         } else {
             views.primaryActionView.setTag(null);
         }
 
-        // Store away the voicemail information so we can play it directly.
-        if (callType == Calls.VOICEMAIL_TYPE) {
-            String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
-            final long rowId = c.getLong(CallLogQuery.ID);
-            views.secondaryActionView.setTag(
-                    IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
-        } else if (!TextUtils.isEmpty(number)) {
-            // Store away the number so we can call it directly if you click on the call icon.
-            views.secondaryActionView.setTag(
-                    IntentProvider.getReturnCallIntentProvider(number));
+        if ( mShowSecondaryActionButton ) {
+            // Store away the voicemail information so we can play it directly.
+            if (callType == Calls.VOICEMAIL_TYPE) {
+                String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
+                final long rowId = c.getLong(CallLogQuery.ID);
+                views.secondaryActionButtonView.setTag(
+                        IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
+            } else {
+                // Store the call details information.
+                views.secondaryActionButtonView.setTag(
+                        IntentProvider.getCallDetailIntentProvider(
+                                getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
+            }
         } else {
             // No action enabled.
-            views.secondaryActionView.setTag(null);
+            views.secondaryActionButtonView.setTag(null);
         }
 
         // Lookup contacts with this number
@@ -568,7 +575,7 @@
                 mContactInfoCache.getCachedValue(numberCountryIso);
         ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
         if (!PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)
-                || new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
+                || isVoicemailNumber) {
             // If this is a number that cannot be dialed, there is no point in looking up a contact
             // for it.
             info = ContactInfo.EMPTY;
@@ -608,6 +615,7 @@
         CharSequence formattedNumber = info.formattedNumber;
         final int[] callTypes = getCallTypes(c, count);
         final String geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
+        final int sourceType = info.sourceType;
         final PhoneCallDetails details;
 
         if (TextUtils.isEmpty(name)) {
@@ -617,23 +625,39 @@
         } else {
             details = new PhoneCallDetails(number, numberPresentation,
                     formattedNumber, countryIso, geocode, callTypes, date,
-                    duration, name, ntype, label, lookupUri, photoUri);
+                    duration, name, ntype, label, lookupUri, photoUri, sourceType);
         }
 
         final boolean isNew = c.getInt(CallLogQuery.IS_READ) == 0;
         // New items also use the highlighted version of the text.
         final boolean isHighlighted = isNew;
         mCallLogViewsHelper.setPhoneCallDetails(views, details, isHighlighted,
-                mUseCallAsPrimaryAction);
+                mShowSecondaryActionButton);
 
-        if (photoId == 0 && photoUri != null) {
-            setPhoto(views, photoUri, lookupUri);
-        } else {
-            setPhoto(views, photoId, lookupUri);
+        int contactType = ContactPhotoManager.TYPE_DEFAULT;
+
+        if (isVoicemailNumber) {
+            contactType = ContactPhotoManager.TYPE_VOICEMAIL;
+        } else if (mContactInfoHelper.isBusiness(info.sourceType)) {
+            contactType = ContactPhotoManager.TYPE_BUSINESS;
         }
 
-        views.quickContactView.setContentDescription(views.phoneCallDetailsViews.nameView.
-                getText());
+        String lookupKey = lookupUri == null ? null
+                : ContactInfoHelper.getLookupKeyFromUri(lookupUri);
+
+        String nameForDefaultImage = null;
+        if (TextUtils.isEmpty(name)) {
+            nameForDefaultImage = mPhoneNumberHelper.getDisplayNumber(details.number,
+                    details.numberPresentation, details.formattedNumber).toString();
+        } else {
+            nameForDefaultImage = name;
+        }
+
+        if (photoId == 0 && photoUri != null) {
+            setPhoto(views, photoUri, lookupUri, nameForDefaultImage, lookupKey, contactType);
+        } else {
+            setPhoto(views, photoId, lookupUri, nameForDefaultImage, lookupKey, contactType);
+        }
 
         // Listen for the first draw
         if (mViewTreeObserver == null) {
@@ -855,15 +879,22 @@
         return callTypes;
     }
 
-    private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri) {
+    private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri,
+            String displayName, String identifier, int contactType) {
         views.quickContactView.assignContactUri(contactUri);
-        mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, false /* darkTheme */);
+        DefaultImageRequest request = new DefaultImageRequest(displayName, identifier,
+                contactType);
+        mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, false /* darkTheme */,
+                request);
     }
 
-    private void setPhoto(CallLogListItemViews views, Uri photoUri, Uri contactUri) {
+    private void setPhoto(CallLogListItemViews views, Uri photoUri, Uri contactUri,
+            String displayName, String identifier, int contactType) {
         views.quickContactView.assignContactUri(contactUri);
+        DefaultImageRequest request = new DefaultImageRequest(displayName, identifier,
+                contactType);
         mContactPhotoManager.loadDirectoryPhoto(views.quickContactView, photoUri,
-                false /* darkTheme */);
+                false /* darkTheme */, request);
     }
 
 
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index e8d8b27..46c02a7 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -234,7 +234,7 @@
         updateEmptyMessage(mCallTypeFilter);
         String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
         mAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, new ContactInfoHelper(
-                getActivity(), currentCountryIso), false, true);
+                getActivity(), currentCountryIso), true, true);
         setListAdapter(mAdapter);
         getListView().setItemsCanFocus(true);
     }
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 576a432..a85cd01 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -17,6 +17,7 @@
 package com.android.dialer.calllog;
 
 import android.content.res.Resources;
+import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.text.TextUtils;
 import android.view.View;
@@ -32,7 +33,7 @@
     /** Helper for populating the details of a phone call. */
     private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
     /** Helper for handling phone numbers. */
-    private final PhoneNumberHelper mPhoneNumberHelper;
+    private final PhoneNumberDisplayHelper mPhoneNumberHelper;
     /** Resources to look up strings. */
     private final Resources mResources;
 
@@ -43,7 +44,7 @@
      * @param phoneNumberHelper used to process phone number
      */
     public CallLogListItemHelper(PhoneCallDetailsHelper phoneCallDetailsHelper,
-            PhoneNumberHelper phoneNumberHelper, Resources resources) {
+            PhoneNumberDisplayHelper phoneNumberHelper, Resources resources) {
         mPhoneCallDetailsHelper = phoneCallDetailsHelper;
         mPhoneNumberHelper = phoneNumberHelper;
         mResources = resources;
@@ -55,37 +56,201 @@
      * @param views the views to populate
      * @param details the details of a phone call needed to fill in the data
      * @param isHighlighted whether to use the highlight text for the call
+     * @param showSecondaryActionButton whether to show the secondary action button or not
      */
     public void setPhoneCallDetails(CallLogListItemViews views, PhoneCallDetails details,
-            boolean isHighlighted, boolean useCallAsPrimaryAction) {
+            boolean isHighlighted, boolean showSecondaryActionButton) {
         mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details,
                 isHighlighted);
-        boolean canCall = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number,
-                details.numberPresentation);
         boolean canPlay = details.callTypes[0] == Calls.VOICEMAIL_TYPE;
 
-        if (canPlay) {
-            // Playback action takes preference.
-            configurePlaySecondaryAction(views, isHighlighted);
-        } else if (canCall && !useCallAsPrimaryAction) {
-            // Call is the secondary action.
-            configureCallSecondaryAction(views, details);
+        // Set the accessibility text for the contact badge
+        views.quickContactView.setContentDescription(getContactBadgeDescription(details));
+
+        // Set the primary action accessibility description
+        views.primaryActionView.setContentDescription(getCallDescription(details));
+
+        // If secondary action is visible, either show voicemail playback icon, or
+        // show the "clock" icon corresponding to the call details screen.
+        if (showSecondaryActionButton) {
+            if (canPlay) {
+                // Playback action takes preference.
+                configurePlaySecondaryAction(views, isHighlighted);
+            } else {
+                // Call details is the secondary action.
+                configureCallDetailsSecondaryAction(views, details);
+            }
         } else {
-            // No action available.
+            // No secondary action is to be shown (ie this is likely a PhoneFavoriteFragment)
             views.secondaryActionView.setVisibility(View.GONE);
         }
     }
 
-    /** Sets the secondary action to correspond to the call button. */
-    private void configureCallSecondaryAction(CallLogListItemViews views,
+    /**
+     * Sets the secondary action to invoke call details.
+     *
+     * @param views   the views to populate
+     * @param details the details of a phone call needed to fill in the call details data
+     */
+    private void configureCallDetailsSecondaryAction(CallLogListItemViews views,
             PhoneCallDetails details) {
         views.secondaryActionView.setVisibility(View.VISIBLE);
-        views.secondaryActionView.setImageResource(R.drawable.ic_phone_dk);
-        views.secondaryActionView.setContentDescription(getCallActionDescription(details));
+        // Use the small dark grey clock icon.
+        views.secondaryActionButtonView.setImageResource(R.drawable.ic_menu_history_dk);
+        views.secondaryActionButtonView.setContentDescription(
+                mResources.getString(R.string.description_call_details));
     }
 
-    /** Returns the description used by the call action for this phone call. */
-    private CharSequence getCallActionDescription(PhoneCallDetails details) {
+    /**
+     * Returns the accessibility description for the contact badge for a call log entry.
+     *
+     * @param details Details of call.
+     * @return Accessibility description.
+     */
+    private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
+        return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
+    }
+
+    /**
+     * Returns the accessibility description of the "return call/call" action for a call log
+     * entry.
+     * Accessibility text is a combination of:
+     * {Voicemail Prefix}. {Number of Calls}. {Caller information}.
+     * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "".
+     *
+     * If more than one call for the caller, {Number of Calls} is:
+     * "{number of calls} calls.", otherwise "".
+     *
+     * The {Caller Information} references the most recent call associated with the caller.
+     * For incoming calls:
+     * If missed call:  Return missed call from {Name/Number} {Call Type} {Call Time}.
+     * If answered call: Return answered call from {Name/Number} {Call Type} {Call Time}.
+     *
+     * For unknown callers, drop the "Return" part, since the call can't be returned:
+     * If answered unknown: Answered call from {Name/Number} {Call Time}.
+     * If missed unknown: Missed call from {Name/Number} {Call Time}.
+     *
+     * For outgoing calls:
+     * If outgoing:  Call {Name/Number] {Call Type}.  {Last} called {Call Time}.
+     * Where {Last} is dropped if the number of calls for the caller is 1.
+     *
+     * Where:
+     * {Name/Number} is the name or number of the caller (as shown in call log).
+     * {Call type} is the contact phone number type (eg mobile) or location.
+     * {Call Time} is the time since the last call for the contact occurred.
+     *
+     * Examples:
+     * 3 calls.  New Voicemail.  Return missed call from Joe Smith mobile 2 hours ago.
+     * 2 calls.  Call John Doe mobile.  Last called 1 hour ago.
+     * @param details Details of call.
+     * @return Return call action description.
+     */
+    public CharSequence getCallDescription(PhoneCallDetails details) {
+        int lastCallType = getLastCallType(details.callTypes);
+        boolean isVoiceMail = lastCallType == Calls.VOICEMAIL_TYPE;
+
+        // Get the name or number of the caller.
+        final CharSequence nameOrNumber = getNameOrNumber(details);
+
+        // Get the call type or location of the caller; null if not applicable
+        final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
+
+        // Get the time/date of the call
+        final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
+
+        StringBuilder callDescription = new StringBuilder();
+
+        // Prepend the voicemail indication.
+        if (isVoiceMail) {
+            callDescription.append(mResources.getString(R.string.description_new_voicemail));
+        }
+
+        // Add number of calls if more than one.
+        if (details.callTypes.length > 1) {
+            callDescription.append(mResources.getString(R.string.description_num_calls,
+                    details.callTypes.length));
+        }
+
+        int stringID = getCallDescriptionStringID(details);
+
+        // Use chosen string resource to build up the message.
+        callDescription.append(mResources.getString(stringID,
+                nameOrNumber,
+                // If no type or location can be determined, sub in empty string.
+                typeOrLocation == null ? "" : typeOrLocation,
+                timeOfCall));
+
+        return callDescription;
+    }
+
+    /**
+     * Determine the appropriate string ID to describe a call for accessibility purposes.
+     *
+     * @param details Call details.
+     * @return String resource ID to use.
+     */
+    public int getCallDescriptionStringID(PhoneCallDetails details) {
+        int lastCallType = getLastCallType(details.callTypes);
+        boolean isNumberCallable = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number,
+                details.numberPresentation);
+
+        // Default string to use is "call XYZ..." just in case we manage to fall through.
+        int stringID = R.string.description_call_last_multiple;
+
+        if (!isNumberCallable) {
+            // Number isn't callable; this is an incoming call from an unknown caller.
+            // An uncallable outgoing call wouldn't be in the call log.
+
+            // Voicemail and missed calls are both considered missed.
+            if (lastCallType == Calls.VOICEMAIL_TYPE ||
+                    lastCallType == Calls.MISSED_TYPE) {
+                stringID = R.string.description_unknown_missed_call;
+            } else if (lastCallType == Calls.INCOMING_TYPE) {
+                stringID = R.string.description_unknown_answered_call;
+            }
+        } else {
+            // Known caller, so callable.
+
+            // Missed call (ie voicemail or missed)
+            if (lastCallType == Calls.VOICEMAIL_TYPE ||
+                    lastCallType == Calls.MISSED_TYPE) {
+                stringID = R.string.description_return_missed_call;
+            } else if (lastCallType == Calls.INCOMING_TYPE) {
+                // Incoming answered.
+                stringID = R.string.description_return_answered_call;
+            } else {
+                // Outgoing call.
+
+                // If we have a history of multiple calls
+                if (details.callTypes.length > 1) {
+                    stringID = R.string.description_call_last_multiple;
+                } else {
+                    stringID = R.string.description_call_last;
+                }
+            }
+        }
+        return stringID;
+    }
+
+    /**
+     * Determine the call type for the most recent call.
+     * @param callTypes Call types to check.
+     * @return Call type.
+     */
+    private int getLastCallType(int[] callTypes) {
+        if (callTypes.length > 0) {
+            return callTypes[0];
+        } else {
+            return Calls.MISSED_TYPE;
+        }
+    }
+
+    /**
+     * Return the name or number of the caller specified by the details.
+     * @param details Call details
+     * @return the name (if known) of the caller, otherwise the formatted number.
+     */
+    private CharSequence getNameOrNumber(PhoneCallDetails details) {
         final CharSequence recipient;
         if (!TextUtils.isEmpty(details.name)) {
             recipient = details.name;
@@ -93,15 +258,15 @@
             recipient = mPhoneNumberHelper.getDisplayNumber(
                     details.number, details.numberPresentation, details.formattedNumber);
         }
-        return mResources.getString(R.string.description_call, recipient);
+        return recipient;
     }
 
     /** Sets the secondary action to correspond to the play button. */
     private void configurePlaySecondaryAction(CallLogListItemViews views, boolean isHighlighted) {
         views.secondaryActionView.setVisibility(View.VISIBLE);
-        views.secondaryActionView.setImageResource(
+        views.secondaryActionButtonView.setImageResource(
                 isHighlighted ? R.drawable.ic_play_active_holo_dark : R.drawable.ic_play_holo_light);
-        views.secondaryActionView.setContentDescription(
+        views.secondaryActionButtonView.setContentDescription(
                 mResources.getString(R.string.description_call_log_play_button));
     }
 }
diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java
index ed6218f..a378956 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViews.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViews.java
@@ -34,19 +34,25 @@
     public final QuickContactBadge quickContactView;
     /** The primary action view of the entry. */
     public final View primaryActionView;
+    /** The secondary action view, which includes both the vertical divider line and
+     *  the action button itself.  Used so that the button and divider line can be
+     *  made visible/hidden as a whole. */
+    public final View secondaryActionView;
     /** The secondary action button on the entry. */
-    public final ImageView secondaryActionView;
+    public final ImageView secondaryActionButtonView;
     /** The details of the phone call. */
     public final PhoneCallDetailsViews phoneCallDetailsViews;
     /** The text of the header of a section. */
     public final TextView listHeaderTextView;
 
     private CallLogListItemViews(QuickContactBadge quickContactView, View primaryActionView,
-            ImageView secondaryActionView, PhoneCallDetailsViews phoneCallDetailsViews,
+            View secondaryActionView, ImageView secondaryActionButtonView,
+            PhoneCallDetailsViews phoneCallDetailsViews,
             TextView listHeaderTextView) {
         this.quickContactView = quickContactView;
         this.primaryActionView = primaryActionView;
         this.secondaryActionView = secondaryActionView;
+        this.secondaryActionButtonView = secondaryActionButtonView;
         this.phoneCallDetailsViews = phoneCallDetailsViews;
         this.listHeaderTextView = listHeaderTextView;
     }
@@ -55,6 +61,7 @@
         return new CallLogListItemViews(
                 (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
                 view.findViewById(R.id.primary_action_view),
+                view.findViewById(R.id.secondary_action_view),
                 (ImageView) view.findViewById(R.id.secondary_action_icon),
                 PhoneCallDetailsViews.fromView(view),
                 (TextView) view.findViewById(R.id.call_log_header));
@@ -65,6 +72,7 @@
         return new CallLogListItemViews(
                 new QuickContactBadge(context),
                 new View(context),
+                new View(context),
                 new ImageView(context),
                 PhoneCallDetailsViews.createForTest(context),
                 new TextView(context));
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
index 3270963..ccd9335 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java
@@ -70,6 +70,10 @@
 
     @Override
     protected void onHandleIntent(Intent intent) {
+        if (intent == null) {
+            Log.d(TAG, "onHandleIntent: could not handle null intent");
+            return;
+        }
         if (ACTION_MARK_NEW_VOICEMAILS_AS_OLD.equals(intent.getAction())) {
             mCallLogQueryHandler.markNewVoicemailsAsOld();
         } else if (ACTION_UPDATE_NOTIFICATIONS.equals(intent.getAction())) {
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index def3c97..3992676 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -258,7 +258,7 @@
     }
 
     @Override
-    protected void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor) {
+    protected synchronized void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor) {
         if (token == QUERY_CALLLOG_TOKEN) {
             int requestId = ((Integer) cookie).intValue();
             if (requestId != mCallsRequestId) {
diff --git a/src/com/android/dialer/calllog/ContactInfo.java b/src/com/android/dialer/calllog/ContactInfo.java
index 601f552..41afa5a 100644
--- a/src/com/android/dialer/calllog/ContactInfo.java
+++ b/src/com/android/dialer/calllog/ContactInfo.java
@@ -27,6 +27,7 @@
  */
 public class ContactInfo {
     public Uri lookupUri;
+    public String lookupKey;
     public String name;
     public int type;
     public String label;
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index bcb98fd..e1a01b4 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -1,17 +1,15 @@
 /*
  * Copyright (C) 2011 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
  */
 
 package com.android.dialer.calllog;
@@ -36,6 +34,8 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.util.List;
+
 /**
  * Utility class to look up the contact information for a given number.
  */
@@ -100,7 +100,7 @@
                 updatedInfo = new ContactInfo();
                 updatedInfo.number = number;
                 updatedInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
-                updatedInfo.lookupUri = createTemporaryContactUri(number);
+                updatedInfo.lookupUri = createTemporaryContactUri(updatedInfo.formattedNumber);
             } else {
                 updatedInfo = info;
             }
@@ -112,23 +112,20 @@
      * Creates a JSON-encoded lookup uri for a unknown number without an associated contact
      *
      * @param number - Unknown phone number
-     * @return JSON-encoded URI that can be used to perform a lookup when clicking
-     * on the quick contact card.
+     * @return JSON-encoded URI that can be used to perform a lookup when clicking on the quick
+     *         contact card.
      */
     private static Uri createTemporaryContactUri(String number) {
         try {
-            final JSONObject contactRows = new JSONObject()
-                    .put(Phone.CONTENT_ITEM_TYPE, new JSONObject()
-                            .put(Phone.NUMBER, number)
-                                    .put(Phone.TYPE, Phone.TYPE_CUSTOM));
+            final JSONObject contactRows = new JSONObject().put(Phone.CONTENT_ITEM_TYPE,
+                    new JSONObject().put(Phone.NUMBER, number).put(Phone.TYPE, Phone.TYPE_CUSTOM));
 
-            final String jsonString = new JSONObject()
-                    .put(Contacts.DISPLAY_NAME, number)
-                            .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
-                            .put(Contacts.CONTENT_ITEM_TYPE, contactRows)
-                            .toString();
+            final String jsonString = new JSONObject().put(Contacts.DISPLAY_NAME, number)
+                    .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
+                    .put(Contacts.CONTENT_ITEM_TYPE, contactRows).toString();
 
-            return Contacts.CONTENT_LOOKUP_URI.buildUpon()
+            return Contacts.CONTENT_LOOKUP_URI
+                    .buildUpon()
                     .appendPath(Constants.LOOKUP_URI_ENCODED)
                     .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
                             String.valueOf(Long.MAX_VALUE))
@@ -151,8 +148,7 @@
     private ContactInfo lookupContactFromUri(Uri uri) {
         final ContactInfo info;
         Cursor phonesCursor =
-                mContext.getContentResolver().query(
-                        uri, PhoneQuery._PROJECTION, null, null, null);
+                mContext.getContentResolver().query(uri, PhoneQuery._PROJECTION, null, null, null);
 
         if (phonesCursor != null) {
             try {
@@ -160,6 +156,7 @@
                     info = new ContactInfo();
                     long contactId = phonesCursor.getLong(PhoneQuery.PERSON_ID);
                     String lookupKey = phonesCursor.getString(PhoneQuery.LOOKUP_KEY);
+                    info.lookupKey = lookupKey;
                     info.lookupUri = Contacts.getLookupUri(contactId, lookupKey);
                     info.name = phonesCursor.getString(PhoneQuery.NAME);
                     info.type = phonesCursor.getInt(PhoneQuery.PHONE_TYPE);
@@ -229,8 +226,8 @@
         if (info != null && info != ContactInfo.EMPTY) {
             info.formattedNumber = formatPhoneNumber(number, null, countryIso);
         } else if (mCachedNumberLookupService != null) {
-            CachedContactInfo cacheInfo = mCachedNumberLookupService
-                .lookupCachedContactFromNumber(mContext, number);
+            CachedContactInfo cacheInfo =
+                    mCachedNumberLookupService.lookupCachedContactFromNumber(mContext, number);
             info = cacheInfo != null ? cacheInfo.getContactInfo() : null;
         }
         return info;
@@ -241,14 +238,12 @@
      *
      * @param number the number to be formatted.
      * @param normalizedNumber the normalized number of the given number.
-     * @param countryIso the ISO 3166-1 two letters country code, the country's
-     *        convention will be used to format the number if the normalized
-     *        phone is null.
+     * @param countryIso the ISO 3166-1 two letters country code, the country's convention will be
+     *        used to format the number if the normalized phone is null.
      *
      * @return the formatted number, or the given number if it was formatted.
      */
-    private String formatPhoneNumber(String number, String normalizedNumber,
-            String countryIso) {
+    private String formatPhoneNumber(String number, String normalizedNumber, String countryIso) {
         if (TextUtils.isEmpty(number)) {
             return "";
         }
@@ -261,4 +256,33 @@
         }
         return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
     }
+
+    /**
+     * Parses the given URI to determine the original lookup key of the contact.
+     */
+    public static String getLookupKeyFromUri(Uri lookupUri) {
+        // Would be nice to be able to persist the lookup key somehow to avoid having to parse
+        // the uri entirely just to retrieve the lookup key, but every uri is already parsed
+        // once anyway to check if it is an encoded JSON uri, so this has negligible effect
+        // on performance.
+        if (lookupUri != null && !UriUtils.isEncodedContactUri(lookupUri)) {
+            final List<String> segments = lookupUri.getPathSegments();
+            // This returns the third path segment of the uri, where the lookup key is located.
+            // See {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+            return (segments.size() < 3) ? null : segments.get(2);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Given a contact's sourceType, return true if the contact is a business
+     *
+     * @param sourceType sourceType of the contact. This is usually populated by
+     *        {@link #mCachedNumberLookupService}.
+     */
+    public boolean isBusiness(int sourceType) {
+        return mCachedNumberLookupService != null
+                && mCachedNumberLookupService.isBusiness(sourceType);
+    }
 }
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
index 14e1bed..fedd012 100644
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
@@ -57,7 +57,7 @@
     private final NotificationManager mNotificationManager;
     private final NewCallsQuery mNewCallsQuery;
     private final NameLookupQuery mNameLookupQuery;
-    private final PhoneNumberHelper mPhoneNumberHelper;
+    private final PhoneNumberDisplayHelper mPhoneNumberHelper;
 
     /** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */
     public static synchronized DefaultVoicemailNotifier getInstance(Context context) {
@@ -75,7 +75,7 @@
 
     private DefaultVoicemailNotifier(Context context,
             NotificationManager notificationManager, NewCallsQuery newCallsQuery,
-            NameLookupQuery nameLookupQuery, PhoneNumberHelper phoneNumberHelper) {
+            NameLookupQuery nameLookupQuery, PhoneNumberDisplayHelper phoneNumberHelper) {
         mContext = context;
         mNotificationManager = notificationManager;
         mNewCallsQuery = newCallsQuery;
@@ -340,7 +340,7 @@
      * This will cause some Disk I/O, at least the first time it is created, so it should not be
      * called from the main thread.
      */
-    public static PhoneNumberHelper createPhoneNumberHelper(Context context) {
-        return new PhoneNumberHelper(context.getResources());
+    public static PhoneNumberDisplayHelper createPhoneNumberHelper(Context context) {
+        return new PhoneNumberDisplayHelper(context.getResources());
     }
 }
diff --git a/src/com/android/dialer/calllog/PhoneNumberHelper.java b/src/com/android/dialer/calllog/PhoneNumberDisplayHelper.java
similarity index 80%
rename from src/com/android/dialer/calllog/PhoneNumberHelper.java
rename to src/com/android/dialer/calllog/PhoneNumberDisplayHelper.java
index 336facb..5d7ce7e 100644
--- a/src/com/android/dialer/calllog/PhoneNumberHelper.java
+++ b/src/com/android/dialer/calllog/PhoneNumberDisplayHelper.java
@@ -18,18 +18,25 @@
 
 import android.content.res.Resources;
 import android.provider.CallLog.Calls;
-import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.dialer.R;
 
 /**
- * Helper for formatting and managing phone numbers.
+ * Helper for formatting and managing the display of phone numbers.
  */
-public class PhoneNumberHelper {
+public class PhoneNumberDisplayHelper {
+    private final PhoneNumberUtilsWrapper mPhoneNumberUtils;
     private final Resources mResources;
 
-    public PhoneNumberHelper(Resources resources) {
+    public PhoneNumberDisplayHelper(Resources resources) {
+        mResources = resources;
+        mPhoneNumberUtils = new PhoneNumberUtilsWrapper();
+    }
+
+    public PhoneNumberDisplayHelper(PhoneNumberUtilsWrapper phoneNumberUtils, Resources resources) {
+        mPhoneNumberUtils = phoneNumberUtils;
         mResources = resources;
     }
 
@@ -43,7 +50,7 @@
         if (presentation == Calls.PRESENTATION_PAYPHONE) {
             return mResources.getString(R.string.payphone);
         }
-        if (new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
+        if (mPhoneNumberUtils.isVoicemailNumber(number)) {
             return mResources.getString(R.string.voicemail);
         }
         if (PhoneNumberUtilsWrapper.isLegacyUnknownNumbers(number)) {
@@ -62,7 +69,6 @@
             int presentation, CharSequence formattedNumber) {
 
         final CharSequence displayName = getDisplayName(number, presentation);
-
         if (!TextUtils.isEmpty(displayName)) {
             return displayName;
         }
diff --git a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
index 9913c20..2faab75 100644
--- a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
+++ b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
@@ -20,7 +20,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
-import com.google.android.collect.Sets;
+import com.google.common.collect.Sets;
 
 import java.util.Set;
 
@@ -28,7 +28,7 @@
  *
  */
 public class PhoneNumberUtilsWrapper {
-
+    public static final PhoneNumberUtilsWrapper INSTANCE = new PhoneNumberUtilsWrapper();
     private static final Set<String> LEGACY_UNKNOWN_NUMBERS = Sets.newHashSet("-1", "-2", "-3");
 
     /** Returns true if it is possible to place a call to the given number. */
@@ -50,7 +50,7 @@
      * mock-out this, it is not a static method.
      */
     public boolean isVoicemailNumber(CharSequence number) {
-        return PhoneNumberUtils.isVoiceMailNumber(number.toString());
+        return number!= null && PhoneNumberUtils.isVoiceMailNumber(number.toString());
     }
 
     /**
@@ -58,7 +58,7 @@
      * static method.
      */
     public boolean isSipNumber(CharSequence number) {
-        return PhoneNumberUtils.isUriNumber(number.toString());
+        return number != null && PhoneNumberUtils.isUriNumber(number.toString());
     }
 
     public static boolean isUnknownNumberThatCanBeLookedUp(CharSequence number, int presentation) {
@@ -74,16 +74,16 @@
         if (TextUtils.isEmpty(number)) {
             return false;
         }
-        if (new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
+        if (INSTANCE.isVoicemailNumber(number)) {
             return false;
         }
-        if (isLegacyUnknownNumbers(number.toString())) {
+        if (isLegacyUnknownNumbers(number)) {
             return false;
         }
         return true;
     }
 
     public static boolean isLegacyUnknownNumbers(CharSequence number) {
-        return LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
+        return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
     }
 }
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
index e9d7735..f6f5f6c 100644
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/src/com/android/dialer/database/DialerDatabaseHelper.java
@@ -37,6 +37,7 @@
 import android.util.Log;
 
 import com.android.contacts.common.util.StopWatch;
+import com.android.dialer.R;
 import com.android.dialer.dialpad.SmartDialNameMatcher;
 import com.android.dialer.dialpad.SmartDialPrefix;
 
@@ -162,6 +163,17 @@
         /** Selects only rows that have been updated after a certain time stamp.*/
         static final String SELECT_UPDATED_CLAUSE =
                 Phone.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
+
+        /** Ignores contacts that have an unreasonably long lookup key. These are likely to be
+         * the result of multiple (> 50) merged raw contacts, and are likely to cause
+         * OutOfMemoryExceptions within SQLite, or cause memory allocation problems later on
+         * when iterating through the cursor set (see b/13133579)
+         */
+        static final String SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE =
+                "length(" + Phone.LOOKUP_KEY + ") < 1000";
+
+        static final String SELECTION = SELECT_UPDATED_CLAUSE + " AND " +
+                SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE;
     }
 
     /** Query options for querying the deleted contact database.*/
@@ -510,6 +522,9 @@
                 DeleteContactQuery.PROJECTION,
                 DeleteContactQuery.SELECT_UPDATED_CLAUSE,
                 new String[] {last_update_time}, null);
+        if (deletedContactCursor == null) {
+            return;
+        }
 
         db.beginTransaction();
         try {
@@ -627,11 +642,35 @@
 
             updatedContactCursor.moveToPosition(-1);
             while (updatedContactCursor.moveToNext()) {
+                insert.clearBindings();
+
+                // Handle string columns which can possibly be null first. In the case of certain
+                // null columns (due to malformed rows possibly inserted by third-party apps
+                // or sync adapters), skip the phone number row.
+                final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
+                if (TextUtils.isEmpty(number)) {
+                    continue;
+                } else {
+                    insert.bindString(2, number);
+                }
+
+                final String lookupKey = updatedContactCursor.getString(
+                        PhoneQuery.PHONE_LOOKUP_KEY);
+                if (TextUtils.isEmpty(lookupKey)) {
+                    continue;
+                } else {
+                    insert.bindString(4, lookupKey);
+                }
+
+                final String displayName = updatedContactCursor.getString(
+                        PhoneQuery.PHONE_DISPLAY_NAME);
+                if (displayName == null) {
+                    insert.bindString(5, mContext.getResources().getString(R.string.missing_name));
+                } else {
+                    insert.bindString(5, displayName);
+                }
                 insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
-                insert.bindString(2, updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER));
                 insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
-                insert.bindString(4, updatedContactCursor.getString(PhoneQuery.PHONE_LOOKUP_KEY));
-                insert.bindString(5, updatedContactCursor.getString(PhoneQuery.PHONE_DISPLAY_NAME));
                 insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
                 insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
                 insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
@@ -641,8 +680,6 @@
                 insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
                 insert.bindLong(13, currentMillis);
                 insert.executeInsert();
-                insert.clearBindings();
-
                 final String contactPhoneNumber =
                         updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
                 final ArrayList<String> numberPrefixes =
@@ -731,7 +768,7 @@
              * update time.
              */
             final Cursor updatedContactCursor = mContext.getContentResolver().query(PhoneQuery.URI,
-                    PhoneQuery.PROJECTION, PhoneQuery.SELECT_UPDATED_CLAUSE,
+                    PhoneQuery.PROJECTION, PhoneQuery.SELECTION,
                     new String[]{lastUpdateMillis}, null);
 
             /** Sets the time after querying the database as the current update time. */
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 00b8281..83cfbd8 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -36,11 +36,9 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.provider.ContactsContract.Contacts;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
 import android.provider.Contacts.PhonesColumns;
-import android.provider.ContactsContract.Intents;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
@@ -51,17 +49,13 @@
 import android.text.TextWatcher;
 import android.text.style.RelativeSizeSpan;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnPreDrawListener;
@@ -77,17 +71,12 @@
 
 import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.activity.TransactionSafeActivity;
-import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.contacts.common.util.PhoneNumberFormatter;
 import com.android.contacts.common.util.StopWatch;
 import com.android.dialer.NeededForReflection;
 import com.android.dialer.DialtactsActivity;
 import com.android.dialer.R;
 import com.android.dialer.SpecialCharSequenceMgr;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.util.OrientationUtil;
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.common.CallLogAsync;
 import com.android.phone.common.HapticFeedback;
@@ -106,8 +95,19 @@
         DialpadKeyButton.OnPressedListener {
     private static final String TAG = DialpadFragment.class.getSimpleName();
 
-    public interface OnDialpadFragmentStartedListener {
-        public void onDialpadFragmentStarted();
+    /**
+     * This interface allows the DialpadFragment to tell its hosting Activity when and when not
+     * to display the "dial" button. While this is logically part of the DialpadFragment, the
+     * need to have a particular kind of slick animation puts the "dial" button in the parent.
+     *
+     * The parent calls dialButtonPressed() and optionsMenuInvoked() on the dialpad fragment
+     * when appropriate.
+     *
+     * TODO: Refactor the app so this interchange is a bit cleaner.
+     */
+    public interface HostInterface {
+        void setDialButtonEnabled(boolean enabled);
+        void setDialButtonContainerVisible(boolean visible);
     }
 
     /**
@@ -188,8 +188,6 @@
     /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
     private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
 
-    private ContactsPreferences mContactsPrefs;
-
     private OnDialpadQueryChangedListener mDialpadQueryListener;
 
     /**
@@ -213,8 +211,6 @@
      */
     private final HashSet<View> mPressedDialpadKeys = new HashSet<View>(12);
 
-    private View mDialButtonContainer;
-    private View mDialButton;
     private ListView mDialpadChooser;
     private DialpadChooserAdapter mDialpadChooserAdapter;
 
@@ -350,7 +346,6 @@
     public void onCreate(Bundle state) {
         super.onCreate(state);
         mFirstLaunch = true;
-        mContactsPrefs = new ContactsPreferences(getActivity());
         mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
 
         try {
@@ -400,7 +395,6 @@
         // Load up the resources for the text field.
         Resources r = getResources();
 
-        mDialButtonContainer = fragmentView.findViewById(R.id.dialButtonContainer);
         mDigitsContainer = fragmentView.findViewById(R.id.digits_container);
         mDigits = (EditText) fragmentView.findViewById(R.id.digits);
         mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
@@ -415,15 +409,6 @@
             setupKeypad(fragmentView);
         }
 
-        mDialButton = fragmentView.findViewById(R.id.dialButton);
-        if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
-            mDialButton.setOnClickListener(this);
-            mDialButton.setOnLongClickListener(this);
-        } else {
-            mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
-            mDialButton = null;
-        }
-
         mDelete = fragmentView.findViewById(R.id.deleteButton);
         if (mDelete != null) {
             mDelete.setOnClickListener(this);
@@ -435,7 +420,7 @@
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 if (isDigitsEmpty()) {
-                    hideAndClearDialpad();
+                    hideAndClearDialpad(true);
                     return true;
                 }
                 return false;
@@ -461,18 +446,6 @@
     @Override
     public void onStart() {
         super.onStart();
-
-        final Activity activity = getActivity();
-
-        try {
-            ((OnDialpadFragmentStartedListener) activity).onDialpadFragmentStarted();
-        } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString()
-                    + " must implement OnDialpadFragmentStartedListener");
-        }
-
-        final View overflowButton = getView().findViewById(R.id.overflow_menu_on_dialpad);
-        overflowButton.setOnClickListener(this);
     }
 
     private boolean isLayoutReady() {
@@ -658,10 +631,6 @@
             dialpadKey.setContentDescription(numberString);
             if (lettersView != null) {
                 lettersView.setText(resources.getString(letterIds[i]));
-                if (buttonIds[i] == R.id.zero) {
-                    lettersView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(
-                            R.dimen.dialpad_key_plus_size));
-                }
             }
         }
 
@@ -879,7 +848,7 @@
         switch (view.getId()) {
             case R.id.digits:
                 if (keyCode == KeyEvent.KEYCODE_ENTER) {
-                    dialButtonPressed();
+                    handleDialButtonPressed();
                     return true;
                 }
                 break;
@@ -961,27 +930,36 @@
         }
     }
 
+    /**
+     * Called by the containing Activity to tell this Fragment to build an overflow options
+     * menu for display by the container when appropriate.
+     *
+     * @param invoker the View that invoked the options menu, to act as an anchor location.
+     */
+    public PopupMenu buildOptionsMenu(View invoker) {
+        final PopupMenu popupMenu = new PopupMenu(getActivity(), invoker);
+        popupMenu.inflate(R.menu.dialpad_options);
+        popupMenu.setOnMenuItemClickListener(this);
+        setupMenuItems(popupMenu.getMenu());
+        return popupMenu;
+    }
+
+    /**
+     * Called by the containing Activity to tell this Fragment that the dial button has been
+     * pressed.
+     */
+    public void dialButtonPressed() {
+        mHaptic.vibrate();
+        handleDialButtonPressed();
+    }
+
     @Override
     public void onClick(View view) {
         switch (view.getId()) {
-            case R.id.overflow_menu_on_dialpad: {
-                final PopupMenu popupMenu = new PopupMenu(getActivity(), view);
-                final Menu menu = popupMenu.getMenu();
-                popupMenu.inflate(R.menu.dialpad_options);
-                popupMenu.setOnMenuItemClickListener(this);
-                setupMenuItems(menu);
-                popupMenu.show();
-                break;
-            }
             case R.id.deleteButton: {
                 keyPressed(KeyEvent.KEYCODE_DEL);
                 return;
             }
-            case R.id.dialButton: {
-                mHaptic.vibrate();  // Vibrate here too, just like we do for the regular keys
-                dialButtonPressed();
-                return;
-            }
             case R.id.digits: {
                 if (!isDigitsEmpty()) {
                     mDigits.setCursorVisible(true);
@@ -1056,16 +1034,6 @@
                 mDigits.setCursorVisible(true);
                 return false;
             }
-            case R.id.dialButton: {
-                if (isDigitsEmpty()) {
-                    handleDialButtonClickWithEmptyDigits();
-                    // This event should be consumed so that onClick() won't do the exactly same
-                    // thing.
-                    return true;
-                } else {
-                    return false;
-                }
-            }
         }
         return false;
     }
@@ -1085,11 +1053,11 @@
 
     public void callVoicemail() {
         startActivity(getVoicemailIntent());
-        hideAndClearDialpad();
+        hideAndClearDialpad(false);
     }
 
-    private void hideAndClearDialpad() {
-        ((DialtactsActivity) getActivity()).hideDialpadFragment(false, true);
+    private void hideAndClearDialpad(boolean animate) {
+        ((DialtactsActivity) getActivity()).hideDialpadFragment(animate, true);
     }
 
     public static class ErrorDialogFragment extends DialogFragment {
@@ -1158,7 +1126,7 @@
      * user needs to press the dial button again, to dial it (general
      * case described above).
      */
-    public void dialButtonPressed() {
+    private void handleDialButtonPressed() {
         if (isDigitsEmpty()) { // No number entered.
             handleDialButtonClickWithEmptyDigits();
         } else {
@@ -1185,7 +1153,7 @@
                         (getActivity() instanceof DialtactsActivity ?
                                 ((DialtactsActivity) getActivity()).getCallOrigin() : null));
                 startActivity(intent);
-                hideAndClearDialpad();
+                hideAndClearDialpad(false);
             }
         }
     }
@@ -1329,7 +1297,7 @@
                 mDigits.setVisibility(View.GONE);
             }
             if (mDialpad != null) mDialpad.setVisibility(View.GONE);
-            if (mDialButtonContainer != null) mDialButtonContainer.setVisibility(View.GONE);
+            ((HostInterface) getActivity()).setDialButtonContainerVisible(false);
 
             mDialpadChooser.setVisibility(View.VISIBLE);
 
@@ -1347,7 +1315,7 @@
                 mDigits.setVisibility(View.VISIBLE);
             }
             if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
-            if (mDialButtonContainer != null) mDialButtonContainer.setVisibility(View.VISIBLE);
+            ((HostInterface) getActivity()).setDialButtonContainerVisible(true);
             mDialpadChooser.setVisibility(View.GONE);
         }
     }
@@ -1560,9 +1528,9 @@
      * or Wait character (;).
      */
     private void updateDialString(char newDigit) {
-        if(newDigit != WAIT && newDigit != PAUSE) {
-            Log.wtf(TAG, "Not expected for anything other than PAUSE & WAIT");
-            return;
+        if (newDigit != WAIT && newDigit != PAUSE) {
+            throw new IllegalArgumentException(
+                    "Not expected for anything other than PAUSE & WAIT");
         }
 
         int selectionStart;
@@ -1595,24 +1563,22 @@
      * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
      */
     private void updateDialAndDeleteButtonEnabledState() {
-        final boolean digitsNotEmpty = !isDigitsEmpty();
-
-        if (mDialButton != null) {
-            // On CDMA phones, if we're already on a call, we *always*
-            // enable the Dial button (since you can press it without
-            // entering any digits to send an empty flash.)
-            if (phoneIsCdma() && phoneIsOffhook()) {
-                mDialButton.setEnabled(true);
-            } else {
-                // Common case: GSM, or CDMA but not on a call.
-                // Enable the Dial button if some digits have
-                // been entered, or if there is a last dialed number
-                // that could be redialed.
-                mDialButton.setEnabled(digitsNotEmpty ||
-                        !TextUtils.isEmpty(mLastNumberDialed));
-            }
+        if (getActivity() == null) {
+            return;
         }
+        final boolean digitsNotEmpty = !isDigitsEmpty();
         mDelete.setEnabled(digitsNotEmpty);
+        // On CDMA phones, if we're already on a call, we *always* enable the Dial button (since
+        // you can press it without entering any digits to send an empty flash.)
+        if (phoneIsCdma() && phoneIsOffhook()) {
+            ((HostInterface) getActivity()).setDialButtonEnabled(true);
+        } else {
+            // Common case: GSM, or CDMA but not on a call. Enable the Dial button if something
+            // has been entered into the digits field, or if there is a last dialed number that
+            // could be redialed.
+            ((HostInterface) getActivity()).setDialButtonEnabled(
+                    digitsNotEmpty || !TextUtils.isEmpty(mLastNumberDialed));
+        }
     }
 
     /**
@@ -1642,8 +1608,8 @@
     /* package */ static boolean canAddDigit(CharSequence digits, int start, int end,
                                              char newDigit) {
         if(newDigit != WAIT && newDigit != PAUSE) {
-            Log.wtf(TAG, "Should not be called for anything other than PAUSE & WAIT");
-            return false;
+            throw new IllegalArgumentException(
+                    "Should not be called for anything other than PAUSE & WAIT");
         }
 
         // False if no selection, or selection is reversed (end < start)
diff --git a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
index 30d01d2..c7e4c5f 100644
--- a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
+++ b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
@@ -41,16 +41,16 @@
             if (TextUtils.isEmpty(number)) {
                 return;
             }
-            final long id = getContactIdFromPhoneNumber(context, number);
-            if (id != NO_CONTACT_FOUND) {
-                final Thread thread = new Thread() {
-                    @Override
-                    public void run() {
+            final Thread thread = new Thread() {
+                @Override
+                public void run() {
+                    final long id = getContactIdFromPhoneNumber(context, number);
+                    if (id != NO_CONTACT_FOUND) {
                         undemoteContactWithId(context, id);
                     }
-                };
-                thread.start();
-            }
+                }
+            };
+            thread.start();
         }
     }
 
@@ -68,6 +68,9 @@
                 Uri.encode(number));
         final Cursor cursor = context.getContentResolver().query(contactUri, new String[] {
                 PhoneLookup._ID}, null, null, null);
+        if (cursor == null) {
+            return NO_CONTACT_FOUND;
+        }
         try {
             if (cursor.moveToFirst()) {
                 final long id = cursor.getLong(0);
diff --git a/src/com/android/dialer/list/DragDropController.java b/src/com/android/dialer/list/DragDropController.java
new file mode 100644
index 0000000..399cd09
--- /dev/null
+++ b/src/com/android/dialer/list/DragDropController.java
@@ -0,0 +1,71 @@
+package com.android.dialer.list;
+
+import android.view.View;
+
+import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that handles and combines drag events generated from multiple views, and then fires
+ * off events to any OnDragDropListeners that have registered for callbacks.
+ */
+public class DragDropController {
+    private List<OnDragDropListener> mOnDragDropListeners = new ArrayList<OnDragDropListener>();
+
+    /**
+     * @return True if the drag is started, false if the drag is cancelled for some reason.
+     */
+    boolean handleDragStarted(int x, int y, ContactTileRow tileRow) {
+        final PhoneFavoriteTileView tileView =
+                (PhoneFavoriteTileView) tileRow.getViewAtPosition(x, y);
+
+        final int itemIndex = tileRow.getItemIndex(x, y);
+        if (itemIndex != -1 && !mOnDragDropListeners.isEmpty()) {
+            for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+                mOnDragDropListeners.get(i).onDragStarted(itemIndex, x, y, tileView);
+            }
+        }
+
+        return true;
+    }
+
+    public void handleDragHovered(int x, int y, View view) {
+        int itemIndex;
+        if (!(view instanceof ContactTileRow)) {
+            itemIndex = -1;
+        } else {
+            final ContactTileRow tile = (ContactTileRow) view;
+            itemIndex = tile.getItemIndex(x, y);
+        }
+        for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+            mOnDragDropListeners.get(i).onDragHovered(itemIndex, x, y);
+        }
+    }
+
+    public void handleDragFinished(int x, int y, boolean isRemoveView) {
+        if (isRemoveView) {
+            for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+                mOnDragDropListeners.get(i).onDroppedOnRemove();
+            }
+        }
+
+        for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+            mOnDragDropListeners.get(i).onDragFinished(x, y);
+        }
+    }
+
+    public void addOnDragDropListener(OnDragDropListener listener) {
+        if (!mOnDragDropListeners.contains(listener)) {
+            mOnDragDropListeners.add(listener);
+        }
+    }
+
+    public void removeOnDragDropListener(OnDragDropListener listener) {
+        if (mOnDragDropListeners.contains(listener)) {
+            mOnDragDropListeners.remove(listener);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/android/dialer/list/OnDragDropListener.java b/src/com/android/dialer/list/OnDragDropListener.java
new file mode 100644
index 0000000..7f6d179
--- /dev/null
+++ b/src/com/android/dialer/list/OnDragDropListener.java
@@ -0,0 +1,41 @@
+package com.android.dialer.list;
+
+
+/**
+ * Classes that want to receive callbacks in response to drag events should implement this
+ * interface.
+ */
+public interface OnDragDropListener {
+    /**
+     * Called when a drag is started.
+     * @param itemIndex Index of the contact on which the drag was triggered
+     * @param x X-coordinate of the drag event
+     * @param y Y-coordinate of the drag event
+     * @param view The contact tile which the drag was started on
+     */
+    public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view);
+
+    /**
+     * Called when a drag is in progress and the user moves the dragged contact to a
+     * location.
+     * @param itemIndex Index of the contact in the ListView which is currently being displaced
+     * by the dragged contact
+     * @param x X-coordinate of the drag event
+     * @param y Y-coordinate of the drag event
+     */
+    public void onDragHovered(int itemIndex, int x, int y);
+
+    /**
+     * Called when a drag is completed (whether by dropping it somewhere or simply by dragging
+     * the contact off the screen)
+     * @param x X-coordinate of the drag event
+     * @param y Y-coordinate of the drag event
+     */
+    public void onDragFinished(int x, int y);
+
+    /**
+     * Called when a contact has been dropped on the remove view, indicating that the user
+     * wants to remove this contact.
+     */
+    public void onDroppedOnRemove();
+}
\ No newline at end of file
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index 34666ee..0d33b4c 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -17,9 +17,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.LoaderManager;
@@ -27,7 +25,6 @@
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.content.SharedPreferences;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -36,12 +33,13 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
@@ -55,10 +53,10 @@
 import com.android.contacts.common.list.ContactTileView;
 import com.android.dialer.DialtactsActivity;
 import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogQuery;
-import com.android.dialer.calllog.ContactInfoHelper;
 import com.android.dialer.calllog.CallLogAdapter;
+import com.android.dialer.calllog.CallLogQuery;
 import com.android.dialer.calllog.CallLogQueryHandler;
+import com.android.dialer.calllog.ContactInfoHelper;
 import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
 import com.android.dialerbind.ObjectFactory;
 
@@ -109,6 +107,10 @@
         public void onCallNumberDirectly(String phoneNumber);
     }
 
+    public interface HostInterface {
+        public void setDragDropController(DragDropController controller);
+    }
+
     private class MissedCallLogLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
 
         @Override
@@ -197,8 +199,7 @@
 
     private PhoneFavoriteListView mListView;
 
-    private View mShowAllContactsButton;
-    private View mShowAllContactsInEmptyViewButton;
+    private View mPhoneFavoritesMenu;
     private View mContactTileFrame;
 
     private TileInteractionTeaserView mTileInteractionTeaserView;
@@ -238,8 +239,8 @@
         // that will be available on onCreateView().
         mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
                 this,
-                getResources().getInteger(R.integer.contact_tile_column_count_in_favorites_new),
-                1);
+                getResources().getInteger(R.integer.contact_tile_column_count_in_favorites),
+                PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
         mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
     }
 
@@ -253,7 +254,7 @@
                 this, 1);
         final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
         mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this,
-                new ContactInfoHelper(getActivity(), currentCountryIso), true, false);
+                new ContactInfoHelper(getActivity(), currentCountryIso), false, false);
         setHasOptionsMenu(true);
     }
 
@@ -282,7 +283,7 @@
         mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
         mListView.setOnItemSwipeListener(mContactTileAdapter);
-        mListView.setOnDragDropListener(mContactTileAdapter);
+        mListView.getDragDropController().addOnDragDropListener(mContactTileAdapter);
 
         final ImageView dragShadowOverlay =
                 (ImageView) mParentView.findViewById(R.id.contact_tile_drag_shadow_overlay);
@@ -290,14 +291,8 @@
 
         mEmptyView = mParentView.findViewById(R.id.phone_no_favorites_view);
 
-        mShowAllContactsInEmptyViewButton = mParentView.findViewById(
-                R.id.show_all_contact_button_in_nofav);
-        prepareAllContactsButton(mShowAllContactsInEmptyViewButton);
-
-        mShowAllContactsButton = inflater.inflate(R.layout.show_all_contact_button, mListView,
-                false);
-
-        prepareAllContactsButton(mShowAllContactsButton);
+        mPhoneFavoritesMenu = inflater.inflate(R.layout.phone_favorites_menu, mListView, false);
+        prepareFavoritesMenu(mPhoneFavoritesMenu);
 
         mContactTileFrame = mParentView.findViewById(R.id.contact_tile_frame);
 
@@ -305,7 +300,7 @@
                 R.layout.tile_interactions_teaser_view, mListView, false);
 
         mAdapter = new PhoneFavoriteMergedAdapter(getActivity(), this, mContactTileAdapter,
-                mCallLogAdapter, mShowAllContactsButton, mTileInteractionTeaserView);
+                mCallLogAdapter, mPhoneFavoritesMenu, mTileInteractionTeaserView);
 
         mTileInteractionTeaserView.setAdapter(mAdapter);
 
@@ -356,6 +351,15 @@
                     + " must implement OnShowAllContactsListener");
         }
 
+        try {
+            OnDragDropListener listener = (OnDragDropListener) activity;
+            mListView.getDragDropController().addOnDragDropListener(listener);
+            ((HostInterface) activity).setDragDropController(mListView.getDragDropController());
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString()
+                    + " must implement OnDragDropListener and HostInterface");
+        }
+
         // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
         // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
         // be called, on which we'll check if "all" contacts should be reloaded again or not.
@@ -424,7 +428,10 @@
     }
 
     /**
-     * Saves the current view offsets into memory
+     * Cache the current view offsets into memory. Once a relayout of views in the ListView
+     * has happened due to a dataset change, the cached offsets are used to create animations
+     * that slide views from their previous positions to their new ones, to give the appearance
+     * that the views are sliding into their new positions.
      */
     @SuppressWarnings("unchecked")
     private void saveOffsets(int removedItemHeight) {
@@ -437,10 +444,12 @@
             final int position = firstVisiblePosition + i;
             final long itemId = mAdapter.getItemId(position);
             final int itemViewType = mAdapter.getItemViewType(position);
-            if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) {
+            if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP &&
+                    child instanceof ContactTileRow) {
                 // This is a tiled row, so save horizontal offsets instead
                 saveHorizontalOffsets((ContactTileRow) child, (ArrayList<ContactEntry>)
-                        mAdapter.getItem(position));
+                        mAdapter.getItem(position),
+                        mAdapter.getAdjustedPositionInContactTileAdapter(position));
             }
             if (DEBUG) {
                 Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: "
@@ -452,15 +461,25 @@
         mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
     }
 
-    private void saveHorizontalOffsets(ContactTileRow row, ArrayList<ContactEntry> list) {
-        for (int i = 0; i < list.size(); i++) {
+    /**
+     * Saves the horizontal offsets for contacts that are displayed as tiles in a row. Saving
+     * these offsets allow us to animate tiles sliding left and right within the same row.
+     * See {@link #saveOffsets(int removedItemHeight)}
+     */
+    private void saveHorizontalOffsets(ContactTileRow row, ArrayList<ContactEntry> list,
+            int currentRowIndex) {
+        for (int i = 0; i < list.size() && i < row.getChildCount(); i++) {
             final View child = row.getChildAt(i);
+            if (child == null) {
+                continue;
+            }
             final ContactEntry entry = list.get(i);
             final long itemId = mContactTileAdapter.getAdjustedItemId(entry.id);
             if (DEBUG) {
                 Log.d(TAG, "Saving itemId: " + itemId + " for tileview child " + i + " Left: "
                         + child.getTop());
             }
+            mItemIdTopMap.put(itemId, currentRowIndex);
             mItemIdLeftMap.put(itemId, child.getLeft());
         }
     }
@@ -469,7 +488,7 @@
      * Performs a animations for a row of tiles
      */
     private void performHorizontalAnimations(ContactTileRow row, ArrayList<ContactEntry> list,
-            long[] idsInPlace) {
+            long[] idsInPlace, int currentRow) {
         if (mItemIdLeftMap.isEmpty()) {
             return;
         }
@@ -487,6 +506,25 @@
             } else {
                 Integer startLeft = mItemIdLeftMap.get(itemId);
                 int left = child.getLeft();
+
+                Integer startRow = mItemIdTopMap.get(itemId);
+                if (startRow != null) {
+                    if (startRow > currentRow) {
+                        // Item has shifted upwards to the previous row.
+                        // It should now animate in from right to left.
+                        startLeft = left + child.getWidth();
+                    } else if (startRow < currentRow) {
+                        // Item has shifted downwards to the next row.
+                        // It should now animate in from left to right.
+                        startLeft = left - child.getWidth();
+                    }
+
+                    // If the item hasn't shifted rows (startRow == currentRow), it either remains
+                    // in the same position or has shifted left or right within its current row.
+                    // Either way, startLeft has already been correctly saved and retrieved from
+                    // mItemIdTopMap.
+                }
+
                 if (startLeft != null) {
                     if (startLeft != left) {
                         int delta = startLeft - left;
@@ -498,10 +536,6 @@
                         animators.add(ObjectAnimator.ofFloat(
                                 child, "translationX", delta, 0.0f));
                     }
-                } else {
-                    // In case the last square row is pushed up from the non-square section.
-                    animators.add(ObjectAnimator.ofFloat(
-                            child, "translationX", left, 0.0f));
                 }
             }
         }
@@ -538,10 +572,12 @@
                     final View child = mListView.getChildAt(i);
                     int position = firstVisiblePosition + i;
                     final int itemViewType = mAdapter.getItemViewType(position);
-                    if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) {
+                    if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP &&
+                            child instanceof ContactTileRow) {
                         // This is a tiled row, so perform horizontal animations instead
                         performHorizontalAnimations((ContactTileRow) child, (
-                                ArrayList<ContactEntry>) mAdapter.getItem(position), idsInPlace);
+                                ArrayList<ContactEntry>) mAdapter.getItem(position), idsInPlace,
+                                mAdapter.getAdjustedPositionInContactTileAdapter(position));
                     }
 
                     final long itemId = mAdapter.getItemId(position);
@@ -571,11 +607,6 @@
                             }
                             startTop = top + (i > 0 ? itemHeight : -itemHeight);
                             delta = startTop - top;
-                        } else {
-                            // In case the first non-square row is pushed down
-                            // from the square section.
-                            animators.add(ObjectAnimator.ofFloat(
-                                    child, "alpha", 0.0f, 1.0f));
                         }
                         if (DEBUG) {
                             Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
@@ -633,31 +664,17 @@
     }
 
     /**
-     * Returns a view that is laid out and styled to look like a regular contact, with the correct
-     * click behavior (to launch the all contacts activity when it is clicked).
+     * Prepares the favorites menu which contains the static label "Speed Dial" and the
+     * "All Contacts" button.  Sets the onClickListener for the "All Contacts" button.
      */
-    private View prepareAllContactsButton(View v) {
-        final ContactListItemView view = (ContactListItemView) v;
-        view.setOnClickListener(new OnClickListener() {
+    private void prepareFavoritesMenu(View favoritesMenu) {
+        Button allContactsButton = (Button) favoritesMenu.findViewById(R.id.all_contacts_button);
+        // Set the onClick listener for the button to bring up the all contacts view.
+        allContactsButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
                 showAllContacts();
             }
         });
-
-        view.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
-        final Resources resources = getResources();
-        view.setBackgroundResource(R.drawable.contact_list_item_background);
-
-        view.setPaddingRelative(
-                resources.getDimensionPixelSize(R.dimen.favorites_row_start_padding),
-                resources.getDimensionPixelSize(R.dimen.favorites_row_end_padding),
-                resources.getDimensionPixelSize(R.dimen.favorites_row_top_padding),
-                resources.getDimensionPixelSize(R.dimen.favorites_row_bottom_padding));
-
-        view.setDisplayName(resources.getString(R.string.show_all_contacts_button_text));
-        view.setDrawableResource(R.drawable.list_item_avatar_bg,
-                R.drawable.ic_menu_all_contacts_dk);
-        return view;
     }
 }
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
index 99979dd..078cf3e 100644
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteListView.java
@@ -29,7 +29,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ListView;
 
@@ -44,7 +43,8 @@
  * - Swiping, which is borrowed from packages/apps/UnifiedEmail (com.android.mail.ui.Swipeable)
  * - Drag and drop
  */
-public class PhoneFavoriteListView extends ListView implements SwipeHelperCallback {
+public class PhoneFavoriteListView extends ListView implements SwipeHelperCallback,
+        OnDragDropListener {
 
     public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
 
@@ -52,7 +52,6 @@
     private boolean mEnableSwipe = true;
 
     private OnItemGestureListener mOnItemGestureListener;
-    private OnDragDropListener mOnDragDropListener;
 
     private float mDensityScale;
     private float mTouchSlop;
@@ -63,7 +62,7 @@
 
     private Handler mScrollHandler;
     private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
-    private final int DRAG_SCROLL_PX_UNIT = 10;
+    private final int DRAG_SCROLL_PX_UNIT = 25;
 
     private boolean mIsDragScrollerRunning = false;
     private int mTouchDownForDragStartX;
@@ -81,6 +80,8 @@
     private int mDragShadowLeft;
     private int mDragShadowTop;
 
+    private DragDropController mDragDropController = new DragDropController();
+
     private final float DRAG_SHADOW_ALPHA = 0.7f;
 
     /**
@@ -130,6 +131,7 @@
         mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this,
                 mDensityScale, mTouchSlop);
         setItemsCanFocus(true);
+        mDragDropController.addOnDragDropListener(this);
     }
 
     @Override
@@ -156,10 +158,10 @@
         mOnItemGestureListener = listener;
     }
 
-    public void setOnDragDropListener(OnDragDropListener listener) {
-        mOnDragDropListener = listener;
-    }
-
+    /**
+     * TODO: This is all swipe to remove code (nothing to do with drag to remove). This should
+     * be cleaned up and removed once drag to remove becomes the only way to remove contacts.
+     */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -232,6 +234,10 @@
         requestDisallowInterceptTouchEvent(true);
     }
 
+    /**
+     * End of swipe-to-remove code
+     */
+
     @Override
     public boolean dispatchDragEvent(DragEvent event) {
         final int action = event.getAction();
@@ -239,13 +245,34 @@
         final int eY = (int) event.getY();
         switch (action) {
             case DragEvent.ACTION_DRAG_STARTED:
-                if (!handleDragStarted(mTouchDownForDragStartX, mTouchDownForDragStartY)) {
+                final int[] coordinates = new int[2];
+                getLocationOnScreen(coordinates);
+                // Calculate the X and Y coordinates of the drag event relative to the view
+                final int viewX = eX - coordinates[0];
+                final int viewY = eY - coordinates[1];
+                final View child = getViewAtPosition(viewX, viewY);
+
+                if (!(child instanceof ContactTileRow)) {
+                    // Bail early.
                     return false;
-                };
+                }
+
+                final ContactTileRow tile = (ContactTileRow) child;
+
+                // Disable drag and drop if there is a contact that has been swiped and is currently
+                // in the pending remove state
+                if (tile.getTileAdapter().hasPotentialRemoveEntryIndex()) {
+                    return false;
+                }
+
+                if (!mDragDropController.handleDragStarted(viewX, viewY, tile)) {
+                    return false;
+                }
                 break;
             case DragEvent.ACTION_DRAG_LOCATION:
                 mLastDragY = eY;
-                handleDragHovered(eX, eY);
+                final View view = getViewAtPosition(eX, eY);
+                mDragDropController.handleDragHovered(eX, eY, view);
                 // Kick off {@link #mScrollHandler} if it's not started yet.
                 if (!mIsDragScrollerRunning &&
                         // And if the distance traveled while dragging exceeds the touch slop
@@ -268,13 +295,13 @@
                 mIsDragScrollerRunning = false;
                 // Either a successful drop or it's ended with out drop.
                 if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
-                    handleDragFinished(eX, eY);
+                    mDragDropController.handleDragFinished(eX, eY, false);
                 }
                 break;
             default:
                 break;
         }
-        // This ListView will consumer the drag events on behalf of its children.
+        // This ListView will consume the drag events on behalf of its children.
         return true;
     }
 
@@ -303,69 +330,48 @@
         }
     }
 
-    /**
-     * @return True if the drag is started.
-     */
-    private boolean handleDragStarted(int x, int y) {
-        final View child = getViewAtPosition(x, y);
-        if (!(child instanceof ContactTileRow)) {
-            // Bail early.
-            return false;
-        }
-
-        final ContactTileRow tile = (ContactTileRow) child;
-
-        if (tile.getTileAdapter().hasPotentialRemoveEntryIndex()) {
-            return false;
-        }
-
-        final int itemIndex = tile.getItemIndex(x, y);
-        if (itemIndex != -1 && mOnDragDropListener != null) {
-            final PhoneFavoriteTileView tileView =
-                    (PhoneFavoriteTileView) tile.getViewAtPosition(x, y);
-            if (mDragShadowOverlay == null) {
-                return false;
-            }
-
-            mDragShadowOverlay.clearAnimation();
-            mDragShadowBitmap = createDraggedChildBitmap(tileView);
-            if (mDragShadowBitmap == null) {
-                return false;
-            }
-
-            if (tileView instanceof PhoneFavoriteRegularRowView) {
-                mDragShadowLeft = tile.getLeft();
-                mDragShadowTop = tile.getTop();
-            } else {
-                // Square tile is relative to the contact tile,
-                // and contact tile is relative to this list view.
-                mDragShadowLeft = tileView.getLeft() + tileView.getParentRow().getLeft();
-                mDragShadowTop = tileView.getTop() + tileView.getParentRow().getTop();
-            }
-
-            mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
-            mDragShadowOverlay.setVisibility(VISIBLE);
-            mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
-
-            mDragShadowOverlay.setX(mDragShadowLeft);
-            mDragShadowOverlay.setY(mDragShadowTop);
-
-            // x and y passed in are the coordinates of where the user has touched down, calculate
-            // the offset to the top left coordinate of the dragged child.  This will be used for
-            // drawing the drag shadow.
-            mTouchOffsetToChildLeft = x - mDragShadowLeft;
-            mTouchOffsetToChildTop = y - mDragShadowTop;
-
-            // invalidate to trigger a redraw of the drag shadow.
-            invalidate();
-
-            mOnDragDropListener.onDragStarted(itemIndex);
-        }
-
-        return true;
+    public DragDropController getDragDropController() {
+        return mDragDropController;
     }
 
-    private void handleDragHovered(int x, int y) {
+    @Override
+    public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView tileView) {
+        if (mDragShadowOverlay == null) {
+            return;
+        }
+
+        mDragShadowOverlay.clearAnimation();
+        mDragShadowBitmap = createDraggedChildBitmap(tileView);
+        if (mDragShadowBitmap == null) {
+            return;
+        }
+
+        if (tileView instanceof PhoneFavoriteRegularRowView) {
+            mDragShadowLeft = tileView.getParentRow().getLeft();
+            mDragShadowTop = tileView.getParentRow().getTop();
+        } else {
+            // Square tile is relative to the contact tile,
+            // and contact tile is relative to this list view.
+            mDragShadowLeft = tileView.getLeft() + tileView.getParentRow().getLeft();
+            mDragShadowTop = tileView.getTop() + tileView.getParentRow().getTop();
+        }
+
+        mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
+        mDragShadowOverlay.setVisibility(VISIBLE);
+        mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
+
+        mDragShadowOverlay.setX(mDragShadowLeft);
+        mDragShadowOverlay.setY(mDragShadowTop);
+
+        // x and y passed in are the coordinates of where the user has touched down,
+        // calculate the offset to the top left coordinate of the dragged child.  This
+        // will be used for drawing the drag shadow.
+        mTouchOffsetToChildLeft = x - mDragShadowLeft;
+        mTouchOffsetToChildTop = y - mDragShadowTop;
+    }
+
+    @Override
+    public void onDragHovered(int itemIndex, int x, int y) {
         // Update the drag shadow location.
         mDragShadowLeft = x - mTouchOffsetToChildLeft;
         mDragShadowTop = y - mTouchOffsetToChildTop;
@@ -374,21 +380,10 @@
             mDragShadowOverlay.setX(mDragShadowLeft);
             mDragShadowOverlay.setY(mDragShadowTop);
         }
-
-        final View child = getViewAtPosition(x, y);
-        if (!(child instanceof ContactTileRow)) {
-            // Bail early.
-            return;
-        }
-
-        final ContactTileRow tile = (ContactTileRow) child;
-        final int itemIndex = tile.getItemIndex(x, y);
-        if (itemIndex != -1 && mOnDragDropListener != null) {
-            mOnDragDropListener.onDragHovered(itemIndex);
-        }
     }
 
-    private void handleDragFinished(int x, int y) {
+    @Override
+    public void onDragFinished(int x, int y) {
         // Update the drag shadow location.
         mDragShadowLeft = x - mTouchOffsetToChildLeft;
         mDragShadowTop = y - mTouchOffsetToChildTop;
@@ -400,12 +395,11 @@
                     .setListener(mDragShadowOverAnimatorListener)
                     .start();
         }
-
-        if (mOnDragDropListener != null) {
-            mOnDragDropListener.onDragFinished();
-        }
     }
 
+    @Override
+    public void onDroppedOnRemove() {}
+
     private Bitmap createDraggedChildBitmap(View view) {
         view.setDrawingCacheEnabled(true);
         final Bitmap cache = view.getDrawingCache();
@@ -425,10 +419,4 @@
 
         return bitmap;
     }
-
-    public interface OnDragDropListener {
-        public void onDragStarted(int itemIndex);
-        public void onDragHovered(int itemIndex);
-        public void onDragFinished();
-    }
 }
diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
index daba39e..6307613 100644
--- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
@@ -49,12 +49,12 @@
 
     private static final String TAG = PhoneFavoriteMergedAdapter.class.getSimpleName();
 
-    private static final int TILE_INTERACTION_TEASER_VIEW_POSITION = 2;
+    private static final int TILE_INTERACTION_TEASER_VIEW_POSITION = 3;
     private static final int TILE_INTERACTION_TEASER_VIEW_ID = -2;
-    private static final int ALL_CONTACTS_BUTTON_ITEM_ID = -1;
+    private static final int FAVORITES_MENU_ITEM_ID = -3;
     private final PhoneFavoritesTileAdapter mContactTileAdapter;
     private final CallLogAdapter mCallLogAdapter;
-    private final View mShowAllContactsButton;
+    private final View mPhoneFavoritesMenu;
     private final PhoneFavoriteFragment mFragment;
     private final TileInteractionTeaserView mTileInteractionTeaserView;
 
@@ -103,7 +103,7 @@
             PhoneFavoriteFragment fragment,
             PhoneFavoritesTileAdapter contactTileAdapter,
             CallLogAdapter callLogAdapter,
-            View showAllContactsButton,
+            View phoneFavoritesMenu,
             TileInteractionTeaserView tileInteractionTeaserView) {
         final Resources resources = context.getResources();
         mContext = context;
@@ -114,20 +114,24 @@
         mObserver = new CustomDataSetObserver();
         mCallLogAdapter.registerDataSetObserver(mObserver);
         mContactTileAdapter.registerDataSetObserver(mObserver);
-        mShowAllContactsButton = showAllContactsButton;
+        mPhoneFavoritesMenu = phoneFavoritesMenu;
         mTileInteractionTeaserView = tileInteractionTeaserView;
         mCallLogQueryHandler = new CallLogQueryHandler(mContext.getContentResolver(),
                 mCallLogQueryHandlerListener);
     }
 
+    /**
+     * Determines the number of items in the adapter.
+     * mCallLogAdapter contains the item for the most recent caller.
+     * mContactTileAdapter contains the starred contacts.
+     * The +1 is to account for the presence of the favorites menu.
+     *
+     * @return Number of items in the adapter.
+     */
     @Override
     public int getCount() {
-        if (mContactTileAdapter.getCount() > 0) {
-            return mContactTileAdapter.getCount() + mCallLogAdapter.getCount() + 1 +
-                    getTeaserViewCount();
-        } else {
-            return mCallLogAdapter.getCount();
-        }
+        return mContactTileAdapter.getCount() + mCallLogAdapter.getCount() + getTeaserViewCount()
+                + 1;
     }
 
     @Override
@@ -140,7 +144,7 @@
             }
         }
         // Set position to the position of the actual favorite contact in the favorites adapter
-        position = getAdjustedFavoritePosition(position, callLogAdapterCount);
+        position = getAdjustedPositionInContactTileAdapter(position);
 
         return mContactTileAdapter.getItem(position);
     }
@@ -151,9 +155,9 @@
      *
      * These are the ranges of IDs reserved for each item type.
      *
-     * -(N + 1) to -3: CallLogAdapterItems, where N is equal to the number of call log items
+     * -4 and lower: CallLogAdapterItems representing most recent call.
+     * -3: Favorites menu
      * -2: Teaser
-     * -1: All contacts button
      * 0 to (N -1): Rows of tiled contacts, where N is equal to the max rows of tiled contacts
      * N to infinity: Rows of regular contacts. Their item id is calculated by N + contact_id,
      * where contact_id is guaranteed to never be negative.
@@ -163,17 +167,19 @@
         final int callLogAdapterCount = mCallLogAdapter.getCount();
         if (position < callLogAdapterCount) {
             // Call log items are not animated, so reusing their position for IDs is fine.
-            return ALL_CONTACTS_BUTTON_ITEM_ID - 2 - position;
+            return FAVORITES_MENU_ITEM_ID - 1 - position;
         } else if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount &&
-                mTileInteractionTeaserView.getShouldDisplayInList()){
+                mTileInteractionTeaserView.getShouldDisplayInList()) {
             return TILE_INTERACTION_TEASER_VIEW_ID;
+        } else if (position == callLogAdapterCount) {
+            return FAVORITES_MENU_ITEM_ID;
         } else if (position < (callLogAdapterCount + mContactTileAdapter.getCount() +
-                getTeaserViewCount())) {
+                getTeaserViewCount() + 1)) {
             return mContactTileAdapter.getItemId(
-                    getAdjustedFavoritePosition(position, callLogAdapterCount));
+                    getAdjustedPositionInContactTileAdapter(position));
         } else {
-            // All contacts button
-            return ALL_CONTACTS_BUTTON_ITEM_ID;
+            // Default fallback.  We don't normally get here.
+            return FAVORITES_MENU_ITEM_ID;
         }
     }
 
@@ -182,12 +188,15 @@
         return true;
     }
 
+    /**
+     * Determine the number of view types present.
+     */
     @Override
     public int getViewTypeCount() {
         return (mContactTileAdapter.getViewTypeCount() +            /* Favorite and frequent */
                 mCallLogAdapter.getViewTypeCount() +                /* Recent call log */
                 getTeaserViewCount() +                              /* Teaser */
-                1);                                                 /* Show all contacts button. */
+                1);                                                 /* Favorites menu. */
     }
 
     @Override
@@ -200,61 +209,67 @@
             return mContactTileAdapter.getViewTypeCount();
         } else if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount &&
                 mTileInteractionTeaserView.getShouldDisplayInList()) {
-            // View type of the teaser row is the last view type of the contact tile adapter + 3
+            // View type of the teaser row is the last view type of the contact tile adapter +2
             return mContactTileAdapter.getViewTypeCount() + 2;
-        } else if (position < getCount() - 1) {
+        } else if (position == callLogAdapterCount) {
+            // View type of the favorites menu is last view type of contact tile adapter +3
+            return mContactTileAdapter.getViewTypeCount() + 3;
+        } else if (position < getCount()) {
             return mContactTileAdapter.getItemViewType(
-                    getAdjustedFavoritePosition(position, callLogAdapterCount));
+                    getAdjustedPositionInContactTileAdapter(position));
         } else {
-            // View type of the show all contact button is the last view type of the contact tile
-            // adapter + 2
-            return mContactTileAdapter.getViewTypeCount() + 1;
+            // Catch-all - we shouldn't get here but if we do use the same as the favorites menu.
+            return mContactTileAdapter.getViewTypeCount() + 3;
         }
     }
 
+    /**
+     * Determines the view for a specified position.
+     *
+     * @param position Position for which to retrieve view.
+     * @return view corresponding to position.
+     */
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         final int callLogAdapterCount = mCallLogAdapter.getCount();
 
-        if ((position == getCount() - 1) && (mContactTileAdapter.getCount() > 0)) {
-            return mShowAllContactsButton;
-        }
+        // Get the view for the "teaser view" which describes how to re-arrange favorites.
+        if (mTileInteractionTeaserView.getShouldDisplayInList()
+                && position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount) {
+            return mTileInteractionTeaserView;
+        } else if (callLogAdapterCount > 0 && position < callLogAdapterCount) {
+            // Handle case where we are requesting the view for the "most recent caller".
 
-        if (mTileInteractionTeaserView.getShouldDisplayInList())  {
-            if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount) {
-                return mTileInteractionTeaserView;
+            final SwipeableCallLogRow wrapper;
+            if (convertView == null) {
+                wrapper = new SwipeableCallLogRow(mContext);
+                wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener);
+            } else {
+                wrapper = (SwipeableCallLogRow) convertView;
             }
+
+            // Special case wrapper view for the most recent call log item. This allows
+            // us to create a card-like effect for the more recent call log item in
+            // the PhoneFavoriteMergedAdapter, but keep the original look of the item in
+            // the CallLogAdapter.
+            final View view = mCallLogAdapter.getView(position, convertView == null ?
+                    null : wrapper.getChildAt(0), parent);
+            wrapper.removeAllViews();
+            final View callLogItem = view.findViewById(R.id.call_log_list_item);
+            // Reset the internal call log item view if it is being recycled
+            callLogItem.setTranslationX(0);
+            callLogItem.setAlpha(1);
+            wrapper.addView(view);
+            return wrapper;
+        } else if (position == callLogAdapterCount) {
+            // If position is just after the entries in the mCallLogAdapter (most recent call),
+            // return the favorites menu.
+            return mPhoneFavoritesMenu;
         }
 
-        if (callLogAdapterCount > 0) {
-            if (position == 0) {
-                final SwipeableCallLogRow wrapper;
-                if (convertView == null) {
-                    wrapper = new SwipeableCallLogRow(mContext);
-                    wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener);
-                } else {
-                    wrapper = (SwipeableCallLogRow) convertView;
-                }
-
-                // Special case wrapper view for the most recent call log item. This allows
-                // us to create a card-like effect for the more recent call log item in
-                // the PhoneFavoriteMergedAdapter, but keep the original look of the item in
-                // the CallLogAdapter.
-                final View view = mCallLogAdapter.getView(position, convertView == null ?
-                        null : wrapper.getChildAt(0), parent);
-                wrapper.removeAllViews();
-                final View callLogItem = view.findViewById(R.id.call_log_list_item);
-                // Reset the internal call log item view if it is being recycled
-                callLogItem.setTranslationX(0);
-                callLogItem.setAlpha(1);
-                wrapper.addView(view);
-                return wrapper;
-            }
-        }
-
-        // Set position to the position of the actual favorite contact in the
-        // favorites adapter
-        position = getAdjustedFavoritePosition(position, callLogAdapterCount);
+        // Set position to the position of the actual favorite contact in the favorites adapter.
+        // Adjusts based on the presence of other views, such as the favorites menu.
+        position = getAdjustedPositionInContactTileAdapter(position);
 
         // Favorites section
         final View view = mContactTileAdapter.getView(position, convertView, parent);
@@ -281,19 +296,31 @@
             return mCallLogAdapter.isEnabled(position);
         } else { // For favorites section
             return mContactTileAdapter.isEnabled(
-                    getAdjustedFavoritePosition(position, callLogAdapterCount));
+                    getAdjustedPositionInContactTileAdapter(position));
         }
     }
 
-    private int getAdjustedFavoritePosition(int position, int callLogAdapterCount) {
+    /**
+     * Given the current position in the merged adapter, return the index in the
+     * mContactTileAdapter this position corresponds to.
+     *
+     * @param position current position in the overall (merged) adapter.
+     * @return position in the mContactTileAdapter.
+     */
+    public int getAdjustedPositionInContactTileAdapter(int position) {
+        final int callLogAdapterCount = mCallLogAdapter.getCount();
         if (position - callLogAdapterCount > TILE_INTERACTION_TEASER_VIEW_POSITION &&
                 mTileInteractionTeaserView.getShouldDisplayInList()) {
-            return position - callLogAdapterCount - 1;
+            return position - callLogAdapterCount - 2;
         } else {
-            return position - callLogAdapterCount;
+            return position - callLogAdapterCount - 1;
         }
     }
 
+    /**
+     * Determines the number of teaser views visible.
+     * @return 1 or 0 depending on if the teaser view is showing.
+     */
     private int getTeaserViewCount() {
         return (mContactTileAdapter.getCount() > TILE_INTERACTION_TEASER_VIEW_POSITION &&
                 mTileInteractionTeaserView.getShouldDisplayInList() ? 1 : 0);
@@ -318,7 +345,7 @@
 
         @Override
         public void addView(View view) {
-            view.setBackgroundResource(R.drawable.dialer_recent_card_bg);
+            view.setBackgroundResource(R.drawable.ic_tile_for_recents_and_contact_tile);
 
             final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                     FrameLayout.LayoutParams.MATCH_PARENT,
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
index 85e7216..181d602 100644
--- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
@@ -36,10 +36,6 @@
     private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
     private ImageButton mSecondaryButton;
 
-    // TODO: Use a more expansive name token separator if needed. For now it should be fine to
-    // not split by dashes, underscore etc.
-    private static final Pattern NAME_TOKEN_SEPARATOR_PATTERN = Pattern.compile("\\s+");
-
     public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -63,35 +59,15 @@
     }
 
     @Override
-    protected String getNameForView(String name) {
-        if (TextUtils.isEmpty(name)) return name;
-        final String[] tokens = NAME_TOKEN_SEPARATOR_PATTERN.split(name, 2);
-        if (tokens.length < 1) return name;
-        return tokens[0];
-    }
-
-    @Override
     public void loadFromContact(ContactEntry entry) {
         super.loadFromContact(entry);
         if (entry != null) {
-            final boolean contactIsFavorite = entry.isFavorite;
-            mSecondaryButton.setVisibility(contactIsFavorite ? GONE : VISIBLE);
-
-            if (contactIsFavorite) {
-                mStarView.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        launchQuickContact();
-                    }
-                });
-            } else {
-                mSecondaryButton.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        launchQuickContact();
-                    }
-                });
-            }
+            mSecondaryButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    launchQuickContact();
+                }
+            });
         }
     }
 }
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
index 371c805..f55d4fc 100644
--- a/src/com/android/dialer/list/PhoneFavoriteTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java
@@ -24,13 +24,12 @@
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.GestureDetector;
-import android.view.MotionEvent;
 import android.view.View;
-import android.widget.ImageView;
 
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.list.ContactEntry;
 import com.android.contacts.common.list.ContactTileView;
 import com.android.dialer.R;
@@ -49,7 +48,13 @@
     private static final String TAG = PhoneFavoriteTileView.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    /** Length of all animations in miniseconds. */
+    // These parameters instruct the photo manager to display the default image/letter at 70% of
+    // its normal size, and vertically offset upwards 14% towards the top of the letter tile, to
+    // make room for the contact name and number label at the bottom of the image.
+    private static final float DEFAULT_IMAGE_LETTER_OFFSET = -0.14f;
+    private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.70f;
+
+    /** Length of all animations in milliseconds. */
     private int mAnimationDuration;
 
     /** The view that holds the front layer of the favorite contact card. */
@@ -60,8 +65,8 @@
     private View mUndoRemovalButton;
     /** The view that holds the list view row. */
     protected ContactTileRow mParentRow;
-    /** The view that indicates whether the contact is a favorate. */
-    protected ImageView mStarView;
+    /** View that contains the transparent shadow that is overlaid on top of the contact image. */
+    private View mShadowOverlay;
 
     /** Users' most frequent phone number. */
     private String mPhoneNumberString;
@@ -85,11 +90,10 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mFavoriteContactCard = findViewById(com.android.dialer.R.id.contact_favorite_card);
-        mRemovalDialogue = findViewById(com.android.dialer.R.id.favorite_remove_dialogue);
-        mUndoRemovalButton = findViewById(com.android.dialer.R.id
-                .favorite_remove_undo_button);
-        mStarView = (ImageView) findViewById(com.android.dialer.R.id.contact_favorite_star);
+        mShadowOverlay = findViewById(R.id.shadow_overlay);
+        mFavoriteContactCard = findViewById(R.id.contact_favorite_card);
+        mRemovalDialogue = findViewById(R.id.favorite_remove_dialogue);
+        mUndoRemovalButton = findViewById(R.id.favorite_remove_undo_button);
 
         mUndoRemovalButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -127,7 +131,6 @@
             // Grab the phone-number to call directly... see {@link onClick()}
             mPhoneNumberString = entry.phoneNumber;
 
-            mStarView.setVisibility(entry.isFavorite ? VISIBLE : GONE);
             // If this is a blank entry, don't show anything.
             // TODO krelease:Just hide the view for now. For this to truly look like an empty row
             // the entire ContactTileRow needs to be hidden.
@@ -241,4 +244,18 @@
             }
         };
     }
+
+    @Override
+    protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+        return new DefaultImageRequest(displayName, lookupKey, ContactPhotoManager.TYPE_DEFAULT,
+                DEFAULT_IMAGE_LETTER_SCALE, DEFAULT_IMAGE_LETTER_OFFSET);
+    }
+
+    @Override
+    protected void configureViewForImage(boolean isDefaultImage) {
+        // Hide the shadow overlay if the image is a default image (i.e. colored letter tile)
+        if (mShadowOverlay != null) {
+            mShadowOverlay.setVisibility(isDefaultImage ? View.GONE : View.VISIBLE);
+        }
+    }
 }
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index e28fd73..d1ac955 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -15,7 +15,9 @@
  */
 package com.android.dialer.list;
 
-import android.animation.ObjectAnimator;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ComparisonChain;
+
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -43,13 +45,9 @@
 import com.android.contacts.common.list.ContactTileView;
 import com.android.dialer.list.SwipeHelper.OnItemGestureListener;
 import com.android.dialer.list.SwipeHelper.SwipeHelperCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.common.collect.ComparisonChain;
 
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.PriorityQueue;
@@ -61,11 +59,13 @@
  *
  */
 public class PhoneFavoritesTileAdapter extends BaseAdapter implements
-        SwipeHelper.OnItemGestureListener, PhoneFavoriteListView.OnDragDropListener {
+        SwipeHelper.OnItemGestureListener, OnDragDropListener {
     private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    public static final int ROW_LIMIT_DEFAULT = 1;
+    public static final int NO_ROW_LIMIT = -1;
+
+    public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT;
 
     private ContactTileView.Listener mListener;
     private OnDataSetChangedForAnimationListener mDataSetChangedListener;
@@ -88,6 +88,7 @@
     private long mIdToKeepInPlace = -1;
 
     private boolean mAwaitingRemove = false;
+    private boolean mDelayCursorUpdates = false;
 
     private ContactPhotoManager mPhotoManager;
     protected int mNumFrequents;
@@ -155,7 +156,7 @@
         mContactEntries = new ArrayList<ContactEntry>();
         // Converting padding in dips to padding in pixels
         mPaddingInPixels = mContext.getResources()
-                .getDimensionPixelSize(R.dimen.contact_tile_divider_padding);
+                .getDimensionPixelSize(R.dimen.contact_tile_divider_width);
 
         bindColumnIndices();
     }
@@ -178,6 +179,7 @@
      * @param inDragging Boolean variable indicating whether there is a drag in process.
      */
     public void setInDragging(boolean inDragging) {
+        mDelayCursorUpdates = inDragging;
         mInDragging = inDragging;
     }
 
@@ -224,7 +226,7 @@
      * Else use {@link ContactTileLoaderFactory}
      */
     public void setContactCursor(Cursor cursor) {
-        if (cursor != null && !cursor.isClosed()) {
+        if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
             mNumStarred = getNumStarredContacts(cursor);
             if (mAwaitingRemove) {
                 mDataSetChangedListener.cacheOffsetsForDatasetChange();
@@ -296,7 +298,8 @@
             contact.name = (!TextUtils.isEmpty(name)) ? name :
                     mResources.getString(R.string.missing_name);
             contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
-            contact.lookupKey = ContentUris.withAppendedId(
+            contact.lookupKey = lookupKey;
+            contact.lookupUri = ContentUris.withAppendedId(
                     Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
             contact.isFavorite = isStarred;
             contact.isDefaultNumber = isDefaultNumber;
@@ -385,18 +388,24 @@
     protected int getRowCount(int entryCount) {
         if (entryCount == 0) return 0;
         final int nonLimitedRows = ((entryCount - 1) / mColumnCount) + 1;
+        if (mMaxTiledRows == NO_ROW_LIMIT) {
+            return nonLimitedRows;
+        }
         return Math.min(mMaxTiledRows, nonLimitedRows);
     }
 
     private int getMaxContactsInTiles() {
+        if (mMaxTiledRows == NO_ROW_LIMIT) {
+            return Integer.MAX_VALUE;
+        }
         return mColumnCount * mMaxTiledRows;
     }
 
     public int getRowIndex(int entryIndex) {
-        if (entryIndex < mMaxTiledRows * mColumnCount) {
+        if (entryIndex < getMaxContactsInTiles()) {
             return entryIndex / mColumnCount;
         } else {
-            return entryIndex - mMaxTiledRows * mColumnCount + mMaxTiledRows;
+            return entryIndex - mMaxTiledRows * (mColumnCount + 1);
         }
     }
 
@@ -466,9 +475,13 @@
     }
 
     /**
-     * Calculates the stable itemId for a particular entry based on its contactID
+     * Calculates the stable itemId for a particular entry based on the entry's contact ID. This
+     * stable itemId is used for animation purposes.
      */
     public long getAdjustedItemId(long id) {
+        if (mMaxTiledRows == NO_ROW_LIMIT) {
+            return id;
+        }
         return mMaxTiledRows + id;
     }
 
@@ -478,7 +491,6 @@
     }
 
     @Override
-
     public boolean areAllItemsEnabled() {
         // No dividers, so all items are enabled.
         return true;
@@ -540,7 +552,7 @@
 
     @Override
     public int getItemViewType(int position) {
-        if (position < getRowCount(getMaxContactsInTiles())) {
+        if (position < getMaxContactsInTiles()) {
             return ViewTypes.TOP;
         } else {
             return ViewTypes.FREQUENT;
@@ -656,7 +668,7 @@
         boolean removed = false;
         if (isIndexInBound(mPotentialRemoveEntryIndex)) {
             final ContactEntry entry = mContactEntries.get(mPotentialRemoveEntryIndex);
-            unstarAndUnpinContact(entry.lookupKey);
+            unstarAndUnpinContact(entry.lookupUri);
             removed = true;
             mAwaitingRemove = true;
         }
@@ -699,6 +711,7 @@
         private final int mRowPaddingEnd;
         private final int mRowPaddingTop;
         private final int mRowPaddingBottom;
+        private final float mHeightToWidthRatio;
         private int mPosition;
         private SwipeHelper mSwipeHelper;
         private OnItemGestureListener mOnItemSwipeListener;
@@ -711,6 +724,9 @@
 
             final Resources resources = mContext.getResources();
 
+            mHeightToWidthRatio = getResources().getFraction(
+                    R.dimen.contact_tile_height_to_width_ratio, 1, 1);
+
             if (mItemViewType == ViewTypes.TOP) {
                 // For tiled views, we still want padding to be set on the ContactTileRow.
                 // Otherwise the padding would be set around each of the tiles, which we don't want
@@ -722,8 +738,6 @@
                         R.dimen.favorites_row_start_padding);
                 mRowPaddingEnd = resources.getDimensionPixelSize(
                         R.dimen.favorites_row_end_padding);
-
-                setBackgroundResource(R.drawable.bottom_border_background);
             } else {
                 // For row views, padding is set on the view itself.
                 mRowPaddingTop = 0;
@@ -893,21 +907,22 @@
 
             // Preferred width / height for images (excluding the padding).
             // The actual width may be 1 pixel larger than this if we have a remainder.
-            final int imageSize = (width - totalPaddingsInPixels) / mColumnCount;
-            final int remainder = width - (imageSize * mColumnCount) - totalPaddingsInPixels;
+            final int imageWidth = (width - totalPaddingsInPixels) / mColumnCount;
+            final int remainder = width - (imageWidth * mColumnCount) - totalPaddingsInPixels;
+
+            final int height = (int) (mHeightToWidthRatio * imageWidth);
 
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
-                final int childWidth = imageSize + child.getPaddingRight()
+                final int childWidth = imageWidth + child.getPaddingRight()
                         // Compensate for the remainder
                         + (i < remainder ? 1 : 0);
-                final int childHeight = imageSize;
                 child.measure(
                         MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)
+                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
                         );
             }
-            setMeasuredDimension(width, imageSize + getPaddingTop() + getPaddingBottom());
+            setMeasuredDimension(width, height + getPaddingTop() + getPaddingBottom());
         }
 
         /**
@@ -918,7 +933,7 @@
          * @return Index of the selected item in the cached array.
          */
         public int getItemIndex(float itemX, float itemY) {
-            if (mPosition < mMaxTiledRows) {
+            if (mMaxTiledRows == NO_ROW_LIMIT || mPosition < mMaxTiledRows) {
                 if (DEBUG) {
                     Log.v(TAG, String.valueOf(itemX) + " " + String.valueOf(itemY));
                 }
@@ -1190,24 +1205,38 @@
     }
 
     @Override
-    public void onDragStarted(int itemIndex) {
+    public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view) {
         setInDragging(true);
         popContactEntry(itemIndex);
     }
 
     @Override
-    public void onDragHovered(int itemIndex) {
+    public void onDragHovered(int itemIndex, int x, int y) {
         if (mInDragging &&
                 mDragEnteredEntryIndex != itemIndex &&
                 isIndexInBound(itemIndex) &&
-                itemIndex < PIN_LIMIT) {
+                itemIndex < PIN_LIMIT &&
+                itemIndex >= 0) {
             markDropArea(itemIndex);
         }
     }
 
     @Override
-    public void onDragFinished() {
+    public void onDragFinished(int x, int y) {
         setInDragging(false);
-        handleDrop();
+        // A contact has been dragged to the RemoveView in order to be unstarred,  so simply wait
+        // for the new contact cursor which will cause the UI to be refreshed without the unstarred
+        // contact.
+        if (!mAwaitingRemove) {
+            handleDrop();
+        }
+    }
+
+    @Override
+    public void onDroppedOnRemove() {
+        if (mDraggedEntry != null) {
+            unstarAndUnpinContact(mDraggedEntry.lookupUri);
+            mAwaitingRemove = true;
+        }
     }
 }
diff --git a/src/com/android/dialer/list/RemoveView.java b/src/com/android/dialer/list/RemoveView.java
new file mode 100644
index 0000000..16942fe
--- /dev/null
+++ b/src/com/android/dialer/list/RemoveView.java
@@ -0,0 +1,90 @@
+package com.android.dialer.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.dialer.R;
+
+public class RemoveView extends LinearLayout {
+
+    DragDropController mDragDropController;
+    TextView mRemoveText;
+    ImageView mRemoveIcon;
+    int mUnhighlightedColor;
+    int mHighlightedColor;
+    Drawable mRemoveDrawable;
+    Drawable mRemoveHighlightedDrawable;
+
+    public RemoveView(Context context) {
+      super(context);
+    }
+
+    public RemoveView(Context context, AttributeSet attrs) {
+        this(context, attrs, -1);
+    }
+
+    public RemoveView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mRemoveText = (TextView) findViewById(R.id.remove_view_text);
+        mRemoveIcon = (ImageView) findViewById(R.id.remove_view_icon);
+        final Resources r = getResources();
+        mUnhighlightedColor = r.getColor(R.color.remove_text_color);
+        mHighlightedColor = r.getColor(R.color.remove_highlighted_text_color);
+        mRemoveDrawable = r.getDrawable(R.drawable.ic_remove);
+        mRemoveHighlightedDrawable = r.getDrawable(R.drawable.ic_remove_highlight);
+    }
+
+    public void setDragDropController(DragDropController controller) {
+        mDragDropController = controller;
+    }
+
+    @Override
+    public boolean dispatchDragEvent(DragEvent event) {
+      final int action = event.getAction();
+      switch (action) {
+        case DragEvent.ACTION_DRAG_ENTERED:
+            setAppearanceHighlighted();
+            break;
+        case DragEvent.ACTION_DRAG_EXITED:
+            setAppearanceNormal();
+            break;
+        case DragEvent.ACTION_DRAG_LOCATION:
+            if (mDragDropController != null) {
+                mDragDropController.handleDragHovered((int) event.getX(),
+                        // the true y-coordinate of the event with respect to the listview is
+                        // offset by the height of the remove view
+                        (int) event.getY() - getHeight(), null);
+            }
+            break;
+        case DragEvent.ACTION_DROP:
+            if (mDragDropController != null) {
+                mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(), true);
+            }
+            setAppearanceNormal();
+            break;
+      }
+      return true;
+    }
+
+    private void setAppearanceNormal() {
+        mRemoveText.setTextColor(mUnhighlightedColor);
+        mRemoveIcon.setImageDrawable(mRemoveDrawable);
+        invalidate();
+    }
+
+    private void setAppearanceHighlighted() {
+        mRemoveText.setTextColor(mHighlightedColor);
+        mRemoveIcon.setImageDrawable(mRemoveHighlightedDrawable);
+        invalidate();
+    }
+}
diff --git a/src/com/android/dialer/service/CachedNumberLookupService.java b/src/com/android/dialer/service/CachedNumberLookupService.java
index 5745c9d..73fd895 100644
--- a/src/com/android/dialer/service/CachedNumberLookupService.java
+++ b/src/com/android/dialer/service/CachedNumberLookupService.java
@@ -33,6 +33,8 @@
 
     public boolean isCacheUri(String uri);
 
+    public boolean isBusiness(int sourceType);
+
     public boolean addPhoto(Context context, String number, byte[] photo);
 
     /**
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java b/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java
index 1dbae65..826dec0 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java
@@ -74,7 +74,9 @@
     };
 
     private VoicemailPlaybackPresenter mPresenter;
-    private ScheduledExecutorService mScheduledExecutorService;
+    private static int mMediaPlayerRefCount = 0;
+    private static MediaPlayerProxy mMediaPlayerInstance;
+    private static ScheduledExecutorService mScheduledExecutorService;
     private View mPlaybackLayout;
 
     @Override
@@ -87,7 +89,6 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
-        mScheduledExecutorService = createScheduledExecutorService();
         Bundle arguments = getArguments();
         Preconditions.checkNotNull(arguments, "fragment must be started with arguments");
         Uri voicemailUri = arguments.getParcelable(EXTRA_VOICEMAIL_URI);
@@ -99,8 +100,8 @@
                 powerManager.newWakeLock(
                         PowerManager.SCREEN_DIM_WAKE_LOCK, getClass().getSimpleName());
         mPresenter = new VoicemailPlaybackPresenter(createPlaybackViewImpl(),
-                createMediaPlayer(mScheduledExecutorService), voicemailUri,
-                mScheduledExecutorService, startPlayback,
+                getMediaPlayerInstance(), voicemailUri,
+                getScheduledExecutorServiceInstance(), startPlayback,
                 AsyncTaskExecutors.createAsyncTaskExecutor(), wakeLock);
         mPresenter.onCreate(savedInstanceState);
     }
@@ -113,8 +114,8 @@
 
     @Override
     public void onDestroy() {
+        shutdownMediaPlayer();
         mPresenter.onDestroy();
-        mScheduledExecutorService.shutdown();
         super.onDestroy();
     }
 
@@ -129,12 +130,36 @@
                 mPlaybackLayout);
     }
 
-    private MediaPlayerProxy createMediaPlayer(ExecutorService executorService) {
-        return VariableSpeed.createVariableSpeed(executorService);
+    private static synchronized MediaPlayerProxy getMediaPlayerInstance() {
+        ++mMediaPlayerRefCount;
+        if (mMediaPlayerInstance == null) {
+            mMediaPlayerInstance = VariableSpeed.createVariableSpeed(
+                    getScheduledExecutorServiceInstance());
+        }
+        return mMediaPlayerInstance;
     }
 
-    private ScheduledExecutorService createScheduledExecutorService() {
-        return Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);
+    private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
+        if (mScheduledExecutorService == null) {
+            mScheduledExecutorService = Executors.newScheduledThreadPool(
+                    NUMBER_OF_THREADS_IN_POOL);
+        }
+        return mScheduledExecutorService;
+    }
+
+    private static synchronized void shutdownMediaPlayer() {
+        --mMediaPlayerRefCount;
+        if (mMediaPlayerRefCount > 0) {
+            return;
+        }
+        if (mScheduledExecutorService != null) {
+            mScheduledExecutorService.shutdown();
+            mScheduledExecutorService = null;
+        }
+        if (mMediaPlayerInstance != null) {
+            mMediaPlayerInstance.release();
+            mMediaPlayerInstance = null;
+        }
     }
 
     /**
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index ef29f13..085ef66 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -16,8 +16,6 @@
 
 package com.android.dialer.voicemail;
 
-import static android.util.MathUtils.constrain;
-
 import android.content.Context;
 import android.database.ContentObserver;
 import android.media.AudioManager;
@@ -37,6 +35,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -313,6 +312,7 @@
                             mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
                             mPlayer.setAudioStreamType(PLAYBACK_STREAM);
                             mPlayer.prepare();
+                            mDuration.set(mPlayer.getDuration());
                             return null;
                         } catch (Exception e) {
                             return e;
@@ -346,7 +346,7 @@
         mView.setSpeakerPhoneOn(mView.isSpeakerPhoneOn());
         mView.setRateDecreaseButtonListener(createRateDecreaseListener());
         mView.setRateIncreaseButtonListener(createRateIncreaseListener());
-        mView.setClipPosition(0, mPlayer.getDuration());
+        mView.setClipPosition(0, mDuration.get());
         mView.playbackStopped();
         // Always disable on stop.
         mView.disableProximitySensor();
@@ -365,6 +365,10 @@
     }
 
     public void onDestroy() {
+        if (mPrepareTask != null) {
+            mPrepareTask.cancel(false);
+            mPrepareTask = null;
+        }
         mPlayer.release();
         if (mFetchResultHandler != null) {
             mFetchResultHandler.destroy();
@@ -432,49 +436,67 @@
         }
     }
 
+    private class AsyncPrepareTask extends AsyncTask<Void, Void, Exception> {
+        private int mClipPositionInMillis;
+
+        AsyncPrepareTask(int clipPositionInMillis) {
+            mClipPositionInMillis = clipPositionInMillis;
+        }
+
+        @Override
+        public Exception doInBackground(Void... params) {
+            try {
+                if (!mPlayer.isReadyToPlay()) {
+                    mPlayer.reset();
+                    mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
+                    mPlayer.setAudioStreamType(PLAYBACK_STREAM);
+                    mPlayer.prepare();
+                }
+                return null;
+            } catch (Exception e) {
+                return e;
+            }
+        }
+
+        @Override
+        public void onPostExecute(Exception exception) {
+            mPrepareTask = null;
+            if (exception == null) {
+                final int duration = mPlayer.getDuration();
+                mDuration.set(duration);
+                int startPosition =
+                    constrain(mClipPositionInMillis, 0, duration);
+                mPlayer.seekTo(startPosition);
+                mView.setClipPosition(startPosition, duration);
+                try {
+                    // Can throw RejectedExecutionException
+                    mPlayer.start();
+                    mView.playbackStarted();
+                    if (!mWakeLock.isHeld()) {
+                        mWakeLock.acquire();
+                    }
+                    // Only enable if we are not currently using the speaker phone.
+                    if (!mView.isSpeakerPhoneOn()) {
+                        mView.enableProximitySensor();
+                    }
+                    // Can throw RejectedExecutionException
+                    mPositionUpdater.startUpdating(startPosition, duration);
+                } catch (RejectedExecutionException e) {
+                    handleError(e);
+                }
+            } else {
+                handleError(exception);
+            }
+        }
+    }
+
     private void resetPrepareStartPlaying(final int clipPositionInMillis) {
         if (mPrepareTask != null) {
             mPrepareTask.cancel(false);
+            mPrepareTask = null;
         }
         mPrepareTask = mAsyncTaskExecutor.submit(Tasks.RESET_PREPARE_START_MEDIA_PLAYER,
-                new AsyncTask<Void, Void, Exception>() {
-                    @Override
-                    public Exception doInBackground(Void... params) {
-                        try {
-                            mPlayer.reset();
-                            mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
-                            mPlayer.setAudioStreamType(PLAYBACK_STREAM);
-                            mPlayer.prepare();
-                            return null;
-                        } catch (Exception e) {
-                            return e;
-                        }
-                    }
-
-                    @Override
-                    public void onPostExecute(Exception exception) {
-                        mPrepareTask = null;
-                        if (exception == null) {
-                            mDuration.set(mPlayer.getDuration());
-                            int startPosition =
-                                    constrain(clipPositionInMillis, 0, mDuration.get());
-                            mView.setClipPosition(startPosition, mDuration.get());
-                            mPlayer.seekTo(startPosition);
-                            mPlayer.start();
-                            mView.playbackStarted();
-                            if (!mWakeLock.isHeld()) {
-                                mWakeLock.acquire();
-                            }
-                            // Only enable if we are not currently using the speaker phone.
-                            if (!mView.isSpeakerPhoneOn()) {
-                                mView.enableProximitySensor();
-                            }
-                            mPositionUpdater.startUpdating(startPosition, mDuration.get());
-                        } else {
-                            handleError(exception);
-                        }
-                    }
-                });
+                new AsyncPrepareTask(clipPositionInMillis));
     }
 
     private void handleError(Exception e) {
@@ -600,6 +622,7 @@
             synchronized (mLock) {
                 if (mScheduledFuture != null) {
                     mScheduledFuture.cancel(false);
+                    mScheduledFuture = null;
                 }
                 mScheduledFuture = mExecutorService.scheduleAtFixedRate(this, 0, mPeriodMillis,
                         TimeUnit.MILLISECONDS);
@@ -622,9 +645,14 @@
         }
         if (mPrepareTask != null) {
             mPrepareTask.cancel(false);
+            mPrepareTask = null;
         }
         if (mWakeLock.isHeld()) {
             mWakeLock.release();
         }
     }
+
+    private static int constrain(int amount, int low, int high) {
+        return amount < low ? low : (amount > high ? high : amount);
+    }
 }
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index c43dffc..be91e33 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -34,10 +34,20 @@
         return null;
     }
 
+    /**
+     * Create a new instance of the call log adapter.
+     * @param context The context to use.
+     * @param callFetcher Instance of call fetcher to use.
+     * @param contactInfoHelper Instance of contact info helper class to use.
+     * @param hideSecondaryAction If true, secondary action will be hidden (ie call details
+     *                            or play voicemail).
+     * @param isCallLog Is this call log adapter being used on the call log?
+     * @return Instance of CallLogAdapter.
+     */
     public static CallLogAdapter newCallLogAdapter(Context context, CallFetcher callFetcher,
-            ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction,
+            ContactInfoHelper contactInfoHelper, boolean hideSecondaryAction,
             boolean isCallLog) {
-        return new CallLogAdapter(context, callFetcher, contactInfoHelper, useCallAsPrimaryAction,
+        return new CallLogAdapter(context, callFetcher, contactInfoHelper, hideSecondaryAction,
                 isCallLog);
     }
 }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 3a714e3..94aa3aa 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
 
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
     <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
     <uses-permission android:name="android.permission.READ_SYNC_STATS" />
diff --git a/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java b/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java
index 6a9817f..9b7d9de 100644
--- a/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java
@@ -51,6 +51,8 @@
     private static final String TEST_COUNTRY_ISO = "US";
     /** The geocoded location used in the tests. */
     private static final String TEST_GEOCODE = "United States";
+    /** Empty geocode label */
+    private static final String EMPTY_GEOCODE = "";
 
     /** The object under test. */
     private PhoneCallDetailsHelper mHelper;
@@ -183,18 +185,18 @@
     public void testSetPhoneCallDetails_NoGeocode() {
         setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", null);
         assertNameEquals("1-412-555-5555");  // The phone number is shown as the name.
-        assertLabelEquals("-"); // The empty geocode is shown as the label.
+        assertLabelEquals(EMPTY_GEOCODE); // The empty geocode is shown as the label.
     }
 
     public void testSetPhoneCallDetails_EmptyGeocode() {
         setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", "");
         assertNameEquals("1-412-555-5555");  // The phone number is shown as the name.
-        assertLabelEquals("-"); // The empty geocode is shown as the label.
+        assertLabelEquals(EMPTY_GEOCODE); // The empty geocode is shown as the label.
     }
 
     public void testSetPhoneCallDetails_NoGeocodeForVoicemail() {
         setPhoneCallDetailsWithNumberAndGeocode(TEST_VOICEMAIL_NUMBER, "", "United States");
-        assertLabelEquals("-"); // The empty geocode is shown as the label.
+        assertLabelEquals(EMPTY_GEOCODE); // The empty geocode is shown as the label.
     }
 
     public void testSetPhoneCallDetails_Highlighted() {
@@ -333,6 +335,6 @@
                 new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
                         TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO, TEST_GEOCODE,
                         new int[]{ Calls.INCOMING_TYPE }, TEST_DATE, TEST_DURATION,
-                        name, 0, "", null, null));
+                        name, 0, "", null, null, 0));
     }
 }
diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
index 7ceec8f..49d32e5 100644
--- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
@@ -70,6 +70,8 @@
     /** The formatted version of {@link #TEST_NUMBER}. */
     private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000";
 
+    private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel";
+
     /** The activity in which we are hosting the fragment. */
     private FragmentTestActivity mActivity;
     private CallLogFragment mFragment;
@@ -115,7 +117,7 @@
         FragmentManager fragmentManager = mActivity.getFragmentManager();
         FragmentTransaction transaction = fragmentManager.beginTransaction();
         transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment);
-        transaction.commit();
+        transaction.commitAllowingStateLoss();
         // Wait for the fragment to be loaded.
         getInstrumentation().waitForIdleSync();
 
@@ -218,8 +220,10 @@
     @MediumTest
     public void testBindView_WithCachedName() {
         mCursor.moveToFirst();
+        // provide a default custom label instead of an empty string, which corresponds to
+        // {@value com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL}
         insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
-                "John Doe", Phone.TYPE_HOME, "");
+                "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
@@ -232,20 +236,22 @@
     public void testBindView_UriNumber() {
         mCursor.moveToFirst();
         insertWithCachedValues("sip:johndoe@gmail.com", NOW, 0, Calls.INCOMING_TYPE,
-                "John Doe", Phone.TYPE_HOME, "");
+                "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
         assertNameIs(views, "John Doe");
-        assertLabel(views, "sip:johndoe@gmail.com", null);
+        assertLabel(views, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com");
     }
 
     @MediumTest
     public void testBindView_HomeLabel() {
         mCursor.moveToFirst();
+        // provide a default custom label instead of an empty string, which corresponds to
+        // {@value com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL}
         insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
-                "John Doe", Phone.TYPE_HOME, "");
+                "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
@@ -257,8 +263,10 @@
     @MediumTest
     public void testBindView_WorkLabel() {
         mCursor.moveToFirst();
+        // provide a default custom label instead of an empty string, which corresponds to
+        // {@link com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL}
         insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
-                "John Doe", Phone.TYPE_WORK, "");
+                "John Doe", Phone.TYPE_WORK, TEST_DEFAULT_CUSTOM_LABEL);
         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
@@ -312,7 +320,12 @@
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
-        IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
+
+        // The primaryActionView tag is set in the
+        // {@link com.android.dialer.calllog.CallLogAdapter#bindView} method.  If it is possible
+        // to place a call to the phone number, a call intent will have been created for the
+        // primaryActionView.
+        IntentProvider intentProvider = (IntentProvider) views.primaryActionView.getTag();
         Intent intent = intentProvider.getIntent(mActivity);
         // Starts a call.
         assertEquals(Intent.ACTION_CALL_PRIVILEGED, intent.getAction());
@@ -328,7 +341,7 @@
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
-        IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
+        IntentProvider intentProvider = (IntentProvider) views.secondaryActionButtonView.getTag();
         Intent intent = intentProvider.getIntent(mActivity);
         // Starts the call detail activity.
         assertEquals(new ComponentName(mActivity, CallDetailActivity.class),
@@ -354,8 +367,9 @@
     // HELPERS to check conditions on the DB/views
     //
     /**
-     * Go over all the views in the list and check that the Call
-     * icon's visibility matches the nature of the number.
+     * Go over the views in the list and check to ensure that
+     * callable numbers have an associated call intent, where numbers
+     * which are not callable have a null intent.
      */
     private void checkCallStatus() {
         for (int i = 0; i < mList.length; i++) {
@@ -366,9 +380,17 @@
             int presentation = getPhoneNumberPresentationForListEntry(i);
             if (presentation == Calls.PRESENTATION_RESTRICTED ||
                     presentation == Calls.PRESENTATION_UNKNOWN) {
-                assertFalse(View.VISIBLE == mItem.secondaryActionView.getVisibility());
+                //If number is not callable, the primary action view should have a null tag.
+                assertNull(mItem.primaryActionView.getTag());
             } else {
-                assertEquals(View.VISIBLE, mItem.secondaryActionView.getVisibility());
+                //If the number is callable, the primary action view should have a non-null tag.
+                assertNotNull(mItem.primaryActionView.getTag());
+
+                IntentProvider intentProvider = (IntentProvider)mItem.primaryActionView.getTag();
+                Intent callIntent = intentProvider.getIntent(mActivity);
+
+                //The intent should be to make the call
+                assertEquals(Intent.ACTION_CALL_PRIVILEGED, callIntent.getAction());
             }
         }
     }
@@ -607,7 +629,9 @@
                 privateOrUnknownOrVm[2] = true;
             } else {
                 int inout = mRnd.nextBoolean() ? Calls.OUTGOING_TYPE :  Calls.INCOMING_TYPE;
-                String number = new Formatter().format("1800123%04d", i).toString();
+                final Formatter formatter = new Formatter();
+                String number = formatter.format("1800123%04d", i).toString();
+                formatter.close();
                 insert(number, Calls.PRESENTATION_ALLOWED, NOW, RAND_DURATION, inout);
             }
         }
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
index bb940ed..7e4736e 100644
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
@@ -24,6 +24,7 @@
 
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.PhoneCallDetailsHelper;
+import com.android.dialer.R;
 
 /**
  * Unit tests for {@link CallLogListItemHelper}.
@@ -49,19 +50,24 @@
 
     /** The views used in the tests. */
     private CallLogListItemViews mViews;
-    private PhoneNumberHelper mPhoneNumberHelper;
+    private PhoneNumberDisplayHelper mPhoneNumberHelper;
+    private PhoneNumberDisplayHelper mPhoneNumberDisplayHelper;
+
+    private Resources mResources;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         Context context = getContext();
-        Resources resources = context.getResources();
-        CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
+        mResources = context.getResources();
+        CallTypeHelper callTypeHelper = new CallTypeHelper(mResources);
         final TestPhoneNumberUtilsWrapper phoneUtils = new TestPhoneNumberUtilsWrapper(
                 TEST_VOICEMAIL_NUMBER);
         PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
-                resources, callTypeHelper, phoneUtils);
-        mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberHelper, resources);
+                mResources, callTypeHelper, phoneUtils);
+        mPhoneNumberDisplayHelper = new PhoneNumberDisplayHelper(mResources);
+        mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberDisplayHelper,
+                mResources);
         mViews = CallLogListItemViews.createForTest(context);
     }
 
@@ -75,92 +81,286 @@
     public void testSetPhoneCallDetails() {
         setPhoneCallDetailsWithNumber("12125551234", Calls.PRESENTATION_ALLOWED,
                 "1-212-555-1234");
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites() {
         setPhoneCallDetailsWithNumberInFavorites("12125551234", Calls.PRESENTATION_ALLOWED,
                 "1-212-555-1234");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_Unknown() {
         setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_UNKNOWN, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_Unknown() {
         setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_UNKNOWN, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_Private() {
         setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_RESTRICTED, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_Private() {
         setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_RESTRICTED, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_Payphone() {
         setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_PAYPHONE, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_Payphone() {
         setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_PAYPHONE, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_VoicemailNumber() {
         setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER,
                 Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_ReadVoicemail() {
         setPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_UnreadVoicemail() {
         setUnreadPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_VoicemailFromUnknown() {
         setPhoneCallDetailsWithNumberAndType("", Calls.PRESENTATION_UNKNOWN,
                 "", Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites_VoicemailNumber() {
         setPhoneCallDetailsWithNumberInFavorites(TEST_VOICEMAIL_NUMBER,
                 Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_ReadVoicemail() {
         setPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites_UnreadVoicemail() {
         setUnreadPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites_VoicemailFromUnknown() {
         setPhoneCallDetailsWithNumberAndTypeInFavorites("", Calls.PRESENTATION_UNKNOWN,
                 "", Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
-    /** Asserts that the whole call area is gone. */
-    private void assertNoCallButton() {
-        assertEquals(View.GONE, mViews.secondaryActionView.getVisibility());
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an answered unknown call is received.
+     */
+    public void testGetCallDescriptionID_UnknownAnswered() {
+        PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_unknown_answered_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an missed unknown call is received.
+     */
+    public void testGetCallDescriptionID_UnknownMissed() {
+        PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_unknown_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an missed unknown call is received and a voicemail was left.
+     */
+    public void testGetCallDescriptionID_UnknownVoicemail() {
+        PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_unknown_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an answered call from a known caller is received.
+     */
+    public void testGetCallDescriptionID_KnownAnswered() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_return_answered_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where a missed call from a known caller is received.
+     */
+    public void testGetCallDescriptionID_KnownMissed() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_return_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where a missed call from a known caller is received and a voicemail was left.
+     */
+    public void testGetCallDescriptionID_KnownVoicemail() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_return_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an outgoing call is made to a known number and there is a history of
+     * only a single call for this caller.
+     */
+    public void testGetCallDescriptionID_OutgoingSingle() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_call_last,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an outgoing call is made to a known number and there is a history of
+     * many calls for this caller.
+     */
+    public void testGetCallDescriptionID_OutgoingMultiple() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_call_last_multiple,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For outgoing calls, we should NOT have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_NoVoicemailOutgoing() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For regular incoming calls, we should NOT have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_NoVoicemailIncoming() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For regular missed calls, we should NOT have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_NoVoicemailMissed() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.MISSED_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For voicemail calls, we should have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_Voicemail() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertTrue(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * Test that the "X calls" message is not present if there is only a single call.
+     */
+    public void testGetCallDescription_NumCallsSingle() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+
+        // Rather than hard coding the "X calls" string message, we'll generate it with an empty
+        // number of calls, and trim the resulting string.  This gets us just the word "calls",
+        // and ensures any trivial changes to that string resource won't unnecessarily break
+        // the unit test.
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_num_calls, "").trim()));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * Test that the "X calls" message is present if there are many calls.
+     */
+    public void testGetCallDescription_NumCallsMultiple() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertTrue(description.toString()
+                .contains(this.mResources.getString(R.string.description_num_calls, 2)));
+    }
+
+    /** Asserts that the primary action view does not have a call intent. */
+    private void assertNoCallIntent() {
+        Object intentProvider = (IntentProvider)mViews.primaryActionView.getTag();
+        // The intent provider should be null as there is no ability to make a call.
+        assertNull(intentProvider);
     }
 
     /** Sets the details of a phone call using the specified phone number. */
diff --git a/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java b/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java
index 0dbd914..7266d88 100644
--- a/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java
+++ b/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java
@@ -16,10 +16,8 @@
 
 package com.android.dialer.calllog;
 
-import android.content.res.Resources;
-
 /**
- * Modified version of {@link com.android.dialer.calllog.PhoneNumberHelper} to be used in tests
+ * Modified version of {@link com.android.dialer.calllog.PhoneNumberDisplayHelper} to be used in tests
  * that allows injecting the voicemail number.
  */
 public final class TestPhoneNumberUtilsWrapper extends PhoneNumberUtilsWrapper {
diff --git a/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java b/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
index a123e74..6f18fe6 100644
--- a/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
+++ b/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
@@ -34,7 +34,11 @@
 
     public void testCanAddDigit_InvalidCharacter() {
         // only handles wait/pause
-        assertFalse(DialpadFragment.canAddDigit("123", 1, 1, '5'));
+        try {
+            DialpadFragment.canAddDigit("123", 1, 1, '5');
+            fail("Calling canAddDigit with invalid character should throw an exception");
+        } catch (IllegalArgumentException e) {
+        }
     }
 
     public void testCanAddDigit_BadOrNoSelection() {
diff --git a/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
index f86675e..fbc64cd 100644
--- a/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
@@ -91,7 +91,7 @@
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
                 .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
-                        Phone.CONTENT_ITEM_TYPE);
+                        Phone.CONTENT_ITEM_TYPE, 13);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, ContactDisplayUtils.INTERACTION_SMS, null);
@@ -110,7 +110,7 @@
         Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, 1);
         expectQuery(dataUri, true /* isDataUri */ )
                 .returnRow(1, "987", 0, null, null, Phone.TYPE_HOME, null,
-                        Phone.CONTENT_ITEM_TYPE);
+                        Phone.CONTENT_ITEM_TYPE, 1);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, ContactDisplayUtils.INTERACTION_SMS, null);
@@ -128,10 +128,10 @@
     public void testSendSmsWhenThereIsPrimaryNumber() {
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
-                .returnRow(
-                        1, "123", 0, null, null, Phone.TYPE_HOME, null, Phone.CONTENT_ITEM_TYPE)
-                .returnRow(
-                        2, "456", 1, null, null, Phone.TYPE_HOME, null, Phone.CONTENT_ITEM_TYPE);
+                .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
+                        Phone.CONTENT_ITEM_TYPE, 13)
+                .returnRow(2, "456", 1, null, null, Phone.TYPE_HOME, null,
+                        Phone.CONTENT_ITEM_TYPE, 13);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, ContactDisplayUtils.INTERACTION_SMS, null);
@@ -170,9 +170,9 @@
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
                 .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
-                        Phone.CONTENT_ITEM_TYPE)
+                        Phone.CONTENT_ITEM_TYPE, 13)
                 .returnRow(2, "123", 0, null, null, Phone.TYPE_WORK, null,
-                        Phone.CONTENT_ITEM_TYPE);
+                        Phone.CONTENT_ITEM_TYPE, 13);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, ContactDisplayUtils.INTERACTION_CALL, null);
@@ -191,7 +191,7 @@
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
                 .returnRow(1, "example@example.com", 0, null, null, Phone.TYPE_HOME, null,
-                        SipAddress.CONTENT_ITEM_TYPE);
+                        SipAddress.CONTENT_ITEM_TYPE, 13);
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, ContactDisplayUtils.INTERACTION_CALL, null);
 
@@ -209,9 +209,9 @@
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
                 .returnRow(1, "123", 0, "account", null, Phone.TYPE_HOME, "label",
-                        Phone.CONTENT_ITEM_TYPE)
+                        Phone.CONTENT_ITEM_TYPE, 13)
                 .returnRow(2, "456", 0, null, null, Phone.TYPE_WORK, null,
-                        Phone.CONTENT_ITEM_TYPE);
+                        Phone.CONTENT_ITEM_TYPE, 13);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, ContactDisplayUtils.INTERACTION_CALL, null);
@@ -252,7 +252,8 @@
                         RawContacts.DATA_SET,
                         Phone.TYPE,
                         Phone.LABEL,
-                        Phone.MIMETYPE)
+                        Phone.MIMETYPE,
+                        Phone.CONTACT_ID)
                 .withSelection("mimetype IN ('vnd.android.cursor.item/phone_v2',"
                         + " 'vnd.android.cursor.item/sip_address') AND data1 NOT NULL");
     }
diff --git a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
index 611b3f1..7a2076d 100644
--- a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
+++ b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
@@ -1,15 +1,26 @@
 package com.android.dialer.list;
 
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.ContactsContract.PinnedPositions;
 import android.test.AndroidTestCase;
 
+import com.android.contacts.common.ContactTileLoaderFactory;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.dialer.list.PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener;
+
+import java.util.ArrayList;
+
 public class PhoneFavoritesTileAdapterTest extends AndroidTestCase {
     private PhoneFavoritesTileAdapter mAdapter;
+    private static final OnDataSetChangedForAnimationListener
+            sOnDataSetChangedForAnimationListener = new OnDataSetChangedForAnimationListener() {
+                @Override
+                public void onDataSetChangedForAnimation(long... idsInPlace) {}
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mAdapter = new PhoneFavoritesTileAdapter(getContext(), null, null, 3, 1);
-    }
+                @Override
+                public void cacheOffsetsForDatasetChange() {}
+            };
 
     /**
      * TODO: Add tests
@@ -42,5 +53,124 @@
 
     }
 
+    public void testGetRowIndex_NoRowLimit() {
+        mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+        assertEquals(0, mAdapter.getRowCount(0));
+        assertEquals(1, mAdapter.getRowCount(1));
+        assertEquals(1, mAdapter.getRowCount(2));
+        assertEquals(2, mAdapter.getRowCount(4));
+        assertEquals(4, mAdapter.getRowCount(7));
+        assertEquals(100, mAdapter.getRowCount(199));
 
+        mAdapter = getAdapterForTest(5, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+        assertEquals(0, mAdapter.getRowCount(0));
+        assertEquals(1, mAdapter.getRowCount(1));
+        assertEquals(1, mAdapter.getRowCount(3));
+        assertEquals(1, mAdapter.getRowCount(5));
+        assertEquals(2, mAdapter.getRowCount(7));
+        assertEquals(2, mAdapter.getRowCount(10));
+        assertEquals(40, mAdapter.getRowCount(199));
+    }
+
+    public void testGetItemId_NoRowLimit() {
+        mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+        assertEquals(0, mAdapter.getItemId(0));
+        assertEquals(1, mAdapter.getItemId(1));
+        assertEquals(5, mAdapter.getItemId(5));
+        assertEquals(10, mAdapter.getItemId(10));
+    }
+
+    public void testGetAdjustedItemId_NoRowLimit() {
+        mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+        assertEquals(0, mAdapter.getAdjustedItemId(0));
+        assertEquals(1, mAdapter.getAdjustedItemId(1));
+        assertEquals(5, mAdapter.getAdjustedItemId(5));
+        assertEquals(10, mAdapter.getAdjustedItemId(10));
+    }
+
+    public void testGetItem_NoRowLimit() {
+        mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+        mAdapter.setContactCursor(getCursorForTest(5, 5));
+
+        final ArrayList<ContactEntry> row1 = new ArrayList<ContactEntry> ();
+        row1.add(getTestContactEntry(0, true));
+        row1.add(getTestContactEntry(1, true));
+        assertContactEntryRowsEqual(row1, mAdapter.getItem(0));
+
+        final ArrayList<ContactEntry> row3 = new ArrayList<ContactEntry> ();
+        row3.add(getTestContactEntry(4, true));
+        row3.add(getTestContactEntry(5, false));
+        assertContactEntryRowsEqual(row3, mAdapter.getItem(2));
+
+        final ArrayList<ContactEntry> row5 = new ArrayList<ContactEntry> ();
+        row5.add(getTestContactEntry(8, false));
+        row5.add(getTestContactEntry(9, false));
+        assertContactEntryRowsEqual(row5, mAdapter.getItem(4));
+    }
+
+    /**
+     * Ensures that PhoneFavoritesTileAdapter returns true for hasStableIds. This is needed for
+     * animation purposes.
+     */
+    public void testHasStableIds() {
+        mAdapter = new PhoneFavoritesTileAdapter(getContext(), null, null, 2, 2);
+        assertTrue(mAdapter.hasStableIds());
+    }
+
+    private PhoneFavoritesTileAdapter getAdapterForTest(int numCols, int numRows) {
+        return new PhoneFavoritesTileAdapter(getContext(), null,
+                sOnDataSetChangedForAnimationListener, numCols, numRows);
+    }
+
+    /**
+     * Returns a cursor containing starred and frequent contacts for test purposes.
+     *
+     * @param numStarred Number of starred contacts in the cursor. Cannot be a negative number.
+     * @param numFrequents Number of frequent contacts in the cursor. Cannot be a negative number.
+     * @return Cursor containing the required number of rows, each representing one ContactEntry
+     */
+    private Cursor getCursorForTest(int numStarred, int numFrequents) {
+        assertTrue(numStarred >= 0);
+        assertTrue(numFrequents >= 0);
+        final MatrixCursor c = new MatrixCursor(ContactTileLoaderFactory.COLUMNS_PHONE_ONLY);
+        int countId = 0;
+
+        // Add starred contact entries. These entries have the starred field set to 1 (true).
+        // 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});
+            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});
+            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) {
+        ContactEntry contactEntry = new ContactEntry();
+        contactEntry.id = id;
+        contactEntry.isFavorite = isFavorite;
+        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);
+        }
+    }
 }