Merge "Clean-up of several small issues"
diff --git a/res/drawable-hdpi/change_photo_box_focused_holo_light.9.png b/res/drawable-hdpi/change_photo_box_focused_holo_light.9.png
new file mode 100644
index 0000000..5254473
--- /dev/null
+++ b/res/drawable-hdpi/change_photo_box_focused_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/change_photo_box_normal_holo_light.9.png b/res/drawable-hdpi/change_photo_box_normal_holo_light.9.png
new file mode 100644
index 0000000..f026cc8
--- /dev/null
+++ b/res/drawable-hdpi/change_photo_box_normal_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/change_photo_box_pressed_holo_light.9.png b/res/drawable-hdpi/change_photo_box_pressed_holo_light.9.png
new file mode 100644
index 0000000..a0770ea
--- /dev/null
+++ b/res/drawable-hdpi/change_photo_box_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/contact_picture_border_highlight.9.png b/res/drawable-hdpi/contact_picture_border_highlight.9.png
deleted file mode 100755
index 776d614..0000000
--- a/res/drawable-hdpi/contact_picture_border_highlight.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/contact_picture_border_normal.9.png b/res/drawable-hdpi/contact_picture_border_normal.9.png
deleted file mode 100755
index be67b1a..0000000
--- a/res/drawable-hdpi/contact_picture_border_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/contact_picture_border_pressed.9.png b/res/drawable-hdpi/contact_picture_border_pressed.9.png
deleted file mode 100755
index 32fbaa5..0000000
--- a/res/drawable-hdpi/contact_picture_border_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_add_picture.png b/res/drawable-hdpi/ic_menu_add_picture.png
deleted file mode 100755
index 85faf1c..0000000
--- a/res/drawable-hdpi/ic_menu_add_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_focused_holo_light.9.png b/res/drawable-mdpi/change_photo_box_focused_holo_light.9.png
new file mode 100644
index 0000000..36c8b87
--- /dev/null
+++ b/res/drawable-mdpi/change_photo_box_focused_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_holo_light.9.png b/res/drawable-mdpi/change_photo_box_holo_light.9.png
deleted file mode 100644
index 375ed4a..0000000
--- a/res/drawable-mdpi/change_photo_box_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_normal_holo_light.9.png b/res/drawable-mdpi/change_photo_box_normal_holo_light.9.png
new file mode 100644
index 0000000..d380b40
--- /dev/null
+++ b/res/drawable-mdpi/change_photo_box_normal_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_pressed_holo_light.9.png b/res/drawable-mdpi/change_photo_box_pressed_holo_light.9.png
new file mode 100644
index 0000000..21d8a96
--- /dev/null
+++ b/res/drawable-mdpi/change_photo_box_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/contact_picture_border_highlight.9.png b/res/drawable-mdpi/contact_picture_border_highlight.9.png
deleted file mode 100644
index c48b83f..0000000
--- a/res/drawable-mdpi/contact_picture_border_highlight.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/contact_picture_border_normal.9.png b/res/drawable-mdpi/contact_picture_border_normal.9.png
deleted file mode 100644
index 84bfad5..0000000
--- a/res/drawable-mdpi/contact_picture_border_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/contact_picture_border_pressed.9.png b/res/drawable-mdpi/contact_picture_border_pressed.9.png
deleted file mode 100644
index d5328f3..0000000
--- a/res/drawable-mdpi/contact_picture_border_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_add_picture.png b/res/drawable-mdpi/ic_menu_add_picture.png
deleted file mode 100644
index 35f10b8..0000000
--- a/res/drawable-mdpi/ic_menu_add_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/btn_contact_picture.xml b/res/drawable/change_photo_box_holo_light.xml
similarity index 74%
rename from res/drawable/btn_contact_picture.xml
rename to res/drawable/change_photo_box_holo_light.xml
index 643231a..f08beb0 100644
--- a/res/drawable/btn_contact_picture.xml
+++ b/res/drawable/change_photo_box_holo_light.xml
@@ -16,11 +16,11 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_window_focused="false"
-        android:drawable="@drawable/contact_picture_border_normal" />
+        android:drawable="@drawable/change_photo_box_normal_holo_light" />
     <item android:state_pressed="true"
-        android:drawable="@drawable/contact_picture_border_pressed" />
+        android:drawable="@drawable/change_photo_box_pressed_holo_light" />
     <item android:state_focused="true"
-        android:drawable="@drawable/contact_picture_border_highlight" />
+        android:drawable="@drawable/change_photo_box_focused_holo_light" />
     <item
-         android:drawable="@drawable/contact_picture_border_normal" />
+        android:drawable="@drawable/change_photo_box_normal_holo_light" />
 </selector>
diff --git a/res/layout-xlarge/item_photo_editor.xml b/res/layout-xlarge/item_photo_editor.xml
deleted file mode 100644
index 35ade24..0000000
--- a/res/layout-xlarge/item_photo_editor.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<com.android.contacts.views.editor.PhotoEditorView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/edit_photo_size"
-    android:layout_height="@dimen/edit_photo_size"
-    android:clickable="true"
-    android:focusable="true"
-    android:src="@drawable/ic_menu_add_picture"
-    android:cropToPadding="true"
-    android:scaleType="center"
-    android:background="@drawable/btn_contact_picture"
-    android:contentDescription="@string/description_contact_photo"
-    android:gravity="center" />
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index 35ade24..3b816b8 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -14,15 +14,28 @@
      limitations under the License.
 -->
 
-<com.android.contacts.views.editor.PhotoEditorView
+<view
+    class="com.android.contacts.views.editor.PhotoEditorView"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/edit_photo_size"
     android:layout_height="@dimen/edit_photo_size"
-    android:clickable="true"
-    android:focusable="true"
-    android:src="@drawable/ic_menu_add_picture"
-    android:cropToPadding="true"
-    android:scaleType="center"
-    android:background="@drawable/btn_contact_picture"
-    android:contentDescription="@string/description_contact_photo"
-    android:gravity="center" />
+    >
+    <ImageView
+        android:id="@+id/photo"
+        android:layout_width="@dimen/edit_photo_size"
+        android:layout_height="@dimen/edit_photo_size"
+        android:src="@drawable/ic_contact_picture"
+        android:cropToPadding="true"
+        android:scaleType="centerCrop"
+        android:gravity="center"
+    />
+    <View
+        android:id="@+id/frame"
+        android:layout_width="@dimen/edit_photo_size"
+        android:layout_height="@dimen/edit_photo_size"
+        android:clickable="true"
+        android:focusable="true"
+        android:contentDescription="@string/description_contact_photo"
+        android:background="@drawable/change_photo_box_holo_light"
+    />
+</view>
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/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index e970688..1f035a3 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -166,7 +166,7 @@
         if (fragment instanceof ContactBrowseListFragment) {
             mListFragment = (ContactBrowseListFragment)fragment;
             mListFragment.setOnContactListActionListener(new ContactBrowserActionListener());
-            configureListSelection(false);
+            configureListSelection();
         } else if (fragment instanceof ContactDetailFragment) {
             mDetailFragment = (ContactDetailFragment)fragment;
             mDetailFragment.setListener(mDetailFragmentListener);
@@ -231,11 +231,7 @@
             });
         }
 
-        configureListFragment(true /* from request */);
-
-        if (mContactContentDisplayed) {
-            setupContactDetailFragment(mListFragment.getSelectedContactUri());
-        }
+        configureFragments(true /* from request */);
     }
 
     @Override
@@ -259,6 +255,7 @@
             }
 
             mListFragment.setSelectedContactUri(uri);
+            mListFragment.saveSelectedUri(mPrefs);
             mListFragment.requestSelectionOnScreen(true);
             if (mContactContentDisplayed) {
                 setupContactDetailFragment(uri);
@@ -284,13 +281,11 @@
 
     @Override
     protected void onStart() {
-        if (mContactListFilterController != null) {
-            mContactListFilterController.startLoading();
-        }
+        mContactListFilterController.startLoading();
         super.onStart();
     }
 
-    private void configureListFragment(boolean fromRequest) {
+    private void configureFragments(boolean fromRequest) {
         boolean searchMode = mSearchMode;
         if (fromRequest) {
             ContactListFilter filter = null;
@@ -320,59 +315,45 @@
                 mContactListFilterController.setContactListFilter(new ContactListFilter(
                         ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false);
             }
-        } else {
-            if (mHasActionBar) {
-                searchMode = mActionBarAdapter.isSearchMode();
-            }
+        } else if (mHasActionBar) {
+            searchMode = mActionBarAdapter.isSearchMode();
         }
 
         boolean replaceList = mListFragment == null || (mSearchMode != searchMode);
         if (replaceList) {
-            closeListFragment();
+            if (mListFragment != null) {
+                mListFragment.setOnContactListActionListener(null);
+            }
+
             mSearchMode = searchMode;
+
             if (mSearchMode) {
                 mListFragment = createContactSearchFragment();
                 // When switching to the search mode, erase previous state of the search UI
-                mListFragment.saveSelectedUri(mPrefs);
+                mListFragment.eraseSelectedUri(mPrefs);
             } else {
                 mListFragment = createListFragment(ContactsRequest.ACTION_DEFAULT);
-                mListFragment.requestSelectionOnScreen(false);
             }
         }
 
-        if (mHasActionBar && mSearchMode) {
-            mListFragment.setQueryString(mActionBarAdapter.getQueryString());
-        }
-
-        if (fromRequest) {
-            Uri selectUri = mRequest.getContactUri();
-            if (selectUri != null) {
-                mListFragment.setSelectedContactUri(selectUri);
-                mListFragment.requestSelectionOnScreen(false);
+        if (mSearchMode) {
+            if (mHasActionBar) {
+                mListFragment.setQueryString(mActionBarAdapter.getQueryString());
             }
+        } else {
+            configureListSelection();
         }
 
         if (replaceList) {
             getFragmentManager().openTransaction()
                     .replace(R.id.list_container, mListFragment)
                     .commit();
-
-            if (mContactContentDisplayed) {
-                setupContactDetailFragment(mListFragment.getSelectedContactUri());
-            }
-        }
-    }
-
-    private void closeListFragment() {
-        if (mListFragment != null) {
-            mListFragment.setOnContactListActionListener(null);
-            mListFragment = null;
         }
     }
 
     @Override
     public void onContactListFiltersLoaded() {
-        configureListSelection(mRequest.getContactUri() == null);
+        configureListSelection();
 
         // Filters have been loaded - now we can start loading the list itself
         mListFragment.startLoading();
@@ -381,14 +362,28 @@
     @Override
     public void onContactListFilterChanged() {
         resetContactSelectionInIntent();
-        configureListSelection(true);
-        mListFragment.reloadData();
+
+        if (mListFragment == null) {
+            return;
+        }
+
+        DefaultContactBrowseListFragment fragment =
+                (DefaultContactBrowseListFragment) mListFragment;
+        ContactListFilter filter = mContactListFilterController.getFilter();
+        fragment.setFilter(filter);
+        fragment.reloadData();
+        fragment.restoreSelectedUri(mPrefs);
+        fragment.requestSelectionOnScreen(false);
+
+        if (mContactContentDisplayed) {
+            setupContactDetailFragment(mListFragment.getSelectedContactUri());
+        }
     }
 
     /**
      * Configures filter-specific persistent selection.
      */
-    private void configureListSelection(boolean restoreSelectedUri) {
+    private void configureListSelection() {
         if (mListFragment == null) {
             return;
         }
@@ -398,8 +393,12 @@
                 && mContactListFilterController.isLoaded()) {
             DefaultContactBrowseListFragment fragment =
                     (DefaultContactBrowseListFragment) mListFragment;
-            fragment.setFilter(mContactListFilterController.getFilter());
-            if (restoreSelectedUri) {
+            ContactListFilter filter = mContactListFilterController.getFilter();
+            fragment.setFilter(filter);
+            if (mRequest.getContactUri() != null) {
+                fragment.setSelectedContactUri(mRequest.getContactUri());
+                fragment.saveSelectedUri(mPrefs);
+            } else {
                 fragment.restoreSelectedUri(mPrefs);
             }
             fragment.requestSelectionOnScreen(false);
@@ -449,6 +448,7 @@
             requestedContactUri = mListFragment.getFirstContactUri();
             if (requestedContactUri != null) {
                 mListFragment.setSelectedContactUri(requestedContactUri);
+                mListFragment.eraseSelectedUri(mPrefs);
                 mListFragment.requestSelectionOnScreen(false);
             }
         }
@@ -485,6 +485,8 @@
         }
 
         mListFragment.setSelectedContactUri(selectedUri);
+        mListFragment.eraseSelectedUri(mPrefs);
+        mListFragment.requestSelectionOnScreen(true);
         if (mContactContentDisplayed) {
             setupContactDetailFragment(selectedUri);
         }
@@ -513,7 +515,7 @@
      */
     @Override
     public void onAction() {
-        configureListFragment(false /* from request */);
+        configureFragments(false /* from request */);
     }
 
     /**
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 65c61a6..4914906 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -207,6 +207,7 @@
     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
         super.onLoadFinished(loader, data);
         checkSelection();
+        requestSelectionOnScreenIfNeeded();
     }
 
     private void checkSelection() {
@@ -221,11 +222,9 @@
         ContactListAdapter adapter = getAdapter();
         if (adapter.hasValidSelection()) {
             mSelectionVerified = true;
-            requestSelectionOnScreenIfNeeded();
-            return;
+        } else {
+            notifyInvalidSelection();
         }
-
-        notifyInvalidSelection();
     }
 
     @Override
@@ -317,7 +316,7 @@
         }
 
         ContactListAdapter adapter = getAdapter();
-        if (adapter == null) {
+        if (adapter == null || adapter.isLoading()) {
             return;
         }
 
@@ -336,4 +335,7 @@
 
     public void restoreSelectedUri(SharedPreferences preferences) {
     }
+
+    public void eraseSelectedUri(SharedPreferences preferences) {
+    }
 }
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/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 46597d1..6ec43f8 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -305,6 +305,11 @@
     }
 
     protected void startLoading() {
+        if (mAdapter == null) {
+            // The method was called before the fragment was started
+            return;
+        }
+
         configureAdapter();
         int partitionCount = mAdapter.getPartitionCount();
         for (int i = 0; i < partitionCount; i++) {
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
index 4b890cf..d5554ce 100644
--- a/src/com/android/contacts/list/ContactListFilterController.java
+++ b/src/com/android/contacts/list/ContactListFilterController.java
@@ -124,7 +124,9 @@
     public void startLoading() {
         // Set the "ready" flag right away - we only want to start the loader once
         mFiltersLoaded = false;
-        mFilter = ContactListFilter.restoreFromPreferences(getSharedPreferences());
+        if (mFilter == null) {
+            mFilter = ContactListFilter.restoreFromPreferences(getSharedPreferences());
+        }
         loadFilters();
     }
 
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/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 919c740..68f0607 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -191,13 +191,20 @@
 
     @Override
     public void saveSelectedUri(SharedPreferences preferences) {
-        Editor editor = preferences.edit();
         Uri uri = getSelectedContactUri();
         if (uri == null) {
-            editor.remove(getPersistentSelectionKey());
+            eraseSelectedUri(preferences);
         } else {
+            Editor editor = preferences.edit();
             editor.putString(getPersistentSelectionKey(), uri.toString());
+            editor.apply();
         }
+    }
+
+    @Override
+    public void eraseSelectedUri(SharedPreferences preferences) {
+        Editor editor = preferences.edit();
+        editor.remove(getPersistentSelectionKey());
         editor.apply();
     }
 
diff --git a/src/com/android/contacts/views/editor/PhotoEditorView.java b/src/com/android/contacts/views/editor/PhotoEditorView.java
index ba3372c..0ab44d2 100644
--- a/src/com/android/contacts/views/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/views/editor/PhotoEditorView.java
@@ -28,7 +28,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import java.io.ByteArrayOutputStream;
@@ -37,9 +37,12 @@
 /**
  * Simple editor for {@link Photo}.
  */
-public class PhotoEditorView extends ImageView implements Editor, OnClickListener {
+public class PhotoEditorView extends FrameLayout implements Editor {
     private static final String TAG = "PhotoEditorView";
 
+    private ImageView mPhotoImageView;
+    private View mFrameView;
+
     private ValuesDelta mEntry;
     private EditorListener mListener;
 
@@ -58,22 +61,26 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        this.setOnClickListener(this);
+        mPhotoImageView = (ImageView) findViewById(R.id.photo);
+        mFrameView = findViewById(R.id.frame);
+        mFrameView.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mListener != null) {
+                    mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
+                }
+            }
+        });
     }
 
     /** {@inheritDoc} */
-    public void onClick(View v) {
-        if (mListener != null) {
-            mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
-        }
-    }
-
-    /** {@inheritDoc} */
+    @Override
     public void onFieldChanged(String column, String value) {
         throw new UnsupportedOperationException("Photos don't support direct field changes");
     }
 
     /** {@inheritDoc} */
+    @Override
     public void setValues(DataKind kind, ValuesDelta values, EntityDelta state, boolean readOnly,
             ViewIdGenerator vig) {
         mEntry = values;
@@ -88,8 +95,7 @@
                 final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
                         photoBytes.length);
 
-                setScaleType(ImageView.ScaleType.CENTER_CROP);
-                setImageBitmap(photo);
+                mPhotoImageView.setImageBitmap(photo);
                 setEnabled(true);
                 mHasSetPhoto = true;
                 mEntry.setFromTemplate(false);
@@ -129,7 +135,7 @@
             out.close();
 
             mEntry.put(Photo.PHOTO, out.toByteArray());
-            setImageBitmap(photo);
+            mPhotoImageView.setImageBitmap(photo);
             setEnabled(true);
             mHasSetPhoto = true;
             mEntry.setFromTemplate(false);
@@ -150,19 +156,14 @@
 
     protected void resetDefault() {
         // Invalid photo, show default "add photo" place-holder
-        setScaleType(ImageView.ScaleType.CENTER);
-        if (mReadOnly) {
-            setImageResource(R.drawable.ic_contact_picture);
-            setEnabled(false);
-        } else {
-            setImageResource(R.drawable.ic_menu_add_picture);
-            setEnabled(true);
-        }
+        mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
+        setEnabled(!mReadOnly);
         mHasSetPhoto = false;
         mEntry.setFromTemplate(true);
     }
 
     /** {@inheritDoc} */
+    @Override
     public void setEditorListener(EditorListener listener) {
         mListener = listener;
     }
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);
             }