Show count of contacts in account and group view

* Change contact list item height from 64dp to 56dp per b/29831679.
* Move name view in contact list item up by 2dp, per UX.
* Change the top padding to 8dp per b/29831679.

* Show the header in: account view, group member view and group member
  picker view.
* Set top padding for list view only when there's no account header.
* Change action bar title to "From <account>" in account view.
* Override onScrollListener to hide and show header divider.

Bug 29190106
Bug 29831679

Change-Id: I36a2083c91b6b052bf887a8e7c639085d2bf4691
diff --git a/res/drawable/account_header_background.xml b/res/drawable/account_header_background.xml
new file mode 100644
index 0000000..af72c6d
--- /dev/null
+++ b/res/drawable/account_header_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/list_item_account_header_border_color" />
+        </shape>
+    </item>
+    <item android:bottom="1dp">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/background_primary" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d71363d..ee4f55a 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -60,6 +60,9 @@
     <!-- Background color of pinned header items. -->
     <color name="list_item_pinned_header_color">@color/background_primary</color>
 
+    <!-- 8% black. -->
+    <color name="list_item_account_header_border_color">#15000000</color>
+
     <!-- Color of the mime-type icons inside the editor. 50% black. -->
     <color name="editor_icon_color">#7f7f7f</color>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index dc263fc..2badc01 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -260,7 +260,9 @@
       @dimen/contact_browser_list_item_photo_size) / 2 or greater. Otherwise, this padding
       will never take affect inside list items. As a result, the padding at the very top
       of ListView's will not match the padding inside list items -->
-    <dimen name="contact_browser_list_item_padding_top_or_bottom">12dp</dimen>
+    <dimen name="contact_browser_list_item_padding_top_or_bottom">8dp</dimen>
+
+    <dimen name="contact_browser_list_item_height">56dp</dimen>
 
     <!-- Ideal item width in photo picker -->
     <dimen name="photo_picker_item_ideal_width">135dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b765585..06e4ad7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -206,6 +206,18 @@
         <item quantity="other">Contacts deleted</item>
     </plurals>
 
+    <!-- List header indicating the number of contacts in the list [CHAR LIMIT=30] -->
+    <plurals name="contacts_count">
+        <item quantity="one">1 contact</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> contacts</item>
+    </plurals>
+
+    <!-- Activity title indicating contacts are from a Google account [CHAR LIMIT=30] -->
+    <string name="title_from_google">From Google</string>
+
+    <!-- Activity title indicating contacts are from a specific account [CHAR LIMIT=15] -->
+    <string name="title_from_other_accounts">From <xliff:g id="account">%s</xliff:g></string>
+
     <!-- Menu item that opens the Options activity for a given contact [CHAR LIMIT=15] -->
     <string name="menu_set_ring_tone">Set ringtone</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 04252238..50d53dc 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -59,7 +59,7 @@
         <item name="android:colorPrimary">@color/primary_color</item>
         <item name="android:colorAccent">@color/primary_color</item>
         <item name="android:alertDialogTheme">@style/ContactsAlertDialogTheme</item>
-        <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
+        <item name="list_item_height">@dimen/contact_browser_list_item_height</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
         <item name="list_section_header_height">24dip</item>
@@ -91,6 +91,7 @@
         <item name="contact_browser_list_padding_right">0dip</item>
         <item name="contact_browser_background">@color/background_primary</item>
         <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
+        <item name="list_item_text_offset_top">-2dp</item>
         <!-- Favorites -->
         <item name="favorites_padding_bottom">0dip</item>
     </style>
@@ -134,7 +135,7 @@
         <item name="colorAccent">@color/primary_color</item>
         <item name="android:alertDialogTheme">@style/ContactsAlertDialogThemeAppCompat</item>
         <item name="alertDialogTheme">@style/ContactsAlertDialogThemeAppCompat</item>
-        <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
+        <item name="list_item_height">@dimen/contact_browser_list_item_height</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
         <item name="list_section_header_height">24dip</item>
@@ -169,6 +170,7 @@
         <item name="contact_browser_list_padding_right">0dip</item>
         <item name="contact_browser_background">@color/background_primary</item>
         <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
+        <item name="list_item_text_offset_top">-2dp</item>
         <!-- Favorites -->
         <item name="favorites_padding_bottom">0dip</item>
         <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index bde88f3..bad1dde 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -68,6 +68,7 @@
 import com.android.contacts.common.logging.ScreenEvent.ScreenType;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.GoogleAccountType;
 import com.android.contacts.common.util.Constants;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.common.widget.FloatingActionButtonController;
@@ -1398,11 +1399,11 @@
         updateFilterMenu(filter);
 
         if (getSupportActionBar() != null) {
-            String actionBarTitle = null;
+            String actionBarTitle;
             if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) {
                 actionBarTitle = getString(R.string.account_phone);
             } else if (!TextUtils.isEmpty(filter.accountName)) {
-                actionBarTitle = filter.accountName;
+                actionBarTitle = getActionBarTitleForAccount(filter);
             } else {
                 actionBarTitle = getString(R.string.contactsList);
             }
@@ -1410,6 +1411,13 @@
         }
     }
 
+    private String getActionBarTitleForAccount(ContactListFilter filter) {
+        if (GoogleAccountType.ACCOUNT_TYPE.equals(filter.accountType)) {
+            return getString(R.string.title_from_google);
+        }
+        return getString(R.string.title_from_other_accounts, filter.accountName);
+    }
+
     // Persist filter only when it's of the type FILTER_TYPE_ALL_ACCOUNTS.
     private void persistFilterIfNeeded(ContactListFilter filter) {
         mContactListFilterController.setContactListFilter(filter,
diff --git a/src/com/android/contacts/group/GroupMembersFragment.java b/src/com/android/contacts/group/GroupMembersFragment.java
index 0d880e5..266e5fc 100644
--- a/src/com/android/contacts/group/GroupMembersFragment.java
+++ b/src/com/android/contacts/group/GroupMembersFragment.java
@@ -37,7 +37,6 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
@@ -47,6 +46,7 @@
 import com.android.contacts.common.logging.ListEvent.ListType;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.group.GroupMembersAdapter.GroupMembersQuery;
 import com.android.contacts.list.MultiSelectContactsListFragment;
 
@@ -293,16 +293,13 @@
                 R.id.account_filter_header_container);
         final View emptyGroupView = getView().findViewById(R.id.empty_group);
         if (memberCount > 0) {
-            accountFilterContainer.setVisibility(View.VISIBLE);
-
-            final TextView accountFilterHeader = (TextView) accountFilterContainer.findViewById(
-                    R.id.account_filter_header);
-            accountFilterHeader.setText(mGroupMetadata.accountName);
-            accountFilterHeader.setAllCaps(false);
-
+            final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
+                    mGroupMetadata.accountName, mGroupMetadata.accountType, mGroupMetadata.dataSet);
+            bindListHeader(getContext(), getListView(), accountFilterContainer,
+                    accountWithDataSet, memberCount);
             emptyGroupView.setVisibility(View.GONE);
         } else {
-            accountFilterContainer.setVisibility(View.GONE);
+            hideHeaderAndAddPadding(getContext(), getListView(), accountFilterContainer);
             emptyGroupView.setVisibility(View.VISIBLE);
         }
     }
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 6134047..fd6c0c0 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -17,22 +17,25 @@
 
 import android.content.Context;
 import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
 import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
-import android.widget.ListView;
 import android.widget.TextView;
 
 import com.android.contacts.R;
 import com.android.contacts.common.list.ContactListAdapter;
+import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.ContactListFilterController;
 import com.android.contacts.common.list.ContactListItemView;
 import com.android.contacts.common.list.DefaultContactListAdapter;
 import com.android.contacts.common.list.FavoritesAndContactsLoader;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 
 /**
  * Fragment containing a contact list used for browsing (as compared to
@@ -59,6 +62,29 @@
     }
 
     @Override
+    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+        bindListHeader(data.getCount());
+        super.onLoadFinished(loader, data);
+    }
+
+    private void bindListHeader(int numberOfContacts) {
+        final View accountFilterContainer = getView().findViewById(
+                R.id.account_filter_header_container);
+        final ContactListFilterController contactListFilterController =
+                ContactListFilterController.getInstance(getContext());
+        final ContactListFilter filter = contactListFilterController.getFilter();
+        if (!isSearchMode()
+                && filter.filterType != ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+            final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
+                    filter.accountName, filter.accountType, filter.dataSet);
+            bindListHeader(getContext(), getListView(), accountFilterContainer,
+                    accountWithDataSet, numberOfContacts);
+        } else {
+            hideHeaderAndAddPadding(getContext(), getListView(), accountFilterContainer);
+        }
+    }
+
+    @Override
     protected void onItemClick(int position, long id) {
         final Uri uri = getAdapter().getContactUri(position);
         if (uri == null) {
diff --git a/src/com/android/contacts/list/GroupMemberPickerFragment.java b/src/com/android/contacts/list/GroupMemberPickerFragment.java
index f4dde36..d981de0 100644
--- a/src/com/android/contacts/list/GroupMemberPickerFragment.java
+++ b/src/com/android/contacts/list/GroupMemberPickerFragment.java
@@ -28,7 +28,6 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 import com.android.contacts.activities.ContactSelectionActivity;
 import com.android.contacts.common.R;
@@ -36,6 +35,7 @@
 import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.ContactsSectionIndexer;
 import com.android.contacts.common.list.DefaultContactListAdapter;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.group.GroupUtil;
 
 import java.util.ArrayList;
@@ -229,16 +229,15 @@
             // Wait until contacts are loaded before showing the scrollbar
             setVisibleScrollbarEnabled(true);
 
-            // Bind account filter header.
+            final FilterCursorWrapper cursorWrapper = new FilterCursorWrapper(data);
             final View accountFilterContainer = getView().findViewById(
                     R.id.account_filter_header_container);
-            accountFilterContainer.setVisibility(View.VISIBLE);
-            final TextView accountFilterHeader = (TextView) accountFilterContainer.findViewById(
-                    R.id.account_filter_header);
-            accountFilterHeader.setText(mAccountName);
-            accountFilterHeader.setAllCaps(false);
+            final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(mAccountName,
+                    mAccountType, mAccountDataSet);
+            bindListHeader(getContext(), getListView(), accountFilterContainer,
+                    accountWithDataSet, cursorWrapper.getCount());
 
-            super.onLoadFinished(loader, new FilterCursorWrapper(data));
+            super.onLoadFinished(loader, cursorWrapper);
         }
     }
 
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index 682f39b..af403ca 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -16,18 +16,30 @@
 
 package com.android.contacts.list;
 
+import com.android.contacts.R;
 import com.android.contacts.common.list.ContactEntryListFragment;
 import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
 import com.android.contacts.common.list.MultiSelectEntryContactListAdapter.SelectedContactsListener;
 import com.android.contacts.common.logging.ListEvent.ActionType;
 import com.android.contacts.common.logging.Logger;
 import com.android.contacts.common.logging.SearchState;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.GoogleAccountType;
 
+import android.content.Context;
 import android.database.Cursor;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.util.Log;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.AbsListView;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -269,4 +281,69 @@
         }
         return searchState;
     }
+
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+            int totalItemCount) {
+        final View accountFilterContainer = getView().findViewById(
+                R.id.account_filter_header_container);
+        if (accountFilterContainer == null) {
+            return;
+        }
+        if (firstVisibleItem == 0) {
+            accountFilterContainer.setBackground(
+                    new ColorDrawable(getResources().getColor(R.color.background_primary)));
+        } else {
+            accountFilterContainer.setBackground(
+                    getResources().getDrawable(R.drawable.account_header_background));
+        }
+    }
+
+    /**
+     * Show account icon, count of contacts and account name in the header of the list.
+     */
+    protected void bindListHeader(Context context, View listView, View accountFilterContainer,
+            AccountWithDataSet accountWithDataSet, int memberCount) {
+        if (memberCount < 0) {
+            hideHeaderAndAddPadding(context, listView, accountFilterContainer);
+            return;
+        }
+
+        // Show header and remove top padding of the list
+        accountFilterContainer.setVisibility(View.VISIBLE);
+        listView.setPadding(0, 0, 0, 0);
+
+        // Set text of count of contacts and account name (if it's a Google account)
+        final TextView accountFilterHeader = (TextView) accountFilterContainer.findViewById(
+                R.id.account_filter_header);
+        final String headerText =  String.format(context.getResources().getQuantityString(
+                R.plurals.contacts_count, memberCount), memberCount);
+        final StringBuilder sb = new StringBuilder(headerText);
+        if (GoogleAccountType.ACCOUNT_TYPE.equals(accountWithDataSet.type)) {
+            sb.append(" \u00B7 "); // Unicode character 'MIDDLE DOT'
+            sb.append(accountWithDataSet.name);
+        }
+        accountFilterHeader.setText(sb.toString());
+        accountFilterHeader.setAllCaps(false);
+
+        // Set icon of the account
+        final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
+        final AccountType accountType = accountTypeManager.getAccountType(
+                accountWithDataSet.type, accountWithDataSet.dataSet);
+        final Drawable icon = accountType != null ? accountType.getDisplayIcon(context) : null;
+        final ImageView accountFilterHeaderIcon = (ImageView) accountFilterContainer
+                .findViewById(R.id.account_filter_icon);
+        accountFilterHeaderIcon.setImageDrawable(icon);
+    }
+
+    /**
+     * Hide header of list view and add padding to the top of list view.
+     */
+    protected void hideHeaderAndAddPadding(Context context, View listView,
+            View accountFilterContainer) {
+        accountFilterContainer.setVisibility(View.GONE);
+        listView.setPadding(0, context.getResources().getDimensionPixelSize(
+                R.dimen.contact_browser_list_item_padding_top_or_bottom), 0, 0);
+    }
+
 }