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);
}
}