Merge "Fix contact editor account header"
diff --git a/res/color/list_primary_text_color.xml b/res/color/list_primary_text_color.xml
new file mode 100644
index 0000000..7c185fc
--- /dev/null
+++ b/res/color/list_primary_text_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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
+
+          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_activated="true" android:color="@color/primary_text_color_activated"/>
+    <item android:color="@color/primary_text_color" /> <!-- not selected -->
+
+</selector>
diff --git a/res/color/list_secondary_text_color.xml b/res/color/list_secondary_text_color.xml
new file mode 100644
index 0000000..edf8678
--- /dev/null
+++ b/res/color/list_secondary_text_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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
+
+          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_activated="true" android:color="@color/secondary_text_color_activated" />
+    <item android:color="@color/secondary_text_color" /> <!-- not selected -->
+
+</selector>
diff --git a/res/drawable-hdpi/ab_solid_custom_blue_inverse_holo.9.png b/res/drawable-hdpi/ab_solid_custom_blue_inverse_holo.9.png
index 80fb400..956d61b 100644
--- a/res/drawable-hdpi/ab_solid_custom_blue_inverse_holo.9.png
+++ b/res/drawable-hdpi/ab_solid_custom_blue_inverse_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/account_spinner_icon.png b/res/drawable-hdpi/account_spinner_icon.png
index 3e82e51..9566386 100644
--- a/res/drawable-hdpi/account_spinner_icon.png
+++ b/res/drawable-hdpi/account_spinner_icon.png
Binary files differ
diff --git a/res/drawable-hdpi/action_bar_item_pressed_holo_light.9.png b/res/drawable-hdpi/action_bar_item_pressed_holo_light.9.png
new file mode 100644
index 0000000..cf5ff8e
--- /dev/null
+++ b/res/drawable-hdpi/action_bar_item_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_activated_holo.9.png b/res/drawable-hdpi/list_activated_holo.9.png
index 046b24a..4ea7afa 100644
--- a/res/drawable-hdpi/list_activated_holo.9.png
+++ b/res/drawable-hdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_focused_holo.9.png b/res/drawable-hdpi/list_focused_holo.9.png
index e962ce8..516f5c7 100644
--- a/res/drawable-hdpi/list_focused_holo.9.png
+++ b/res/drawable-hdpi/list_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_pressed_holo_light.9.png b/res/drawable-hdpi/list_pressed_holo_light.9.png
index cf5ff8e..5654cd6 100644
--- a/res/drawable-hdpi/list_pressed_holo_light.9.png
+++ b/res/drawable-hdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_section_divider_holo_custom.9.png b/res/drawable-hdpi/list_section_divider_holo_custom.9.png
index 6c0d251..1e3e778 100644
--- a/res/drawable-hdpi/list_section_divider_holo_custom.9.png
+++ b/res/drawable-hdpi/list_section_divider_holo_custom.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ab_solid_custom_blue_inverse_holo.9.png b/res/drawable-mdpi/ab_solid_custom_blue_inverse_holo.9.png
index f3c0a90..3e9f167 100644
--- a/res/drawable-mdpi/ab_solid_custom_blue_inverse_holo.9.png
+++ b/res/drawable-mdpi/ab_solid_custom_blue_inverse_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/account_spinner_icon.png b/res/drawable-mdpi/account_spinner_icon.png
index d9aaf02..e159d59 100644
--- a/res/drawable-mdpi/account_spinner_icon.png
+++ b/res/drawable-mdpi/account_spinner_icon.png
Binary files differ
diff --git a/res/drawable-mdpi/action_bar_item_pressed_holo_light.9.png b/res/drawable-mdpi/action_bar_item_pressed_holo_light.9.png
new file mode 100644
index 0000000..3a9686d
--- /dev/null
+++ b/res/drawable-mdpi/action_bar_item_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_activated_holo.9.png b/res/drawable-mdpi/list_activated_holo.9.png
index 1ff3373..3bf8e03 100644
--- a/res/drawable-mdpi/list_activated_holo.9.png
+++ b/res/drawable-mdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_focused_holo.9.png b/res/drawable-mdpi/list_focused_holo.9.png
index 6e041f3..7c0599e 100644
--- a/res/drawable-mdpi/list_focused_holo.9.png
+++ b/res/drawable-mdpi/list_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_pressed_holo_light.9.png b/res/drawable-mdpi/list_pressed_holo_light.9.png
index 3a9686d..6e77525 100644
--- a/res/drawable-mdpi/list_pressed_holo_light.9.png
+++ b/res/drawable-mdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_section_divider_holo_custom.9.png b/res/drawable-mdpi/list_section_divider_holo_custom.9.png
index cfc57ee..1d8fd09 100644
--- a/res/drawable-mdpi/list_section_divider_holo_custom.9.png
+++ b/res/drawable-mdpi/list_section_divider_holo_custom.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ab_solid_custom_blue_inverse_holo.9.png b/res/drawable-xhdpi/ab_solid_custom_blue_inverse_holo.9.png
index 3c97b20..31fea59 100644
--- a/res/drawable-xhdpi/ab_solid_custom_blue_inverse_holo.9.png
+++ b/res/drawable-xhdpi/ab_solid_custom_blue_inverse_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/account_spinner_icon.png b/res/drawable-xhdpi/account_spinner_icon.png
index 8b888cb..d3d3cac 100644
--- a/res/drawable-xhdpi/account_spinner_icon.png
+++ b/res/drawable-xhdpi/account_spinner_icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/action_bar_item_pressed_holo_light.9.png b/res/drawable-xhdpi/action_bar_item_pressed_holo_light.9.png
new file mode 100644
index 0000000..8ff9dca
--- /dev/null
+++ b/res/drawable-xhdpi/action_bar_item_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_activated_holo.9.png b/res/drawable-xhdpi/list_activated_holo.9.png
index 2eb7c7e..eda10e6 100644
--- a/res/drawable-xhdpi/list_activated_holo.9.png
+++ b/res/drawable-xhdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_focused_holo.9.png b/res/drawable-xhdpi/list_focused_holo.9.png
index 9c90c2b..690cb1e 100644
--- a/res/drawable-xhdpi/list_focused_holo.9.png
+++ b/res/drawable-xhdpi/list_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_pressed_holo_light.9.png b/res/drawable-xhdpi/list_pressed_holo_light.9.png
index 8ff9dca..e4b3393 100644
--- a/res/drawable-xhdpi/list_pressed_holo_light.9.png
+++ b/res/drawable-xhdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_section_divider_holo_custom.9.png b/res/drawable-xhdpi/list_section_divider_holo_custom.9.png
index 32e79e1..0bd8a0f 100644
--- a/res/drawable-xhdpi/list_section_divider_holo_custom.9.png
+++ b/res/drawable-xhdpi/list_section_divider_holo_custom.9.png
Binary files differ
diff --git a/res/drawable/action_bar_item_background.xml b/res/drawable/action_bar_item_background.xml
index 1fd4614..8dd8d9b 100644
--- a/res/drawable/action_bar_item_background.xml
+++ b/res/drawable/action_bar_item_background.xml
@@ -16,6 +16,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
           android:exitFadeDuration="@android:integer/config_mediumAnimTime">
-    <item android:state_pressed="true" android:drawable="@drawable/list_pressed_holo_light"/>
+    <item android:state_pressed="true" android:drawable="@drawable/action_bar_item_pressed_holo_light"/>
     <item android:drawable="@android:color/transparent" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/res/drawable/group_list_item_background.xml b/res/drawable/group_list_item_background.xml
index 0e2e604..345117f 100644
--- a/res/drawable/group_list_item_background.xml
+++ b/res/drawable/group_list_item_background.xml
@@ -17,6 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
         android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_activated="true" android:drawable="@drawable/list_activated_holo" />
-    <item android:state_pressed="true" android:drawable="@drawable/list_pressed_holo" />
+    <item android:state_pressed="true" android:drawable="@drawable/list_pressed_holo_light" />
     <item android:state_focused="true" android:drawable="@drawable/list_focused_holo" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/res/drawable/list_item_activated_background.xml b/res/drawable/list_item_activated_background.xml
index 6ea21d3..a58577e 100644
--- a/res/drawable/list_item_activated_background.xml
+++ b/res/drawable/list_item_activated_background.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_activated="true" android:drawable="@drawable/list_activated_holo" />
     <item android:drawable="@drawable/list_background_holo" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/res/layout-sw580dp/contact_detail_container.xml b/res/layout-sw580dp/contact_detail_container.xml
index 58a6eb3..cdb789f 100644
--- a/res/layout-sw580dp/contact_detail_container.xml
+++ b/res/layout-sw580dp/contact_detail_container.xml
@@ -17,8 +17,7 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="@color/background_primary">
+    android:orientation="vertical">
 
     <android.support.v4.view.ViewPager
         android:id="@+id/pager"
diff --git a/res/layout-sw580dp/group_detail_fragment.xml b/res/layout-sw580dp/group_detail_fragment.xml
index 90ca03a..438a987 100644
--- a/res/layout-sw580dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp/group_detail_fragment.xml
@@ -22,8 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingLeft="@dimen/group_detail_border_padding"
-    android:paddingRight="@dimen/group_detail_border_padding"
-    android:background="@drawable/panel_content">
+    android:paddingRight="@dimen/group_detail_border_padding">
 
     <TextView
         android:id="@+id/group_title"
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index e15c079..fecb848 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -58,6 +58,7 @@
             android:layout_width="0dip"
             android:layout_height="match_parent"
             android:layout_weight="1"
+            android:background="@color/background_primary"
             ex:clipMarginLeft="0dip"
             ex:clipMarginTop="3dip"
             ex:clipMarginRight="3dip"
@@ -72,7 +73,10 @@
                 android:id="@+id/contact_detail_container"
                 layout="@layout/contact_detail_container"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"/>
+                android:layout_height="match_parent"
+                android:layout_marginTop="16dip"
+                android:layout_marginLeft="16dip"
+                android:layout_marginRight="16dip"/>
 
             <!-- This invisible worker fragment loads the contact's details -->
             <fragment
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index df1ec62..c69f89f 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -196,4 +196,18 @@
             </LinearLayout>
         </FrameLayout>
     </RelativeLayout>
+
+    <!--
+         Used to hide the UI when playing a voicemail and the proximity sensor
+         is detecting something near the screen.
+      -->
+    <View
+        android:id="@+id/blank"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:background="#000000"
+        android:visibility="gone"
+    />
 </RelativeLayout>
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
index cc388f7..b213754 100644
--- a/res/layout/group_browse_list_item.xml
+++ b/res/layout/group_browse_list_item.xml
@@ -71,6 +71,7 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textColor="@color/list_primary_text_color"
                 android:ellipsize="end"
                 android:singleLine="true" />
 
@@ -79,7 +80,7 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="?android:attr/textColorTertiary"
+                android:textColor="@color/list_secondary_text_color"
                 android:ellipsize="end"
                 android:singleLine="true" />
 
diff --git a/res/layout/item_read_only_field.xml b/res/layout/item_read_only_field.xml
index 03778cc..5f8367f 100644
--- a/res/layout/item_read_only_field.xml
+++ b/res/layout/item_read_only_field.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical">
@@ -23,13 +24,29 @@
         android:id="@+id/kind_title_layout"
         layout="@layout/edit_kind_title" />
 
-    <TextView android:id="@+id/data"
-        android:layout_width="wrap_content"
-        android:layout_height="0px"
-        android:layout_weight="1"
-        android:layout_marginLeft="16dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        android:singleLine="true"/>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/editor_min_line_item_height"
+        android:orientation="horizontal">
+        <TextView
+            android:id="@+id/data"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="16dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="?android:attr/textColorPrimary"
+            android:singleLine="true"/>
+        <TextView
+            android:id="@+id/type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="8dip"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:singleLine="true"/>
+    </LinearLayout>
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/layout/stream_item_container.xml b/res/layout/stream_item_container.xml
index 308c5f5..de4f87d 100644
--- a/res/layout/stream_item_container.xml
+++ b/res/layout/stream_item_container.xml
@@ -21,7 +21,7 @@
     android:paddingLeft="@dimen/detail_update_section_side_padding"
     android:paddingRight="@dimen/detail_update_section_side_padding">
 
-    <TableLayout
+    <LinearLayout
         android:id="@+id/stream_item_content"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -31,6 +31,7 @@
         android:paddingBottom="@dimen/detail_update_section_item_vertical_padding"
         android:background="?android:attr/selectableItemBackground"
         android:layout_gravity="center_vertical"
+        android:orientation="vertical"
         />
 
     <View
diff --git a/res/layout/stream_item_row_image_and_text.xml b/res/layout/stream_item_row_image_and_text.xml
index 882a23b..c5699f4 100644
--- a/res/layout/stream_item_row_image_and_text.xml
+++ b/res/layout/stream_item_row_image_and_text.xml
@@ -14,68 +14,41 @@
      limitations under the License.
 -->
 
-<TableRow
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts">
+    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
 
-    <view
-        class="com.android.contacts.widget.ProportionalLayout"
-        android:layout_width="0dip"
+    <LinearLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginRight="@dimen/detail_update_section_between_items_horizontal_padding"
-        android:layout_weight="1"
-        ex:ratio="1"
-        ex:direction="widthToHeight">
-        <include
-            android:id="@+id/stream_item_first_image"
-            layout="@layout/stream_item_photo"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-    </view>
+        android:layout_marginBottom="8dip"
+        android:orientation="horizontal"
+        android:weightSum="2">
 
-    <view
-        class="com.android.contacts.widget.ProportionalLayout"
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        ex:ratio="1"
-        ex:direction="widthToHeight">
-        <LinearLayout
-            android:id="@+id/stream_item_second_text"
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+        <view
+            class="com.android.contacts.widget.ProportionalLayout"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginRight="@dimen/detail_update_section_between_items_horizontal_padding"
+            ex:ratio="1"
+            ex:direction="widthToHeight">
 
-            <TextView android:id="@+id/stream_item_html"
+            <include
+                android:id="@+id/stream_item_first_image"
+                layout="@layout/stream_item_photo"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:textSize="16sp"
-                android:textColor="?android:attr/textColorPrimary" />
+                android:layout_height="match_parent"/>
 
-            <LinearLayout
-                android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-                <TextView android:id="@+id/stream_item_attribution"
-                    android:layout_width="0dip"
-                    android:layout_height="wrap_content"
-                    android:textAppearance="?android:attr/textAppearanceSmall"
-                    android:textColor="?android:attr/textColorSecondary"
-                    android:maxLines="1"
-                    android:ellipsize="end"
-                    android:layout_weight="1" />
+        </view>
 
-                <TextView android:id="@+id/stream_item_comments"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_marginLeft="@dimen/detail_update_section_attribution_comments_padding"
-                    android:textAppearance="?android:attr/textAppearanceSmall"
-                    android:textColor="?android:attr/textColorSecondary"
-                    android:visibility="gone"
-                    android:maxLines="1" />
-            </LinearLayout>
+    </LinearLayout>
 
-        </LinearLayout>
-    </view>
+    <include
+        android:id="@+id/stream_item_second_text"
+        layout="@layout/stream_item_row_text_only"/>
 
-</TableRow>
+</LinearLayout>
diff --git a/res/layout/stream_item_row_two_images.xml b/res/layout/stream_item_row_two_images.xml
index f10b7d8..3a524bf 100644
--- a/res/layout/stream_item_row_two_images.xml
+++ b/res/layout/stream_item_row_two_images.xml
@@ -14,9 +14,11 @@
      limitations under the License.
 -->
 
-<TableRow
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts">
+    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
 
     <view
         class="com.android.contacts.widget.ProportionalLayout"
@@ -47,4 +49,4 @@
             android:layout_height="match_parent"/>
     </view>
 
-</TableRow>
+</LinearLayout>
diff --git a/res/menu-sw580dp-w720dp/actions.xml b/res/menu-sw580dp-w720dp/actions.xml
index 3aca102..2d21676 100644
--- a/res/menu-sw580dp-w720dp/actions.xml
+++ b/res/menu-sw580dp-w720dp/actions.xml
@@ -39,21 +39,20 @@
         android:title="@string/menu_contacts_filter" />
 
     <item
-        android:id="@+id/menu_settings"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:orderInCategory="2"
-        android:title="@string/menu_settings" />
-
-    <item
         android:id="@+id/menu_import_export"
         android:icon="@drawable/ic_menu_import_export_holo_light"
-        android:orderInCategory="3"
+        android:orderInCategory="2"
         android:title="@string/menu_import_export" />
 
     <item
         android:id="@+id/menu_accounts"
         android:icon="@drawable/ic_menu_accounts_holo_light"
-        android:orderInCategory="4"
+        android:orderInCategory="3"
         android:title="@string/menu_accounts" />
 
+    <item
+        android:id="@+id/menu_settings"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:orderInCategory="4"
+        android:title="@string/menu_settings" />
 </menu>
diff --git a/res/menu-sw580dp/actions.xml b/res/menu-sw580dp/actions.xml
index c2fd3e6..08fea42 100644
--- a/res/menu-sw580dp/actions.xml
+++ b/res/menu-sw580dp/actions.xml
@@ -42,21 +42,20 @@
         android:title="@string/menu_contacts_filter" />
 
     <item
-        android:id="@+id/menu_settings"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:orderInCategory="2"
-        android:title="@string/menu_settings" />
-
-    <item
         android:id="@+id/menu_import_export"
         android:icon="@drawable/ic_menu_import_export_holo_light"
-        android:orderInCategory="3"
+        android:orderInCategory="2"
         android:title="@string/menu_import_export" />
 
     <item
         android:id="@+id/menu_accounts"
         android:icon="@drawable/ic_menu_accounts_holo_light"
-        android:orderInCategory="4"
+        android:orderInCategory="3"
         android:title="@string/menu_accounts" />
 
+    <item
+        android:id="@+id/menu_settings"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:orderInCategory="4"
+        android:title="@string/menu_settings" />
 </menu>
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
index 2af9e92..852bfa9 100644
--- a/res/menu/actions.xml
+++ b/res/menu/actions.xml
@@ -38,11 +38,6 @@
         android:title="@string/menu_contacts_filter" />
 
     <item
-        android:id="@+id/menu_settings"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:title="@string/menu_settings" />
-
-    <item
         android:id="@+id/menu_import_export"
         android:icon="@drawable/ic_menu_import_export_holo_light"
         android:title="@string/menu_import_export" />
@@ -52,4 +47,8 @@
         android:icon="@drawable/ic_menu_accounts_holo_light"
         android:title="@string/menu_accounts" />
 
+    <item
+        android:id="@+id/menu_settings"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_settings" />
 </menu>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 0b8f1b7..5e8bd70 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -83,10 +83,16 @@
     <!-- Primary text color in the People app -->
     <color name="primary_text_color">#333333</color>
 
+    <!-- Activated primary text color in the People app -->
+    <color name="primary_text_color_activated">#FFFFFF</color>
+
     <!-- Secondary text color in the People app -->
     <color name="secondary_text_color">#777777</color>
     <color name="dialtacts_secondary_text_color">#888888</color>
 
+    <!-- Activated secondary text color in the People app -->
+    <color name="secondary_text_color_activated">#FFFFFF</color>
+
     <!-- Colors in the contact browser list -->
     <color name="contact_count_text_color">#AAAAAA</color>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 102ddbd..43c4107 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1490,7 +1490,7 @@
     <string name="activity_title_contacts_filter">Contacts to display</string>
 
     <!-- Menu item for the settings activity [CHAR LIMIT=64] -->
-    <string name="menu_settings">Display options</string>
+    <string name="menu_settings" msgid="377929915873428211">Settings</string>
 
     <!-- The preference section title for contact display options [CHAR LIMIT=128] -->
     <string name="preference_displayOptions">Display options</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dc209c5..0a5352a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -181,6 +181,8 @@
     </style>
 
     <style name="ContactPickerTheme" parent="@style/PeopleTheme">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
     </style>
     <style name="ContactPickerLayout" parent="ContactPickerTheme">
         <item name="android:layout_width">match_parent</item>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 6ab4b68..0fadbfb 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -69,9 +69,14 @@
  * This activity can be either started with the URI of a single call log entry, or with the
  * {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
  */
-public class CallDetailActivity extends Activity {
+public class CallDetailActivity extends Activity implements ProximitySensorAware {
     private static final String TAG = "CallDetail";
 
+    /** The time to wait before enabling the blank the screen due to the proximity sensor. */
+    private static final long PROXIMITY_BLANK_DELAY_MILLIS = 100;
+    /** The time to wait before disabling the blank the screen due to the proximity sensor. */
+    private static final long PROXIMITY_UNBLANK_DELAY_MILLIS = 500;
+
     /** The enumeration of {@link AsyncTask} objects used in this class. */
     public enum Tasks {
         MARK_VOICEMAIL_READ,
@@ -115,6 +120,60 @@
     /** Whether we should show "edit number before call" in the options menu. */
     private boolean mHasEditNumberBeforeCall;
 
+    private ProximitySensorManager mProximitySensorManager;
+    private final ProximitySensorListener mProximitySensorListener = new ProximitySensorListener();
+
+    /** Listener to changes in the proximity sensor state. */
+    private class ProximitySensorListener implements ProximitySensorManager.Listener {
+        /** Used to show a blank view and hide the action bar. */
+        private final Runnable mBlankRunnable = new Runnable() {
+            @Override
+            public void run() {
+                View blankView = findViewById(R.id.blank);
+                blankView.setVisibility(View.VISIBLE);
+                getActionBar().hide();
+            }
+        };
+        /** Used to remove the blank view and show the action bar. */
+        private final Runnable mUnblankRunnable = new Runnable() {
+            @Override
+            public void run() {
+                View blankView = findViewById(R.id.blank);
+                blankView.setVisibility(View.GONE);
+                getActionBar().show();
+            }
+        };
+
+        @Override
+        public synchronized void onNear() {
+            clearPendingRequests();
+            postDelayed(mBlankRunnable, PROXIMITY_BLANK_DELAY_MILLIS);
+        }
+
+        @Override
+        public synchronized void onFar() {
+            clearPendingRequests();
+            postDelayed(mUnblankRunnable, PROXIMITY_UNBLANK_DELAY_MILLIS);
+        }
+
+        /** Removed any delayed requests that may be pending. */
+        public synchronized void clearPendingRequests() {
+            View blankView = findViewById(R.id.blank);
+            blankView.removeCallbacks(mBlankRunnable);
+            blankView.removeCallbacks(mUnblankRunnable);
+        }
+
+        /** Post a {@link Runnable} with a delay on the main thread. */
+        private synchronized void postDelayed(Runnable runnable, long delayMillis) {
+            // Post these instead of executing immediately so that:
+            // - They are guaranteed to be executed on the main thread.
+            // - If the sensor values changes rapidly for some time, the UI will not be
+            //   updated immediately.
+            View blankView = findViewById(R.id.blank);
+            blankView.postDelayed(runnable, delayMillis);
+        }
+    }
+
     static final String[] CALL_LOG_PROJECTION = new String[] {
         CallLog.Calls.DATE,
         CallLog.Calls.DURATION,
@@ -189,6 +248,7 @@
         mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
         mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
         mContactPhotoManager = ContactPhotoManager.getInstance(this);
+        mProximitySensorManager = new ProximitySensorManager(this, mProximitySensorListener);
         configureActionBar();
         optionallyHandleVoicemail();
     }
@@ -770,4 +830,22 @@
         startActivity(intent);
         finish();
     }
+
+    @Override
+    protected void onPause() {
+        // Immediately stop the proximity sensor.
+        disableProximitySensor(false);
+        mProximitySensorListener.clearPendingRequests();
+        super.onPause();
+    }
+
+    @Override
+    public void enableProximitySensor() {
+        mProximitySensorManager.enable();
+    }
+
+    @Override
+    public void disableProximitySensor(boolean waitForFarState) {
+        mProximitySensorManager.disable(waitForFarState);
+    }
 }
diff --git a/src/com/android/contacts/ProximitySensorAware.java b/src/com/android/contacts/ProximitySensorAware.java
new file mode 100644
index 0000000..0fb233d
--- /dev/null
+++ b/src/com/android/contacts/ProximitySensorAware.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ *
+ *      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.
+ */
+
+package com.android.contacts;
+
+/**
+ * An object that is aware of the state of the proximity sensor.
+ */
+public interface ProximitySensorAware {
+    /** Start tracking the state of the proximity sensor. */
+    public void enableProximitySensor();
+
+    /**
+     * Stop tracking the state of the proximity sensor.
+     *
+     * @param waitForFarState if true and the sensor is currently in the near state, it will wait
+     *         until it is again in the far state before stopping to track its state.
+     */
+    public void disableProximitySensor(boolean waitForFarState);
+}
diff --git a/src/com/android/contacts/ProximitySensorManager.java b/src/com/android/contacts/ProximitySensorManager.java
new file mode 100644
index 0000000..69601bf
--- /dev/null
+++ b/src/com/android/contacts/ProximitySensorManager.java
@@ -0,0 +1,237 @@
+/*
+ * 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
+ *
+ *      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.
+ */
+
+package com.android.contacts;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Manages the proximity sensor and notifies a listener when enabled.
+ */
+public class ProximitySensorManager {
+    /**
+     * Listener of the state of the proximity sensor.
+     * <p>
+     * This interface abstracts two possible states for the proximity sensor, near and far.
+     * <p>
+     * The actual meaning of these states depends on the actual sensor.
+     */
+    public interface Listener {
+        /** Called when the proximity sensor transitions from the far to the near state. */
+        public void onNear();
+        /** Called when the proximity sensor transitions from the near to the far state. */
+        public void onFar();
+    }
+
+    public static enum State {
+        NEAR, FAR
+    }
+
+    private final ProximitySensorEventListener mProximitySensorListener;
+
+    /**
+     * The current state of the manager, i.e., whether it is currently tracking the state of the
+     * sensor.
+     */
+    private boolean mManagerEnabled;
+
+    /**
+     * The listener to the state of the sensor.
+     * <p>
+     * Contains most of the logic concerning tracking of the sensor.
+     * <p>
+     * After creating an instance of this object, one should call {@link #register()} and
+     * {@link #unregister()} to enable and disable the notifications.
+     * <p>
+     * Instead of calling unregister, one can call {@link #unregisterWhenFar()} to unregister the
+     * listener the next time the sensor reaches the {@link State#FAR} state if currently in the
+     * {@link State#NEAR} state.
+     */
+    private static class ProximitySensorEventListener implements SensorEventListener {
+        private static final float FAR_THRESHOLD = 5.0f;
+
+        private final SensorManager mSensorManager;
+        private final Sensor mProximitySensor;
+        private final float mMaxValue;
+        private final Listener mListener;
+
+        /**
+         * The last state of the sensor.
+         * <p>
+         * Before registering and after unregistering we are always in the {@link State#FAR} state.
+         */
+        @GuardedBy("this") private State mLastState;
+        /**
+         * If this flag is set to true, we are waiting to reach the {@link State#FAR} state and
+         * should notify the listener and unregister when that happens.
+         */
+        @GuardedBy("this") private boolean mWaitingForFarState;
+
+        public ProximitySensorEventListener(SensorManager sensorManager, Sensor proximitySensor,
+                Listener listener) {
+            mSensorManager = sensorManager;
+            mProximitySensor = proximitySensor;
+            mMaxValue = proximitySensor.getMaximumRange();
+            mListener = listener;
+            // Initialize at far state.
+            mLastState = State.FAR;
+            mWaitingForFarState = false;
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            // Make sure we have a valid value.
+            if (event.values == null) return;
+            if (event.values.length == 0) return;
+            float value = event.values[0];
+            // Convert the sensor into a NEAR/FAR state.
+            State state = getStateFromValue(value);
+            synchronized (this) {
+                // No change in state, do nothing.
+                if (state == mLastState) return;
+                // Keep track of the current state.
+                mLastState = state;
+                // If we are waiting to reach the far state and we are now in it, unregister.
+                if (mWaitingForFarState && mLastState == State.FAR) {
+                    unregisterWithoutNotification();
+                }
+            }
+            // Notify the listener of the state change.
+            switch (state) {
+                case NEAR:
+                    mListener.onNear();
+                    break;
+
+                case FAR:
+                    mListener.onFar();
+                    break;
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // Nothing to do here.
+        }
+
+        /** Returns the state of the sensor given its current value. */
+        private State getStateFromValue(float value) {
+            // Determine if the current value corresponds to the NEAR or FAR state.
+            // Take case of the case where the proximity sensor is binary: if the current value is
+            // equal to the maximum, we are always in the FAR state.
+            return (value > FAR_THRESHOLD || value == mMaxValue) ? State.FAR : State.NEAR;
+        }
+
+        /**
+         * Unregister the next time the sensor reaches the {@link State#FAR} state.
+         */
+        public synchronized void unregisterWhenFar() {
+            if (mLastState == State.FAR) {
+                // We are already in the far state, just unregister now.
+                unregisterWithoutNotification();
+            } else {
+                mWaitingForFarState = true;
+            }
+        }
+
+        /** Register the listener and call the listener as necessary. */
+        public synchronized void register() {
+            // It is okay to register multiple times.
+            mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_UI);
+            // We should no longer be waiting for the far state if we are registering again.
+            mWaitingForFarState = false;
+        }
+
+        public void unregister() {
+            State lastState;
+            synchronized (this) {
+                unregisterWithoutNotification();
+                lastState = mLastState;
+                // Always go back to the FAR state. That way, when we register again we will get a
+                // transition when the sensor gets into the NEAR state.
+                mLastState = State.FAR;
+            }
+            // Notify the listener if we changed the state to FAR while unregistering.
+            if (lastState != State.FAR) {
+                mListener.onFar();
+            }
+        }
+
+        @GuardedBy("this")
+        private void unregisterWithoutNotification() {
+            mSensorManager.unregisterListener(this);
+            mWaitingForFarState = false;
+        }
+    }
+
+    public ProximitySensorManager(Context context, Listener listener) {
+        SensorManager sensorManager =
+                (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        if (proximitySensor == null) {
+            // If there is no sensor, we should not do anything.
+            mProximitySensorListener = null;
+        } else {
+            mProximitySensorListener =
+                    new ProximitySensorEventListener(sensorManager, proximitySensor, listener);
+        }
+    }
+
+    /**
+     * Enables the proximity manager.
+     * <p>
+     * The listener will start getting notifications of events.
+     * <p>
+     * This method is idempotent.
+     */
+    public void enable() {
+        if (mProximitySensorListener != null && !mManagerEnabled) {
+            mProximitySensorListener.register();
+            mManagerEnabled = true;
+        }
+    }
+
+    /**
+     * Disables the proximity manager.
+     * <p>
+     * The listener will stop receiving notifications of events, possibly after receiving a last
+     * {@link Listener#onFar()} callback.
+     * <p>
+     * If {@code waitForFarState} is true, if the sensor is not currently in the {@link State#FAR}
+     * state, the listener will receive a {@link Listener#onFar()} callback the next time the sensor
+     * actually reaches the {@link State#FAR} state.
+     * <p>
+     * If {@code waitForFarState} is false, the listener will receive a {@link Listener#onFar()}
+     * callback immediately if the sensor is currently not in the {@link State#FAR} state.
+     * <p>
+     * This method is idempotent.
+     */
+    public void disable(boolean waitForFarState) {
+        if (mProximitySensorListener != null && mManagerEnabled) {
+            if (waitForFarState) {
+                mProximitySensorListener.unregisterWhenFar();
+            } else {
+                mProximitySensorListener.unregister();
+            }
+            mManagerEnabled = false;
+        }
+    }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 5d6c7d5..cd4add6 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -237,8 +237,6 @@
         if (!contactData.getStreamItems().isEmpty()) {
             StreamItemEntry firstEntry = contactData.getStreamItems().get(0);
             snippet = Html.fromHtml(firstEntry.getText());
-            // Add quotes around the text
-            snippet = context.getString(R.string.recent_updates_tab_text, snippet);
             if (!firstEntry.getPhotos().isEmpty()) {
                 StreamItemPhotoEntry firstPhoto = firstEntry.getPhotos().get(0);
                 photoUri = firstPhoto.getPhotoUri();
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index 106ff0e..b01316b 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -88,7 +88,7 @@
     private View mDetailFragmentView;
     private View mUpdatesFragmentView;
 
-    private final Handler mHandler = new Handler();
+    private boolean mScrollToCurrentPage = false;
 
     public ContactDetailFragmentCarousel(Context context) {
         this(context, null);
@@ -144,6 +144,28 @@
                 resolveSize(screenHeight, heightMeasureSpec));
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        if (mScrollToCurrentPage) {
+            mScrollToCurrentPage = false;
+            // Use scrollTo() instead of smoothScrollTo() to prevent a visible flicker to the user
+            scrollTo(mCurrentPage == ABOUT_PAGE ? 0 : mAllowedHorizontalScrollLength, 0);
+            updateTouchInterceptors();
+        }
+    }
+
+    /**
+     * Set the current page that should be restored when the view is first laid out.
+     */
+    public void restoreCurrentPage(int pageIndex) {
+        setCurrentPage(pageIndex);
+        // It is only possible to scroll the view after onMeasure() has been called (where the
+        // allowed horizontal scroll length is determined). Hence, set a flag that will be read
+        // in onLayout() after the children and this view have finished being laid out.
+        mScrollToCurrentPage = true;
+    }
+
     /**
      * Set the current page. This auto-scrolls the carousel to the current page and dims out
      * the non-selected page.
@@ -183,31 +205,13 @@
             mEnableSwipe = enable;
             if (mUpdatesFragmentView != null) {
                 mUpdatesFragmentView.setVisibility(enable ? View.VISIBLE : View.GONE);
+                mScrollToCurrentPage = true;
                 requestLayout();
                 invalidate();
             }
-            // This method could have been called before the view has been measured (i.e.
-            // immediately after a rotation), so snap to edge only after the view is ready.
-            postRunnableToSnapToEdge();
         }
     }
 
-    /**
-     * Snap to the currently selected page only once all the view setup and measurement has
-     * completed (i.e. we need to know the allowed horizontal scroll width in order to
-     * snap to the correct page).
-     */
-    private void postRunnableToSnapToEdge() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (isAttachedToWindow() && mAboutFragment != null && mUpdatesFragment != null) {
-                    snapToEdge();
-                }
-            }
-        });
-    }
-
     public int getCurrentPage() {
         return mCurrentPage;
     }
@@ -302,8 +306,4 @@
         }
         return false;
     }
-
-    private boolean isAttachedToWindow() {
-        return getWindowToken() != null;
-    }
 }
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index 6b8829e..7a7f400 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -21,6 +21,8 @@
 import com.android.contacts.R;
 import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
 
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
 import android.app.FragmentManager;
@@ -77,6 +79,7 @@
 
     private ContactLoader.Result mContactData;
 
+    private boolean mTabCarouselIsAnimating;
     private boolean mContactHasUpdates;
 
     private LayoutMode mLayoutMode;
@@ -176,6 +179,7 @@
                 }
 
                 mTabCarousel.setListener(mTabCarouselListener);
+                mTabCarousel.restoreCurrentTab(currentPageIndex);
                 mDetailFragment.setVerticalScrollListener(
                         new VerticalScrollListener(TAB_INDEX_DETAIL));
                 mUpdatesFragment.setVerticalScrollListener(
@@ -211,7 +215,7 @@
 
                 mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView);
                 mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment);
-                mFragmentCarousel.setCurrentPage(currentPageIndex);
+                mFragmentCarousel.restoreCurrentPage(currentPageIndex);
                 break;
             }
         }
@@ -239,6 +243,7 @@
     public void showEmptyState() {
         switch (mLayoutMode) {
             case FRAGMENT_CAROUSEL: {
+                mFragmentCarousel.setCurrentPage(0);
                 mFragmentCarousel.enableSwipe(false);
                 mDetailFragment.showEmptyState();
                 break;
@@ -323,6 +328,7 @@
                 break;
             case FRAGMENT_CAROUSEL: {
                 // Disable swipe so only the detail fragment shows
+                mFragmentCarousel.setCurrentPage(0);
                 mFragmentCarousel.enableSwipe(false);
                 break;
             }
@@ -449,12 +455,14 @@
                     mTabCarousel, "y", desiredValue).setDuration(75);
             mTabCarouselAnimator.setInterpolator(AnimationUtils.loadInterpolator(
                     mActivity, android.R.anim.accelerate_decelerate_interpolator));
+            mTabCarouselAnimator.addListener(mTabCarouselAnimatorListener);
         }
 
         private void cancelTabCarouselAnimator() {
             if (mTabCarouselAnimator != null) {
                 mTabCarouselAnimator.cancel();
                 mTabCarouselAnimator = null;
+                mTabCarouselIsAnimating = false;
             }
         }
     };
@@ -478,6 +486,34 @@
         }
     }
 
+    /**
+     * This listener keeps track of whether the tab carousel animation is currently going on or not,
+     * in order to prevent other simultaneous changes to the Y position of the tab carousel which
+     * can cause flicker.
+     */
+    private final AnimatorListener mTabCarouselAnimatorListener = new AnimatorListener() {
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mTabCarouselIsAnimating = false;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mTabCarouselIsAnimating = false;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+            mTabCarouselIsAnimating = true;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mTabCarouselIsAnimating = true;
+        }
+    };
+
     private final ContactDetailTabCarousel.Listener mTabCarouselListener =
             new ContactDetailTabCarousel.Listener() {
 
@@ -529,10 +565,11 @@
                 int totalItemCount) {
             int currentPageIndex = mViewPager.getCurrentItem();
             // Don't move the carousel if: 1) the contact does not have social updates because then
-            // tab carousel must not be visible, 2) if the view pager is still being scrolled, or
-            // 3) if the current page being viewed is not this one.
+            // tab carousel must not be visible, 2) if the view pager is still being scrolled,
+            // 3) if the current page being viewed is not this one, or 4) if the tab carousel
+            // is already being animated vertically.
             if (!mContactHasUpdates || mViewPagerState != ViewPager.SCROLL_STATE_IDLE ||
-                    mPageIndex != currentPageIndex) {
+                    mPageIndex != currentPageIndex || mTabCarouselIsAnimating) {
                 return;
             }
             // If the FIRST item is not visible on the screen, then the carousel must be pinned
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index 8a51d81..9300b54 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -63,6 +63,7 @@
 
     private int mTabDisplayLabelHeight;
 
+    private boolean mScrollToCurrentTab = false;
     private int mLastScrollPosition;
 
     private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE;
@@ -103,12 +104,7 @@
         mUpdatesTab = (CarouselTab) findViewById(R.id.tab_update);
         mUpdatesTab.setLabel(mContext.getString(R.string.contactDetailUpdates));
 
-        // TODO: We can't always assume the "about" page will be the current page.
-        mAboutTab.showSelectedState();
-        mAboutTab.setAlphaLayerValue(0);
         mAboutTab.enableTouchInterceptor(mAboutTabTouchInterceptListener);
-
-        mUpdatesTab.setAlphaLayerValue(MAX_ALPHA);
         mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener);
 
         // Retrieve the photo view for the "about" tab
@@ -144,6 +140,15 @@
                 resolveSize(tabHeight, heightMeasureSpec));
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        if (mScrollToCurrentTab) {
+            mScrollToCurrentTab = false;
+            scrollTo(mCurrentTab == TAB_INDEX_ABOUT ? 0 : mAllowedHorizontalScrollLength, 0);
+        }
+    }
+
     private final OnClickListener mAboutTabTouchInterceptListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -174,6 +179,17 @@
     }
 
     /**
+     * Set the current tab that should be restored when the view is first laid out.
+     */
+    public void restoreCurrentTab(int position) {
+        setCurrentTab(position);
+        // It is only possible to scroll the view after onMeasure() has been called (where the
+        // allowed horizontal scroll length is determined). Hence, set a flag that will be read
+        // in onLayout() after the children and this view have finished being laid out.
+        mScrollToCurrentTab = true;
+    }
+
+    /**
      * Restore the Y position of this view to the last manually requested value. This can be done
      * after the parent has been re-laid out again, where this view's position could have been
      * lost if the view laid outside its parent's bounds.
@@ -225,8 +241,6 @@
      * Updates the tab selection.
      */
     public void setCurrentTab(int position) {
-        // TODO: Handle device rotation (saving and restoring state of the selected tab)
-        // This will take more work because there is no tab carousel in phone landscape
         switch (position) {
             case TAB_INDEX_ABOUT:
                 mAboutTab.showSelectedState();
@@ -270,10 +284,10 @@
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 mListener.onTouchDown();
-                return false;
+                return true;
             case MotionEvent.ACTION_UP:
                 mListener.onTouchUp();
-                return false;
+                return true;
         }
         return super.onTouchEvent(event);
     }
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index 25bc91d..0a1ae74 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -27,6 +27,7 @@
 
 import android.content.ContentUris;
 import android.content.Context;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -201,35 +202,42 @@
             mEditExternallyButton.setVisibility(View.VISIBLE);
         }
 
+        final Resources res = mContext.getResources();
         // Phones
         ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
         if (phones != null) {
-            for (ValuesDelta phone : phones) {
-                View field = mInflater.inflate(
-                        R.layout.item_read_only_field, mGeneral, false);
-                TextView v;
-                v = (TextView) field.findViewById(R.id.kind_title);
-                v.setText(mContext.getText(R.string.phoneLabelsGroup));
-                v = (TextView) field.findViewById(R.id.data);
-                v.setText(PhoneNumberUtils.formatNumber(phone.getAsString(Phone.NUMBER),
+            for (int i = 0; i < phones.size(); i++) {
+                ValuesDelta phone = phones.get(i);
+                final String phoneNumber = PhoneNumberUtils.formatNumber(
+                        phone.getAsString(Phone.NUMBER),
                         phone.getAsString(Phone.NORMALIZED_NUMBER),
-                        ContactsUtils.getCurrentCountryIso(getContext())));
-                mGeneral.addView(field);
+                        ContactsUtils.getCurrentCountryIso(getContext()));
+                final CharSequence phoneType;
+                if (phone.containsKey(Phone.TYPE)) {
+                    phoneType = Phone.getTypeLabel(
+                            res, phone.getAsInteger(Phone.TYPE), phone.getAsString(Phone.LABEL));
+                } else {
+                    phoneType = null;
+                }
+                bindData(mContext.getText(R.string.phoneLabelsGroup),
+                        phoneNumber, phoneType, i == 0);
             }
         }
 
         // Emails
         ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
         if (emails != null) {
-            for (ValuesDelta email : emails) {
-                View field = mInflater.inflate(
-                        R.layout.item_read_only_field, mGeneral, false);
-                TextView v;
-                v = (TextView) field.findViewById(R.id.kind_title);
-                v.setText(mContext.getText(R.string.emailLabelsGroup));
-                v = (TextView) field.findViewById(R.id.data);
-                v.setText(email.getAsString(Email.DATA));
-                mGeneral.addView(field);
+            for (int i = 0; i < emails.size(); i++) {
+                ValuesDelta email = emails.get(i);
+                final String emailAddress = email.getAsString(Email.DATA);
+                final CharSequence emailType;
+                if (email.containsKey(Email.TYPE)) {
+                    emailType = Email.getTypeLabel(
+                            res, email.getAsInteger(Email.TYPE), email.getAsString(Email.LABEL));
+                } else {
+                    emailType = null;
+                }
+                bindData(mContext.getText(R.string.emailLabelsGroup), emailAddress, null, i == 0);
             }
         }
 
@@ -241,6 +249,28 @@
         }
     }
 
+    private void bindData(
+            CharSequence titleText, CharSequence data, CharSequence type, boolean isFirstEntry) {
+        final View field = mInflater.inflate(R.layout.item_read_only_field, mGeneral, false);
+        if (isFirstEntry) {
+            final TextView titleView = (TextView) field.findViewById(R.id.kind_title);
+            titleView.setText(titleText);
+        } else {
+            View titleContainer = field.findViewById(R.id.kind_title_layout);
+            titleContainer.setVisibility(View.GONE);
+        }
+        final TextView dataView = (TextView) field.findViewById(R.id.data);
+        dataView.setText(data);
+        final TextView typeView = (TextView) field.findViewById(R.id.type);
+        if (!TextUtils.isEmpty(type)) {
+            typeView.setText(type);
+        } else {
+            typeView.setVisibility(View.GONE);
+        }
+
+        mGeneral.addView(field);
+    }
+
     @Override
     public long getRawContactId() {
         return mRawContactId;
diff --git a/src/com/android/contacts/interactions/ImportExportDialogFragment.java b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
index a6a37ef..078f63e 100644
--- a/src/com/android/contacts/interactions/ImportExportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
@@ -136,30 +136,32 @@
         // TODO move the query into a loader and do this in a background thread
         final Cursor cursor = getActivity().getContentResolver().query(Contacts.CONTENT_URI,
                 LOOKUP_PROJECTION, Contacts.IN_VISIBLE_GROUP + "!=0", null, null);
-        try {
-            if (!cursor.moveToFirst()) {
-                Toast.makeText(getActivity(), R.string.share_error, Toast.LENGTH_SHORT).show();
-                return;
-            }
+        if (cursor != null) {
+            try {
+                if (!cursor.moveToFirst()) {
+                    Toast.makeText(getActivity(), R.string.share_error, Toast.LENGTH_SHORT).show();
+                    return;
+                }
 
-            StringBuilder uriListBuilder = new StringBuilder();
-            int index = 0;
-            while (cursor.moveToNext()) {
-                if (index != 0)
-                    uriListBuilder.append(':');
-                uriListBuilder.append(cursor.getString(0));
-                index++;
-            }
-            Uri uri = Uri.withAppendedPath(
-                    Contacts.CONTENT_MULTI_VCARD_URI,
-                    Uri.encode(uriListBuilder.toString()));
+                StringBuilder uriListBuilder = new StringBuilder();
+                int index = 0;
+                do {
+                    if (index != 0)
+                        uriListBuilder.append(':');
+                    uriListBuilder.append(cursor.getString(0));
+                    index++;
+                } while (cursor.moveToNext());
+                Uri uri = Uri.withAppendedPath(
+                        Contacts.CONTENT_MULTI_VCARD_URI,
+                        Uri.encode(uriListBuilder.toString()));
 
-            final Intent intent = new Intent(Intent.ACTION_SEND);
-            intent.setType(Contacts.CONTENT_VCARD_TYPE);
-            intent.putExtra(Intent.EXTRA_STREAM, uri);
-            getActivity().startActivity(intent);
-        } finally {
-            cursor.close();
+                final Intent intent = new Intent(Intent.ACTION_SEND);
+                intent.setType(Contacts.CONTENT_VCARD_TYPE);
+                intent.putExtra(Intent.EXTRA_STREAM, uri);
+                getActivity().startActivity(intent);
+            } finally {
+                cursor.close();
+            }
         }
     }
 
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 528e246..df2f0cc 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -755,8 +755,6 @@
                     "'android.R.id.list'");
         }
 
-        mListView.setSelector(getContext().getResources().getDrawable(R.drawable.list_selector));
-
         View emptyView = mView.findViewById(com.android.internal.R.id.empty);
         if (emptyView != null) {
             mListView.setEmptyView(emptyView);
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
index 724098c..01d76a2 100644
--- a/src/com/android/contacts/list/ContactListFilter.java
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -205,15 +205,13 @@
 
     /**
      * Try to obtain ContactListFilter object saved in SharedPreference.
-     * If there's no info there, return custom filter instead, assuming the user wants contacts
-     * which ContactsProvider remembers as "visible contacts".
-     * (See also {@link Contacts#IN_VISIBLE_GROUP})
+     * If there's no info there, return ALL filter instead.
      */
     public static ContactListFilter restoreDefaultPreferences(SharedPreferences prefs) {
         ContactListFilter filter = restoreFromPreferences(prefs);
         if (filter == null) {
-            // Show contacts in IN_VISIBLE_GROUP instead.
-            filter = ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_CUSTOM);
+            filter = ContactListFilter.createFilterWithType(
+                    ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
         }
         return filter;
     }
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index f16102d..4b86295 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -24,6 +24,7 @@
 import com.android.contacts.widget.TextWithHighlightingFactory;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.database.CharArrayBuffer;
 import android.database.Cursor;
@@ -120,6 +121,9 @@
     private TextView mCountView;
     private ImageView mPresenceIcon;
 
+    private ColorStateList mPrimaryTextColor;
+    private ColorStateList mSecondaryTextColor;
+
     private char[] mHighlightedPrefix;
 
     private int mDefaultPhotoViewSize;
@@ -248,6 +252,9 @@
                         Color.GREEN));
         a.recycle();
 
+        mPrimaryTextColor = getResources().getColorStateList(R.color.list_primary_text_color);
+        mSecondaryTextColor = getResources().getColorStateList(R.color.list_secondary_text_color);
+
         mHorizontalDividerHeight = mHorizontalDividerDrawable.getIntrinsicHeight();
 
         if (mActivatedBackgroundDrawable != null) {
@@ -409,7 +416,7 @@
 
         mBoundsWithoutHeader.set(0, topBound, width, bottomBound);
 
-        if (mActivatedStateSupported) {
+        if (mActivatedStateSupported && isActivated()) {
             mActivatedBackgroundDrawable.setBounds(mBoundsWithoutHeader);
         }
 
@@ -624,7 +631,7 @@
 
     @Override
     public void dispatchDraw(Canvas canvas) {
-        if (mActivatedStateSupported) {
+        if (mActivatedStateSupported && isActivated()) {
             mActivatedBackgroundDrawable.draw(canvas);
         }
         if (mHorizontalDividerVisible) {
@@ -759,6 +766,10 @@
             mNameTextView.setSingleLine(true);
             mNameTextView.setEllipsize(getTextEllipsis());
             mNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
+            mNameTextView.setTextColor(mPrimaryTextColor);
+            // Manually call setActivated() since this view may be added after the first
+            // setActivated() call toward this whole item view.
+            mNameTextView.setActivated(isActivated());
             mNameTextView.setGravity(Gravity.CENTER_VERTICAL);
             addView(mNameTextView);
         }
@@ -815,6 +826,8 @@
             mPhoneticNameTextView.setEllipsize(getTextEllipsis());
             mPhoneticNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             mPhoneticNameTextView.setTypeface(mPhoneticNameTextView.getTypeface(), Typeface.BOLD);
+            mPhoneticNameTextView.setTextColor(mPrimaryTextColor);
+            mPhoneticNameTextView.setActivated(isActivated());
             addView(mPhoneticNameTextView);
         }
         return mPhoneticNameTextView;
@@ -860,6 +873,8 @@
             mLabelView.setEllipsize(getTextEllipsis());
             mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
+            mLabelView.setTextColor(mPrimaryTextColor);
+            mLabelView.setActivated(isActivated());
             addView(mLabelView);
         }
         return mLabelView;
@@ -890,6 +905,8 @@
             mDataView.setSingleLine(true);
             mDataView.setEllipsize(getTextEllipsis());
             mDataView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
+            mDataView.setTextColor(mPrimaryTextColor);
+            mDataView.setActivated(isActivated());
             addView(mDataView);
         }
         return mDataView;
@@ -919,6 +936,8 @@
             mSnippetView.setEllipsize(getTextEllipsis());
             mSnippetView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             mSnippetView.setTypeface(mSnippetView.getTypeface(), Typeface.BOLD);
+            mSnippetView.setTextColor(mPrimaryTextColor);
+            mSnippetView.setActivated(isActivated());
             addView(mSnippetView);
         }
         return mSnippetView;
@@ -927,14 +946,14 @@
     /**
      * Returns the text view for the status, creating it if necessary.
      */
-
     public TextView getStatusView() {
         if (mStatusView == null) {
             mStatusView = new TextView(mContext);
             mStatusView.setSingleLine(true);
             mStatusView.setEllipsize(getTextEllipsis());
             mStatusView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
-            mStatusView.setTextColor(R.color.secondary_text_color);
+            mStatusView.setTextColor(mSecondaryTextColor);
+            mStatusView.setActivated(isActivated());
             addView(mStatusView);
         }
         return mStatusView;
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 7355dbf..4562864 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -124,9 +124,9 @@
                         mQuickContact.assignContactUri(mLookupUri);
                     }
                 } else if (mQuickContact != null) {
-                        mQuickContact.assignContactUri(mLookupUri);
-                        mPhotoManager.loadPhoto(mQuickContact, entry.photoUri);
-                    }
+                    mQuickContact.assignContactUri(mLookupUri);
+                    mPhotoManager.loadPhoto(mQuickContact, entry.photoUri);
+                }
 
             } else {
                 Log.w(TAG, "contactPhotoManager not set");
@@ -151,4 +151,12 @@
     public interface Listener {
         void onClick(ContactTileView contactTileView);
     }
+
+    @Override
+    public void requestLayout() {
+        // We will assume that once measured this will not need to resize
+        // itself, so there is no need to pass the layout request to the parent
+        // view (ListView).
+        forceLayout();
+    }
 }
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 9a3f05e..f202522 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -199,11 +199,7 @@
                 } else {
                     selection.append(" AND " + RawContacts.DATA_SET + " IS NULL");
                 }
-                // TODO (stopship): And also this private API, which is even worse
-                selection.append(") OR " + Contacts._ID + "=(" +
-                        "SELECT contact_id " +
-                        "FROM raw_contacts rc inner join accounts a" +
-                        " ON a.profile_raw_contact_id = rc._id)");
+                selection.append(")");
                 break;
             }
             case ContactListFilter.FILTER_TYPE_GROUP: {
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index 97ab347..a045eb4 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -561,6 +561,11 @@
             return mAfter;
         }
 
+        public boolean containsKey(String key) {
+            return ((mAfter != null && mAfter.containsKey(key)) ||
+                    (mBefore != null && mBefore.containsKey(key)));
+        }
+
         public String getAsString(String key) {
             if (mAfter != null && mAfter.containsKey(key)) {
                 return mAfter.getAsString(key);
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java b/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
index 7d29406..8d549a7 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
@@ -20,6 +20,7 @@
 import static com.android.contacts.CallDetailActivity.EXTRA_VOICEMAIL_URI;
 
 import com.android.common.io.MoreCloseables;
+import com.android.contacts.ProximitySensorAware;
 import com.android.contacts.R;
 import com.android.contacts.util.AsyncTaskExecutors;
 import com.android.ex.variablespeed.MediaPlayerProxy;
@@ -36,6 +37,7 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.PowerManager;
 import android.provider.VoicemailContract;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -91,10 +93,15 @@
         Uri voicemailUri = arguments.getParcelable(EXTRA_VOICEMAIL_URI);
         Preconditions.checkNotNull(voicemailUri, "fragment must contain EXTRA_VOICEMAIL_URI");
         boolean startPlayback = arguments.getBoolean(EXTRA_VOICEMAIL_START_PLAYBACK, false);
+        PowerManager powerManager =
+                (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
+        PowerManager.WakeLock wakeLock =
+                powerManager.newWakeLock(
+                        PowerManager.SCREEN_DIM_WAKE_LOCK, getClass().getSimpleName());
         mPresenter = new VoicemailPlaybackPresenter(createPlaybackViewImpl(),
                 createMediaPlayer(mScheduledExecutorService), voicemailUri,
                 mScheduledExecutorService, startPlayback,
-                AsyncTaskExecutors.createAsyncTaskExecutor());
+                AsyncTaskExecutors.createAsyncTaskExecutor(), wakeLock);
         mPresenter.onCreate(savedInstanceState);
     }
 
@@ -263,6 +270,24 @@
         }
 
         @Override
+        public void enableProximitySensor() {
+            // Only change the state if the activity is still around.
+            Activity activity = mActivityReference.get();
+            if (activity != null && activity instanceof ProximitySensorAware) {
+                ((ProximitySensorAware) activity).enableProximitySensor();
+            }
+        }
+
+        @Override
+        public void disableProximitySensor() {
+            // Only change the state if the activity is still around.
+            Activity activity = mActivityReference.get();
+            if (activity != null && activity instanceof ProximitySensorAware) {
+                ((ProximitySensorAware) activity).disableProximitySensor(true);
+            }
+        }
+
+        @Override
         public void registerContentObserver(Uri uri, ContentObserver observer) {
             mApplicationContext.getContentResolver().registerContentObserver(uri, false, observer);
         }
@@ -370,7 +395,6 @@
 
         @Override
         public void setSpeakerPhoneOn(boolean on) {
-            getAudioManager().setMode(AudioManager.MODE_IN_CALL);
             getAudioManager().setSpeakerphoneOn(on);
             if (on) {
                 mPlaybackSpeakerphone.setImageResource(R.drawable.ic_sound_holo_dark);
@@ -378,6 +402,14 @@
                 mPlaybackSpeakerphone.setImageResource(R.drawable.ic_sound_holo_dark);
             }
         }
+
+        @Override
+        public void setVolumeControlStream(int streamType) {
+            Activity activity = mActivityReference.get();
+            if (activity != null) {
+                activity.setVolumeControlStream(streamType);
+            }
+        }
     }
 
     /**
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
index d54cddc..f8e497d 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
@@ -27,11 +27,13 @@
 
 import android.content.Context;
 import android.database.ContentObserver;
+import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PowerManager;
 import android.view.View;
 import android.widget.SeekBar;
 
@@ -59,6 +61,9 @@
 @NotThreadSafe
 @VisibleForTesting
 public class VoicemailPlaybackPresenter {
+    /** The stream used to playback voicemail. */
+    private static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
+
     /** Contract describing the behaviour we need from the ui we are controlling. */
     public interface PlaybackView {
         Context getDataSourceContext();
@@ -86,6 +91,9 @@
         void setFetchContentTimeout();
         void registerContentObserver(Uri uri, ContentObserver observer);
         void unregisterContentObserver(ContentObserver observer);
+        void enableProximitySensor();
+        void disableProximitySensor();
+        void setVolumeControlStream(int streamType);
     }
 
     /** The enumeration of {@link AsyncTask} objects we use in this class. */
@@ -160,19 +168,23 @@
      * This variable is thread-contained, accessed only on the ui thread.
      */
     private FetchResultHandler mFetchResultHandler;
+    private PowerManager.WakeLock mWakeLock;
 
     public VoicemailPlaybackPresenter(PlaybackView view, MediaPlayerProxy player,
             Uri voicemailUri, ScheduledExecutorService executorService,
-            boolean startPlayingImmediately, AsyncTaskExecutor asyncTaskExecutor) {
+            boolean startPlayingImmediately, AsyncTaskExecutor asyncTaskExecutor,
+            PowerManager.WakeLock wakeLock) {
         mView = view;
         mPlayer = player;
         mVoicemailUri = voicemailUri;
         mStartPlayingImmediately = startPlayingImmediately;
         mAsyncTaskExecutor = asyncTaskExecutor;
         mPositionUpdater = new PositionUpdater(executorService, SLIDER_UPDATE_PERIOD_MILLIS);
+        mWakeLock = wakeLock;
     }
 
     public void onCreate(Bundle bundle) {
+        mView.setVolumeControlStream(PLAYBACK_STREAM);
         checkThatWeHaveContent();
     }
 
@@ -298,6 +310,7 @@
                         try {
                             mPlayer.reset();
                             mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
+                            mPlayer.setAudioStreamType(PLAYBACK_STREAM);
                             mPlayer.prepare();
                             return null;
                         } catch (IOException e) {
@@ -334,6 +347,8 @@
         mView.setRateIncreaseButtonListener(createRateIncreaseListener());
         mView.setClipPosition(0, mPlayer.getDuration());
         mView.playbackStopped();
+        // Always disable on stop.
+        mView.disableProximitySensor();
         if (mStartPlayingImmediately) {
             resetPrepareStartPlaying(0);
         }
@@ -348,16 +363,16 @@
         }
     }
 
-    /**
-     * This method should be called <b>only on the ui thread</b>.
-     */
     public void onDestroy() {
+        mPlayer.release();
         if (mFetchResultHandler != null) {
             mFetchResultHandler.destroy();
             mFetchResultHandler = null;
         }
         mPositionUpdater.stopUpdating();
-        mPlayer.release();
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
     }
 
     private class MediaPlayerErrorListener implements MediaPlayer.OnErrorListener {
@@ -420,6 +435,7 @@
         try {
             mPlayer.reset();
             mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
+            mPlayer.setAudioStreamType(PLAYBACK_STREAM);
             mPlayer.prepare();
             mDuration.set(mPlayer.getDuration());
             int startPosition = constrain(clipPositionInMillis, 0, mDuration.get());
@@ -427,6 +443,13 @@
             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());
         } catch (IOException e) {
             handleError(e);
@@ -446,6 +469,11 @@
     private void stopPlaybackAtPosition(int clipPosition, int duration) {
         mPositionUpdater.stopUpdating();
         mView.playbackStopped();
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+        // Always disable on stop.
+        mView.disableProximitySensor();
         mView.setClipPosition(clipPosition, duration);
         if (mPlayer.isPlaying()) {
             mPlayer.pause();
@@ -489,7 +517,16 @@
     private class SpeakerphoneListener implements View.OnClickListener {
         @Override
         public void onClick(View v) {
-            mView.setSpeakerPhoneOn(!mView.isSpeakerPhoneOn());
+            boolean previousState = mView.isSpeakerPhoneOn();
+            mView.setSpeakerPhoneOn(!previousState);
+            if (mPlayer.isPlaying() && previousState) {
+                // If we are currently playing and we are disabling the speaker phone, enable the
+                // sensor.
+                mView.enableProximitySensor();
+            } else {
+                // If we are not currently playing, disable the sensor.
+                mView.disableProximitySensor();
+            }
         }
     }
 
@@ -560,5 +597,8 @@
         if (mPlayer.isPlaying()) {
             stopPlaybackAtPosition(mPlayer.getCurrentPosition(), mDuration.get());
         }
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
     }
 }
diff --git a/src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java b/src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java
index 9e481f8..d88b5d3 100644
--- a/src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java
+++ b/src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java
@@ -221,8 +221,13 @@
         Uri actionUri = null;
         if (action == Action.CALL_VOICEMAIL) {
             actionUri = UriUtils.parseUriOrNull(cursor.getString(VOICEMAIL_ACCESS_URI_INDEX));
+            // Even if actionUri is null, it is still be useful to show the notification.
         } else if (action == Action.CONFIGURE_VOICEMAIL) {
             actionUri = UriUtils.parseUriOrNull(cursor.getString(SETTINGS_URI_INDEX));
+            // If there is no settings URI, there is no point in showing the notification.
+            if (actionUri == null) {
+                return null;
+            }
         }
         return new MessageStatusWithPriority(
                 new StatusMessage(sourcePackage, overallState.getCallLogMessageId(),