Merge "Updating sticky headers." into ub-launcher3-burnaby
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index e29cac5..a726cd8 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -43,7 +43,7 @@
                 android:paddingBottom="12dp"
                 android:contentDescription="@string/all_apps_button_label"
                 android:src="@drawable/ic_arrow_back_grey" />
-            <EditText
+            <com.android.launcher3.AppsContainerSearchEditTextView
                 android:id="@+id/app_search_box"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
@@ -59,6 +59,7 @@
                 android:textColor="#4c4c4c"
                 android:textColorHint="#9c9c9c"
                 android:imeOptions="actionDone|flagNoExtractUi"
+                android:focusableInTouchMode="true"
                 android:background="@android:color/transparent" />
         </LinearLayout>
         <ImageView
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index e9a52d5..824ccde 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -104,28 +104,38 @@
         public int position;
         // Whether or not the item at this adapter position is a section or not
         public boolean isSectionHeader;
-        // The name of this section, or the section that this app is contained in
+        // The name of this section, or the section section name of the app.  Note that if this
+        // app was merged into another section, then this may be a different name than the
+        // sectionInfo's sectionName
         public String sectionName;
+        // The section to which this app belongs
+        public SectionInfo sectionInfo;
+        // The index of this app in the section
+        public int sectionAppIndex;
         // The associated AppInfo, or null if this adapter item is a section
         public AppInfo appInfo;
         // The index of this app (not including sections), or -1 if this adapter item is a section
         public int appIndex;
 
-        public static AdapterItem asSection(int pos, String name) {
+        public static AdapterItem asSection(int pos, SectionInfo section) {
             AdapterItem item = new AdapterItem();
             item.position = pos;
             item.isSectionHeader = true;
-            item.sectionName = name;
+            item.sectionInfo = section;
+            item.sectionName = section.sectionName;
             item.appInfo = null;
             item.appIndex = -1;
             return item;
         }
 
-        public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, int appIndex) {
+        public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
+                                        int sectionAppIndex, AppInfo appInfo, int appIndex) {
             AdapterItem item = new AdapterItem();
             item.position = pos;
             item.isSectionHeader = false;
+            item.sectionInfo = section;
             item.sectionName = sectionName;
+            item.sectionAppIndex = sectionAppIndex;
             item.appInfo = appInfo;
             item.appIndex = appIndex;
             return item;
@@ -140,7 +150,7 @@
     }
 
     // The maximum number of rows allowed in a merged section before we stop merging
-    private static final int MAX_ROWS_IN_MERGED_SECTION = Integer.MAX_VALUE;
+    private static final int MAX_ROWS_IN_MERGED_SECTION = 3;
 
     private List<AppInfo> mApps = new ArrayList<>();
     private List<AppInfo> mFilteredApps = new ArrayList<>();
@@ -314,6 +324,7 @@
         SectionInfo lastSectionInfo = null;
         int position = 0;
         int appIndex = 0;
+        int sectionAppIndex = 0;
         for (AppInfo info : mApps) {
             String sectionName = mIndexer.computeSectionName(info.title.toString().trim());
 
@@ -325,11 +336,12 @@
             // Create a new section if necessary
             if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) {
                 lastSectionInfo = new SectionInfo(sectionName);
+                sectionAppIndex = 0;
                 mSections.add(lastSectionInfo);
 
                 // Create a new section item, this item is used to break the flow of items in the
                 // list
-                AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName);
+                AdapterItem sectionItem = AdapterItem.asSection(position++, lastSectionInfo);
                 if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
                     lastSectionInfo.sectionItem = sectionItem;
                     mSectionedFilteredApps.add(sectionItem);
@@ -337,7 +349,8 @@
             }
 
             // Create an app item
-            AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
+            AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
+                    sectionAppIndex++, info, appIndex++);
             lastSectionInfo.numAppsInSection++;
             if (lastSectionInfo.firstAppItem == null) {
                 lastSectionInfo.firstAppItem = appItem;
@@ -361,25 +374,33 @@
                 // some limit, and also if there are no lessons to merge.
                 while (0 < (sectionAppCount % mNumAppsPerRow) &&
                         (sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow &&
-                        (int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
+                        (sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
                         (i + 1) < mSections.size()) {
                     SectionInfo nextSection = mSections.remove(i + 1);
+
                     // Merge the section names
                     if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
                         mergedSectionName += nextSection.sectionName;
                     }
                     // Remove the next section break
                     mSectionedFilteredApps.remove(nextSection.sectionItem);
+                    int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
                     if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
                         // Update the section names for the two sections
-                        int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
                         for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
                             AdapterItem item = mSectionedFilteredApps.get(j);
                             item.sectionName = mergedSectionName;
+                            item.sectionInfo = section;
                         }
                     }
-                    // Update the following adapter items of the removed section
-                    int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
+                    // Point the section for these new apps to the merged section
+                    for (int j = pos + section.numAppsInSection; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
+                        AdapterItem item = mSectionedFilteredApps.get(j);
+                        item.sectionInfo = section;
+                        item.sectionAppIndex += section.numAppsInSection;
+                    }
+                    // Update the following adapter items of the removed section item
+                    pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
                     for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
                         AdapterItem item = mSectionedFilteredApps.get(j);
                         item.position--;
diff --git a/src/com/android/launcher3/AppsContainerSearchEditTextView.java b/src/com/android/launcher3/AppsContainerSearchEditTextView.java
new file mode 100644
index 0000000..c688237
--- /dev/null
+++ b/src/com/android/launcher3/AppsContainerSearchEditTextView.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.launcher3;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+
+/**
+ * The edit text for the search container
+ */
+public class AppsContainerSearchEditTextView extends EditText {
+
+    /**
+     * Implemented by listeners of the back key.
+     */
+    public interface OnBackKeyListener {
+        public void onBackKey();
+    }
+
+    private OnBackKeyListener mBackKeyListener;
+
+    public AppsContainerSearchEditTextView(Context context) {
+        this(context, null);
+    }
+
+    public AppsContainerSearchEditTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppsContainerSearchEditTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setOnBackKeyListener(OnBackKeyListener listener) {
+        mBackKeyListener = listener;
+    }
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        // If this is a back key, propagate the key back to the listener
+        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+            if (mBackKeyListener != null) {
+                mBackKeyListener.onBackKey();
+            }
+            return false;
+        }
+        return super.onKeyPreIme(keyCode, event);
+    }
+}
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 024e539..9c05d0d 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -51,9 +51,11 @@
 
     private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
     private static final boolean DYNAMIC_HEADER_ELEVATION = false;
+    private static final boolean DISMISS_SEARCH_ON_BACK = true;
     private static final float HEADER_ELEVATION_DP = 4;
     private static final int FADE_IN_DURATION = 175;
-    private static final int FADE_OUT_DURATION = 125;
+    private static final int FADE_OUT_DURATION = 100;
+    private static final int SEARCH_TRANSLATION_X_DP = 18;
 
     @Thunk Launcher mLauncher;
     @Thunk AlphabeticalAppsList mApps;
@@ -67,7 +69,7 @@
     private View mSearchBarContainerView;
     private View mSearchButtonView;
     private View mDismissSearchButtonView;
-    private EditText mSearchBarEditView;
+    private AppsContainerSearchEditTextView mSearchBarEditView;
 
     private int mNumAppsPerRow;
     private Point mLastTouchDownPos = new Point(-1, -1);
@@ -192,10 +194,19 @@
         mSearchBarContainerView = findViewById(R.id.app_search_container);
         mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
         mDismissSearchButtonView.setOnClickListener(this);
-        mSearchBarEditView = (EditText) findViewById(R.id.app_search_box);
+        mSearchBarEditView = (AppsContainerSearchEditTextView) findViewById(R.id.app_search_box);
         if (mSearchBarEditView != null) {
             mSearchBarEditView.addTextChangedListener(this);
             mSearchBarEditView.setOnEditorActionListener(this);
+            if (DISMISS_SEARCH_ON_BACK) {
+                mSearchBarEditView.setOnBackKeyListener(
+                        new AppsContainerSearchEditTextView.OnBackKeyListener() {
+                            @Override
+                            public void onBackKey() {
+                                hideSearchField(true, true);
+                            }
+                        });
+            }
         }
         mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
         mAppsRecyclerView.setApps(mApps);
@@ -563,9 +574,16 @@
      */
     private void showSearchField() {
         // Show the search bar and focus the search
+        final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+                getContext().getResources().getDisplayMetrics());
         mSearchBarContainerView.setVisibility(View.VISIBLE);
         mSearchBarContainerView.setAlpha(0f);
-        mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer()
+        mSearchBarContainerView.setTranslationX(translationX);
+        mSearchBarContainerView.animate()
+                .alpha(1f)
+                .translationX(0)
+                .setDuration(FADE_IN_DURATION)
+                .withLayer()
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
@@ -574,38 +592,57 @@
                                 InputMethodManager.SHOW_IMPLICIT);
                     }
                 });
-        mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer();
+        mSearchButtonView.animate()
+                .alpha(0f)
+                .translationX(-translationX)
+                .setDuration(FADE_OUT_DURATION)
+                .withLayer();
     }
 
     /**
      * Hides the search field.
      */
     private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
+        final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
+        final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+                getContext().getResources().getDisplayMetrics());
         if (animated) {
             // Hide the search bar and focus the recycler view
-            mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer()
+            mSearchBarContainerView.animate()
+                    .alpha(0f)
+                    .translationX(0)
+                    .setDuration(FADE_IN_DURATION)
+                    .withLayer()
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
                             mSearchBarContainerView.setVisibility(View.INVISIBLE);
-                            mSearchBarEditView.setText("");
+                            if (resetTextField) {
+                                mSearchBarEditView.setText("");
+                            }
                             mApps.setFilter(null);
                             if (returnFocusToRecyclerView) {
                                 mAppsRecyclerView.requestFocus();
                             }
-                            scrollToTop();
                         }
                     });
-            mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer();
+            mSearchButtonView.setTranslationX(-translationX);
+            mSearchButtonView.animate()
+                    .alpha(1f)
+                    .translationX(0)
+                    .setDuration(FADE_OUT_DURATION)
+                    .withLayer();
         } else {
             mSearchBarContainerView.setVisibility(View.INVISIBLE);
-            mSearchBarEditView.setText("");
+            if (resetTextField) {
+                mSearchBarEditView.setText("");
+            }
             mApps.setFilter(null);
             mSearchButtonView.setAlpha(1f);
+            mSearchButtonView.setTranslationX(0f);
             if (returnFocusToRecyclerView) {
                 mAppsRecyclerView.requestFocus();
             }
-            scrollToTop();
         }
         getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
     }
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 62d9129..d83d6c9 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -5,10 +5,10 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -84,8 +84,10 @@
 
         private static final boolean FADE_OUT_SECTIONS = false;
 
-        private HashMap<String, Point> mCachedSectionBounds = new HashMap<>();
+        private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
         private Rect mTmpBounds = new Rect();
+        private String[] mTmpSections = new String[2];
+        private PointF[] mTmpSectionBounds = new PointF[2];
 
         @Override
         public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
@@ -94,75 +96,83 @@
             }
 
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-            String lastSectionName = null;
-            int appIndexInSection = 0;
+            int childCount = parent.getChildCount();
             int lastSectionTop = 0;
             int lastSectionHeight = 0;
-            for (int i = 0; i < parent.getChildCount(); i++) {
+            for (int i = 0; i < childCount; i++) {
                 View child = parent.getChildAt(i);
                 ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
                 if (shouldDrawItemSection(holder, child, i, items)) {
-                    int cellTopOffset = (2 * child.getPaddingTop());
+                    // At this point, we only draw sections for each section break;
+                    int viewTopOffset = (2 * child.getPaddingTop());
                     int pos = holder.getPosition();
                     AlphabeticalAppsList.AdapterItem item = items.get(pos);
-                    if (!item.sectionName.equals(lastSectionName)) {
-                        lastSectionName = item.sectionName;
+                    AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo;
+
+                    // Draw all the sections for this index
+                    String lastSectionName = item.sectionName;
+                    for (int j = item.sectionAppIndex; j < sectionInfo.numAppsInSection;j++, pos++) {
+                        AlphabeticalAppsList.AdapterItem nextItem = items.get(pos);
+                        if (nextItem.sectionInfo != sectionInfo) {
+                            break;
+                        }
+                        if (j > item.sectionAppIndex && nextItem.sectionName.equals(lastSectionName)) {
+                            continue;
+                        }
 
                         // Find the section code points
-                        String sectionBegin = null;
-                        String sectionEnd = null;
-                        int charOffset = 0;
-                        while (charOffset < item.sectionName.length()) {
-                            int codePoint = item.sectionName.codePointAt(charOffset);
-                            int codePointSize = Character.charCount(codePoint);
-                            if (charOffset == 0) {
-                                // The first code point
-                                sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize);
-                            } else if ((charOffset + codePointSize) >= item.sectionName.length()) {
-                                // The last code point
-                                sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize);
-                            }
-                            charOffset += codePointSize;
+                        getSectionLetters(nextItem.sectionName, mTmpSections, mTmpSectionBounds);
+                        String sectionBegin = mTmpSections[0];
+                        String sectionEnd = mTmpSections[1];
+                        PointF sectionBeginBounds = mTmpSectionBounds[0];
+                        PointF sectionEndBounds = mTmpSectionBounds[1];
+
+                        // Calculate where to draw the section
+                        int sectionBaseline = (int) (viewTopOffset + sectionBeginBounds.y);
+                        int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
+                                mPaddingStart;
+                        int y = child.getTop() + sectionBaseline;
+
+                        // Determine whether this is the last row with apps in that section, if
+                        // so, then fix the section to the row allowing it to scroll past the
+                        // baseline, otherwise, bound it to the baseline so it's in the viewport
+                        int appIndexInSection = items.get(pos).sectionAppIndex;
+                        int nextRowPos = Math.min(items.size() - 1,
+                                pos + mAppsPerRow - (appIndexInSection % mAppsPerRow));
+                        boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(nextItem.sectionName);
+                        if (!fixedToRow) {
+                            y = Math.max(sectionBaseline, y);
                         }
 
-                        Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin);
-                        int minTop = cellTopOffset + sectionBeginBounds.y;
-                        int top = child.getTop() + cellTopOffset + sectionBeginBounds.y;
-                        int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
-                                mPaddingStart;
-                        int col = appIndexInSection % mAppsPerRow;
-                        int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1);
-                        int alpha = 255;
-                        boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName);
-                        if (fixedToRow) {
-                            alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop)));
-                        } else {
-                            // If we aren't fixed to the current row, then bound into the viewport
-                            top = Math.max(minTop, top);
+                        // In addition, if it overlaps with the last section that was drawn, then
+                        // offset it so that it does not overlap
+                        if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) {
+                            y += lastSectionTop - y + lastSectionHeight;
                         }
-                        if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) {
-                            top += lastSectionTop - top + lastSectionHeight;
-                        }
+
+                        // Draw the section header
                         if (FADE_OUT_SECTIONS) {
+                            int alpha = 255;
+                            if (fixedToRow) {
+                                alpha = Math.min(255, (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
+                            }
                             mSectionTextPaint.setAlpha(alpha);
                         }
                         if (sectionEnd != null) {
-                            Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd);
+                            // If there is a range, draw the range
                             c.drawText(sectionBegin + "/" + sectionEnd,
-                                    left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top,
+                                    x + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, y,
                                     mSectionTextPaint);
                         } else {
-                            c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top,
+                            c.drawText(sectionBegin, (int) (x + (mStartMargin / 2f) - (sectionBeginBounds.x / 2f)), y,
                                     mSectionTextPaint);
                         }
-                        lastSectionTop = top;
-                        lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset;
+
+                        lastSectionTop = y;
+                        lastSectionHeight = (int) (sectionBeginBounds.y + mSectionHeaderOffset);
+                        lastSectionName = nextItem.sectionName;
                     }
-                }
-                if (holder.mIsSectionHeader) {
-                    appIndexInSection = 0;
-                } else {
-                    appIndexInSection++;
+                    i += (sectionInfo.numAppsInSection - item.sectionAppIndex);
                 }
             }
         }
@@ -173,16 +183,50 @@
             // Do nothing
         }
 
-        private Point getAndCacheSectionBounds(String sectionName) {
-            Point bounds = mCachedSectionBounds.get(sectionName);
+        /**
+         * Given a section name, return the first and last section letters.
+         */
+        private void getSectionLetters(String sectionName, String[] lettersOut, PointF[] boundsOut) {
+            lettersOut[0] = lettersOut[1] = null;
+            boundsOut[0] = boundsOut[1] = null;
+            if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
+                int charOffset = 0;
+                while (charOffset < sectionName.length()) {
+                    int codePoint = sectionName.codePointAt(charOffset);
+                    int codePointSize = Character.charCount(codePoint);
+                    if (charOffset == 0) {
+                        // The first code point
+                        lettersOut[0] = sectionName.substring(charOffset, charOffset + codePointSize);
+                        boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]);
+                    } else if ((charOffset + codePointSize) >= sectionName.length()) {
+                        // The last code point
+                        lettersOut[1] = sectionName.substring(charOffset, charOffset + codePointSize);
+                        boundsOut[0] = getAndCacheSectionBounds(lettersOut[1]);
+                    }
+                    charOffset += codePointSize;
+                }
+            } else {
+                lettersOut[0] = sectionName;
+                boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]);
+            }
+        }
+
+        /**
+         * Given a section name, return the first and last section letters.
+         */
+        private PointF getAndCacheSectionBounds(String sectionName) {
+            PointF bounds = mCachedSectionBounds.get(sectionName);
             if (bounds == null) {
                 mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds);
-                bounds = new Point(mTmpBounds.width(), mTmpBounds.height());
+                bounds = new PointF(mSectionTextPaint.measureText(sectionName), mTmpBounds.height());
                 mCachedSectionBounds.put(sectionName, bounds);
             }
             return bounds;
         }
 
+        /**
+         * Returns whether to draw the section for the given child.
+         */
         private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex,
                 List<AlphabeticalAppsList.AdapterItem> items) {
             // Ensure item is not already removed
@@ -201,19 +245,12 @@
             }
             // Ensure we have a holder position
             int pos = holder.getPosition();
-            if (pos < 0 || pos >= items.size()) {
+            if (pos <= 0 || pos >= items.size()) {
                 return false;
             }
-            // Ensure this is not a section header
-            if (items.get(pos).isSectionHeader) {
-                return false;
-            }
-            // Only draw the header for the first item in a section, or whenever the sub-sections
-            // changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but
-            // AppsContainerView.GRID_MERGE_SECTION_HEADERS is false)
+            // Draw the section header for the first item in each section
             return (childIndex == 0) ||
-                    items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader ||
-                    (!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName));
+                    (items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader);
         }
     }