Merge "Pixel perfect for tablet contact editor"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0e28ae8..d18515e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -651,6 +651,7 @@
                 <data
                     android:scheme="content"
                     android:host="com.android.voicemail"
+                    android:mimeType="vnd.android.cursor.item/voicemail"
                 />
             </intent-filter>
             <intent-filter android:priority="100">
diff --git a/proguard.flags b/proguard.flags
index 5a08b9f..f4ec4b2 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -24,3 +24,9 @@
 -keep class ** {
   *** *ForTest(...);
 }
+
+# Any class or method annotated with NeededForTesting.
+-keep @com.android.contacts.test.NeededForTesting class *
+-keepclassmembers class * {
+@com.android.contacts.test.NeededForTesting *;
+}
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_container.xml b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
index 1d4ea35..6b1d094 100644
--- a/res/layout-sw580dp-w1000dp/contact_detail_container.xml
+++ b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
@@ -35,7 +35,14 @@
         android:id="@+id/about_fragment_container"
         android:layout_width="0dip"
         android:layout_height="match_parent"
-        android:layout_weight="1" />
+        android:layout_weight="3" />
+
+    <!-- Vertical divider -->
+    <View
+        android:layout_width="2dip"
+        android:layout_height="match_parent"
+        android:background="?android:attr/listDivider"
+        />
 
     <!--
       Container for the "Updates" fragment on the contact card for a contact
@@ -44,7 +51,8 @@
     -->
     <FrameLayout
         android:id="@+id/updates_fragment_container"
-        android:layout_width="306dip"
+        android:layout_width="0dip"
+        android:layout_weight="2"
         android:layout_height="match_parent" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
index 11779cb..31d3ce1 100644
--- a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
@@ -27,21 +27,22 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingBottom="@dimen/group_detail_vertical_padding"
+        android:paddingTop="12dip"
+        android:paddingBottom="12dip"
         android:orientation="horizontal" >
 
         <LinearLayout
             android:layout_width="0dip"
             android:layout_height="wrap_content"
             android:layout_weight="1"
+            android:paddingLeft="@dimen/group_detail_border_padding"
             android:orientation="vertical" >
 
             <TextView
                 android:id="@+id/group_title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingLeft="@dimen/group_detail_border_padding"
-                android:paddingTop="@dimen/group_detail_vertical_padding"
+                android:paddingLeft="8dip"
                 android:textAppearance="?android:attr/textAppearanceLarge"
                 android:textStyle="bold" />
 
@@ -49,7 +50,7 @@
                 android:id="@+id/group_size"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingLeft="@dimen/group_detail_border_padding"
+                android:paddingLeft="8dip"
                 android:textAppearance="?android:attr/textAppearanceMedium"
                 android:textColor="?android:attr/textColorSecondary" />
 
@@ -67,10 +68,9 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="1dip"
-        android:background="?android:attr/listDivider"
-        android:layout_marginBottom="@dimen/group_detail_vertical_padding"
-        android:layout_marginLeft="@dimen/group_detail_divider_margin"
-        android:layout_marginRight="@dimen/group_detail_divider_margin" />
+        android:background="@color/people_app_theme_color"
+        android:layout_marginLeft="@dimen/group_detail_border_padding"
+        android:layout_marginRight="@dimen/group_detail_border_padding" />
 
     <!-- List of group members -->
     <ListView android:id="@android:id/list"
diff --git a/res/layout-sw580dp-w1000dp/people_activity.xml b/res/layout-sw580dp-w1000dp/people_activity.xml
new file mode 100644
index 0000000..7d8d064
--- /dev/null
+++ b/res/layout-sw580dp-w1000dp/people_activity.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.android.contacts.widget.InterpolatingLayout
+        android:id="@+id/main_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:splitMotionEvents="true">
+
+        <LinearLayout
+            android:id="@+id/browse_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:minWidth="100dip"
+            ex:layout_narrowParentWidth="1000dip"
+            ex:layout_narrowWidth="276dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideWidth="376dip"
+            android:background="@drawable/list_background_holo"
+            android:visibility="gone">
+
+            <!-- All -->
+            <fragment
+                android:id="@+id/all_fragment"
+                class="com.android.contacts.list.DefaultContactBrowseListFragment"
+                android:layout_height="0dip"
+                android:layout_width="match_parent"
+                android:layout_weight="1" />
+
+            <!-- Groups -->
+            <fragment
+                android:id="@+id/groups_fragment"
+                class="com.android.contacts.group.GroupBrowseListFragment"
+                android:layout_height="match_parent"
+                android:layout_width="match_parent" />
+        </LinearLayout>
+
+        <view
+            class="com.android.contacts.widget.TransitionAnimationView"
+            android:id="@+id/details_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowMarginLeft="0dip"
+            ex:layout_narrowMarginRight="0dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideMarginLeft="0dip"
+            ex:layout_wideMarginRight="0dip"
+            ex:clipMarginLeft="0dip"
+            ex:clipMarginTop="3dip"
+            ex:clipMarginRight="3dip"
+            ex:clipMarginBottom="9dip"
+            ex:enterAnimation="@android:animator/fade_in"
+            ex:exitAnimation="@android:animator/fade_out"
+            ex:animationDuration="200"
+            android:visibility="gone">
+
+            <!-- This layout includes all possible views needed for a contact detail page -->
+            <include
+                android:id="@+id/contact_detail_container"
+                layout="@layout/contact_detail_container"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+            <!-- This invisible worker fragment loads the contact's details -->
+            <fragment
+                android:id="@+id/contact_detail_loader_fragment"
+                class="com.android.contacts.detail.ContactLoaderFragment"
+                android:layout_height="0dip"
+                android:layout_width="0dip"
+                android:visibility="gone"/>
+
+            <!-- This is the group detail page -->
+            <fragment
+                android:id="@+id/group_detail_fragment"
+                class="com.android.contacts.group.GroupDetailFragment"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="gone" />
+        </view>
+
+        <view
+            class="com.android.contacts.widget.TransitionAnimationView"
+            android:id="@+id/favorites_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            ex:clipMarginLeft="0dip"
+            ex:clipMarginTop="3dip"
+            ex:clipMarginRight="3dip"
+            ex:clipMarginBottom="9dip"
+            ex:enterAnimation="@android:animator/fade_in"
+            ex:exitAnimation="@android:animator/fade_out"
+            ex:animationDuration="200">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@drawable/list_background_holo">
+
+                <!-- Starred -->
+                <fragment
+                    android:id="@+id/favorites_fragment"
+                    class="com.android.contacts.list.ContactTileListFragment"
+                    android:layout_height="match_parent"
+                    style="@style/FavoritesFragmentStyle"
+                    android:layout_marginTop="32dip"
+                    android:layout_marginLeft="32dip"
+                    android:layout_marginRight="32dip" />
+
+                <!-- Most Frequent -->
+                <fragment
+                    android:id="@+id/frequent_fragment"
+                    class="com.android.contacts.list.ContactTileListFragment"
+                    style="@style/FrequentFragmentStyle"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="32dip"
+                    android:layout_marginRight="32dip"/>
+            </LinearLayout>
+        </view>
+
+    </com.android.contacts.widget.InterpolatingLayout>
+
+    <com.android.contacts.widget.InterpolatingLayout
+        android:id="@+id/contacts_unavailable_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone">
+
+        <FrameLayout
+            android:id="@+id/contacts_unavailable_container"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowMarginLeft="80dip"
+            ex:layout_narrowMarginRight="80dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideMarginLeft="200dip"
+            ex:layout_wideMarginRight="200dip"
+            android:paddingBottom="20dip" />
+
+    </com.android.contacts.widget.InterpolatingLayout>
+</FrameLayout>
diff --git a/res/layout-sw580dp/group_detail_fragment.xml b/res/layout-sw580dp/group_detail_fragment.xml
index a7db154..c11337f 100644
--- a/res/layout-sw580dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp/group_detail_fragment.xml
@@ -21,14 +21,16 @@
     android:orientation="vertical"
     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">
 
     <TextView
         android:id="@+id/group_title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="@dimen/group_detail_border_padding"
-        android:paddingTop="@dimen/group_detail_vertical_padding"
+        android:paddingLeft="8dip"
+        android:paddingTop="12dip"
         android:textAppearance="?android:attr/textAppearanceLarge"
         android:textStyle="bold" />
 
@@ -36,31 +38,25 @@
         android:id="@+id/group_size"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="@dimen/group_detail_border_padding"
+        android:paddingLeft="8dip"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="?android:attr/textColorTertiary" />
 
     <View
         android:layout_width="match_parent"
         android:layout_height="1dip"
-        android:background="?android:attr/listDivider"
-        android:layout_marginTop="@dimen/group_detail_vertical_padding"
-        android:layout_marginBottom="@dimen/group_detail_vertical_padding"
-        android:layout_marginLeft="@dimen/group_detail_divider_margin"
-        android:layout_marginRight="@dimen/group_detail_divider_margin" />
+        android:background="@color/people_app_theme_color"
+        android:layout_marginTop="12dip" />
 
     <FrameLayout
         android:id="@+id/group_source_view_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:paddingLeft="@dimen/group_detail_border_padding" />
+        android:layout_gravity="center_vertical" />
 
     <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginLeft="@dimen/group_detail_border_padding"
-        android:layout_marginRight="@dimen/group_detail_border_padding"
         android:cacheColorHint="#00000000"
         android:divider="@null" />
 
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index 8bf627f..35c11b1 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -20,23 +20,19 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <com.android.contacts.widget.InterpolatingLayout
+    <LinearLayout
         android:id="@+id/main_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginLeft="8dip"
+        android:orientation="horizontal"
         android:splitMotionEvents="true">
 
         <LinearLayout
             android:id="@+id/browse_view"
-            android:layout_width="wrap_content"
+            android:layout_width="0dip"
             android:layout_height="match_parent"
+            android:layout_weight="1"
             android:orientation="vertical"
-            android:minWidth="100dip"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowWidth="310dip"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideWidth="430dip"
             android:background="@drawable/list_background_holo"
             android:visibility="gone">
 
@@ -59,14 +55,9 @@
         <view
             class="com.android.contacts.widget.TransitionAnimationView"
             android:id="@+id/details_view"
-            android:layout_width="match_parent"
+            android:layout_width="0dip"
             android:layout_height="match_parent"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowMarginLeft="0dip"
-            ex:layout_narrowMarginRight="0dip"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideMarginLeft="0dip"
-            ex:layout_wideMarginRight="0dip"
+            android:layout_weight="1"
             ex:clipMarginLeft="0dip"
             ex:clipMarginTop="3dip"
             ex:clipMarginRight="3dip"
@@ -105,19 +96,13 @@
             android:id="@+id/favorites_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowMarginRight="0dip"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideMarginRight="48dip"
             ex:clipMarginLeft="0dip"
             ex:clipMarginTop="3dip"
             ex:clipMarginRight="3dip"
             ex:clipMarginBottom="9dip"
             ex:enterAnimation="@android:animator/fade_in"
             ex:exitAnimation="@android:animator/fade_out"
-            ex:animationDuration="200"
-            android:paddingTop="32dip"
-            android:paddingRight="32dip">
+            ex:animationDuration="200">
 
             <LinearLayout
                 android:layout_width="match_parent"
@@ -129,20 +114,23 @@
                     android:id="@+id/favorites_fragment"
                     class="com.android.contacts.list.ContactTileListFragment"
                     android:layout_height="match_parent"
-                    android:layout_width="0dip"
-                    android:layout_weight="2" />
+                    style="@style/FavoritesFragmentStyle"
+                    android:layout_marginTop="32dip"
+                    android:layout_marginLeft="32dip"
+                    android:layout_marginRight="32dip" />
 
                 <!-- Most Frequent -->
                 <fragment
                     android:id="@+id/frequent_fragment"
                     class="com.android.contacts.list.ContactTileListFragment"
+                    style="@style/FrequentFragmentStyle"
                     android:layout_height="match_parent"
-                    android:layout_width="0dip"
-                    android:layout_weight="1" />
+                    android:layout_marginTop="32dip"
+                    android:layout_marginRight="32dip"/>
             </LinearLayout>
         </view>
 
-    </com.android.contacts.widget.InterpolatingLayout>
+    </LinearLayout>
 
     <com.android.contacts.widget.InterpolatingLayout
         android:id="@+id/contacts_unavailable_view"
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index fbb8d41..f28bda1 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -14,8 +14,9 @@
      limitations under the License.
 -->
 
-<LinearLayout
+<view
     xmlns:android="http://schemas.android.com/apk/res/android"
+    class="com.android.contacts.calllog.CallLogListItemView"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:padding="@dimen/call_log_inner_margin"
@@ -154,4 +155,4 @@
         android:textStyle="bold"
         android:textColor="?attr/call_log_header_color"
     />
-</LinearLayout>
+</view>
diff --git a/res/layout/contact_detail_tab_carousel.xml b/res/layout/contact_detail_tab_carousel.xml
index fe167d1..fb60f17 100644
--- a/res/layout/contact_detail_tab_carousel.xml
+++ b/res/layout/contact_detail_tab_carousel.xml
@@ -28,10 +28,18 @@
         android:layout_height="match_parent"
         android:orientation="horizontal">
 
+        <!-- "About" tab -->
         <include
             android:id="@+id/tab_about"
             layout="@layout/carousel_about_tab" />
 
+        <!-- Vertical divider -->
+        <View
+            android:layout_width="2dip"
+            android:layout_height="match_parent"
+            android:background="@android:color/white"/>
+
+        <!-- "Updates" tab -->
         <include
             android:id="@+id/tab_update"
             layout="@layout/carousel_updates_tab" />
diff --git a/res/layout/playback_layout.xml b/res/layout/playback_layout.xml
index 05b3e43..83b1c01 100644
--- a/res/layout/playback_layout.xml
+++ b/res/layout/playback_layout.xml
@@ -18,28 +18,40 @@
             android:orientation="horizontal"
             android:layout_alignParentTop="true"
         >
-            <ImageButton
-                android:id="@+id/playback_start_stop"
-                android:layout_width="wrap_content"
+            <LinearLayout
+                android:layout_width="match_parent"
                 android:layout_height="58dip"
                 android:layout_marginRight="2dip"
                 android:background="@drawable/dialpad_background"
-                android:src="@drawable/ic_hold_pause_holo_dark"
                 android:layout_weight="1"
-            />
-            <ImageButton
-                android:id="@+id/playback_speakerphone"
-                android:layout_width="wrap_content"
+            >
+                <ImageButton
+                    android:id="@+id/playback_start_stop"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:background="@drawable/btn_dial"
+                    android:src="@drawable/ic_hold_pause_holo_dark"
+                />
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="match_parent"
                 android:layout_height="58dip"
                 android:layout_marginLeft="2dip"
                 android:background="@drawable/dialpad_background"
-                android:src="@drawable/ic_sound_holo_dark"
                 android:layout_weight="1"
-            />
+            >
+                <ImageButton
+                    android:id="@+id/playback_speakerphone"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:background="@drawable/btn_dial"
+                    android:src="@drawable/ic_sound_holo_dark"
+                />
+            </LinearLayout>
         </LinearLayout>
         <RelativeLayout
             android:id="@+id/seek_container"
-            android:layout_width="fill_parent"
+            android:layout_width="match_parent"
             android:layout_height="80dip"
             android:background="@drawable/dialpad_background"
             android:layout_below="@id/buttons_linear_layout"
@@ -52,7 +64,7 @@
                  difference is currently 10dip. -->
             <SeekBar
                 android:id="@+id/playback_seek"
-                android:layout_width="fill_parent"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:progressDrawable="@drawable/seekbar_drawable"
                 android:thumb="@drawable/seek_bar_thumb"
@@ -90,26 +102,22 @@
             <ImageButton
                 android:id="@+id/rate_decrease_button"
                 android:src="@drawable/ic_minus_holo_dark"
-                android:layout_width="wrap_content"
+                android:layout_width="64dip"
                 android:layout_height="wrap_content"
-                android:background="@android:color/transparent"
-                android:paddingLeft="16dip"
-                android:paddingRight="16dip"
-                android:paddingBottom="16dip"
-                android:paddingTop="26dip"
+                android:background="@drawable/btn_dial"
+                android:paddingBottom="19dip"
+                android:paddingTop="29dip"
                 android:layout_alignParentLeft="true"
                 android:layout_centerVertical="true"
             />
             <ImageButton
                 android:id="@+id/rate_increase_button"
                 android:src="@drawable/ic_plus_holo_dark"
-                android:layout_width="wrap_content"
+                android:layout_width="64dip"
                 android:layout_height="wrap_content"
-                android:background="@android:color/transparent"
-                android:paddingLeft="16dip"
-                android:paddingRight="16dip"
-                android:paddingBottom="16dip"
-                android:paddingTop="26dip"
+                android:background="@drawable/btn_dial"
+                android:paddingBottom="19dip"
+                android:paddingTop="29dip"
                 android:layout_alignParentRight="true"
                 android:layout_centerVertical="true"
             />
diff --git a/res/layout/stream_item_one_column.xml b/res/layout/stream_item_container.xml
similarity index 95%
rename from res/layout/stream_item_one_column.xml
rename to res/layout/stream_item_container.xml
index ecab57c..1f96aad 100644
--- a/res/layout/stream_item_one_column.xml
+++ b/res/layout/stream_item_container.xml
@@ -19,16 +19,16 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <LinearLayout
+    <TableLayout
         android:id="@+id/stream_item_content"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingTop="@dimen/detail_update_section_item_vertical_padding"
-        android:paddingBottom="@dimen/detail_update_section_item_vertical_padding"
+        android:paddingBottom="@dimen/detail_update_section_item_last_row_extra_vertical_padding"
         android:paddingLeft="@dimen/detail_update_section_item_horizontal_padding"
         android:paddingRight="@dimen/detail_update_section_item_horizontal_padding"
         android:background="@drawable/list_selector"
-        android:orientation="vertical" />
+        />
 
     <View
         android:id="@+id/horizontal_divider"
@@ -37,4 +37,5 @@
         android:layout_marginLeft="@dimen/detail_update_section_side_padding"
         android:layout_marginRight="@dimen/detail_update_section_side_padding"
         android:background="?android:attr/dividerHorizontal" />
+
 </LinearLayout>
diff --git a/res/layout/stream_item_pair.xml b/res/layout/stream_item_pair.xml
deleted file mode 100644
index 61b248c..0000000
--- a/res/layout/stream_item_pair.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <FrameLayout android:id="@+id/stream_pair_first"
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1" />
-
-    <FrameLayout android:id="@+id/stream_pair_second"
-        android:paddingLeft="@dimen/detail_update_section_internal_padding"
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/stream_item_row_image_and_text.xml b/res/layout/stream_item_row_image_and_text.xml
new file mode 100644
index 0000000..b6f6599
--- /dev/null
+++ b/res/layout/stream_item_row_image_and_text.xml
@@ -0,0 +1,77 @@
+<?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.
+-->
+
+<TableRow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+    android:paddingBottom="@dimen/detail_update_section_between_items_vertical_padding">
+
+    <view
+        class="com.android.contacts.widget.ProportionalLayout"
+        android:layout_width="0dip"
+        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">
+        <ImageView
+            android:id="@+id/stream_item_first_image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </view>
+
+    <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="match_parent">
+
+            <TextView android:id="@+id/stream_item_html"
+                android:layout_width="match_parent"
+                android:layout_height="0dip"
+                android:layout_weight="1"
+                android:textSize="16sp"
+                android:textColor="?android:attr/textColorPrimary" />
+
+            <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TextView android:id="@+id/stream_item_attribution"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:textColor="?android:attr/textColorSecondary" />
+
+                <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" />
+            </LinearLayout>
+
+        </LinearLayout>
+    </view>
+
+</TableRow>
diff --git a/res/layout/stream_item_text.xml b/res/layout/stream_item_row_text_only.xml
similarity index 76%
rename from res/layout/stream_item_text.xml
rename to res/layout/stream_item_row_text_only.xml
index 861d91f..d1d0efd 100644
--- a/res/layout/stream_item_text.xml
+++ b/res/layout/stream_item_row_text_only.xml
@@ -15,15 +15,16 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/detail_update_section_between_items_vertical_padding">
 
     <TextView android:id="@+id/stream_item_html"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textSize="16sp"
-        android:textColor="@color/social_update_text_color" />
+        android:textColor="?android:attr/textColorPrimary" />
 
     <LinearLayout
         android:orientation="horizontal"
@@ -33,14 +34,13 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="@color/social_update_attribution_color" />
+            android:textColor="?android:attr/textColorSecondary" />
 
         <TextView android:id="@+id/stream_item_comments"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="4dip"
+            android:layout_marginLeft="@dimen/detail_update_section_attribution_comments_padding"
             android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="@color/social_update_comments_color" />
+            android:textColor="?android:attr/textColorSecondary" />
     </LinearLayout>
-
 </LinearLayout>
diff --git a/res/layout/stream_item_row_two_images.xml b/res/layout/stream_item_row_two_images.xml
new file mode 100644
index 0000000..6e34b25
--- /dev/null
+++ b/res/layout/stream_item_row_two_images.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+
+<TableRow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+    android:paddingBottom="@dimen/detail_update_section_between_items_vertical_padding">
+
+    <view
+        class="com.android.contacts.widget.ProportionalLayout"
+        android:layout_width="0dip"
+        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">
+        <ImageView
+            android:id="@+id/stream_item_first_image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </view>
+
+    <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">
+        <ImageView
+            android:id="@+id/stream_item_second_image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </view>
+
+</TableRow>
diff --git a/res/values-sw580dp-w1000dp/styles.xml b/res/values-sw580dp-w1000dp/styles.xml
new file mode 100644
index 0000000..85f4733
--- /dev/null
+++ b/res/values-sw580dp-w1000dp/styles.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<resources>
+    <style name="FavoritesFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">7</item>
+    </style>
+
+    <style name="FrequentFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">3</item>
+    </style>
+</resources>
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index 40391d0..f7c0f05 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -33,6 +33,7 @@
     <dimen name="shortcut_icon_size">64dip</dimen>
     <dimen name="list_section_height">37dip</dimen>
     <dimen name="detail_update_section_item_vertical_padding">32dip</dimen>
+    <dimen name="detail_update_section_item_last_row_extra_vertical_padding">16dip</dimen>
     <dimen name="search_view_width">400dip</dimen>
     <!-- Center vertically -->
     <dimen name="quick_contact_top_position">-1px</dimen>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index a9d745f..0005484 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -31,12 +31,6 @@
 
     <color name="background_social_updates">#ffeeeeee</color>
 
-    <color name="social_update_text_color">#ff333333</color>
-
-    <color name="social_update_attribution_color">#ff777777</color>
-
-    <color name="social_update_comments_color">#ff777777</color>
-
     <!-- Color used for the letter in the A-Z section header -->
     <color name="section_header_text_color">#ff999999</color>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4da74b0..5bc8ea0 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -106,12 +106,28 @@
     <!-- Vertical padding above and below individual stream items -->
     <dimen name="detail_update_section_item_vertical_padding">16dip</dimen>
 
+    <!-- Vertical padding between rows of images in a single stream item -->
+    <dimen name="detail_update_section_between_items_vertical_padding">16dip</dimen>
+
+    <!-- The extra padding to be added at the end of last row in a stream items, i.e., the
+         difference between detail_update_section_item_vertical_padding and
+         detail_update_section_between_items_vertical_padding
+      -->
+    <dimen name="detail_update_section_item_last_row_extra_vertical_padding">0dip</dimen>
+
     <!-- Horizontal padding for individual stream items -->
     <dimen name="detail_update_section_item_horizontal_padding">24dip</dimen>
 
+    <!-- Horizontal padding between columns images and/or text in a single stream item -->
+    <dimen name="detail_update_section_between_items_horizontal_padding">16dip</dimen>
+
     <!-- Horizontal padding between content sections within a stream item -->
     <dimen name="detail_update_section_internal_padding">16dip</dimen>
 
+    <!-- Horizontal padding between attribution and comments -->
+    <dimen name="detail_update_section_attribution_comments_padding">4dip</dimen>
+
+
     <!-- Margin around the contact's photo on the contact card -->
     <dimen name="detail_contact_photo_margin">16dip</dimen>
 
@@ -173,14 +189,8 @@
     <!-- Size of group list icons -->
     <dimen name="group_list_icon_size">32dip</dimen>
 
-    <!-- Border padding for the group detail fragment header -->
-    <dimen name="group_detail_border_padding">20dip</dimen>
-
-    <!-- Vertical padding for the group detail fragment header -->
-    <dimen name="group_detail_vertical_padding">15dip</dimen>
-
-    <!-- Margins for the group detail fragment divider in the header -->
-    <dimen name="group_detail_divider_margin">15dip</dimen>
+    <!-- Border padding for the group detail fragment -->
+    <dimen name="group_detail_border_padding">32dip</dimen>
 
     <!-- Height of the member list in the group editor -->
     <dimen name="group_editor_member_list_height">550dip</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 423466f..6d4c4b2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -301,6 +301,16 @@
         <item name="android:background">#7F000000</item>
     </style>
 
+    <style name="FavoritesFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">1</item>
+    </style>
+
+    <style name="FrequentFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">1</item>
+    </style>
+
     <style name="DialtactsActionBarStyle" parent="android:Widget.Holo.ActionBar">
         <item name="android:backgroundSplit">@drawable/ab_bottom_opaque_dark_holo</item>
         <item name="android:backgroundStacked">@drawable/ab_stacked_opaque_dark_holo</item>
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index c06b474..58dcc8b 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -857,7 +857,6 @@
         mVoiceMailNumber = ((TelephonyManager) getActivity().getSystemService(
                 Context.TELEPHONY_SERVICE)).getVoiceMailNumber();
         mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(), this);
-
         setHasOptionsMenu(true);
     }
 
@@ -953,12 +952,22 @@
     @Override
     public void onPause() {
         super.onPause();
-
         // Kill the requests thread
         mAdapter.stopRequestProcessing();
     }
 
     @Override
+    public void onStop() {
+        super.onStop();
+        resetNewCallsFlag();
+        // Clear notifications only when window gains focus.  This activity won't
+        // immediately receive focus if the keyguard screen is above it.
+        if (getActivity().hasWindowFocus()) {
+            removeMissedCallNotifications();
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         mAdapter.stopRequestProcessing();
@@ -1147,7 +1156,6 @@
         // again once being shown.
         mAdapter.invalidateCache();
         startCallsQuery();
-        resetNewCallsFlag();
         startVoicemailStatusQuery();
         mAdapter.mPreDrawListener = null; // Let it restart the thread after next draw
         // Clear notifications only when window gains focus.  This activity won't
diff --git a/src/com/android/contacts/calllog/CallLogListItemView.java b/src/com/android/contacts/calllog/CallLogListItemView.java
new file mode 100644
index 0000000..584a7f2
--- /dev/null
+++ b/src/com/android/contacts/calllog/CallLogListItemView.java
@@ -0,0 +1,46 @@
+/*
+ * 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.calllog;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+/**
+ * An entry in the call log.
+ */
+public class CallLogListItemView extends LinearLayout {
+    public CallLogListItemView(Context context) {
+        super(context);
+    }
+
+    public CallLogListItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CallLogListItemView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @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/calllog/CallTypeIconsView.java b/src/com/android/contacts/calllog/CallTypeIconsView.java
index 4fbe7d7..e0242c8 100644
--- a/src/com/android/contacts/calllog/CallTypeIconsView.java
+++ b/src/com/android/contacts/calllog/CallTypeIconsView.java
@@ -17,6 +17,7 @@
 package com.android.contacts.calllog;
 
 import com.android.contacts.R;
+import com.android.contacts.test.NeededForTesting;
 import com.google.common.collect.Lists;
 
 import android.content.Context;
@@ -64,10 +65,12 @@
         invalidate();
     }
 
+    @NeededForTesting
     public int getCount() {
         return mCallTypes.size();
     }
 
+    @NeededForTesting
     public int getCallType(int index) {
         return mCallTypes.get(index);
     }
diff --git a/src/com/android/contacts/calllog/ExtendedCursor.java b/src/com/android/contacts/calllog/ExtendedCursor.java
index b17c018..499a350 100644
--- a/src/com/android/contacts/calllog/ExtendedCursor.java
+++ b/src/com/android/contacts/calllog/ExtendedCursor.java
@@ -19,7 +19,9 @@
 import com.android.common.io.MoreCloseables;
 
 import android.database.AbstractCursor;
+import android.database.ContentObserver;
 import android.database.Cursor;
+import android.database.DataSetObserver;
 
 /**
  * Wraps a cursor to add an additional column with the same value for all rows.
@@ -129,4 +131,24 @@
         MoreCloseables.closeQuietly(mCursor);
         super.close();
     }
-}
\ No newline at end of file
+
+    @Override
+    public void registerContentObserver(ContentObserver observer) {
+        mCursor.registerContentObserver(observer);
+    }
+
+    @Override
+    public void unregisterContentObserver(ContentObserver observer) {
+        mCursor.unregisterContentObserver(observer);
+    }
+
+    @Override
+    public void registerDataSetObserver(DataSetObserver observer) {
+        mCursor.registerDataSetObserver(observer);
+    }
+
+    @Override
+    public void unregisterDataSetObserver(DataSetObserver observer) {
+        mCursor.unregisterDataSetObserver(observer);
+    }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 3bff950..94150b3 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -60,7 +60,6 @@
  * {@link ContactLoader.Result} data object to appropriate {@link View}s.
  */
 public class ContactDetailDisplayUtils {
-
     private static final int PHOTO_FADE_IN_ANIMATION_DURATION_MILLIS = 100;
 
     private ContactDetailDisplayUtils() {
@@ -252,83 +251,68 @@
     /** Creates the view that represents a stream item. */
     public static View createStreamItemView(LayoutInflater inflater, Context context,
             StreamItemEntry streamItem, LinearLayout parent) {
-        View oneColumnView = inflater.inflate(R.layout.stream_item_one_column,
-                parent, false);
-        ViewGroup contentBox = (ViewGroup) oneColumnView.findViewById(R.id.stream_item_content);
-        int internalPadding = context.getResources().getDimensionPixelSize(
-                R.dimen.detail_update_section_internal_padding);
+        View container = inflater.inflate(R.layout.stream_item_container, parent, false);
+        ViewGroup contentTable = (ViewGroup) container.findViewById(R.id.stream_item_content);
 
-        // TODO: This is not the correct layout for a stream item with photos.  Photos should be
-        // displayed first, then the update text either to the right of the final image (if there
-        // are an odd number of images) or below the last row of images (if there are an even
-        // number of images).  Since this is designed as a two-column grid, we should also consider
-        // using a TableLayout instead of the series of nested LinearLayouts that we have now.
-        // See the Updates section of the Contacts Architecture document for details.
-
-        // If there are no photos, just display the text in a single column.
+        ContactPhotoManager contactPhotoManager = ContactPhotoManager.getInstance(context);
         List<StreamItemPhotoEntry> photos = streamItem.getPhotos();
-        if (photos.isEmpty()) {
-            addStreamItemText(inflater, context, streamItem, contentBox);
-        } else {
-            // If the first photo is square or portrait mode, show the text alongside it.
-            boolean isFirstPhotoAlongsideText = false;
-            StreamItemPhotoEntry firstPhoto = photos.get(0);
-            isFirstPhotoAlongsideText = firstPhoto.getHeight() >= firstPhoto.getWidth();
-            if (isFirstPhotoAlongsideText) {
-                View twoColumnView = inflater.inflate(R.layout.stream_item_pair, contentBox, false);
-                addStreamItemPhoto(inflater, context, firstPhoto,
-                        (ViewGroup) twoColumnView.findViewById(R.id.stream_pair_first));
-                addStreamItemText(inflater, context, streamItem,
-                        (ViewGroup) twoColumnView.findViewById(R.id.stream_pair_second));
-                contentBox.addView(twoColumnView);
-            } else {
-                // Just add the stream item text at the top of the entry.
-                addStreamItemText(inflater, context, streamItem, contentBox);
-            }
-            for (int i = isFirstPhotoAlongsideText ? 1 : 0; i < photos.size(); i++) {
-                StreamItemPhotoEntry photo = photos.get(i);
+        final int photoCount = photos.size();
 
-                // If the photo is landscape, show it at full-width.
-                if (photo.getWidth() > photo.getHeight()) {
-                    View photoView = addStreamItemPhoto(inflater, context, photo, contentBox);
-                    photoView.setPadding(0, internalPadding, 0, 0);
-                } else {
-                    // If this photo and the next are both square or portrait, show them as a pair.
-                    StreamItemPhotoEntry nextPhoto = i + 1 < photos.size()
-                            ? photos.get(i + 1) : null;
-                    if (nextPhoto != null && nextPhoto.getHeight() >= nextPhoto.getWidth()) {
-                        View twoColumnView = inflater.inflate(R.layout.stream_item_pair,
-                                contentBox, false);
-                        addStreamItemPhoto(inflater, context, photo,
-                                (ViewGroup) twoColumnView.findViewById(R.id.stream_pair_first));
-                        addStreamItemPhoto(inflater, context, nextPhoto,
-                                (ViewGroup) twoColumnView.findViewById(R.id.stream_pair_second));
-                        twoColumnView.setPadding(0, internalPadding, 0, 0);
-                        contentBox.addView(twoColumnView);
-                        i++;
-                    } else {
-                        View photoView = addStreamItemPhoto(inflater, context, photo, contentBox);
-                        photoView.setPadding(0, internalPadding, 0, 0);
-                    }
-                }
+        // Process the photos, two at a time.
+        for (int index = 0; index < photoCount; index += 2) {
+            final StreamItemPhotoEntry firstPhoto = photos.get(index);
+            if (index + 1 < photoCount) {
+                // Put in two photos, side by side.
+                final StreamItemPhotoEntry secondPhoto = photos.get(index + 1);
+
+                View photoContainer = inflater.inflate(R.layout.stream_item_row_two_images,
+                        contentTable, false);
+                loadPhoto(contactPhotoManager, firstPhoto, photoContainer,
+                        R.id.stream_item_first_image);
+                loadPhoto(contactPhotoManager, secondPhoto, photoContainer,
+                        R.id.stream_item_second_image);
+                contentTable.addView(photoContainer);
+            } else {
+                // Put in a single photo with text on the side.
+                View photoContainer = inflater.inflate(
+                        R.layout.stream_item_row_image_and_text, contentTable, false);
+                loadPhoto(contactPhotoManager, firstPhoto, photoContainer,
+                        R.id.stream_item_first_image);
+                addStreamItemText(context, streamItem,
+                        photoContainer.findViewById(R.id.stream_item_second_text));
+                contentTable.addView(photoContainer);
             }
         }
 
+        if (photoCount % 2 == 0) {
+            // Even number of photos, add the text below them. Otherwise, it should have been
+            // already added next to the last photo.
+            View textContainer = inflater.inflate(R.layout.stream_item_row_text_only, contentTable,
+                    false);
+            addStreamItemText(context, streamItem, textContainer);
+            contentTable.addView(textContainer);
+        }
+
         if (parent != null) {
-            parent.addView(oneColumnView);
+            parent.addView(container);
         }
 
-        return oneColumnView;
+        return container;
+    }
+
+    /** Loads a photo into an image view. The image view is identifiedc by the given id. */
+    private static void loadPhoto(ContactPhotoManager contactPhotoManager,
+            final StreamItemPhotoEntry firstPhoto, View photoContainer, int imageViewId) {
+        ImageView firstImageView = (ImageView) photoContainer.findViewById(imageViewId);
+        contactPhotoManager.loadPhoto(firstImageView, Uri.parse(firstPhoto.getPhotoUri()));
     }
 
     @VisibleForTesting
-    static View addStreamItemText(LayoutInflater inflater, Context context,
-            StreamItemEntry streamItem, ViewGroup parent) {
-        View textUpdate = inflater.inflate(R.layout.stream_item_text, parent, false);
-        TextView htmlView = (TextView) textUpdate.findViewById(R.id.stream_item_html);
-        TextView attributionView = (TextView) textUpdate.findViewById(
+    static View addStreamItemText(Context context, StreamItemEntry streamItem, View rootView) {
+        TextView htmlView = (TextView) rootView.findViewById(R.id.stream_item_html);
+        TextView attributionView = (TextView) rootView.findViewById(
                 R.id.stream_item_attribution);
-        TextView commentsView = (TextView) textUpdate.findViewById(R.id.stream_item_comments);
+        TextView commentsView = (TextView) rootView.findViewById(R.id.stream_item_comments);
         htmlView.setText(Html.fromHtml(streamItem.getText()));
         attributionView.setText(ContactBadgeUtil.getSocialDate(streamItem, context));
         if (streamItem.getComments() != null) {
@@ -337,19 +321,7 @@
         } else {
             commentsView.setVisibility(View.GONE);
         }
-        if (parent != null) {
-            parent.addView(textUpdate);
-        }
-        return textUpdate;
-    }
-
-    private static View addStreamItemPhoto(LayoutInflater inflater, Context context,
-            StreamItemPhotoEntry streamItemPhoto, ViewGroup parent) {
-        ImageView image = new ImageView(context);
-        ContactPhotoManager.getInstance(context).loadPhoto(
-                image, Uri.parse(streamItemPhoto.getPhotoUri()));
-        parent.addView(image);
-        return image;
+        return rootView;
     }
 
     /**
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 383f23b..47617d3 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -118,7 +118,7 @@
 import java.util.Map;
 
 public class ContactDetailFragment extends Fragment implements FragmentKeyListener, ViewOverlay,
-        SelectAccountDialogFragment.Listener {
+        SelectAccountDialogFragment.Listener, OnItemClickListener {
 
     private static final String TAG = "ContactDetailFragment";
 
@@ -279,6 +279,7 @@
 
         mListView = (ListView) mView.findViewById(android.R.id.list);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+        mListView.setOnItemClickListener(this);
         mListView.setItemsCanFocus(true);
         mListView.setOnScrollListener(mVerticalScrollListener);
 
@@ -1709,6 +1710,21 @@
         }
     }
 
+    /**
+     * Default (fallback) list item click listener.  Note the click event for DetailViewEntry is
+     * caught by individual views in the list item view to distinguish the primary action and the
+     * secondary action, so this method won't be invoked for that.  (The listener is set in the
+     * bindview in the adapter)
+     * This listener is used for other kind of entries.
+     */
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        if (mListener == null) return;
+        final ViewEntry entry = mAdapter.getItem(position);
+        if (entry == null) return;
+        entry.click(view, mListener);
+    }
+
     @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
         super.onCreateContextMenu(menu, view, menuInfo);
diff --git a/src/com/android/contacts/test/NeededForTesting.java b/src/com/android/contacts/test/NeededForTesting.java
new file mode 100644
index 0000000..f40fe2c
--- /dev/null
+++ b/src/com/android/contacts/test/NeededForTesting.java
@@ -0,0 +1,30 @@
+/*
+ * 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.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the class, constructor, method or field is used by tests and therefore cannot be
+ * removed by tools like ProGuard.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
+public @interface NeededForTesting {}
diff --git a/src/com/android/contacts/util/ExpirableCache.java b/src/com/android/contacts/util/ExpirableCache.java
index 0ee3bbe..196b03a 100644
--- a/src/com/android/contacts/util/ExpirableCache.java
+++ b/src/com/android/contacts/util/ExpirableCache.java
@@ -16,6 +16,8 @@
 
 package com.android.contacts.util;
 
+import com.android.contacts.test.NeededForTesting;
+
 import android.util.LruCache;
 
 import java.util.concurrent.atomic.AtomicInteger;
@@ -198,6 +200,7 @@
      *
      * @param key the key to look up
      */
+    @NeededForTesting
     public V get(K key) {
         CachedValue<V> cachedValue = getCachedValue(key);
         return cachedValue == null || cachedValue.isExpired() ? null : cachedValue.getValue();
diff --git a/src/com/android/contacts/util/StreamItemEntry.java b/src/com/android/contacts/util/StreamItemEntry.java
index dc54229..db15e3a 100644
--- a/src/com/android/contacts/util/StreamItemEntry.java
+++ b/src/com/android/contacts/util/StreamItemEntry.java
@@ -16,6 +16,8 @@
 
 package com.android.contacts.util;
 
+import com.android.contacts.test.NeededForTesting;
+
 import android.database.Cursor;
 import android.provider.ContactsContract.StreamItems;
 
@@ -46,6 +48,7 @@
     // Photos associated with this stream item.
     private List<StreamItemPhotoEntry> mPhotos;
 
+    @NeededForTesting
     public StreamItemEntry(long id, String text, String comments, long timestamp, String action,
             String actionUri, String resPackage, int iconRes, int labelRes) {
         mId = id;
diff --git a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
index aebb8c2..487eec9 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
@@ -27,7 +27,6 @@
 import android.text.Spanned;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.TextView;
 
 /**
@@ -105,11 +104,11 @@
 
     /**
      * Calls {@link ContactDetailDisplayUtils#addStreamItemText(LayoutInflater, Context,
-     * StreamItemEntry, ViewGroup)} with the default parameters and the given stream item.
+     * StreamItemEntry, View)} with the default parameters and the given stream item.
      */
     private View addStreamItemText(StreamItemEntry streamItem) {
-        return ContactDetailDisplayUtils.addStreamItemText(
-                mLayoutInflater, getContext(), streamItem, null);
+        return ContactDetailDisplayUtils.addStreamItemText(getContext(), streamItem,
+                mLayoutInflater.inflate(R.layout.stream_item_row_text_only, null));
     }
 
     private StreamItemEntryBuilder getTestBuilder() {