Adjusting section header layout according to UI design

Change-Id: Ia027d807798eb4c2619f5ee542d6c802a62b545f
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index e616582..1ced586 100644
--- a/res/values-xlarge/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -30,9 +30,11 @@
         <item name="list_item_call_button_padding">14dip</item>
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
-        <item name="list_item_header_text_width">56dip</item>
         <item name="list_item_photo_size">64dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
+        <item name="list_item_header_text_indent">77dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
         <item name="contact_filter_popup_width">320dip</item>
     </style>
 
@@ -48,11 +50,11 @@
         <item name="list_item_call_button_padding">14dip</item>
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
-        <item name="list_item_header_text_width">56dip</item>
         <item name="list_item_photo_size">64dip</item>
+        <item name="list_item_header_text_indent">77dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
     </style>
-    
-    
 
     <style name="ContactsPreferencesTheme" parent="@android:Theme.Light.Holo">
     </style>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 9bb9728..2c1d250 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -103,9 +103,11 @@
         <attr name="list_item_call_button_padding" format="dimension"/>
         <attr name="list_item_vertical_divider_margin" format="dimension"/>
         <attr name="list_item_presence_icon_margin" format="dimension"/>
-        <attr name="list_item_header_text_width" format="dimension"/>
         <attr name="list_item_photo_size" format="dimension"/>
         <attr name="list_item_prefix_highlight_color" format="color"/>
+        <attr name="list_item_header_text_indent" format="dimension" />
+        <attr name="list_item_header_text_color" format="color" />
+        <attr name="list_item_header_text_size" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="MultiplePhonePickerItemView">
@@ -127,9 +129,11 @@
         <item name="list_item_call_button_padding">14dip</item>
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
-        <item name="list_item_header_text_width">56dip</item>
         <item name="list_item_photo_size">56dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
+        <item name="list_item_header_text_indent">56dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
         <item name="contact_filter_popup_width">320dip</item>
     </style>
 
@@ -145,12 +149,14 @@
         <item name="list_item_call_button_padding">14dip</item>
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
-        <item name="list_item_header_text_width">56dip</item>
         <item name="list_item_photo_size">56dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_chip_width">4dip</item>
         <item name="list_item_header_chip_right_margin">4dip</item>
         <item name="list_item_header_checkbox_margin">5dip</item>
+        <item name="list_item_header_text_indent">56dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
     </style>
 
     <style name="JoinContactActivityTheme" parent="ContactPickerTheme">
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index 41136d4..7cfd672 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -71,10 +71,20 @@
     private boolean mSelectionVisible;
 
     public ContactEntryListAdapter(Context context) {
-        super(context, R.layout.list_section, R.id.header_text);
+        super(context);
         addPartitions();
     }
 
+    @Override
+    protected View createPinnedSectionHeaderView(Context context, ViewGroup parent) {
+        return new ContactListPinnedHeaderView(context, null);
+    }
+
+    @Override
+    protected void setPinnedSectionTitle(View pinnedHeaderView, String title) {
+        ((ContactListPinnedHeaderView)pinnedHeaderView).setSectionHeader(title);
+    }
+
     protected void addPartitions() {
         addPartition(createDefaultDirectoryPartition());
     }
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index ded0651..e16972a 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -66,8 +66,10 @@
     private final int mGapBetweenLabelAndData;
     private final int mCallButtonPadding;
     private final int mPresenceIconMargin;
-    private final int mHeaderTextWidth;
     private final int mPrefixHightlightColor;
+    private final int mHeaderTextColor;
+    private final int mHeaderTextIndent;
+    private final int mHeaderTextSize;
 
     private Drawable mPressedBackgroundDrawable;
     private Drawable mActivatedBackgroundDrawable;
@@ -151,7 +153,7 @@
                 a.getDimensionPixelSize(android.R.styleable.Theme_listPreferredItemHeight, 0);
         a.recycle();
 
-        a = getContext().obtainStyledAttributes(attrs,R.styleable.ContactListItemView);
+        a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
         mPressedBackgroundDrawable = a.getDrawable(
                 R.styleable.ContactListItemView_pressedBackground);
         mHeaderBackgroundDrawable = a.getDrawable(
@@ -176,12 +178,16 @@
                 R.styleable.ContactListItemView_list_item_call_button_padding, 0);
         mPresenceIconMargin = a.getDimensionPixelOffset(
                 R.styleable.ContactListItemView_list_item_presence_icon_margin, 0);
-        mHeaderTextWidth = a.getDimensionPixelOffset(
-                R.styleable.ContactListItemView_list_item_header_text_width, 0);
         mDefaultPhotoViewSize = a.getDimensionPixelOffset(
                 R.styleable.ContactListItemView_list_item_photo_size, 0);
         mPrefixHightlightColor = a.getColor(
                 R.styleable.ContactListItemView_list_item_prefix_highlight_color, Color.GREEN);
+        mHeaderTextIndent = a.getDimensionPixelOffset(
+                R.styleable.ContactListItemView_list_item_header_text_indent, 0);
+        mHeaderTextColor = a.getColor(
+                R.styleable.ContactListItemView_list_item_header_text_color, Color.BLACK);
+        mHeaderTextSize = a.getDimensionPixelSize(
+                R.styleable.ContactListItemView_list_item_header_text_size, 12);
 
         a.recycle();
 
@@ -268,7 +274,7 @@
 
         if (mHeaderVisible) {
             mHeaderTextView.measure(
-                    MeasureSpec.makeMeasureSpec(mHeaderTextWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
             height += mHeaderBackgroundHeight;
         }
@@ -291,7 +297,7 @@
                     0,
                     width,
                     mHeaderBackgroundHeight);
-            mHeaderTextView.layout(0, 0, width, mHeaderBackgroundHeight);
+            mHeaderTextView.layout(mHeaderTextIndent, 0, width, mHeaderBackgroundHeight);
             topBound += mHeaderBackgroundHeight;
         }
 
@@ -519,11 +525,10 @@
         if (!TextUtils.isEmpty(title)) {
             if (mHeaderTextView == null) {
                 mHeaderTextView = new TextView(mContext);
+                mHeaderTextView.setTextColor(mHeaderTextColor);
+                mHeaderTextView.setTextSize(mHeaderTextSize);
                 mHeaderTextView.setTypeface(mHeaderTextView.getTypeface(), Typeface.BOLD);
-                mHeaderTextView.setTextColor(mContext.getResources()
-                        .getColor(R.color.section_header_text_color));
-                mHeaderTextView.setTextSize(14);
-                mHeaderTextView.setGravity(Gravity.CENTER);
+                mHeaderTextView.setGravity(Gravity.CENTER_VERTICAL);
                 addView(mHeaderTextView);
             }
             mHeaderTextView.setText(title);
diff --git a/src/com/android/contacts/list/ContactListPinnedHeaderView.java b/src/com/android/contacts/list/ContactListPinnedHeaderView.java
new file mode 100644
index 0000000..e850511
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListPinnedHeaderView.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * A custom view for the pinned section header shown at the top of the contact list.
+ */
+public class ContactListPinnedHeaderView extends ViewGroup {
+
+    protected final Context mContext;
+
+    private final int mHeaderTextColor;
+    private final int mHeaderTextIndent;
+    private final int mHeaderTextSize;
+
+    private Drawable mHeaderBackgroundDrawable;
+    private int mHeaderBackgroundHeight;
+    private TextView mHeaderTextView;
+
+    public ContactListPinnedHeaderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+
+        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+
+        mHeaderBackgroundDrawable = a.getDrawable(
+                R.styleable.ContactListItemView_section_header_background);
+        mHeaderTextIndent = a.getDimensionPixelOffset(
+                R.styleable.ContactListItemView_list_item_header_text_indent, 0);
+        mHeaderTextColor = a.getColor(
+                R.styleable.ContactListItemView_list_item_header_text_color, Color.BLACK);
+        mHeaderTextSize = a.getDimensionPixelSize(
+                R.styleable.ContactListItemView_list_item_header_text_size, 12);
+
+        a.recycle();
+
+        mHeaderBackgroundHeight = mHeaderBackgroundDrawable.getIntrinsicHeight();
+
+        mHeaderTextView = new TextView(mContext);
+        mHeaderTextView.setTextColor(mHeaderTextColor);
+        mHeaderTextView.setTextSize(mHeaderTextSize);
+        mHeaderTextView.setTypeface(mHeaderTextView.getTypeface(), Typeface.BOLD);
+        mHeaderTextView.setGravity(Gravity.CENTER_VERTICAL);
+        addView(mHeaderTextView);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+        // We will match parent's width and wrap content vertically.
+        int width = resolveSize(0, widthMeasureSpec);
+
+        mHeaderTextView.measure(
+                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
+
+        setMeasuredDimension(width, mHeaderBackgroundHeight);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int width = right - left;
+        mHeaderBackgroundDrawable.setBounds(0, 0, width, mHeaderBackgroundHeight);
+        mHeaderTextView.layout(mHeaderTextIndent, 0, width, mHeaderBackgroundHeight);
+    }
+
+    @Override
+    public void dispatchDraw(Canvas canvas) {
+        mHeaderBackgroundDrawable.draw(canvas);
+        super.dispatchDraw(canvas);
+    }
+
+    /**
+     * Sets section header or makes it invisible if the title is null.
+     */
+    public void setSectionHeader(String title) {
+        if (!TextUtils.isEmpty(title)) {
+            mHeaderTextView.setText(title);
+            mHeaderTextView.setVisibility(View.VISIBLE);
+        } else {
+            mHeaderTextView.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public void requestLayout() {
+        // We will assume that once measured this will not need to resize
+        // itself, so there is no need to pass the layout request to the parent
+        // view (ListView).
+        forceLayout();
+    }
+}
diff --git a/src/com/android/contacts/widget/IndexerListAdapter.java b/src/com/android/contacts/widget/IndexerListAdapter.java
index b26c2dc..d264254 100644
--- a/src/com/android/contacts/widget/IndexerListAdapter.java
+++ b/src/com/android/contacts/widget/IndexerListAdapter.java
@@ -16,7 +16,6 @@
 package com.android.contacts.widget;
 
 import android.content.Context;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ListView;
@@ -28,15 +27,11 @@
  */
 public abstract class IndexerListAdapter extends PinnedHeaderListAdapter implements SectionIndexer {
 
-    private final int mSectionHeaderTextViewId;
-    private final int mSectionHeaderLayoutResId;
-
     protected Context mContext;
     private SectionIndexer mIndexer;
     private int mIndexedPartition = 0;
     private boolean mSectionHeaderDisplayEnabled;
     private View mHeader;
-    private TextView mTitleView;
 
     /**
      * An item view is displayed differently depending on whether it is placed
@@ -59,19 +54,23 @@
 
     /**
      * Constructor.
-     *
-     * @param context
-     * @param sectionHeaderLayoutResourceId section header layout resource ID
-     * @param sectionHeaderTextViewId section header text view ID
      */
-    public IndexerListAdapter(Context context, int sectionHeaderLayoutResourceId,
-            int sectionHeaderTextViewId) {
+    public IndexerListAdapter(Context context) {
         super(context);
         mContext = context;
-        mSectionHeaderLayoutResId = sectionHeaderLayoutResourceId;
-        mSectionHeaderTextViewId = sectionHeaderTextViewId;
     }
 
+    /**
+     * Creates a section header view that will be pinned at the top of the list
+     * as the user scrolls.
+     */
+    protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent);
+
+    /**
+     * Sets the title in the pinned header as the user scrolls.
+     */
+    protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title);
+
     public boolean isSectionHeaderDisplayEnabled() {
         return mSectionHeaderDisplayEnabled;
     }
@@ -140,9 +139,7 @@
     public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
         if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) {
             if (mHeader == null) {
-                mHeader = LayoutInflater.from(mContext).
-                        inflate(mSectionHeaderLayoutResId, parent, false);
-                mTitleView = (TextView)mHeader.findViewById(mSectionHeaderTextViewId);
+                mHeader = createPinnedSectionHeaderView(mContext, parent);
             }
             return mHeader;
         } else {
@@ -177,8 +174,7 @@
             if (section == -1) {
                 listView.setHeaderInvisible(index, false);
             } else {
-                String title = (String)mIndexer.getSections()[section];
-                mTitleView.setText(title);
+                setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]);
 
                 // Compute the item position where the current partition begins
                 int partitionStart = getPositionForPartition(mIndexedPartition);
diff --git a/src/com/android/contacts/widget/PinnedHeaderListView.java b/src/com/android/contacts/widget/PinnedHeaderListView.java
index a533927..35291bf 100644
--- a/src/com/android/contacts/widget/PinnedHeaderListView.java
+++ b/src/com/android/contacts/widget/PinnedHeaderListView.java
@@ -25,6 +25,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.AdapterView;
@@ -344,9 +345,9 @@
         if (view.isLayoutRequested()) {
             int widthSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
             int heightSpec;
-            int lpHeight = view.getLayoutParams().height;
-            if (lpHeight > 0) {
-                heightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
+            ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+            if (layoutParams != null && layoutParams.height > 0) {
+                heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
             } else {
                 heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
             }