Merge change I53f94fc2 into eclair

* changes:
  Tweaked the dialer screen to match the designer's PPL.
diff --git a/res/drawable-hdpi-finger/ic_join.png b/res/drawable-hdpi-finger/ic_join.png
new file mode 100644
index 0000000..8f140d4
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_join.png
Binary files differ
diff --git a/res/drawable-hdpi/bg_infobar_new.9.png b/res/drawable-hdpi/bg_infobar_new.9.png
new file mode 100644
index 0000000..104ced9
--- /dev/null
+++ b/res/drawable-hdpi/bg_infobar_new.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_join.png b/res/drawable-mdpi-finger/ic_join.png
new file mode 100644
index 0000000..177a582
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_join.png
Binary files differ
diff --git a/res/drawable-mdpi/bg_infobar_new.9.png b/res/drawable-mdpi/bg_infobar_new.9.png
new file mode 100644
index 0000000..f3a83d4
--- /dev/null
+++ b/res/drawable-mdpi/bg_infobar_new.9.png
Binary files differ
diff --git a/res/layout-finger/contact_card_layout.xml b/res/layout-finger/contact_card_layout.xml
index fe931ec..3f090f5 100644
--- a/res/layout-finger/contact_card_layout.xml
+++ b/res/layout-finger/contact_card_layout.xml
@@ -36,16 +36,6 @@
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"/>
         
-        <TextView android:id="@+id/account_name"
-            style="?android:attr/listSeparatorTextViewStyle"
-            android:textColor="@*android:color/dim_foreground_dark"
-            android:textSize="12sp"
-            android:textStyle="normal"
-            android:background="@drawable/section_dark"
-            android:paddingLeft="7dp"
-            android:gravity="left|center_vertical"
-            android:visibility="gone" />
-
         <FrameLayout android:id="@android:id/tabcontent"
             android:layout_width="fill_parent"
             android:layout_height="0dip"
diff --git a/res/layout-finger/contacts_list_content_join.xml b/res/layout-finger/contacts_list_content_join.xml
index ce82d2c..95f9c20 100644
--- a/res/layout-finger/contacts_list_content_join.xml
+++ b/res/layout-finger/contacts_list_content_join.xml
@@ -25,14 +25,15 @@
             android:layout_height="wrap_content" 
             android:orientation="horizontal"
             android:background="@*android:drawable/title_bar_medium"
-            android:padding="5dip"
+            android:paddingLeft="5dip"
+            android:paddingRight="5dip"
             android:gravity="center_vertical"
             >
     
         <ImageView
             android:layout_width="48dip"
             android:layout_height="48dip"
-            android:src="@drawable/ic_menu_merge"
+            android:src="@drawable/ic_join"
             android:gravity="center"
             android:scaleType="fitCenter"
         />
@@ -48,9 +49,11 @@
                 android:textAppearance="?android:attr/textAppearanceMedium"
             />
             <TextView
+                android:id="@+id/join_contact_blurb"
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/blurbJoinContactDataWith"
+                android:layout_marginTop="-2dip"
+                android:maxLines="2"
                 android:textAppearance="?android:attr/textAppearanceSmall"
             />
         </LinearLayout>
diff --git a/res/layout-finger/tab_account_name.xml b/res/layout-finger/tab_account_name.xml
new file mode 100644
index 0000000..dc99af1
--- /dev/null
+++ b/res/layout-finger/tab_account_name.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<!-- looks like Widget.TextView.ListSeparator -->
+<TextView  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/account_name"
+    android:background="@drawable/bg_infobar_new"
+    android:layout_width="fill_parent"
+    android:layout_height="@dimen/account_name_height"
+    android:layout_below="@+id/tab_scroll_view"
+    android:textStyle="normal"
+    android:textColor="@*android:color/dim_foreground_dark"
+    android:textSize="12sp"
+    android:gravity="left|center_vertical"
+    android:paddingLeft="7dip"
+/>
diff --git a/res/layout-finger/tab_layout.xml b/res/layout-finger/tab_layout.xml
index 23359b7..9a793c3 100644
--- a/res/layout-finger/tab_layout.xml
+++ b/res/layout-finger/tab_layout.xml
@@ -15,6 +15,7 @@
 -->
 
 <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tab_scroll_view"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:layout_alignParentLeft="true"
diff --git a/res/layout-finger/total_contacts.xml b/res/layout-finger/total_contacts.xml
index 1221ef3..ab15ce1 100644
--- a/res/layout-finger/total_contacts.xml
+++ b/res/layout-finger/total_contacts.xml
@@ -24,6 +24,5 @@
     android:textStyle="normal"
     android:background="@drawable/section_dark"
     android:paddingLeft="7dp"
-    android:gravity="left|center_vertical"
-    android:visibility="gone"
+    android:gravity="center|center_vertical"
 />
\ No newline at end of file
diff --git a/res/layout/act_edit.xml b/res/layout/act_edit.xml
index 47f1454..250a0e1 100644
--- a/res/layout/act_edit.xml
+++ b/res/layout/act_edit.xml
@@ -37,16 +37,6 @@
             android:layout_width="fill_parent"
             android:layout_height="wrap_content" />
 
-        <TextView android:id="@+id/account_name"
-            style="?android:attr/listSeparatorTextViewStyle"
-            android:textColor="@*android:color/dim_foreground_dark"
-            android:textSize="12sp"
-            android:textStyle="normal"
-            android:background="@drawable/section_dark"
-            android:paddingLeft="7dp"
-            android:gravity="left|center_vertical"
-            android:visibility="gone" />
-        
         <include
             android:id="@android:id/tabcontent"
             android:layout_width="fill_parent"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0742250..4361fd4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,4 +20,5 @@
         
     <!-- The height of the ScrollingTabWidget -->
     <dimen name="tab_height">40dip</dimen>
+    <dimen name="account_name_height">25dip</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fdbaab6..da7bb88 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -139,11 +139,10 @@
     <string name="titleJoinAggregate">Join contact</string>
 
     <!-- Heading of the Join Contact screen -->
-    <string name="titleJoinContactDataWith">Join contact data with:</string>
+    <string name="titleJoinContactDataWith">Join contacts</string>
 
     <!-- Info blurb on the Join Contact screen -->
-    <string name="blurbJoinContactDataWith">Contact data will be combined with
-        another contact selected from the list.</string>
+    <string name="blurbJoinContactDataWith">Select the contact you want to join with <xliff:g id="name">%s</xliff:g>.</string>
 
     <!-- List separator for the Join Contact list: Suggestions -->
     <string name="separatorJoinAggregateSuggestions">Suggestions</string>
@@ -334,7 +333,7 @@
     <string name="showFilterPhonesDescrip">Only display contacts that have phone numbers</string>
 
     <!-- The header over the list of all contacts groups -->
-    <string name="headerContactGroups">Contact groups</string>
+    <string name="headerContactGroups">Choose contacts to display</string>
 
     <!-- The description of a group with the total number of contacts -->
     <plurals name="groupDescrip">
@@ -361,8 +360,8 @@
     <!-- Displayed in a spinner dialog after the user creates a contact and it's being saved to the database -->
     <string name="savingContact">Saving contact\u2026</string>
 
-    <!-- Displayed in a spinner dialog as user changes to display groups are saved -->
-    <string name="savingDisplayGroups">Saving display groups\u2026</string>
+    <!-- Displayed in a spinner dialog as user changes to display options are saved -->
+    <string name="savingDisplayGroups">Saving display options\u2026</string>
 
     <!-- Toast displayed when a contact is created -->
     <string name="contactCreatedToast">Contact created.</string>
@@ -969,9 +968,12 @@
     <string name="dialog_sync_add">Add sync group</string>
     <string name="display_more_groups">More groups\u2026</string>
 
+    <!-- List title for a special contacts group that covers all contacts.-->
+    <string name="display_ungrouped">All Other Contacts</string>
+
     <!-- List title for a special contacts group that covers all contacts that
-         aren't members of any other group.  -->
-    <string name="display_ungrouped">(Ungrouped contacts)</string>
+         aren't members of any other group. -->
+    <string name="display_all_contacts">All Contacts</string>
 
     <!-- Warning message given to users just before they remove a currently syncing
          group that would also cause all ungrouped contacts to stop syncing. -->
@@ -1153,6 +1155,6 @@
     <string name="split_explanation">Make this data its own contact.</string>
     
     <!-- Formatting string for account name -->
-    <string name="account_name_format">From account: <xliff:g id="account" example="user@gmail.com">%s</xliff:g></string>
+    <string name="account_name_format">From <xliff:g id="source" example="Gmail">%1$s</xliff:g> account: <xliff:g id="account" example="user@gmail.com">%2$s</xliff:g></string>
 
 </resources>
diff --git a/src/com/android/contacts/Collapser.java b/src/com/android/contacts/Collapser.java
index db1da1f..3872dfd 100644
--- a/src/com/android/contacts/Collapser.java
+++ b/src/com/android/contacts/Collapser.java
@@ -39,38 +39,42 @@
      */
     public interface Collapsible<T> {
         public boolean collapseWith(T t);
-        public String getCollapseKey();
+        public boolean shouldCollapseWith(T t);
     }
 
     /**
      * Collapses a list of Collapsible items into a list of collapsed items. Items are collapsed
-     * if they produce equal collapseKeys {@Link Collapsible#getCollapseKey()}, and are collapsed
-     * through the {@Link Collapsible#doCollapseWith(Object)} function implemented by the data item.
+     * if {@link Collapsible#shouldCollapseWith(Object) return strue, and are collapsed
+     * through the {@Link Collapsible#collapseWith(Object)} function implemented by the data item.
      *
      * @param list ArrayList of Objects of type <T extends Collapsible<T>> to be collapsed.
      */
     public static <T extends Collapsible<T>> void collapseList(ArrayList<T> list) {
-        HashMap<String, T> collapseMap = new HashMap<String, T>();
-        ArrayList<String> collapseKeys = new ArrayList<String>();
 
         int listSize = list.size();
-        for (int j = 0; j < listSize; j++) {
-            T entry = list.get(j);
-            String collapseKey = entry.getCollapseKey();
-            if (!collapseMap.containsKey(collapseKey)) {
-                collapseMap.put(collapseKey, entry);
-                collapseKeys.add(collapseKey);
-            } else {
-                collapseMap.get(collapseKey).collapseWith(entry);
+
+        for (int i = 0; i < listSize; i++) {
+            T iItem = list.get(i);
+            if (iItem != null) {
+                for (int j = i + 1; j < listSize; j++) {
+                    T jItem = list.get(j);
+                    if (jItem != null) {
+                        if (iItem.shouldCollapseWith(jItem)) {
+                            iItem.collapseWith(jItem);
+                            list.set(j, null);
+                        }
+                    }
+                }
             }
         }
 
-        if (collapseKeys.size() < listSize) {
-            list.clear();
-            Iterator<String> itr = collapseKeys.iterator();
-            while (itr.hasNext()) {
-                list.add(collapseMap.get(itr.next()));
+        // Remove the null items
+        Iterator<T> itr = list.iterator();
+        while (itr.hasNext()) {
+            if (itr.next() == null) {
+                itr.remove();
             }
         }
+
     }
 }
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index cc2f02f..aa6654d 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -86,7 +86,6 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView;
@@ -161,6 +160,14 @@
      */
     public static final String EXTRA_AGGREGATE_ID =
             "com.android.contacts.action.AGGREGATE_ID";
+    /**
+     * Used with {@link #JOIN_AGGREGATE} to give it the name of the aggregation target.
+     * <p>
+     * Type: STRING
+     */
+    public static final String EXTRA_AGGREGATE_NAME =
+            "com.android.contacts.action.AGGREGATE_NAME";
+
 
     public static final String AUTHORITIES_FILTER_KEY = "authorities";
 
@@ -551,6 +558,15 @@
 
         if (mMode == MODE_JOIN_CONTACT) {
             setContentView(R.layout.contacts_list_content_join);
+            TextView blurbView = (TextView)findViewById(R.id.join_contact_blurb);
+            String contactName = intent.getStringExtra(EXTRA_AGGREGATE_NAME);
+            if (contactName == null) {
+                contactName = "";
+            }
+
+            String blurb = getString(R.string.blurbJoinContactDataWith, contactName);
+            blurbView.setText(blurb);
+
         } else {
             setContentView(R.layout.contacts_list_content);
         }
@@ -567,14 +583,9 @@
             list.setTextFilterEnabled(true);
         }
 
-        final LayoutInflater inflater = getLayoutInflater();
-        if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
-            View totalContacts = inflater.inflate(R.layout.total_contacts, list, false);
-            list.addHeaderView(totalContacts);
-        }
-
         if ((mMode & MODE_MASK_CREATE_NEW) != 0) {
             // Add the header for creating a new contact
+            final LayoutInflater inflater = getLayoutInflater();
             View header = inflater.inflate(R.layout.create_new_contact, list, false);
             list.addHeaderView(header);
         }
@@ -586,11 +597,6 @@
         setListAdapter(mAdapter);
         getListView().setOnScrollListener(mAdapter);
 
-        if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
-            TextView totalContacts = (TextView) findViewById(R.id.totalContactsText);
-            totalContacts.setVisibility(View.VISIBLE);
-        }
-
         // We manually save/restore the listview state
         list.setSaveEnabled(false);
 
@@ -830,6 +836,10 @@
     @Override
     protected Dialog onCreateDialog(int id) {
         switch (id) {
+            case R.string.import_from_sim:
+            case R.string.import_from_sdcard: {
+                return AccountSelectionUtil.getSelectAccountDialog(this, id);
+            }
             case R.id.dialog_sdcard_not_found: {
                 AlertDialog.Builder builder = new AlertDialog.Builder(this)
                 .setTitle(R.string.no_sdcard_title)
@@ -1091,13 +1101,6 @@
                 getSystemService(Context.INPUT_METHOD_SERVICE);
         inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
 
-        if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
-            if (position == 0) {
-                return;
-            }
-            position--;
-        }
-
         if (mMode == MODE_INSERT_OR_EDIT_CONTACT) {
             Intent intent;
             if (position == 0) {
@@ -1541,7 +1544,10 @@
         mQueryHandler.setLoadingJoinSuggestions(false);
 
         String[] projection = getProjectionForQuery();
-        Uri uri = getUriToQuery();
+        Uri uri = getUriToQuery().buildUpon()
+                .appendQueryParameter(ContactsContract.REQUESTING_PACKAGE_PARAM_KEY,
+                        getCallingPackage())
+                .build();
 
         // Kick off the new query
         switch (mMode) {
@@ -1754,7 +1760,7 @@
                             getColumnIndex(Phone.IS_SUPER_PRIMARY)) != 0) {
                         // Found super primary, call it.
                         phone = phonesCursor.
-                        getString(phonesCursor.getColumnIndex(Phone.NUMBER));
+                                getString(phonesCursor.getColumnIndex(Phone.NUMBER));
                         break;
                     }
                 }
@@ -2068,6 +2074,9 @@
 
         @Override
         public int getItemViewType(int position) {
+            if (position == 0 && (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+                return IGNORE_ITEM_VIEW_TYPE;
+            }
             if (getSeparatorId(position) != 0) {
                 // We don't want the separator view to be recycled.
                 return IGNORE_ITEM_VIEW_TYPE;
@@ -2082,6 +2091,17 @@
                         "this should only be called when the cursor is valid");
             }
 
+            // handle the total contacts item
+            if (position == 0 && (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+                final LayoutInflater inflater = getLayoutInflater();
+                TextView totalContacts = (TextView) inflater.inflate(R.layout.total_contacts,
+                        parent, false);
+                int stringId = mDisplayOnlyPhones ? R.string.listTotalPhoneContacts
+                        : R.string.listTotalAllContacts;
+                totalContacts.setText(getString(stringId, getCount()));
+                return totalContacts;
+            }
+
             // Handle the separator specially
             int separatorId = getSeparatorId(position);
             if (separatorId != 0) {
@@ -2377,12 +2397,6 @@
             }
 
             super.changeCursor(cursor);
-            if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
-                TextView totalContacts = (TextView) findViewById(R.id.totalContactsText);
-                int stringId = mDisplayOnlyPhones
-                    ? R.string.listTotalPhoneContacts : R.string.listTotalAllContacts;
-                totalContacts.setText(getString(stringId, cursorCount));
-            }
             // Update the indexer for the fast scroll widget
             updateIndexer(cursor);
         }
@@ -2494,6 +2508,12 @@
 
         @Override
         public boolean isEnabled(int position) {
+            if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+                if (position == 0) {
+                    return false;
+                }
+                position--;
+            }
             if (mSuggestionsCursorCount > 0) {
                 return position != 0 && position != mSuggestionsCursorCount + 1;
             }
@@ -2516,6 +2536,9 @@
         }
 
         private int getRealPosition(int pos) {
+            if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+                pos--;
+            }
             if (mSuggestionsCursorCount != 0) {
                 // When showing suggestions, we have 2 additional list items: the "Suggestions"
                 // and "All contacts" separators.
diff --git a/src/com/android/contacts/PhoneDisambigDialog.java b/src/com/android/contacts/PhoneDisambigDialog.java
index 58d3721..b727c77 100644
--- a/src/com/android/contacts/PhoneDisambigDialog.java
+++ b/src/com/android/contacts/PhoneDisambigDialog.java
@@ -17,8 +17,9 @@
 package com.android.contacts;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.List;
+
+import com.android.contacts.Collapser.Collapsible;
 
 import android.app.AlertDialog;
 import android.content.ContentUris;
@@ -28,12 +29,13 @@
 import android.database.Cursor;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.telephony.PhoneNumberUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ArrayAdapter;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
-import android.widget.SimpleCursorAdapter;
+import android.widget.ListAdapter;
 
 /**
  * Class used for displaying a dialog with a list of phone numbers of which
@@ -47,6 +49,8 @@
     private AlertDialog mDialog;
     private boolean mSendSms;
     private Cursor mPhonesCursor;
+    private ListAdapter mPhonesAdapter;
+    private ArrayList<PhoneItem> mPhoneItemList;
 
     public PhoneDisambigDialog(Context context, Cursor phonesCursor) {
         this(context, phonesCursor, false /*make call*/);
@@ -57,6 +61,11 @@
         mSendSms = sendSms;
         mPhonesCursor = phonesCursor;
 
+        mPhoneItemList = makePhoneItemsList(phonesCursor);
+        Collapser.collapseList(mPhoneItemList);
+
+        mPhonesAdapter = new PhonesAdapter(mContext, mPhoneItemList);
+
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         View setPrimaryView = inflater.
@@ -66,9 +75,10 @@
 
         // Need to show disambig dialogue.
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext).
-            setCursor(mPhonesCursor, this, Phone.NUMBER).
-                    setTitle(sendSms ? R.string.sms_disambig_title : R.string.call_disambig_title).
-                    setView(setPrimaryView);
+                setAdapter(mPhonesAdapter, this).
+                        setTitle(sendSms ?
+                                R.string.sms_disambig_title : R.string.call_disambig_title).
+                        setView(setPrimaryView);
 
         mDialog = dialogBuilder.create();
     }
@@ -77,18 +87,25 @@
      * Show the dialog.
      */
     public void show() {
+        if (mPhoneItemList.size() == 1) {
+            // If there is only one after collapse, just select it, and close;
+            onClick(mDialog, 0);
+            return;
+        }
         mDialog.show();
     }
 
     public void onClick(DialogInterface dialog, int which) {
-        if (mPhonesCursor.moveToPosition(which)) {
-            long id = mPhonesCursor.getLong(mPhonesCursor.getColumnIndex(Data._ID));
-            String phone = mPhonesCursor.getString(mPhonesCursor.getColumnIndex(Phone.NUMBER));
+        if (mPhoneItemList.size() > which && which >= 0) {
+            PhoneItem phoneItem = mPhoneItemList.get(which);
+            long id = phoneItem.id;
+            String phone = phoneItem.phoneNumber;
+
             if (mMakePrimary) {
                 ContentValues values = new ContentValues(1);
                 values.put(Data.IS_SUPER_PRIMARY, 1);
-                mContext.getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, id),
-                        values, null, null);
+                mContext.getContentResolver().update(ContentUris.
+                        withAppendedId(Data.CONTENT_URI, id), values, null, null);
             }
 
             if (mSendSms) {
@@ -108,4 +125,56 @@
     public void onDismiss(DialogInterface dialog) {
         mPhonesCursor.close();
     }
+
+    private static class PhonesAdapter extends ArrayAdapter<PhoneItem> {
+
+        public PhonesAdapter(Context context, List<PhoneItem> objects) {
+            super(context, android.R.layout.simple_dropdown_item_1line,
+                    android.R.id.text1, objects);
+        }
+    }
+
+    private class PhoneItem implements Collapsible<PhoneItem> {
+
+        String phoneNumber;
+        long id;
+
+        public PhoneItem(String newPhoneNumber, long newId) {
+            phoneNumber = newPhoneNumber;
+            id = newId;
+        }
+
+        public boolean collapseWith(PhoneItem phoneItem) {
+            if (!shouldCollapseWith(phoneItem)) {
+                return false;
+            }
+            // Just keep the number and id we already have.
+            return true;
+        }
+
+        public boolean shouldCollapseWith(PhoneItem phoneItem) {
+            if (PhoneNumberUtils.compare(PhoneDisambigDialog.this.mContext,
+                    phoneNumber, phoneItem.phoneNumber)) {
+                return true;
+            }
+            return false;
+        }
+
+        public String toString() {
+            return phoneNumber;
+        }
+    }
+
+    private ArrayList<PhoneItem> makePhoneItemsList(Cursor phonesCursor) {
+        ArrayList<PhoneItem> phoneList = new ArrayList<PhoneItem>();
+
+        phonesCursor.moveToPosition(-1);
+        while (phonesCursor.moveToNext()) {
+            long id = phonesCursor.getLong(phonesCursor.getColumnIndex(Data._ID));
+            String phone = phonesCursor.getString(phonesCursor.getColumnIndex(Phone.NUMBER));
+            phoneList.add(new PhoneItem(phone, id));
+        }
+
+        return phoneList;
+    }
 }
diff --git a/src/com/android/contacts/ScrollingTabWidget.java b/src/com/android/contacts/ScrollingTabWidget.java
index 6974a6e..b45abe4 100644
--- a/src/com/android/contacts/ScrollingTabWidget.java
+++ b/src/com/android/contacts/ScrollingTabWidget.java
@@ -18,12 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -32,7 +27,6 @@
 import android.view.View.OnFocusChangeListener;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
 /*
@@ -82,6 +76,7 @@
         mTabsScrollWrapper = (HorizontalScrollView) mInflater.inflate(
                 R.layout.tab_layout, this, false);
         mTabsView = (TabStripView) mTabsScrollWrapper.findViewById(android.R.id.tabs);
+        View accountNameView = mInflater.inflate(R.layout.tab_account_name, this, false);
 
         mLeftArrowView.setVisibility(View.INVISIBLE);
         mRightArrowView.setVisibility(View.INVISIBLE);
@@ -89,6 +84,7 @@
         addView(mTabsScrollWrapper);
         addView(mLeftArrowView);
         addView(mRightArrowView);
+        addView(accountNameView);
     }
 
     @Override
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/TwelveKeyDialer.java
index d07d785..58ba9d8 100644
--- a/src/com/android/contacts/TwelveKeyDialer.java
+++ b/src/com/android/contacts/TwelveKeyDialer.java
@@ -1028,6 +1028,8 @@
                 digits.replace(selectionStart, selectionStart, newDigits);
             } else {
                 digits.replace(selectionStart, selectionEnd, newDigits);
+                // Unselect: back to a regular cursor, just pass the character inserted.
+                mDigits.setSelection(selectionStart + 1);
             }
         } else {
             int len = mDigits.length();
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 8275686..8172423 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -215,9 +215,9 @@
         mTabWidget.setTabSelectionListener(this);
         mTabWidget.setVisibility(View.GONE);
         mTabsVisible = false;
+        mAccountName = (TextView) mTabWidget.findViewById(R.id.account_name);
 
         mBelowHeader = findViewById(R.id.below_header);
-        mAccountName = (TextView) findViewById(R.id.account_name);
 
         mTabRawContactIdMap = new SparseArray<Long>();
 
@@ -397,12 +397,14 @@
             return;
         }
 
-        float tabHeight = getResources().getDimension(R.dimen.tab_height);
+        final Resources resources = getResources();
+        final float tabHeight = resources.getDimension(R.dimen.tab_height)
+            + resources.getDimension(R.dimen.account_name_height);
         if (show) {
             TranslateAnimation showAnimation = new TranslateAnimation(
                     Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
                     Animation.ABSOLUTE, -tabHeight, Animation.ABSOLUTE, 0);
-            showAnimation.setDuration(getResources().getInteger(
+            showAnimation.setDuration(resources.getInteger(
                     android.R.integer.config_longAnimTime));
 
             showAnimation.setAnimationListener(new AnimationListener() {
@@ -428,7 +430,7 @@
             TranslateAnimation hideTabsAnimation = new TranslateAnimation(
                     Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
                     Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -tabHeight);
-            hideTabsAnimation.setDuration(getResources().getInteger(
+            hideTabsAnimation.setDuration(resources.getInteger(
                     android.R.integer.config_longAnimTime));
             hideTabsAnimation.setAnimationListener(new AnimationListener() {
                 public void onAnimationEnd(Animation animation) {
@@ -446,7 +448,7 @@
             TranslateAnimation hideListAnimation = new TranslateAnimation(
                     Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
                     Animation.ABSOLUTE, tabHeight, Animation.ABSOLUTE, 0);
-            hideListAnimation.setDuration(getResources().getInteger(
+            hideListAnimation.setDuration(resources.getInteger(
                     android.R.integer.config_longAnimTime));
 
 
@@ -761,8 +763,15 @@
      * Shows a list of aggregates that can be joined into the currently viewed aggregate.
      */
     public void showJoinAggregateActivity() {
+        String displayName = null;
+        if (mCursor.moveToFirst()) {
+            displayName = mCursor.getString(0);
+        }
         Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
         intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, ContentUris.parseId(mUri));
+        if (displayName != null) {
+            intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
+        }
         startActivityForResult(intent, REQUEST_JOIN_CONTACT);
     }
 
@@ -945,13 +954,11 @@
                     continue;
                 }
 
-                if (mTabsVisible) {
-                    final String accountName = entValues.getAsString(RawContacts.ACCOUNT_NAME);
-                    mAccountName.setText(getString(R.string.account_name_format, accountName));
-                    mAccountName.setVisibility(View.VISIBLE);
-                } else {
-                    mAccountName.setVisibility(View.GONE);
-                }
+                final ContactsSource source = sources.getInflatedSource(accountType,
+                        ContactsSource.LEVEL_SUMMARY);
+                final String accountName = entValues.getAsString(RawContacts.ACCOUNT_NAME);
+                mAccountName.setText(getString(R.string.account_name_format,
+                        source.getDisplayLabel(this), accountName));
 
                 for (NamedContentValues subValue : entity.getSubValues()) {
                     ViewEntry entry = new ViewEntry();
@@ -1154,7 +1161,7 @@
     /**
      * A basic structure with the data for a contact entry in the list.
      */
-    static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
+    class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
         public String resPackageName = null;
         public int actionIcon = -1;
         public boolean isPrimary = false;
@@ -1169,7 +1176,7 @@
 
         public boolean collapseWith(ViewEntry entry) {
             // assert equal collapse keys
-            if (!getCollapseKey().equals(entry.getCollapseKey())) {
+            if (!shouldCollapseWith(entry)) {
                 return false;
             }
 
@@ -1201,16 +1208,43 @@
             return true;
         }
 
-        public String getCollapseKey() {
-            StringBuilder hashSb = new StringBuilder();
-            hashSb.append(data);
-            hashSb.append(mimetype);
-            hashSb.append((intent != null && intent.getAction() != null)
-                    ? intent.getAction() : "");
-            hashSb.append((secondaryIntent != null && secondaryIntent.getAction() != null)
-                    ? secondaryIntent.getAction() : "");
-            hashSb.append(actionIcon);
-            return hashSb.toString();
+        public boolean shouldCollapseWith(ViewEntry entry) {
+            if (entry == null) {
+                return false;
+            }
+
+            if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)
+                    && Phone.CONTENT_ITEM_TYPE.equals(entry.mimetype)) {
+                if (!PhoneNumberUtils.compare(ViewContactActivity.this, data, entry.data)) {
+                    return false;
+                }
+            } else {
+                if (!equals(data, entry.data)) {
+                    return false;
+                }
+            }
+
+            if (!equals(mimetype, entry.mimetype)
+                    || !intentCollapsible(intent, entry.intent)
+                    || !intentCollapsible(secondaryIntent, entry.secondaryIntent)
+                    || actionIcon != entry.actionIcon) {
+                return false;
+            }
+
+            return true;
+        }
+
+        private boolean equals(Object a, Object b) {
+            return a==b || (a != null && a.equals(b));
+        }
+
+        private boolean intentCollapsible(Intent a, Intent b) {
+            if (a == b) {
+                return true;
+            } else if ((a != null && b != null) && equals(a.getAction(), b.getAction())) {
+                return true;
+            }
+            return false;
         }
     }
 
diff --git a/src/com/android/contacts/ui/DisplayGroupsActivity.java b/src/com/android/contacts/ui/DisplayGroupsActivity.java
index dcbe0f6..7bd7b9d 100644
--- a/src/com/android/contacts/ui/DisplayGroupsActivity.java
+++ b/src/com/android/contacts/ui/DisplayGroupsActivity.java
@@ -187,6 +187,7 @@
      */
     protected static class GroupDelta extends ValuesDelta {
         private boolean mUngrouped = false;
+        private boolean mAccountHasGroups;
 
         private GroupDelta() {
             super();
@@ -197,7 +198,7 @@
          * {@link Settings#ACCOUNT_NAME} and {@link Settings#ACCOUNT_TYPE}.
          */
         public static GroupDelta fromSettings(ContentResolver resolver, String accountName,
-                String accountType) {
+                String accountType, boolean accountHasGroups) {
             final Uri settingsUri = Settings.CONTENT_URI.buildUpon()
                     .appendQueryParameter(Settings.ACCOUNT_NAME, accountName)
                     .appendQueryParameter(Settings.ACCOUNT_TYPE, accountType).build();
@@ -214,12 +215,12 @@
                     // Read existing values when present
                     values.put(Settings.SHOULD_SYNC, cursor.getInt(0));
                     values.put(Settings.UNGROUPED_VISIBLE, cursor.getInt(1));
-                    return fromBefore(values).setUngrouped();
+                    return fromBefore(values).setUngrouped(accountHasGroups);
                 } else {
                     // Nothing found, so treat as create
                     values.put(Settings.SHOULD_SYNC, DEFAULT_SHOULD_SYNC);
                     values.put(Settings.UNGROUPED_VISIBLE, DEFAULT_VISIBLE);
-                    return fromAfter(values).setUngrouped();
+                    return fromAfter(values).setUngrouped(accountHasGroups);
                 }
             } finally {
                 if (cursor != null) cursor.close();
@@ -240,8 +241,9 @@
             return entry;
         }
 
-        protected GroupDelta setUngrouped() {
+        protected GroupDelta setUngrouped(boolean accountHasGroups) {
             mUngrouped = true;
+            mAccountHasGroups = accountHasGroups;
             return this;
         }
 
@@ -270,7 +272,11 @@
 
         public CharSequence getTitle(Context context) {
             if (mUngrouped) {
-                return context.getText(R.string.display_ungrouped);
+                if (mAccountHasGroups) {
+                    return context.getText(R.string.display_ungrouped);
+                } else {
+                    return context.getText(R.string.display_all_contacts);
+                }
             } else {
                 final Integer titleRes = getAsInteger(Groups.TITLE_RES);
                 if (titleRes != null) {
@@ -359,9 +365,7 @@
             mName = accountName;
             mType = accountType;
 
-            // Create single entry handling ungrouped status
-            mUngrouped = GroupDelta.fromSettings(resolver, accountName, accountType);
-            addGroup(mUngrouped);
+            boolean hasGroups = false;
 
             final Uri groupsUri = Groups.CONTENT_URI.buildUpon()
                     .appendQueryParameter(Groups.ACCOUNT_NAME, accountName)
@@ -374,12 +378,17 @@
                     final ContentValues values = iterator.next().getEntityValues();
                     final GroupDelta group = GroupDelta.fromBefore(values);
                     addGroup(group);
+                    hasGroups = true;
                 }
             } catch (RemoteException e) {
                 Log.w(TAG, "Problem reading groups: " + e.toString());
             } finally {
                 if (iterator != null) iterator.close();
             }
+
+            // Create single entry handling ungrouped status
+            mUngrouped = GroupDelta.fromSettings(resolver, accountName, accountType, hasGroups);
+            addGroup(mUngrouped);
         }
 
         /**
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index bb10a8a..450f4a9 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -121,12 +121,12 @@
         // Header bar is filled later after queries finish
         mHeader = (ContactHeaderWidget)this.findViewById(R.id.contact_header_widget);
         mHeader.setContactHeaderListener(this);
-        mHeader.showStar(true);
+        mHeader.showStar(false);
         mHeader.enableClickListeners();
 
         mTabWidget = (ScrollingTabWidget)this.findViewById(R.id.tab_widget);
         mTabWidget.setTabSelectionListener(this);
-        mAccountName = (TextView)this.findViewById(R.id.account_name);
+        mAccountName = (TextView)mTabWidget.findViewById(R.id.account_name);
 
         // Build editor and listen for photo requests
         mEditor = (ContactEditorView)this.findViewById(android.R.id.tabcontent);
@@ -181,7 +181,7 @@
                 }
             } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
                 final long rawContactId = ContentUris.parseId(data);
-                selection = RawContacts._ID + "=" + rawContactId;
+                selection = Data.RAW_CONTACT_ID + "=" + rawContactId;
             }
 
             target.mQuerySelection = selection;
@@ -402,7 +402,8 @@
         final ContactsSource source = sources.getInflatedSource(accountType,
                 ContactsSource.LEVEL_CONSTRAINTS);
 
-        mAccountName.setText(getString(R.string.account_name_format, accountName));
+        mAccountName.setText(getString(R.string.account_name_format,
+                source.getDisplayLabel(this), accountName));
         mAccountName.setVisibility(View.VISIBLE);
 
         // Assign editor state based on entity and source
diff --git a/src/com/android/contacts/util/AccountSelectionUtil.java b/src/com/android/contacts/util/AccountSelectionUtil.java
index 86c04eb..cf83581 100644
--- a/src/com/android/contacts/util/AccountSelectionUtil.java
+++ b/src/com/android/contacts/util/AccountSelectionUtil.java
@@ -24,6 +24,7 @@
 
 import android.accounts.Account;
 import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -70,11 +71,11 @@
         }
     }
 
-    public static void displaySelectAccountDialog(Context context, int resId) {
-        displaySelectAccountDialog(context, resId, null);
+    public static Dialog getSelectAccountDialog(Context context, int resId) {
+        return getSelectAccountDialog(context, resId, null);
     }
 
-    public static void displaySelectAccountDialog(Context context, int resId,
+    public static Dialog getSelectAccountDialog(Context context, int resId,
             DialogInterface.OnCancelListener onCancelListener) {
         final Sources sources = Sources.getInstance(context);
         final List<Account> writableAccountList = sources.getAccounts(true);
@@ -119,11 +120,11 @@
 
         AccountSelectedListener accountSelectedListener =
             new AccountSelectedListener(context, writableAccountList, resId);
-        new AlertDialog.Builder(context)
+        return new AlertDialog.Builder(context)
             .setTitle(R.string.dialog_new_contact_account)
             .setSingleChoiceItems(accountAdapter, 0, accountSelectedListener)
             .setOnCancelListener(accountSelectedListener)
-            .show();
+            .create();
     }
 
     public static void doImport(Context context, int resId, Account account) {