Making PinnedHeaderListView more self-contained.

Basically clearing the related logic out of
the ContactsListActivity.

Also introducing a separate configuration and a separate
controller for the main contact list.

Change-Id: Icc327272ed7daa9716e8d49ac7c3f4d6a05b7ca9
diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml
index 4dd680f..f697612 100644
--- a/res/layout-finger/contacts_list_content.xml
+++ b/res/layout-finger/contacts_list_content.xml
@@ -23,7 +23,7 @@
         >
 
     <view
-        class="com.android.contacts.PinnedHeaderListView" 
+        class="com.android.contacts.widget.PinnedHeaderListView" 
         android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="0dip"
diff --git a/res/layout-finger/contacts_list_search_results.xml b/res/layout-finger/contacts_list_search_results.xml
index 244ca80..04c1f13 100644
--- a/res/layout-finger/contacts_list_search_results.xml
+++ b/res/layout-finger/contacts_list_search_results.xml
@@ -53,7 +53,7 @@
     </LinearLayout>
 
     <view
-        class="com.android.contacts.PinnedHeaderListView"
+        class="com.android.contacts.widget.PinnedHeaderListView"
         android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 400763e..8303414 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -490,15 +490,6 @@
     public TextHighlightingAnimation mHighlightingAnimation;
     private SearchEditText mSearchEditText;
 
-    /**
-     * An approximation of the background color of the pinned header. This color
-     * is used when the pinned header is being pushed up.  At that point the header
-     * "fades away".  Rather than computing a faded bitmap based on the 9-patch
-     * normally used for the background, we will use a solid color, which will
-     * provide better performance and reduced complexity.
-     */
-    public int mPinnedHeaderBackgroundColor;
-
     private ContentObserver mProviderStatusObserver = new ContentObserver(new Handler()) {
 
         @Override
@@ -617,14 +608,6 @@
         mAdapter = (ContactEntryListAdapter)adapter;
         setListAdapter(mAdapter);
 
-        if (list instanceof PinnedHeaderListView && mConfig.isSectionHeaderDisplayEnabled()) {
-            mPinnedHeaderBackgroundColor =
-                    getResources().getColor(R.color.pinned_header_background);
-            PinnedHeaderListView pinnedHeaderList = (PinnedHeaderListView)list;
-            View pinnedHeader = inflater.inflate(R.layout.list_section, list, false);
-            pinnedHeaderList.setPinnedHeaderView(pinnedHeader);
-        }
-
         list.setOnScrollListener(this);
         list.setOnKeyListener(this);
         list.setOnFocusChangeListener(this);
@@ -653,9 +636,6 @@
 
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
             int totalItemCount) {
-        if (view instanceof PinnedHeaderListView) {
-            ((PinnedHeaderListView)view).configureHeaderView(firstVisibleItem);
-        }
     }
 
     public void onScrollStateChanged(AbsListView view, int scrollState) {
@@ -674,7 +654,6 @@
         }
     }
 
-
     /**
      * Configures search UI.
      */
@@ -2446,9 +2425,4 @@
         public String phoneNumber;
     }
 
-    public final static class PinnedHeaderCache {
-        public TextView titleView;
-        public ColorStateList textColor;
-        public Drawable background;
-    }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index a353fed..2cf12c5 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -16,19 +16,15 @@
 package com.android.contacts.list;
 
 import android.content.Context;
-import android.widget.CursorAdapter;
 
 /**
  * Common base class for various contact-related lists, e.g. contact list, phone number list
  * etc.
  */
-public abstract class ContactEntryListAdapter extends CursorAdapter {
-
-    private final Context mContext;
+public abstract class ContactEntryListAdapter extends PinnedHeaderListAdapter {
 
     public ContactEntryListAdapter(Context context) {
-        super(context, null, false);
-        this.mContext = context;
+        super(context);
     }
 
     public Context getContext() {
diff --git a/src/com/android/contacts/list/ContactEntryListConfiguration.java b/src/com/android/contacts/list/ContactEntryListConfiguration.java
index 98ad73a..26eeb72 100644
--- a/src/com/android/contacts/list/ContactEntryListConfiguration.java
+++ b/src/com/android/contacts/list/ContactEntryListConfiguration.java
@@ -18,8 +18,10 @@
 
 import com.android.contacts.ContactsApplicationController;
 import com.android.contacts.ContactsListActivity;
+import com.android.contacts.widget.PinnedHeaderListView;
 
 import android.content.Context;
+import android.view.View;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
@@ -60,6 +62,22 @@
         controller.setListView(listView);
 
         ((ContactsListActivity)mContext).setupListView(adapter);
+
+        configurePinnedHeader(listView, adapter);
+    }
+
+    private void configurePinnedHeader(ListView listView, ListAdapter adapter) {
+        if (!mSectionHeaderDisplayEnabled) {
+            return;
+        }
+
+        if (listView instanceof PinnedHeaderListView
+                && adapter instanceof PinnedHeaderListAdapter) {
+            PinnedHeaderListView pinnedHeaderList = (PinnedHeaderListView)listView;
+            PinnedHeaderListAdapter pinnedHeaderListAdapter = (PinnedHeaderListAdapter)adapter;
+            View headerView = pinnedHeaderListAdapter.createPinnedHeaderView(pinnedHeaderList);
+            pinnedHeaderList.setPinnedHeaderView(headerView);
+        }
     }
 
     public void setSectionHeaderDisplayEnabled(boolean flag) {
diff --git a/src/com/android/contacts/list/ContactItemListAdapter.java b/src/com/android/contacts/list/ContactItemListAdapter.java
index d7cd0c9..6b228c6 100644
--- a/src/com/android/contacts/list/ContactItemListAdapter.java
+++ b/src/com/android/contacts/list/ContactItemListAdapter.java
@@ -19,16 +19,13 @@
 import com.android.contacts.ContactPresenceIconUtil;
 import com.android.contacts.ContactsListActivity;
 import com.android.contacts.ContactsSectionIndexer;
-import com.android.contacts.PinnedHeaderListView;
 import com.android.contacts.R;
 import com.android.contacts.ContactsListActivity.ContactListItemCache;
-import com.android.contacts.ContactsListActivity.PinnedHeaderCache;
 import com.android.contacts.TextHighlightingAnimation.TextWithHighlighting;
 
 import android.content.Context;
 import android.database.CharArrayBuffer;
 import android.database.Cursor;
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.provider.ContactsContract;
@@ -48,14 +45,11 @@
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.QuickContactBadge;
-import android.widget.SectionIndexer;
 import android.widget.TextView;
 
-public class ContactItemListAdapter extends ContactEntryListAdapter
-        implements SectionIndexer, PinnedHeaderListView.PinnedHeaderAdapter {
+public class ContactItemListAdapter extends ContactEntryListAdapter {
 
     private final ContactsListActivity contactsListActivity;
-    private SectionIndexer mIndexer;
     private boolean mLoading = true;
     protected CharSequence mUnknownNameText;
     protected boolean mDisplayPhotos = false;
@@ -511,7 +505,7 @@
         } else {
             final int section = getSectionForPosition(position);
             if (getPositionForSection(section) == position) {
-                String title = (String)mIndexer.getSections()[section];
+                String title = (String)getSections()[section];
                 view.setSectionHeader(title);
             } else {
                 view.setDividerVisible(false);
@@ -586,7 +580,7 @@
 
     private void updateIndexer(Cursor cursor) {
         if (cursor == null) {
-            mIndexer = null;
+            setIndexer(null);
             return;
         }
 
@@ -595,9 +589,9 @@
             String sections[] =
                 bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
             int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
-            mIndexer = new ContactsSectionIndexer(sections, counts);
+            setIndexer(new ContactsSectionIndexer(sections, counts));
         } else {
-            mIndexer = null;
+            setIndexer(null);
         }
     }
 
@@ -610,30 +604,6 @@
         return contactsListActivity.doFilter(constraint.toString());
     }
 
-    public Object [] getSections() {
-        if (mIndexer == null) {
-            return new String[] { " " };
-        } else {
-            return mIndexer.getSections();
-        }
-    }
-
-    public int getPositionForSection(int sectionIndex) {
-        if (mIndexer == null) {
-            return -1;
-        }
-
-        return mIndexer.getPositionForSection(sectionIndex);
-    }
-
-    public int getSectionForPosition(int position) {
-        if (mIndexer == null) {
-            return -1;
-        }
-
-        return mIndexer.getSectionForPosition(position);
-    }
-
     @Override
     public boolean areAllItemsEnabled() {
         return contactsListActivity.mMode != ContactsListActivity.MODE_STARRED
@@ -693,6 +663,11 @@
         return super.getCount();
     }
 
+    @Override
+    protected int getCursorPosition(int position) {
+        return getRealPosition(position);
+    }
+
     protected int getRealPosition(int pos) {
         if (contactsListActivity.mShowNumberOfContacts) {
             pos--;
@@ -739,68 +714,4 @@
         }
         return super.getItemId(realPosition);
     }
-
-    /**
-     * Computes the state of the pinned header.  It can be invisible, fully
-     * visible or partially pushed up out of the view.
-     */
-    public int getPinnedHeaderState(int position) {
-        if (mIndexer == null || mCursor == null || mCursor.getCount() == 0) {
-            return PINNED_HEADER_GONE;
-        }
-
-        int realPosition = getRealPosition(position);
-        if (realPosition < 0) {
-            return PINNED_HEADER_GONE;
-        }
-
-        // The header should get pushed up if the top item shown
-        // is the last item in a section for a particular letter.
-        int section = getSectionForPosition(realPosition);
-        int nextSectionPosition = getPositionForSection(section + 1);
-        if (nextSectionPosition != -1 && realPosition == nextSectionPosition - 1) {
-            return PINNED_HEADER_PUSHED_UP;
-        }
-
-        return PINNED_HEADER_VISIBLE;
-    }
-
-    /**
-     * Configures the pinned header by setting the appropriate text label
-     * and also adjusting color if necessary.  The color needs to be
-     * adjusted when the pinned header is being pushed up from the view.
-     */
-    public void configurePinnedHeader(View header, int position, int alpha) {
-        PinnedHeaderCache cache = (PinnedHeaderCache)header.getTag();
-        if (cache == null) {
-            cache = new ContactsListActivity.PinnedHeaderCache();
-            cache.titleView = (TextView)header.findViewById(R.id.header_text);
-            cache.textColor = cache.titleView.getTextColors();
-            cache.background = header.getBackground();
-            header.setTag(cache);
-        }
-
-        int realPosition = getRealPosition(position);
-        int section = getSectionForPosition(realPosition);
-
-        String title = (String)mIndexer.getSections()[section];
-        cache.titleView.setText(title);
-
-        if (alpha == 255) {
-            // Opaque: use the default background, and the original text color
-            header.setBackgroundDrawable(cache.background);
-            cache.titleView.setTextColor(cache.textColor);
-        } else {
-            // Faded: use a solid color approximation of the background, and
-            // a translucent text color
-            header.setBackgroundColor(Color.rgb(
-                    Color.red(contactsListActivity.mPinnedHeaderBackgroundColor) * alpha / 255,
-                    Color.green(contactsListActivity.mPinnedHeaderBackgroundColor) * alpha / 255,
-                    Color.blue(contactsListActivity.mPinnedHeaderBackgroundColor) * alpha / 255));
-
-            int textColor = cache.textColor.getDefaultColor();
-            cache.titleView.setTextColor(Color.argb(alpha,
-                    Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
-        }
-    }
 }
\ No newline at end of file
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
index f0643ee..034e64f 100644
--- a/src/com/android/contacts/list/ContactsIntentResolver.java
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -478,6 +478,13 @@
     public ContactEntryListConfiguration getConfiguration() {
         ContactEntryListConfiguration config;
         switch (mMode) {
+            case MODE_DEFAULT: {
+                config = new MainContactListConfiguration(mContext, mAppController);
+                if (!mSearchMode) {
+                    config.setSectionHeaderDisplayEnabled(true);
+                }
+                break;
+            }
             case MODE_LEGACY_PICK_POSTAL:
             case MODE_PICK_POSTAL:
             case MODE_LEGACY_PICK_PHONE:
diff --git a/src/com/android/contacts/list/MainContactListConfiguration.java b/src/com/android/contacts/list/MainContactListConfiguration.java
new file mode 100644
index 0000000..565f7e1
--- /dev/null
+++ b/src/com/android/contacts/list/MainContactListConfiguration.java
@@ -0,0 +1,47 @@
+/*
+ * 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.ContactsApplicationController;
+import com.android.contacts.ContactsListActivity;
+
+import android.content.Context;
+import android.widget.ListAdapter;
+
+/**
+ * Configuration for the default contact list.
+ */
+public class MainContactListConfiguration extends ContactEntryListConfiguration {
+
+    public MainContactListConfiguration(Context context,
+            ContactsApplicationController applicationController) {
+        super(context, applicationController);
+    }
+
+    @Override
+    public ListAdapter createListAdapter() {
+        ContactItemListAdapter adapter =
+                new ContactItemListAdapter((ContactsListActivity)getContext());
+        adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
+        adapter.setDisplayPhotos(true);
+        return adapter;
+    }
+
+    @Override
+    public ContactEntryListController createController() {
+        return new MainContactListController(getContext(), getApplicationController());
+    }
+}
diff --git a/src/com/android/contacts/list/MainContactListController.java b/src/com/android/contacts/list/MainContactListController.java
new file mode 100644
index 0000000..7aae6b4
--- /dev/null
+++ b/src/com/android/contacts/list/MainContactListController.java
@@ -0,0 +1,39 @@
+/*
+ * 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.ContactsApplicationController;
+
+import android.content.Context;
+
+/**
+ * Controller for the default contact list.
+ */
+public class MainContactListController extends ContactEntryListController {
+
+    public MainContactListController(Context context,
+            ContactsApplicationController appController) {
+        super(context, appController);
+    }
+
+    @Override
+    protected void onItemClick(int position, long id) {
+        // TODO instead of delegating the entire procedure to the ContactsListActivity,
+        // figure out what the specific action is and delegate the specific action.
+        getContactsApplicationController().onListItemClick(position, id);
+    }
+}
diff --git a/src/com/android/contacts/list/PinnedHeaderListAdapter.java b/src/com/android/contacts/list/PinnedHeaderListAdapter.java
new file mode 100644
index 0000000..27deef3
--- /dev/null
+++ b/src/com/android/contacts/list/PinnedHeaderListAdapter.java
@@ -0,0 +1,167 @@
+/*
+ * 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 com.android.contacts.widget.PinnedHeaderListView;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+/**
+ * A list adapter that supports section indexer and a pinned header.
+ */
+public abstract class PinnedHeaderListAdapter extends CursorAdapter
+        implements SectionIndexer, PinnedHeaderListView.PinnedHeaderAdapter {
+
+    /**
+     * An approximation of the background color of the pinned header. This color
+     * is used when the pinned header is being pushed up.  At that point the header
+     * "fades away".  Rather than computing a faded bitmap based on the 9-patch
+     * normally used for the background, we will use a solid color, which will
+     * provide better performance and reduced complexity.
+     */
+    private int mPinnedHeaderBackgroundColor;
+
+    private SectionIndexer mIndexer;
+
+    public PinnedHeaderListAdapter(Context context) {
+        super(context, null, false);
+        this.mContext = context;
+        mPinnedHeaderBackgroundColor =
+                context.getResources().getColor(R.color.pinned_header_background);
+    }
+
+    public void setIndexer(SectionIndexer indexer) {
+        mIndexer = indexer;
+    }
+
+    /**
+     * Maps an adapter position to the corresponding cursor position.  Should not
+     * be needed once we have switched to using header views and composite
+     * list adapters.
+     */
+    @Deprecated
+    protected int getCursorPosition(int position) {
+        return position;
+    }
+
+    public Object [] getSections() {
+        if (mIndexer == null) {
+            return new String[] { " " };
+        } else {
+            return mIndexer.getSections();
+        }
+    }
+
+    public int getPositionForSection(int sectionIndex) {
+        if (mIndexer == null) {
+            return -1;
+        }
+
+        return mIndexer.getPositionForSection(sectionIndex);
+    }
+
+    public int getSectionForPosition(int position) {
+        if (mIndexer == null) {
+            return -1;
+        }
+
+        return mIndexer.getSectionForPosition(position);
+    }
+
+    /**
+     * Computes the state of the pinned header.  It can be invisible, fully
+     * visible or partially pushed up out of the view.
+     */
+    public int getPinnedHeaderState(int position) {
+        if (mIndexer == null || mCursor == null || mCursor.getCount() == 0) {
+            return PINNED_HEADER_GONE;
+        }
+
+        int realPosition = getCursorPosition(position);
+        if (realPosition < 0) {
+            return PINNED_HEADER_GONE;
+        }
+
+        // The header should get pushed up if the top item shown
+        // is the last item in a section for a particular letter.
+        int section = getSectionForPosition(realPosition);
+        int nextSectionPosition = getPositionForSection(section + 1);
+        if (nextSectionPosition != -1 && realPosition == nextSectionPosition - 1) {
+            return PINNED_HEADER_PUSHED_UP;
+        }
+
+        return PINNED_HEADER_VISIBLE;
+    }
+
+    final static class PinnedHeaderCache {
+        public TextView titleView;
+        public ColorStateList textColor;
+        public Drawable background;
+    }
+
+    /**
+     * Configures the pinned header by setting the appropriate text label
+     * and also adjusting color if necessary.  The color needs to be
+     * adjusted when the pinned header is being pushed up from the view.
+     */
+    public void configurePinnedHeader(View header, int position, int alpha) {
+        PinnedHeaderCache cache = (PinnedHeaderCache)header.getTag();
+        if (cache == null) {
+            cache = new PinnedHeaderCache();
+            cache.titleView = (TextView)header.findViewById(R.id.header_text);
+            cache.textColor = cache.titleView.getTextColors();
+            cache.background = header.getBackground();
+            header.setTag(cache);
+        }
+
+        int realPosition = getCursorPosition(position);
+        int section = getSectionForPosition(realPosition);
+
+        String title = (String)mIndexer.getSections()[section];
+        cache.titleView.setText(title);
+
+        if (alpha == 255) {
+            // Opaque: use the default background, and the original text color
+            header.setBackgroundDrawable(cache.background);
+            cache.titleView.setTextColor(cache.textColor);
+        } else {
+            // Faded: use a solid color approximation of the background, and
+            // a translucent text color
+            header.setBackgroundColor(Color.rgb(
+                    Color.red(mPinnedHeaderBackgroundColor) * alpha / 255,
+                    Color.green(mPinnedHeaderBackgroundColor) * alpha / 255,
+                    Color.blue(mPinnedHeaderBackgroundColor) * alpha / 255));
+
+            int textColor = cache.textColor.getDefaultColor();
+            cache.titleView.setTextColor(Color.argb(alpha,
+                    Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
+        }
+    }
+
+    public View createPinnedHeaderView(ViewGroup parent) {
+        return LayoutInflater.from(mContext).inflate(R.layout.list_section, parent, false);
+    }
+}
diff --git a/src/com/android/contacts/PinnedHeaderListView.java b/src/com/android/contacts/widget/PinnedHeaderListView.java
similarity index 85%
rename from src/com/android/contacts/PinnedHeaderListView.java
rename to src/com/android/contacts/widget/PinnedHeaderListView.java
index 9d1391b..a3e6794 100644
--- a/src/com/android/contacts/PinnedHeaderListView.java
+++ b/src/com/android/contacts/widget/PinnedHeaderListView.java
@@ -14,20 +14,22 @@
  * limitations under the License.
  */
 
-package com.android.contacts;
+package com.android.contacts.widget;
 
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.view.View;
+import android.widget.AbsListView;
 import android.widget.ListAdapter;
 import android.widget.ListView;
+import android.widget.AbsListView.OnScrollListener;
 
 /**
  * A ListView that maintains a header pinned at the top of the list. The
  * pinned header can be pushed up and dissolved as needed.
  */
-public class PinnedHeaderListView extends ListView {
+public class PinnedHeaderListView extends ListView implements OnScrollListener {
 
     /**
      * Adapter interface.  The list adapter must implement this interface.
@@ -78,6 +80,8 @@
 
     private int mHeaderViewHeight;
 
+    private OnScrollListener mOnScrollListener;
+
     public PinnedHeaderListView(Context context) {
         super(context);
     }
@@ -88,6 +92,7 @@
 
     public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        super.setOnScrollListener(this);
     }
 
     public void setPinnedHeaderView(View view) {
@@ -109,6 +114,12 @@
     }
 
     @Override
+    public void setOnScrollListener(OnScrollListener onScrollListener) {
+        mOnScrollListener = onScrollListener;
+        super.setOnScrollListener(this);
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (mHeaderView != null) {
@@ -127,6 +138,20 @@
         }
     }
 
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+            int totalItemCount) {
+        configureHeaderView(firstVisibleItem);
+        if (mOnScrollListener != null) {
+            mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount);
+        }
+    }
+
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        if (mOnScrollListener != null) {
+            mOnScrollListener.onScrollStateChanged(this, scrollState);
+        }
+    }
+
     public void configureHeaderView(int position) {
         if (mHeaderView == null) {
             return;
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 53334a8..17ad3f7 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -40,7 +40,6 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>