Merge "Change commit() to commitAllowingStateLoss()"
diff --git a/res/layout-sw580dp/group_editor_view.xml b/res/layout-sw580dp/group_editor_view.xml
index 7874c8c..a31a36a 100644
--- a/res/layout-sw580dp/group_editor_view.xml
+++ b/res/layout-sw580dp/group_editor_view.xml
@@ -19,8 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingTop="50dip"
-    android:paddingLeft="50dip"
-    android:paddingRight="100dip"
+    android:paddingLeft="64dip"
+    android:paddingRight="64dip"
     android:orientation="horizontal"
     android:background="@color/background_primary">
 
@@ -39,11 +39,12 @@
             android:id="@+id/group_name"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:minHeight="48dip"
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:textStyle="bold"
             android:inputType="textCapWords"
             android:hint="@string/group_name_hint"
-            android:layout_marginBottom="5dip"/>
+            android:paddingLeft="16dip"/>
 
     </LinearLayout>
 
@@ -55,6 +56,11 @@
         android:orientation="vertical">
 
         <include
+            android:id="@+id/spacer"
+            layout="@layout/editor_account_header"
+            android:visibility="invisible"/>
+
+        <include
             layout="@layout/group_editor_autocomplete_view"
             android:id="@+id/add_member_field"/>
 
diff --git a/res/layout/editor_account_header.xml b/res/layout/editor_account_header.xml
index 5181708..dd4bc30 100644
--- a/res/layout/editor_account_header.xml
+++ b/res/layout/editor_account_header.xml
@@ -24,7 +24,7 @@
     android:orientation="horizontal"
     android:paddingTop="8dip"
     android:paddingBottom="8dip"
-    android:paddingLeft="16dip"
+    android:paddingLeft="@dimen/account_container_left_padding"
     android:paddingRight="16dip">
 
     <LinearLayout
diff --git a/res/layout/external_group_member_item.xml b/res/layout/external_group_member_item.xml
index 5fa81d0..1fba205 100644
--- a/res/layout/external_group_member_item.xml
+++ b/res/layout/external_group_member_item.xml
@@ -18,9 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
-    android:minHeight="58dip"
-    android:paddingLeft="10dip">
+    android:orientation="horizontal">
 
     <TextView
         android:id="@+id/name"
@@ -31,7 +29,8 @@
         android:gravity="center_vertical"
         android:paddingRight="3dip"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:focusable="true" />
+        android:focusable="true"
+        android:paddingLeft="@dimen/group_member_item_left_padding" />
 
     <QuickContactBadge
         android:id="@+id/badge"
diff --git a/res/layout/group_editor_autocomplete_view.xml b/res/layout/group_editor_autocomplete_view.xml
index 6b35cae..46539de 100644
--- a/res/layout/group_editor_autocomplete_view.xml
+++ b/res/layout/group_editor_autocomplete_view.xml
@@ -22,4 +22,6 @@
     android:layout_height="wrap_content"
     android:textAppearance="?android:attr/textAppearanceMedium"
     android:textColor="?android:attr/textColorPrimary"
-    android:hint="@string/enter_contact_name"/>
\ No newline at end of file
+    android:hint="@string/enter_contact_name"
+    android:minHeight="48dip"
+    android:paddingLeft="@dimen/group_editor_autocomplete_left_padding"/>
\ No newline at end of file
diff --git a/res/layout/group_editor_existing_member_list.xml b/res/layout/group_editor_existing_member_list.xml
index 9765de1..3933670 100644
--- a/res/layout/group_editor_existing_member_list.xml
+++ b/res/layout/group_editor_existing_member_list.xml
@@ -21,5 +21,6 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_marginTop="5dip"
-    android:cacheColorHint="@android:color/transparent"
-    android:divider="@null" />
\ No newline at end of file
+    android:layout_marginLeft="@dimen/group_editor_member_list_left_margin"
+    android:layout_marginRight="@dimen/group_editor_member_list_right_margin"
+    android:cacheColorHint="@android:color/transparent" />
\ No newline at end of file
diff --git a/res/layout/group_editor_fragment.xml b/res/layout/group_editor_fragment.xml
index 3d72384..3a69e05 100644
--- a/res/layout/group_editor_fragment.xml
+++ b/res/layout/group_editor_fragment.xml
@@ -22,7 +22,4 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/background_primary"
-    android:paddingTop="5dip"
-    android:paddingLeft="5dip"
-    android:paddingRight="5dip" />
+    android:background="@color/background_primary" />
diff --git a/res/layout/group_editor_view.xml b/res/layout/group_editor_view.xml
index 59f2986..e94b387 100644
--- a/res/layout/group_editor_view.xml
+++ b/res/layout/group_editor_view.xml
@@ -24,22 +24,30 @@
         android:id="@+id/account_header"
         layout="@layout/editor_account_header"/>
 
-    <EditText
-        android:id="@+id/group_name"
-        android:layout_width="match_parent"
+    <LinearLayout android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textStyle="bold"
-        android:inputType="textCapWords"
-        android:hint="@string/group_name_hint"
-        android:layout_marginBottom="5dip"/>
+        android:paddingLeft="8dip"
+        android:paddingRight="8dip"
+        android:orientation="vertical">
 
-    <include
-        layout="@layout/group_editor_autocomplete_view"
-        android:id="@+id/add_member_field"/>
+        <EditText
+            android:id="@+id/group_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textStyle="bold"
+            android:inputType="textCapWords"
+            android:hint="@string/group_name_hint"
+            android:minHeight="48dip"
+            android:paddingLeft="8dip"/>
 
-    <include
-        layout="@layout/group_editor_existing_member_list"
-        android:id="@android:id/list"/>
+        <include
+            layout="@layout/group_editor_autocomplete_view"
+            android:id="@+id/add_member_field"/>
+
+        <include
+            layout="@layout/group_editor_existing_member_list"
+            android:id="@android:id/list"/>
+    </LinearLayout>
 
 </LinearLayout>
diff --git a/res/layout/group_member_item.xml b/res/layout/group_member_item.xml
index 23f02e0..518e26f 100644
--- a/res/layout/group_member_item.xml
+++ b/res/layout/group_member_item.xml
@@ -18,9 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
-    android:minHeight="58dip"
-    android:paddingLeft="10dip">
+    android:orientation="horizontal">
 
     <TextView
         android:id="@+id/name"
@@ -31,7 +29,8 @@
         android:gravity="center_vertical"
         android:paddingRight="3dip"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:focusable="true" />
+        android:focusable="true"
+        android:paddingLeft="@dimen/group_member_item_left_padding" />
 
     <QuickContactBadge
         android:id="@+id/badge"
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index c75ba6e..0a7bb6b 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -52,4 +52,15 @@
     <dimen name="widget_snippet_bottom_margin">6dip</dimen>
     <dimen name="widget_snippet_top_padding">6dip</dimen>
     <dimen name="widget_snippet_bottom_padding">3dip</dimen>
+
+    <!-- Left padding for a group member list item -->
+    <dimen name="group_member_item_left_padding">12dip</dimen>
+    <!-- Left margin for the group member list to match the built in margin in the autocomplete asset -->
+    <dimen name="group_editor_member_list_left_margin">4dip</dimen>
+    <!-- Right margin for the group member list to match the built in margin in the autocomplete asset -->
+    <dimen name="group_editor_member_list_right_margin">4dip</dimen>
+    <!-- Account title left padding -->
+    <dimen name="account_container_left_padding">16dip</dimen>
+    <!-- Left padding of the auto complete field to line hint text up with member list -->
+    <dimen name="group_editor_autocomplete_left_padding">16dip</dimen>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f5fd3bf..419e9ce 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -190,6 +190,21 @@
     <!-- Border padding for the group list header for each account -->
     <dimen name="group_list_header_padding">5dip</dimen>
 
+    <!-- Account title left padding -->
+    <dimen name="account_container_left_padding">16dip</dimen>
+
+    <!-- Left padding for a group member list item -->
+    <dimen name="group_member_item_left_padding">4dip</dimen>
+
+    <!-- Left margin for the group member list to match the built in margin in the autocomplete asset -->
+    <dimen name="group_editor_member_list_left_margin">4dip</dimen>
+
+    <!-- Right margin for the group member list to match the built in margin in the autocomplete asset -->
+    <dimen name="group_editor_member_list_right_margin">4dip</dimen>
+
+    <!-- Left padding of the auto complete field to line hint text up with member list -->
+    <dimen name="group_editor_autocomplete_left_padding">8dip</dimen>
+
     <!-- Size of group list icons -->
     <dimen name="group_list_icon_size">32dip</dimen>
 
diff --git a/src/com/android/contacts/list/AccountFilterActivity.java b/src/com/android/contacts/list/AccountFilterActivity.java
index 02abb53..8e0f9b8 100644
--- a/src/com/android/contacts/list/AccountFilterActivity.java
+++ b/src/com/android/contacts/list/AccountFilterActivity.java
@@ -68,10 +68,6 @@
 
     private ListView mListView;
 
-    private static final String[] ID_PROJECTION = new String[] {BaseColumns._ID};
-    private static final Uri RAW_CONTACTS_URI_LIMIT_1 = RawContacts.CONTENT_URI.buildUpon()
-            .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, "1").build();
-
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -124,7 +120,7 @@
         List<AccountWithDataSet> accounts = accountTypes.getAccounts(false);
         for (AccountWithDataSet account : accounts) {
             AccountType accountType = accountTypes.getAccountType(account.type, account.dataSet);
-            if (accountType.isExtension() && !hasAccountData(context, account)) {
+            if (accountType.isExtension() && !account.hasData(context)) {
                 // Hide extensions with no raw_contacts.
                 continue;
             }
@@ -149,29 +145,6 @@
         return result;
     }
 
-    private static boolean hasAccountData(Context context, AccountWithDataSet account) {
-        final String BASE_SELECTION =
-                RawContacts.ACCOUNT_TYPE + " = ?" + " AND " + RawContacts.ACCOUNT_NAME + " = ?";
-        final String selection;
-        final String[] args;
-        if (TextUtils.isEmpty(account.dataSet)) {
-            selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " IS NULL";
-            args = new String[] {account.type, account.name};
-        } else {
-            selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " = ?";
-            args = new String[] {account.type, account.name, account.dataSet};
-        }
-
-        final Cursor c = context.getContentResolver().query(RAW_CONTACTS_URI_LIMIT_1,
-                ID_PROJECTION, selection, args, null);
-        if (c == null) return false;
-        try {
-            return c.moveToFirst();
-        } finally {
-            c.close();
-        }
-    }
-
     private class MyLoaderCallbacks implements LoaderCallbacks<List<ContactListFilter>> {
         @Override
         public Loader<List<ContactListFilter>> onCreateLoader(int id, Bundle args) {
diff --git a/src/com/android/contacts/list/CustomContactListFilterActivity.java b/src/com/android/contacts/list/CustomContactListFilterActivity.java
index 26610dc..1ac1d32 100644
--- a/src/com/android/contacts/list/CustomContactListFilterActivity.java
+++ b/src/com/android/contacts/list/CustomContactListFilterActivity.java
@@ -132,6 +132,12 @@
 
             final AccountSet accounts = new AccountSet();
             for (AccountWithDataSet account : accountTypes.getAccounts(false)) {
+                final AccountType accountType = accountTypes.getAccountTypeForAccount(account);
+                if (accountType.isExtension() && !account.hasData(context)) {
+                    // Extension with no data -- skip.
+                    continue;
+                }
+
                 AccountDisplay accountDisplay =
                         new AccountDisplay(resolver, account.name, account.type, account.dataSet);
 
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index d28d5bb..d60f355 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -85,7 +85,15 @@
 
     public abstract List<AccountWithDataSet> getAccounts(boolean writableOnly);
 
-    public abstract AccountType getAccountType(String accountType, String dataSet);
+    public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
+
+    public final AccountType getAccountType(String accountType, String dataSet) {
+        return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet));
+    }
+
+    public final AccountType getAccountTypeForAccount(AccountWithDataSet account) {
+        return getAccountType(account.getAccountTypeWithDataSet());
+    }
 
     /**
      * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s
@@ -482,11 +490,10 @@
      * Return {@link AccountType} for the given account type and data set.
      */
     @Override
-    public AccountType getAccountType(String accountType, String dataSet) {
+    public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
         ensureAccountsLoaded();
         synchronized (this) {
-            AccountType type = mAccountTypesWithDataSets.get(
-                    AccountTypeWithDataSet.get(accountType, dataSet));
+            AccountType type = mAccountTypesWithDataSets.get(accountTypeWithDataSet);
             return type != null ? type : mFallbackAccountType;
         }
     }
@@ -506,7 +513,7 @@
             Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet) {
         HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap();
         for (AccountWithDataSet account : accounts) {
-            AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeAndWithDataSet();
+            AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeWithDataSet();
             AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet);
             if (type == null) continue; // just in case
             if (result.containsKey(accountTypeWithDataSet)) continue;
diff --git a/src/com/android/contacts/model/AccountWithDataSet.java b/src/com/android/contacts/model/AccountWithDataSet.java
index f607737..55af795 100644
--- a/src/com/android/contacts/model/AccountWithDataSet.java
+++ b/src/com/android/contacts/model/AccountWithDataSet.java
@@ -19,7 +19,14 @@
 import com.android.internal.util.Objects;
 
 import android.accounts.Account;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Parcel;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
 
 /**
  * Wrapper for an account that includes a data set (which may be null).
@@ -29,6 +36,11 @@
     public final String dataSet;
     private final AccountTypeWithDataSet mAccountTypeWithDataSet;
 
+    private static final String[] ID_PROJECTION = new String[] {BaseColumns._ID};
+    private static final Uri RAW_CONTACTS_URI_LIMIT_1 = RawContacts.CONTENT_URI.buildUpon()
+            .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, "1").build();
+
+
     public AccountWithDataSet(String name, String type, String dataSet) {
         super(name, type);
         this.dataSet = dataSet;
@@ -41,10 +53,37 @@
         mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
     }
 
-    public AccountTypeWithDataSet getAccountTypeAndWithDataSet() {
+    public AccountTypeWithDataSet getAccountTypeWithDataSet() {
         return mAccountTypeWithDataSet;
     }
 
+    /**
+     * Return {@code true} if this account has any contacts in the database.
+     * Touches DB.  Don't use in the UI thread.
+     */
+    public boolean hasData(Context context) {
+        final String BASE_SELECTION =
+                RawContacts.ACCOUNT_TYPE + " = ?" + " AND " + RawContacts.ACCOUNT_NAME + " = ?";
+        final String selection;
+        final String[] args;
+        if (TextUtils.isEmpty(dataSet)) {
+            selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " IS NULL";
+            args = new String[] {type, name};
+        } else {
+            selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " = ?";
+            args = new String[] {type, name, dataSet};
+        }
+
+        final Cursor c = context.getContentResolver().query(RAW_CONTACTS_URI_LIMIT_1,
+                ID_PROJECTION, selection, args, null);
+        if (c == null) return false;
+        try {
+            return c.moveToFirst();
+        } finally {
+            c.close();
+        }
+    }
+
     @Override
     public boolean equals(Object o) {
         return (o instanceof AccountWithDataSet) && super.equals(o)
diff --git a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
index 2be662d..7a04ae3 100644
--- a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
+++ b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
@@ -25,6 +25,8 @@
 import java.util.List;
 import java.util.Map;
 
+import libcore.util.Objects;
+
 /**
  * A mock {@link AccountTypeManager} class.
  */
@@ -39,9 +41,10 @@
     }
 
     @Override
-    public AccountType getAccountType(String accountType, String dataSet) {
+    public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
         for (AccountType type : mTypes) {
-            if (accountType.equals(type.accountType)) {
+            if (Objects.equal(accountTypeWithDataSet.accountType, type.accountType)
+                    && Objects.equal(accountTypeWithDataSet.dataSet, type.dataSet)) {
                 return type;
             }
         }