Merge "One way to make the keyboard appear on starting search."
diff --git a/res/drawable-hdpi/dial_num_0_wht.png b/res/drawable-hdpi/dial_num_0_wht.png
index f2dcf02..2b194c8 100644
--- a/res/drawable-hdpi/dial_num_0_wht.png
+++ b/res/drawable-hdpi/dial_num_0_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_1_no_vm_wht.png b/res/drawable-hdpi/dial_num_1_no_vm_wht.png
index 8415b05..beaca97 100644
--- a/res/drawable-hdpi/dial_num_1_no_vm_wht.png
+++ b/res/drawable-hdpi/dial_num_1_no_vm_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_2_wht.png b/res/drawable-hdpi/dial_num_2_wht.png
index 852de4e..944d5d8 100644
--- a/res/drawable-hdpi/dial_num_2_wht.png
+++ b/res/drawable-hdpi/dial_num_2_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_3_wht.png b/res/drawable-hdpi/dial_num_3_wht.png
index 69fd333..9ed2bb5 100644
--- a/res/drawable-hdpi/dial_num_3_wht.png
+++ b/res/drawable-hdpi/dial_num_3_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_4_wht.png b/res/drawable-hdpi/dial_num_4_wht.png
index aab94de..92b1ca6 100644
--- a/res/drawable-hdpi/dial_num_4_wht.png
+++ b/res/drawable-hdpi/dial_num_4_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_5_wht.png b/res/drawable-hdpi/dial_num_5_wht.png
index de35f1f..21f1ce6 100644
--- a/res/drawable-hdpi/dial_num_5_wht.png
+++ b/res/drawable-hdpi/dial_num_5_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_6_wht.png b/res/drawable-hdpi/dial_num_6_wht.png
index 407ff2e..b794ffb 100644
--- a/res/drawable-hdpi/dial_num_6_wht.png
+++ b/res/drawable-hdpi/dial_num_6_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_7_wht.png b/res/drawable-hdpi/dial_num_7_wht.png
index 7a5a38c..c3cc401 100644
--- a/res/drawable-hdpi/dial_num_7_wht.png
+++ b/res/drawable-hdpi/dial_num_7_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_8_wht.png b/res/drawable-hdpi/dial_num_8_wht.png
index a29b24d..c199f96 100644
--- a/res/drawable-hdpi/dial_num_8_wht.png
+++ b/res/drawable-hdpi/dial_num_8_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_9_wht.png b/res/drawable-hdpi/dial_num_9_wht.png
index a402147..07251e5 100644
--- a/res/drawable-hdpi/dial_num_9_wht.png
+++ b/res/drawable-hdpi/dial_num_9_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_pound_wht.png b/res/drawable-hdpi/dial_num_pound_wht.png
index 0de2ab4..0e178dc 100644
--- a/res/drawable-hdpi/dial_num_pound_wht.png
+++ b/res/drawable-hdpi/dial_num_pound_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/dial_num_star_wht.png b/res/drawable-hdpi/dial_num_star_wht.png
index b25eb9b..4271556 100644
--- a/res/drawable-hdpi/dial_num_star_wht.png
+++ b/res/drawable-hdpi/dial_num_star_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_0_wht.png b/res/drawable-mdpi/dial_num_0_wht.png
index c3b3f2c..81a2e46 100644
--- a/res/drawable-mdpi/dial_num_0_wht.png
+++ b/res/drawable-mdpi/dial_num_0_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_1_no_vm_wht.png b/res/drawable-mdpi/dial_num_1_no_vm_wht.png
index a5bdb41..8871de5 100644
--- a/res/drawable-mdpi/dial_num_1_no_vm_wht.png
+++ b/res/drawable-mdpi/dial_num_1_no_vm_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_2_wht.png b/res/drawable-mdpi/dial_num_2_wht.png
index ac99cec..129224f 100644
--- a/res/drawable-mdpi/dial_num_2_wht.png
+++ b/res/drawable-mdpi/dial_num_2_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_3_wht.png b/res/drawable-mdpi/dial_num_3_wht.png
index 69170b9..7828567 100644
--- a/res/drawable-mdpi/dial_num_3_wht.png
+++ b/res/drawable-mdpi/dial_num_3_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_4_wht.png b/res/drawable-mdpi/dial_num_4_wht.png
index 48a02a5..730a4d8 100644
--- a/res/drawable-mdpi/dial_num_4_wht.png
+++ b/res/drawable-mdpi/dial_num_4_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_5_wht.png b/res/drawable-mdpi/dial_num_5_wht.png
index e3c9940..8d5a6b3 100644
--- a/res/drawable-mdpi/dial_num_5_wht.png
+++ b/res/drawable-mdpi/dial_num_5_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_6_wht.png b/res/drawable-mdpi/dial_num_6_wht.png
index ab12781..d2bc13d 100644
--- a/res/drawable-mdpi/dial_num_6_wht.png
+++ b/res/drawable-mdpi/dial_num_6_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_7_wht.png b/res/drawable-mdpi/dial_num_7_wht.png
index 9e66205..41c29ae 100644
--- a/res/drawable-mdpi/dial_num_7_wht.png
+++ b/res/drawable-mdpi/dial_num_7_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_8_wht.png b/res/drawable-mdpi/dial_num_8_wht.png
index 2af30fa..b070426 100644
--- a/res/drawable-mdpi/dial_num_8_wht.png
+++ b/res/drawable-mdpi/dial_num_8_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_9_wht.png b/res/drawable-mdpi/dial_num_9_wht.png
index 1c99b61..9392aae 100644
--- a/res/drawable-mdpi/dial_num_9_wht.png
+++ b/res/drawable-mdpi/dial_num_9_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_pound_wht.png b/res/drawable-mdpi/dial_num_pound_wht.png
index e17f2bf..c9a1535 100644
--- a/res/drawable-mdpi/dial_num_pound_wht.png
+++ b/res/drawable-mdpi/dial_num_pound_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_star_wht.png b/res/drawable-mdpi/dial_num_star_wht.png
index 86113ed..4030646 100644
--- a/res/drawable-mdpi/dial_num_star_wht.png
+++ b/res/drawable-mdpi/dial_num_star_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_0_blk.png b/res/drawable-xhdpi/dial_num_0_blk.png
new file mode 100644
index 0000000..db3d157
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_0_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_0_wht.png b/res/drawable-xhdpi/dial_num_0_wht.png
new file mode 100644
index 0000000..c5f032f
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_0_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_1_no_vm_blk.png b/res/drawable-xhdpi/dial_num_1_no_vm_blk.png
new file mode 100644
index 0000000..452eed0
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_1_no_vm_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_1_no_vm_wht.png b/res/drawable-xhdpi/dial_num_1_no_vm_wht.png
new file mode 100644
index 0000000..37d90be
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_1_no_vm_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_2_blk.png b/res/drawable-xhdpi/dial_num_2_blk.png
new file mode 100644
index 0000000..2893683
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_2_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_2_wht.png b/res/drawable-xhdpi/dial_num_2_wht.png
new file mode 100644
index 0000000..3512386
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_2_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_3_blk.png b/res/drawable-xhdpi/dial_num_3_blk.png
new file mode 100644
index 0000000..7ba26fa
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_3_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_3_wht.png b/res/drawable-xhdpi/dial_num_3_wht.png
new file mode 100644
index 0000000..c2be01e
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_3_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_4_blk.png b/res/drawable-xhdpi/dial_num_4_blk.png
new file mode 100644
index 0000000..bff80f8
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_4_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_4_wht.png b/res/drawable-xhdpi/dial_num_4_wht.png
new file mode 100644
index 0000000..eb9f4e2
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_4_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_5_blk.png b/res/drawable-xhdpi/dial_num_5_blk.png
new file mode 100644
index 0000000..b2a9e75
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_5_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_5_wht.png b/res/drawable-xhdpi/dial_num_5_wht.png
new file mode 100644
index 0000000..8da8c82
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_5_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_6_blk.png b/res/drawable-xhdpi/dial_num_6_blk.png
new file mode 100644
index 0000000..34e88ed
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_6_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_6_wht.png b/res/drawable-xhdpi/dial_num_6_wht.png
new file mode 100644
index 0000000..c7a89fb
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_6_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_7_blk.png b/res/drawable-xhdpi/dial_num_7_blk.png
new file mode 100644
index 0000000..c7abc7e
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_7_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_7_wht.png b/res/drawable-xhdpi/dial_num_7_wht.png
new file mode 100644
index 0000000..e532501
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_7_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_8_blk.png b/res/drawable-xhdpi/dial_num_8_blk.png
new file mode 100644
index 0000000..156e276
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_8_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_8_wht.png b/res/drawable-xhdpi/dial_num_8_wht.png
new file mode 100644
index 0000000..bc3873b
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_8_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_9_blk.png b/res/drawable-xhdpi/dial_num_9_blk.png
new file mode 100644
index 0000000..4d77bd0
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_9_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_9_wht.png b/res/drawable-xhdpi/dial_num_9_wht.png
new file mode 100644
index 0000000..38c305f
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_9_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_pound_blk.png b/res/drawable-xhdpi/dial_num_pound_blk.png
new file mode 100644
index 0000000..9946f61
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_pound_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_pound_wht.png b/res/drawable-xhdpi/dial_num_pound_wht.png
new file mode 100644
index 0000000..114ea7b
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_pound_wht.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_star_blk.png b/res/drawable-xhdpi/dial_num_star_blk.png
new file mode 100644
index 0000000..2d9baab
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_star_blk.png
Binary files differ
diff --git a/res/drawable-xhdpi/dial_num_star_wht.png b/res/drawable-xhdpi/dial_num_star_wht.png
new file mode 100644
index 0000000..64c1153
--- /dev/null
+++ b/res/drawable-xhdpi/dial_num_star_wht.png
Binary files differ
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index 969bbf9..6d702e7 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -28,6 +28,7 @@
         android:splitMotionEvents="true">
 
         <LinearLayout
+            android:id="@+id/browse_view"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:orientation="vertical"
@@ -36,40 +37,33 @@
             ex:layout_narrowWidth="310dip"
             ex:layout_wideParentWidth="1280dip"
             ex:layout_wideWidth="430dip"
-            android:background="@drawable/list_background_holo">
+            android:background="@drawable/list_background_holo"
+            android:visibility="gone">
 
             <View
                 style="@style/SectionDivider"
                 android:layout_marginLeft="40dip"
                 android:layout_marginTop="24dip" />
 
-            <!-- Favorites -->
-            <fragment
-                android:id="@+id/favorites_fragment"
-                class="com.android.contacts.list.StrequentContactListFragment"
-                android:layout_height="match_parent"
-                android:layout_width="match_parent" />
-
             <!-- Contacts -->
             <fragment
                 android:id="@+id/contacts_fragment"
                 class="com.android.contacts.list.DefaultContactBrowseListFragment"
                 android:layout_height="0dip"
                 android:layout_width="match_parent"
-                android:layout_weight="1"
-            />
+                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"
-            />
+                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"
@@ -82,19 +76,61 @@
             ex:clipMarginBottom="9dip"
             ex:enterAnimation="@android:animator/fade_in"
             ex:exitAnimation="@android:animator/fade_out"
-            ex:animationDuration="200">
+            ex:animationDuration="200"
+            android:visibility="gone">
+
             <fragment
                 android:id="@+id/contact_detail_fragment"
                 class="com.android.contacts.detail.ContactDetailFragment"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"
-            />
+                android:layout_height="match_parent" />
             <fragment
                 android:id="@+id/group_detail_fragment"
                 class="com.android.contacts.group.GroupDetailFragment"
                 android:layout_width="match_parent"
+                android:layout_height="match_parent" />
+        </view>
+
+        <view
+            class="com.android.contacts.widget.TransitionAnimationView"
+            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">
+
+            <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.StrequentContactListFragment"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dip"
+                    android:layout_weight="2" />
+
+                <!-- Most Frequent -->
+                <fragment
+                    android:id="@+id/frequent_fragment"
+                    class="com.android.contacts.list.StrequentContactListFragment"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dip"
+                    android:layout_weight="1" />
+            </LinearLayout>
         </view>
 
     </com.android.contacts.widget.InterpolatingLayout>
diff --git a/res/layout/contact_tile_regular.xml b/res/layout/contact_tile_regular.xml
index c931e97..0c02318 100644
--- a/res/layout/contact_tile_regular.xml
+++ b/res/layout/contact_tile_regular.xml
@@ -17,7 +17,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     class="com.android.contacts.list.ContactTileView"
     android:focusable="true"
-    android:padding="1dip"
+    android:padding="1px"
     android:background="@drawable/list_selector" >
 
     <RelativeLayout
@@ -30,15 +30,27 @@
             android:layout_height="match_parent"
             android:scaleType="centerCrop" />
 
+        <View
+            android:id="@+id/contact_tile_background"
+            android:layout_width="match_parent"
+            android:layout_height="48dip"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentLeft="true"
+            android:alpha="0.5"
+            android:background="@android:color/black" />
+
         <TextView
             android:id="@+id/contact_tile_name"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="48dip"
             android:layout_alignParentBottom="true"
-            android:background="@color/contact_tile_regular_text"
-            android:textColor="@color/contact_tile_regular_text_background"
-            android:textSize="17sp"
-            android:alpha="0.7" />
+            android:layout_alignParentLeft="true"
+            android:gravity="center_vertical"
+            android:paddingLeft="8dip"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textColor="@android:color/white"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
 
     </RelativeLayout>
 
diff --git a/res/layout/contact_tile_single.xml b/res/layout/contact_tile_single.xml
index e1bdccc..7147f3c 100644
--- a/res/layout/contact_tile_single.xml
+++ b/res/layout/contact_tile_single.xml
@@ -31,7 +31,9 @@
             android:layout_height="fill_parent"
             android:layout_weight="1"
             android:textSize="18sp"
-            android:textColor="#333333"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceMedium"
             android:paddingTop="24dip" />
 
         <ImageView
diff --git a/res/layout/dialpad_chooser_list_item.xml b/res/layout/dialpad_chooser_list_item.xml
index 853ca47..ecd4285 100644
--- a/res/layout/dialpad_chooser_list_item.xml
+++ b/res/layout/dialpad_chooser_list_item.xml
@@ -26,7 +26,7 @@
         android:scaleType="center" />
 
     <TextView android:id="@+id/text"
-        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textAppearance="?android:attr/textAppearanceMediumInverse"
         android:layout_gravity="center_vertical"
         android:layout_width="0dip"
         android:layout_weight="1"
diff --git a/res/layout/favorites_star.xml b/res/layout/favorites_star.xml
new file mode 100644
index 0000000..f2afa31
--- /dev/null
+++ b/res/layout/favorites_star.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingLeft="10dip"
+    android:paddingRight="10dip">
+    <CheckBox
+        android:id="@+id/star"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@string/description_star"
+        android:visibility="invisible"
+        style="?android:attr/starStyle"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/menu/star.xml b/res/menu/star.xml
new file mode 100644
index 0000000..904adbf
--- /dev/null
+++ b/res/menu/star.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_star"
+        android:showAsAction="always" />
+</menu>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 60873b1..9d726e2 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -51,8 +51,4 @@
 
     <!-- Color of the background of the tabs on the contact detail page -->
     <color name="detail_tab_background">#DBDBDB</color>
-
-    <!--  Color of the text foreground and background of Regular Sized ContactTile -->
-    <color name="contact_tile_regular_text">#2B1B17</color>
-    <color name="contact_tile_regular_text_background">#FFFFFF</color>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c9b2787..8e21762 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -967,6 +967,9 @@
     <!-- Title for the call disambiguation dialog -->
     <string name="call_disambig_title">Call using</string>
 
+    <!-- Menu item label for call settings [CHAR LIMIT=30] -->
+    <string name="call_settings">Call settings</string>
+
     <!-- Title for the sms disambiguation dialog -->
     <string name="sms_disambig_title">Text using</string>
 
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index c62084b..e216d54 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -22,6 +22,7 @@
 import com.android.contacts.ContactsSearchManager;
 import com.android.contacts.R;
 import com.android.contacts.detail.ContactDetailAboutFragment;
+import com.android.contacts.detail.ContactDetailDisplayUtils;
 import com.android.contacts.detail.ContactDetailFragment;
 import com.android.contacts.detail.ContactDetailFragmentCarousel;
 import com.android.contacts.detail.ContactDetailTabCarousel;
@@ -42,9 +43,15 @@
 import android.support.v4.view.ViewPager.OnPageChangeListener;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
+import android.widget.CheckBox;
 import android.widget.Toast;
 
 import java.util.ArrayList;
@@ -54,6 +61,9 @@
 
     public static final int FRAGMENT_COUNT = 2;
 
+    private ContactLoader.Result mContactData;
+    private Uri mLookupUri;
+
     private ContactDetailAboutFragment mAboutFragment;
     private ContactDetailUpdatesFragment mUpdatesFragment;
 
@@ -129,6 +139,40 @@
     }
 
     @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.star, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
+        ViewGroup starredContainer = (ViewGroup) getLayoutInflater().inflate(
+                R.layout.favorites_star, null, false);
+        final CheckBox starredView = (CheckBox) starredContainer.findViewById(R.id.star);
+        starredView.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Toggle "starred" state
+                // Make sure there is a contact
+                if (mLookupUri != null) {
+                    Intent intent = ContactSaveService.createSetStarredIntent(
+                            ContactDetailActivity.this, mLookupUri, starredView.isChecked());
+                    ContactDetailActivity.this.startService(intent);
+                }
+            }
+        });
+        // If there is contact data, update the starred state
+        if (mContactData != null) {
+            ContactDetailDisplayUtils.setStarred(mContactData, starredView);
+        }
+        starredMenuItem.setActionView(starredContainer);
+        return true;
+    }
+
+    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         FragmentKeyListener mCurrentFragment;
         switch (getCurrentPage()) {
@@ -165,6 +209,12 @@
 
         @Override
         public void onDetailsLoaded(ContactLoader.Result result) {
+            if (result == null) {
+                return;
+            }
+            mContactData = result;
+            mLookupUri = result.getLookupUri();
+            invalidateOptionsMenu();
             if (mTabCarousel != null) {
                 mTabCarousel.loadData(result);
             }
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 17d871a..a051dc3 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -99,19 +99,10 @@
     private StrequentContactListFragment mStrequentFragment;
 
     /**
-     * The index of the tab that has last been manually selected (the user clicked on a tab).
-     * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
-     */
-    private int mLastManuallySelectedTab;
-
-    /**
      * Fragment for searching phone numbers. Unlike the other Fragments, this doesn't correspond
      * to tab but is shown by a search action.
      */
     private PhoneNumberPickerFragment mPhoneNumberPickerFragment;
-
-    private SearchView mSearchView;
-
     /**
      * True when this Activity is in its search UI (with a {@link SearchView} and
      * {@link PhoneNumberPickerFragment}).
@@ -119,6 +110,14 @@
     private boolean mInSearchUi;
 
     /**
+     * The index of the tab that has last been manually selected (the user clicked on a tab).
+     * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
+     */
+    private int mLastManuallySelectedTab;
+
+    private SearchView mSearchView;
+
+    /**
      * Listener used when one of phone numbers in search UI is selected. This will initiate a
      * phone call using the phone number.
      */
@@ -206,6 +205,7 @@
                 .findFragmentById(R.id.phone_number_picker_fragment);
         mPhoneNumberPickerFragment.setOnPhoneNumberPickerActionListener(
                 mPhoneNumberPickerActionListener);
+        mPhoneNumberPickerFragment.setHighlightSearchPrefix(true);
 
         // Hide all tabs (the current tab will later be reshown once a tab is selected)
         final FragmentTransaction transaction = fragmentManager.beginTransaction();
@@ -364,7 +364,9 @@
         // overwritten by one of the programmatic tab selections
         final int savedTabIndex = mLastManuallySelectedTab;
 
-        if (recentCallsRequest) {
+        if (DialpadFragment.phoneIsInUse()) {
+            getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_DIALER));
+        } else if (recentCallsRequest) {
             getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CALL_LOG));
         } else {
             getActionBar().selectTab(getActionBar().getTabAt(mLastManuallySelectedTab));
@@ -511,10 +513,13 @@
             ft.show(mFragment);
             ft.hide(mPhoneNumberPickerFragment);
 
-            // Remember this tab index. This function is also called, if the tab is set
-            // automatically in which case the setter (setCurrentTab) has to set this to its old
-            // value afterwards
-            mLastManuallySelectedTab = tab.getPosition();
+            // During the call, we don't remember the tab position.
+            if (!DialpadFragment.phoneIsInUse()) {
+                // Remember this tab index. This function is also called, if the tab is set
+                // automatically in which case the setter (setCurrentTab) has to set this to its old
+                // value afterwards
+                mLastManuallySelectedTab = tab.getPosition();
+            }
         }
 
         @Override
@@ -653,7 +658,9 @@
         final ActionBar actionBar = getActionBar();
 
         final Tab tab = actionBar.getSelectedTab();
-        if (tab != null) {
+
+        // User can search during the call, but we don't want to remember the status.
+        if (tab != null && !DialpadFragment.phoneIsInUse()) {
             mLastManuallySelectedTab = tab.getPosition();
         }
 
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 548faa2..e373a6b 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -32,6 +32,7 @@
 import com.android.contacts.list.ContactEntryListFragment;
 import com.android.contacts.list.ContactListFilter;
 import com.android.contacts.list.ContactListFilterController;
+import com.android.contacts.list.ContactTileAdapter.DisplayType;
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.ContactsRequest;
 import com.android.contacts.list.ContactsUnavailableFragment;
@@ -53,7 +54,6 @@
 import android.app.ActionBar.Tab;
 import android.app.ActionBar.TabListener;
 import android.app.Activity;
-import android.app.Dialog;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -93,6 +93,7 @@
     private static final int SUBACTIVITY_NEW_CONTACT = 2;
     private static final int SUBACTIVITY_EDIT_CONTACT = 3;
     private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 4;
+    private static final int FAVORITES_COLUMN_COUNT = 4;
 
     private static final String KEY_SEARCH_MODE = "searchMode";
 
@@ -134,8 +135,13 @@
 
     private DefaultContactBrowseListFragment mContactsFragment;
     private StrequentContactListFragment mFavoritesFragment;
+    private StrequentContactListFragment mFrequentFragment;
     private GroupBrowseListFragment mGroupsFragment;
 
+    private View mFavoritesView;
+    private View mBrowserView;
+    private View mDetailsView;
+
     private enum TabState {
         FAVORITES, CONTACTS, GROUPS
     }
@@ -182,6 +188,8 @@
         } else if (fragment instanceof StrequentContactListFragment) {
             mFavoritesFragment = (StrequentContactListFragment) fragment;
             mFavoritesFragment.setListener(mFavoritesFragmentListener);
+            mFavoritesFragment.setColumnCount(FAVORITES_COLUMN_COUNT);
+            mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
         }
     }
 
@@ -218,19 +226,27 @@
         if (createContentView) {
             setContentView(R.layout.people_activity);
 
+            mFavoritesView = findViewById(R.id.favorites_view);
+            mDetailsView = findViewById(R.id.details_view);
+            mBrowserView = findViewById(R.id.browse_view);
+
             final FragmentManager fragmentManager = getFragmentManager();
             mFavoritesFragment = (StrequentContactListFragment) fragmentManager
                     .findFragmentById(R.id.favorites_fragment);
+            mFrequentFragment = (StrequentContactListFragment) fragmentManager
+                    .findFragmentById(R.id.frequent_fragment);
             mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
                     .findFragmentById(R.id.contacts_fragment);
             mGroupsFragment = (GroupBrowseListFragment) fragmentManager
                     .findFragmentById(R.id.groups_fragment);
-
             // Hide all tabs (the current tab will later be reshown once a tab is selected)
             final FragmentTransaction transaction = fragmentManager.beginTransaction();
-            transaction.hide(mFavoritesFragment);
             transaction.hide(mContactsFragment);
             transaction.hide(mGroupsFragment);
+
+            if (mFrequentFragment != null) {
+                mFrequentFragment.setDisplayType(DisplayType.FREQUENT_ONLY);
+            }
             if (mContactDetailFragment != null) {
                 transaction.hide(mContactDetailFragment);
             }
@@ -261,7 +277,7 @@
             Tab favoritesTab = actionBar.newTab();
             favoritesTab.setText(getString(R.string.strequentList));
             favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment,
-                    mContactDetailFragment, TabState.FAVORITES));
+                    mFrequentFragment, TabState.FAVORITES));
             actionBar.addTab(favoritesTab);
 
             Tab peopleTab = actionBar.newTab();
@@ -333,6 +349,22 @@
 
     private void setSelectedTab(TabState tab) {
         mSelectedTab = tab;
+
+        if (mFrequentFragment != null) {
+            switch (mSelectedTab) {
+                case FAVORITES:
+                    mFavoritesView.setVisibility(View.VISIBLE);
+                    mBrowserView.setVisibility(View.GONE);
+                    mDetailsView.setVisibility(View.GONE);
+                    break;
+                case GROUPS:
+                case CONTACTS:
+                    mFavoritesView.setVisibility(View.GONE);
+                    mBrowserView.setVisibility(View.VISIBLE);
+                    mDetailsView.setVisibility(View.VISIBLE);
+                    break;
+            }
+        }
     }
 
     @Override
@@ -461,6 +493,9 @@
         switch (action) {
             case START_SEARCH_MODE:
                 // Bring the contact list fragment (and detail fragment if applicable) to the front
+                mFavoritesView.setVisibility(View.GONE);
+                mBrowserView.setVisibility(View.VISIBLE);
+                mDetailsView.setVisibility(View.VISIBLE);
                 FragmentTransaction ft = getFragmentManager().beginTransaction();
                 ft.show(mContactsFragment);
                 if (mContactDetailFragment != null) ft.show(mContactDetailFragment);
@@ -480,6 +515,7 @@
                     if (mContactDetailFragment != null) transaction.hide(mContactDetailFragment);
                     transaction.commit();
                 }
+                if (mSelectedTab != null) setSelectedTab(mSelectedTab);
                 break;
             case CHANGE_SEARCH_QUERY:
                 loadSearch(mActionBarAdapter.getQueryString());
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index bcffd1c..31373cc 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -21,6 +21,7 @@
 import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
+import com.android.contacts.util.ExpirableCache;
 import com.android.internal.telephony.CallerInfo;
 import com.google.common.annotations.VisibleForTesting;
 
@@ -68,7 +69,6 @@
 import android.widget.TextView;
 
 import java.lang.ref.WeakReference;
-import java.util.HashMap;
 import java.util.LinkedList;
 
 /**
@@ -78,6 +78,11 @@
         implements View.OnCreateContextMenuListener {
     private static final String TAG = "CallLogFragment";
 
+    /**
+     * The size of the cache of contact info.
+     */
+    private static final int CONTACT_INFO_CACHE_SIZE = 100;
+
     /** The query for the call log table */
     private static final class CallLogQuery {
         public static final String[] _PROJECTION = new String[] {
@@ -164,7 +169,7 @@
     /** Adapter class to fill in data for the Call Log */
     public final class CallLogAdapter extends GroupingListAdapter
             implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener {
-        HashMap<String,ContactInfo> mContactInfo;
+        ExpirableCache<String, ContactInfo> mContactInfoCache;
         private final LinkedList<CallerInfoQuery> mRequests;
         private volatile boolean mDone;
         private boolean mLoading = true;
@@ -228,7 +233,7 @@
         public CallLogAdapter() {
             super(getActivity());
 
-            mContactInfo = new HashMap<String,ContactInfo>();
+            mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
             mRequests = new LinkedList<CallerInfoQuery>();
             mPreDrawListener = null;
 
@@ -268,7 +273,7 @@
         }
 
         public ContactInfo getContactInfo(String number) {
-            return mContactInfo.get(number);
+            return mContactInfoCache.getPossiblyExpired(number);
         }
 
         public void startRequestProcessing() {
@@ -292,10 +297,8 @@
             if (mCallerIdThread != null) mCallerIdThread.interrupt();
         }
 
-        public void clearCache() {
-            synchronized (mContactInfo) {
-                mContactInfo.clear();
-            }
+        public void invalidateCache() {
+            mContactInfoCache.expireAll();
         }
 
         private void updateCallLog(CallerInfoQuery ciq, ContactInfo ci) {
@@ -341,7 +344,7 @@
         private boolean queryContactInfo(CallerInfoQuery ciq) {
             // First check if there was a prior request for the same number
             // that was already satisfied
-            ContactInfo info = mContactInfo.get(ciq.number);
+            ContactInfo info = mContactInfoCache.get(ciq.number);
             boolean needNotify = false;
             if (info != null && info != ContactInfo.EMPTY) {
                 return true;
@@ -446,7 +449,7 @@
                     // cache. Any cache fills happen only on the GUI thread.
                     info.formattedNumber = null;
 
-                    mContactInfo.put(ciq.number, info);
+                    mContactInfoCache.put(ciq.number, info);
 
                     // Inform list to update this item, if in view
                     needNotify = true;
@@ -639,12 +642,15 @@
             }
 
             // Lookup contacts with this number
-            ContactInfo info = mContactInfo.get(number);
-            if (info == null) {
+            ExpirableCache.CachedValue<ContactInfo> cachedInfo =
+                    mContactInfoCache.getCachedValue(number);
+            ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
+            if (cachedInfo == null) {
                 // Mark it as empty and queue up a request to find the name
                 // The db request should happen on a non-UI thread
                 info = ContactInfo.EMPTY;
-                mContactInfo.put(number, info);
+                mContactInfoCache.put(number, info);
+                Log.d(TAG, "Contact info missing: " + number);
                 enqueueRequest(number, c.getPosition(),
                         callerName, callerNumberType, callerNumberLabel, 0L);
             } else if (info != ContactInfo.EMPTY) { // Has been queried
@@ -655,8 +661,17 @@
                         || info.type != callerNumberType
                         || !TextUtils.equals(info.label, callerNumberLabel)) {
                     // Something is amiss, so sync up.
+                    Log.w(TAG, "Contact info inconsistent: " + number);
                     enqueueRequest(number, c.getPosition(),
                             callerName, callerNumberType, callerNumberLabel, info.photoId);
+                } else if (cachedInfo.isExpired()) {
+                    Log.d(TAG, "Contact info expired: " + number);
+                    // Put it back in the cache, therefore marking it as not expired, so that other
+                    // entries with the same number will not re-request it.
+                    mContactInfoCache.put(number, info);
+                    // The contact info is no longer up to date, we should request it.
+                    enqueueRequest(number, c.getPosition(), info.name, info.type, info.label,
+                            info.photoId);
                 }
 
                 // Format and cache phone number for found contact
@@ -809,10 +824,10 @@
 
     @Override
     public void onResume() {
-        // The adapter caches looked up numbers, clear it so they will get
-        // looked up again.
+        // Mark all entries in the contact info cache as out of date, so they will be looked up
+        // again once being shown.
         if (mAdapter != null) {
-            mAdapter.clearCache();
+            mAdapter.invalidateCache();
         }
 
         startQuery();
@@ -1032,7 +1047,7 @@
     private String getBetterNumberFromContacts(String number) {
         String matchingNumber = null;
         // Look in the cache first. If it's not found then query the Phones db
-        ContactInfo ci = mAdapter.mContactInfo.get(number);
+        ContactInfo ci = mAdapter.mContactInfoCache.getPossiblyExpired(number);
         if (ci != null && ci != ContactInfo.EMPTY) {
             matchingNumber = ci.number;
         } else {
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index a12106f..b253b25 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -39,16 +39,13 @@
  * TODO: Create custom views for the tabs so their width can be programatically set as 2/3 of the
  * screen width.
  */
-public class ContactDetailTabCarousel extends HorizontalScrollView
-        implements View.OnClickListener, OnTouchListener {
+public class ContactDetailTabCarousel extends HorizontalScrollView implements OnTouchListener {
     private static final String TAG = "ContactDetailTabCarousel";
 
-    private CheckBox mStarredView;
     private ImageView mPhotoView;
     private TextView mStatusView;
     private TextView mStatusDateView;
 
-    private Uri mContactUri;
     private Listener mListener;
 
     private View[] mTabs = new View[2];
@@ -157,8 +154,6 @@
      * from the outside to fully setup the View
      */
     public void loadData(ContactLoader.Result contactData) {
-        mContactUri = contactData.getLookupUri();
-
         View aboutView = findViewById(R.id.tab_about);
         View updateView = findViewById(R.id.tab_update);
 
@@ -187,17 +182,14 @@
         mTabs[0] = aboutTab;
         mTabs[1] = updatesTab;
 
-        // Retrieve the photo and star views for the "about" tab
+        // Retrieve the photo view for the "about" tab
         mPhotoView = (ImageView) aboutView.findViewById(R.id.photo);
-        mStarredView = (CheckBox) aboutView.findViewById(R.id.star);
-        mStarredView.setOnClickListener(this);
 
         // Retrieve the social update views for the "updates" tab
         mStatusView = (TextView) updateView.findViewById(R.id.status);
         mStatusDateView = (TextView) updateView.findViewById(R.id.status_date);
 
         ContactDetailDisplayUtils.setPhoto(mContext, contactData, mPhotoView);
-        ContactDetailDisplayUtils.setStarred(contactData, mStarredView);
         ContactDetailDisplayUtils.setSocialSnippetAndDate(mContext, contactData, mStatusView,
                 mStatusDateView);
     }
@@ -209,23 +201,6 @@
         mListener = listener;
     }
 
-    // TODO: The starred icon needs to move to the action bar.
-    @Override
-    public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.star: {
-                // Toggle "starred" state
-                // Make sure there is a contact
-                if (mContactUri != null) {
-                    Intent intent = ContactSaveService.createSetStarredIntent(
-                            getContext(), mContactUri, mStarredView.isChecked());
-                    getContext().startService(intent);
-                }
-                break;
-            }
-        }
-    }
-
     @Override
     public boolean onTouch(View v, MotionEvent event) {
         switch (event.getAction()) {
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 778fe42..fd557d5 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -20,7 +20,6 @@
 import com.android.contacts.R;
 import com.android.contacts.SpecialCharSequenceMgr;
 import com.android.contacts.activities.DialtactsActivity;
-import com.android.contacts.list.StrequentContactListFragment.Listener;
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.CallLogAsync;
 import com.android.phone.HapticFeedback;
@@ -111,9 +110,11 @@
     //Member variables for dialpad options
     private MenuItem m2SecPauseMenuItem;
     private MenuItem mWaitMenuItem;
+    private MenuItem mCallSettingsItem;
     private static final int MENU_ADD_CONTACTS = 1;
     private static final int MENU_2S_PAUSE = 2;
     private static final int MENU_WAIT = 3;
+    private static final int MENU_CALL_SETTINGS = 4;
 
     private boolean mHasVoicemail = false;
 
@@ -529,18 +530,27 @@
                 .setIcon(R.drawable.ic_menu_2sec_pause);
         mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
                 .setIcon(R.drawable.ic_menu_wait);
+        // TODO: icon
+        mCallSettingsItem = menu.add(0, MENU_CALL_SETTINGS, 0, R.string.call_settings);
     }
 
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
-        // If we have not been inflated yet, there is no menu
-        if (mDialpadChooser == null) return;
+        if (mDialpadChooser == null || mDigits == null) {
+            // The layout itself isn't ready yet. Let's ignore this call.
+            return;
+        }
 
+        // We show "Call Settings" menu every time
+        mCallSettingsItem.setVisible(true);
+        Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
+        settingsIntent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
+        mCallSettingsItem.setIntent(settingsIntent);
+
+        // We show "add to contacts", "2sec pause", and "add wait" menus only when the user is
+        // seeing usual dialpads and has typed at least one digit.
         // We never show a menu if the "choose dialpad" UI is up.
-        // Otherwise the menu is allowed (see onPrepareOptionsMenu() below.)
-        if (!dialpadChooserVisible()) return;
-
-        if (isDigitsEmpty()) {
+        if (dialpadChooserVisible() || isDigitsEmpty()) {
             mAddToContactMenuItem.setVisible(false);
             m2SecPauseMenuItem.setVisible(false);
             mWaitMenuItem.setVisible(false);
@@ -1020,7 +1030,7 @@
      * @return true if the phone is "in use", meaning that at least one line
      *              is active (ie. off hook or ringing or dialing).
      */
-    private boolean phoneIsInUse() {
+    public static boolean phoneIsInUse() {
         boolean phoneInUse = false;
         try {
             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index ba2794d..91792df 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -272,7 +272,9 @@
 
         // If anything was left unsaved, save it now but keep the editor open.
         if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
-            save(SaveMode.RELOAD);
+            if (mStatus != SaveMode.JOIN) {
+                save(SaveMode.RELOAD);
+            }
         }
     }
 
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index 95a8c2b..62b678c 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -31,6 +31,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Directory;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -320,9 +321,16 @@
 
     /**
      * Updates partitions according to the directory meta-data contained in the supplied
-     * cursor.  Takes ownership of the cursor and will close it.
+     * cursor.
      */
     public void changeDirectories(Cursor cursor) {
+        if (cursor.getCount() == 0) {
+            // Directory table must have at least local directory, without which this adapter will
+            // enter very weird state.
+            Log.e(TAG, "Directory search loader returned an empty cursor, which implies we have " +
+                    "no directory entries.", new RuntimeException());
+            return;
+        }
         HashSet<Long> directoryIds = new HashSet<Long>();
 
         int idColumnIndex = cursor.getColumnIndex(Directory._ID);
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index feb308c..86f6b33 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -102,7 +102,7 @@
             DisplayType displayType) {
         mListener = listener;
         mContext = context;
-        mColumnCount = numCols;
+        mColumnCount = (displayType == DisplayType.FREQUENT_ONLY ? 1 : numCols);
         mDisplayType = displayType;
 
         bindColumnIndices();
@@ -112,6 +112,14 @@
         mPhotoManager = photoLoader;
     }
 
+    public void setColumnCount(int columnCount) {
+        mColumnCount = columnCount;
+    }
+
+    public void setDisplayType(DisplayType displayType) {
+        mDisplayType = displayType;
+    }
+
     /**
      * Sets the column indices for expected {@link Cursor}
      * based on {@link DisplayType}.
@@ -202,7 +210,6 @@
             // Adding Containter that has multi columns
             rowCount += getNumRows(mContacts.size());
         }
-
         // Adding Divider Row if Neccessary
         if (mDisplayType == DisplayType.STREQUENT && mContacts.size() > 0) rowCount++;
 
@@ -232,9 +239,8 @@
         if (contactIndex < mContacts2.size()) {
             contactList = mContacts2;
         } else {
-            if (mDisplayType == DisplayType.STREQUENT) {
-                contactIndex = (position - mDividerRowIndex - 1) * mColumnCount;
-
+            if (mDisplayType == DisplayType.STREQUENT ||
+                    mDisplayType == DisplayType.FREQUENT_ONLY) {
                 resultList.add(mContacts.get(position - mDividerRowIndex - 1));
                 return resultList;
             }
@@ -385,9 +391,9 @@
             for (int columnCounter = 0; columnCounter < columnCount; columnCounter++) {
                 ContactEntry entry =
                         columnCounter < list.size() ? list.get(columnCounter) : null;
-                addTileFromEntry(entry, columnCounter);
+                        addTileFromEntry(entry, columnCounter);
+                }
             }
-        }
 
         private void addTileFromEntry(ContactEntry entry, int tileIndex) {
             ContactTileView contactTile;
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index da1351f..883e3f0 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -45,7 +45,6 @@
     private ContactPhotoManager mPhotoManager = null;
     /**
      * Is set to true if the {@link ContactTileView} is a square.
-     * A {@link ViewType#REGULAR} is displayed as a square.
      */
     private boolean mIsSquare;
 
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index c159ac8..fee1064 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -60,6 +60,7 @@
     private CharSequence mUnknownNameText;
     private int mDisplayNameColumnIndex;
     private int mAlternativeDisplayNameColumnIndex;
+    private boolean mHighlightSearchPrefix;
 
     public PhoneNumberListAdapter(Context context) {
         super(context);
@@ -157,6 +158,9 @@
     protected void bindView(View itemView, int partition, Cursor cursor, int position) {
         ContactListItemView view = (ContactListItemView)itemView;
 
+        view.setHighlightedPrefix(mHighlightSearchPrefix && isSearchMode() ?
+                getUpperCaseQueryString() : null);
+
         // Look at elements before and after this position, checking if contact IDs are same.
         // If they have one same contact ID, it means they can be grouped.
         //
@@ -245,4 +249,8 @@
     protected void unbindPhoto(final ContactListItemView view) {
         view.removePhotoView(true, false);
     }
+
+    public void setHighlightSearchPrefix(boolean highlight) {
+        mHighlightSearchPrefix = highlight;
+    }
 }
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
index a38f734..b40f80a 100644
--- a/src/com/android/contacts/list/PhoneNumberPickerFragment.java
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -111,4 +111,13 @@
     public void onPickerResult(Intent data) {
         mListener.onPickPhoneNumberAction(data.getData());
     }
+
+    public void setHighlightSearchPrefix(boolean highlight) {
+        if (!isLegacyCompatibilityMode()) {
+            PhoneNumberListAdapter adapter = (PhoneNumberListAdapter)getAdapter();
+            adapter.setHighlightSearchPrefix(highlight);
+        } else {
+            // Not supported.
+        }
+    }
 }
diff --git a/src/com/android/contacts/list/StrequentContactListFragment.java b/src/com/android/contacts/list/StrequentContactListFragment.java
index d226950..e0f0dc1 100644
--- a/src/com/android/contacts/list/StrequentContactListFragment.java
+++ b/src/com/android/contacts/list/StrequentContactListFragment.java
@@ -73,6 +73,14 @@
         getLoaderManager().restartLoader(LOADER_STREQUENT, null, mStrequentLoaderListener);
     }
 
+    public void setColumnCount(int columnCount) {
+        mAdapter.setColumnCount(columnCount);
+    }
+
+    public void setDisplayType(DisplayType displayType) {
+        mAdapter.setDisplayType(displayType);
+    }
+
     /**
      * The listener for the strequent meta data loader.
      */
diff --git a/src/com/android/contacts/util/ExpirableCache.java b/src/com/android/contacts/util/ExpirableCache.java
new file mode 100644
index 0000000..0ee3bbe
--- /dev/null
+++ b/src/com/android/contacts/util/ExpirableCache.java
@@ -0,0 +1,263 @@
+/*
+ * 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.util;
+
+import android.util.LruCache;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.concurrent.Immutable;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * An LRU cache in which all items can be marked as expired at a given time and it is possible to
+ * query whether a particular cached value is expired or not.
+ * <p>
+ * A typical use case for this is caching of values which are expensive to compute but which are
+ * still useful when out of date.
+ * <p>
+ * Consider a cache for contact information:
+ * <pre>{@code
+ *     private ExpirableCache<String, Contact> mContactCache;}</pre>
+ * which stores the contact information for a given phone number.
+ * <p>
+ * When we need to store contact information for a given phone number, we can look up the info in
+ * the cache:
+ * <pre>{@code
+ *     CachedValue<Contact> cachedContact = mContactCache.getCachedValue(phoneNumber);
+ * }</pre>
+ * We might also want to fetch the contact information again if the item is expired.
+ * <pre>
+ *     if (cachedContact.isExpired()) {
+ *         fetchContactForNumber(phoneNumber,
+ *                 new FetchListener() {
+ *                     &#64;Override
+ *                     public void onFetched(Contact contact) {
+ *                         mContactCache.put(phoneNumber, contact);
+ *                     }
+ *                 });
+ *     }</pre>
+ * and insert it back into the cache when the fetch completes.
+ * <p>
+ * At a certain point we want to expire the content of the cache because we know the content may
+ * no longer be up-to-date, for instance, when resuming the activity this is shown into:
+ * <pre>
+ *     &#64;Override
+ *     protected onResume() {
+ *         // We were paused for some time, the cached value might no longer be up to date.
+ *         mContactCache.expireAll();
+ *         super.onResume();
+ *     }
+ * </pre>
+ * The values will be still available from the cache, but they will be expired.
+ * <p>
+ * If interested only in the value itself, not whether it is expired or not, one should use the
+ * {@link #getPossiblyExpired(Object)} method. If interested only in non-expired values, one should
+ * use the {@link #get(Object)} method instead.
+ * <p>
+ * This class wraps around an {@link LruCache} instance: it follows the {@link LruCache} behavior
+ * for evicting items when the cache is full. It is possible to supply your own subclass of LruCache
+ * by using the {@link #create(LruCache)} method, which can define a custom expiration policy.
+ * Since the underlying cache maps keys to cached values it can determine which items are expired
+ * and which are not, allowing for an implementation that evicts expired items before non expired
+ * ones.
+ * <p>
+ * This class is thread-safe.
+ *
+ * @param <K> the type of the keys
+ * @param <V> the type of the values
+ */
+@ThreadSafe
+public class ExpirableCache<K, V> {
+    /**
+     * A cached value stored inside the cache.
+     * <p>
+     * It provides access to the value stored in the cache but also allows to check whether the
+     * value is expired.
+     *
+     * @param <V> the type of value stored in the cache
+     */
+    public interface CachedValue<V> {
+        /** Returns the value stored in the cache for a given key. */
+        public V getValue();
+
+        /**
+         * Checks whether the value, while still being present in the cache, is expired.
+         *
+         * @return true if the value is expired
+         */
+        public boolean isExpired();
+    }
+
+    /**
+     * Cached values storing the generation at which they were added.
+     */
+    @Immutable
+    private static class GenerationalCachedValue<V> implements ExpirableCache.CachedValue<V> {
+        /** The value stored in the cache. */
+        public final V mValue;
+        /** The generation at which the value was added to the cache. */
+        private final int mGeneration;
+        /** The atomic integer storing the current generation of the cache it belongs to. */
+        private final AtomicInteger mCacheGeneration;
+
+        /**
+         * @param cacheGeneration the atomic integer storing the generation of the cache in which
+         *        this value will be stored
+         */
+        public GenerationalCachedValue(V value, AtomicInteger cacheGeneration) {
+            mValue = value;
+            mCacheGeneration = cacheGeneration;
+            // Snapshot the current generation.
+            mGeneration = mCacheGeneration.get();
+        }
+
+        @Override
+        public V getValue() {
+            return mValue;
+        }
+
+        @Override
+        public boolean isExpired() {
+            return mGeneration != mCacheGeneration.get();
+        }
+    }
+
+    /** The underlying cache used to stored the cached values. */
+    private LruCache<K, CachedValue<V>> mCache;
+
+    /**
+     * The current generation of items added to the cache.
+     * <p>
+     * Items in the cache can belong to a previous generation, but in that case they would be
+     * expired.
+     *
+     * @see ExpirableCache.CachedValue#isExpired()
+     */
+    private final AtomicInteger mGeneration;
+
+    private ExpirableCache(LruCache<K, CachedValue<V>> cache) {
+        mCache = cache;
+        mGeneration = new AtomicInteger(0);
+    }
+
+    /**
+     * Returns the cached value for the given key, or null if no value exists.
+     * <p>
+     * The cached value gives access both to the value associated with the key and whether it is
+     * expired or not.
+     * <p>
+     * If not interested in whether the value is expired, use {@link #getPossiblyExpired(Object)}
+     * instead.
+     * <p>
+     * If only wants values that are not expired, use {@link #get(Object)} instead.
+     *
+     * @param key the key to look up
+     */
+    public CachedValue<V> getCachedValue(K key) {
+        return mCache.get(key);
+    }
+
+    /**
+     * Returns the value for the given key, or null if no value exists.
+     * <p>
+     * When using this method, it is not possible to determine whether the value is expired or not.
+     * Use {@link #getCachedValue(Object)} to achieve that instead. However, if using
+     * {@link #getCachedValue(Object)} to determine if an item is expired, one should use the item
+     * within the {@link CachedValue} and not call {@link #getPossiblyExpired(Object)} to get the
+     * value afterwards, since that is not guaranteed to return the same value or that the newly
+     * returned value is in the same state.
+     *
+     * @param key the key to look up
+     */
+    public V getPossiblyExpired(K key) {
+        CachedValue<V> cachedValue = getCachedValue(key);
+        return cachedValue == null ? null : cachedValue.getValue();
+    }
+
+    /**
+     * Returns the value for the given key only if it is not expired, or null if no value exists or
+     * is expired.
+     * <p>
+     * This method will return null if either there is no value associated with this key or if the
+     * associated value is expired.
+     *
+     * @param key the key to look up
+     */
+    public V get(K key) {
+        CachedValue<V> cachedValue = getCachedValue(key);
+        return cachedValue == null || cachedValue.isExpired() ? null : cachedValue.getValue();
+    }
+
+    /**
+     * Puts an item in the cache.
+     * <p>
+     * Newly added item will not be expired until {@link #expireAll()} is next called.
+     *
+     * @param key the key to look up
+     * @param value the value to associate with the key
+     */
+    public void put(K key, V value) {
+        mCache.put(key, newCachedValue(value));
+    }
+
+    /**
+     * Mark all items currently in the cache as expired.
+     * <p>
+     * Newly added items after this call will be marked as not expired.
+     * <p>
+     * Expiring the items in the cache does not imply they will be evicted.
+     */
+    public void expireAll() {
+        mGeneration.incrementAndGet();
+    }
+
+    /**
+     * Creates a new {@link CachedValue} instance to be stored in this cache.
+     * <p>
+     * Implementation of {@link LruCache#create(K)} can use this method to create a new entry.
+     */
+    public CachedValue<V> newCachedValue(V value) {
+        return new GenerationalCachedValue<V>(value, mGeneration);
+    }
+
+    /**
+     * Creates a new {@link ExpirableCache} that wraps the given {@link LruCache}.
+     * <p>
+     * The created cache takes ownership of the cache passed in as an argument.
+     *
+     * @param <K> the type of the keys
+     * @param <V> the type of the values
+     * @param cache the cache to store the value in
+     * @return the newly created expirable cache
+     * @throws IllegalArgumentException if the cache is not empty
+     */
+    public static <K, V> ExpirableCache<K, V> create(LruCache<K, CachedValue<V>> cache) {
+        return new ExpirableCache<K, V>(cache);
+    }
+
+    /**
+     * Creates a new {@link ExpirableCache} with the given maximum size.
+     *
+     * @param <K> the type of the keys
+     * @param <V> the type of the values
+     * @return the newly created expirable cache
+     */
+    public static <K, V> ExpirableCache<K, V> create(int maxSize) {
+        return create(new LruCache<K, CachedValue<V>>(maxSize));
+    }
+}
diff --git a/tests/src/com/android/contacts/util/ExpirableCacheTest.java b/tests/src/com/android/contacts/util/ExpirableCacheTest.java
new file mode 100644
index 0000000..309cc0e
--- /dev/null
+++ b/tests/src/com/android/contacts/util/ExpirableCacheTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.util;
+
+import com.android.contacts.util.ExpirableCache.CachedValue;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LruCache;
+
+/**
+ * Unit tests for {@link ExpirableCache}.
+ */
+@SmallTest
+public class ExpirableCacheTest extends AndroidTestCase {
+    /** The object under test. */
+    private ExpirableCache<String, Integer> mCache;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        LruCache<String, CachedValue<Integer>> lruCache =
+            new LruCache<String, ExpirableCache.CachedValue<Integer>>(20);
+        mCache = ExpirableCache.create(lruCache);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mCache = null;
+        super.tearDown();
+    }
+
+    public void testPut() {
+        mCache.put("a", 1);
+        mCache.put("b", 2);
+        assertEquals(1, mCache.getPossiblyExpired("a").intValue());
+        assertEquals(2, mCache.getPossiblyExpired("b").intValue());
+        mCache.put("a", 3);
+        assertEquals(3, mCache.getPossiblyExpired("a").intValue());
+    }
+
+    public void testGet_NotExisting() {
+        assertNull(mCache.getPossiblyExpired("a"));
+        mCache.put("b", 1);
+        assertNull(mCache.getPossiblyExpired("a"));
+    }
+
+    public void testGet_Expired() {
+        mCache.put("a", 1);
+        assertEquals(1, mCache.getPossiblyExpired("a").intValue());
+        mCache.expireAll();
+        assertEquals(1, mCache.getPossiblyExpired("a").intValue());
+    }
+
+    public void testGetNotExpired_NotExisting() {
+        assertNull(mCache.get("a"));
+        mCache.put("b", 1);
+        assertNull(mCache.get("a"));
+    }
+
+    public void testGetNotExpired_Expired() {
+        mCache.put("a", 1);
+        assertEquals(1, mCache.get("a").intValue());
+        mCache.expireAll();
+        assertNull(mCache.get("a"));
+    }
+
+    public void testGetCachedValue_NotExisting() {
+        assertNull(mCache.getCachedValue("a"));
+        mCache.put("b", 1);
+        assertNull(mCache.getCachedValue("a"));
+    }
+
+    public void testGetCachedValue_Expired() {
+        mCache.put("a", 1);
+        assertFalse("Should not be expired", mCache.getCachedValue("a").isExpired());
+        mCache.expireAll();
+        assertTrue("Should be expired", mCache.getCachedValue("a").isExpired());
+    }
+
+    public void testGetChangedValue_PutAfterExpired() {
+        mCache.put("a", 1);
+        mCache.expireAll();
+        mCache.put("a", 1);
+        assertFalse("Should not be expired", mCache.getCachedValue("a").isExpired());
+    }
+
+    public void testComputingCache() {
+        // Creates a cache in which all unknown values default to zero.
+        mCache = ExpirableCache.create(
+                new LruCache<String, ExpirableCache.CachedValue<Integer>>(10) {
+                    @Override
+                    protected CachedValue<Integer> create(String key) {
+                        return mCache.newCachedValue(0);
+                    }
+                });
+
+        // The first time we request a new value, we add it to the cache.
+        CachedValue<Integer> cachedValue = mCache.getCachedValue("a");
+        assertNotNull("Should have been created implicitly", cachedValue);
+        assertEquals(0, cachedValue.getValue().intValue());
+        assertFalse("Should not be expired", cachedValue.isExpired());
+
+        // If we expire all the values, the implicitly created value will also be marked as expired.
+        mCache.expireAll();
+        CachedValue<Integer> expiredCachedValue = mCache.getCachedValue("a");
+        assertNotNull("Should have been created implicitly", expiredCachedValue);
+        assertEquals(0, expiredCachedValue.getValue().intValue());
+        assertTrue("Should be expired", expiredCachedValue.isExpired());
+    }
+}