From 99d96ba6c8e3258f7d99a33d49da2aeb0da5d862 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 8 May 2015 12:04:45 -0700 Subject: Fixing issue with fast scroller not showing sub-section headers. - Removed some old logic to draw merged section headers Change-Id: I2a7ff9948a3dce253d6bdcda89cc9f222faab309 --- .../android/launcher3/AlphabeticalAppsList.java | 112 ++++++++++++--------- .../launcher3/AppsContainerRecyclerView.java | 44 ++++---- src/com/android/launcher3/AppsContainerView.java | 1 - src/com/android/launcher3/AppsGridAdapter.java | 69 +++---------- 4 files changed, 99 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 824ccde64..70e36a744 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -82,17 +82,30 @@ public class AlphabeticalAppsList { * Info about a section in the alphabetic list */ public static class SectionInfo { - // The name of this section - public String sectionName; // The number of applications in this section - public int numAppsInSection; - // The section AdapterItem for this section - public AdapterItem sectionItem; + public int numApps; + // The section break AdapterItem for this section + public AdapterItem sectionBreakItem; // The first app AdapterItem for this section public AdapterItem firstAppItem; + } - public SectionInfo(String name) { - sectionName = name; + /** + * Info about a fast scroller section, depending if sections are merged, the fast scroller + * sections will not be the same set as the section headers. + */ + public static class FastScrollSectionInfo { + // The section name + public String sectionName; + // To map the touch (from 0..1) to the index in the app list to jump to in the fast + // scroller, we use the fraction in range (0..1) of the app index / total app count. + public float appRangeFraction; + // The AdapterItem to scroll to for this section + public AdapterItem appItem; + + public FastScrollSectionInfo(String sectionName, float appRangeFraction) { + this.sectionName = sectionName; + this.appRangeFraction = appRangeFraction; } } @@ -100,31 +113,30 @@ public class AlphabeticalAppsList { * Info about a particular adapter item (can be either section or app) */ public static class AdapterItem { + /** Section & App properties */ // The index of this adapter item in the list 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 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 + // The section for this item public SectionInfo sectionInfo; + + /** App-only properties */ + // The section name of this app. Note that there can be multiple items with different + // sectionNames in the same section + public String sectionName = null; // 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 int sectionAppIndex = -1; + // The associated AppInfo for the app + public AppInfo appInfo = null; + // The index of this app not including sections + public int appIndex = -1; - public static AdapterItem asSection(int pos, SectionInfo section) { + public static AdapterItem asSectionBreak(int pos, SectionInfo section) { AdapterItem item = new AdapterItem(); item.position = pos; item.isSectionHeader = true; item.sectionInfo = section; - item.sectionName = section.sectionName; - item.appInfo = null; - item.appIndex = -1; return item; } @@ -156,6 +168,7 @@ public class AlphabeticalAppsList { private List mFilteredApps = new ArrayList<>(); private List mSectionedFilteredApps = new ArrayList<>(); private List mSections = new ArrayList<>(); + private List mFastScrollerSections = new ArrayList<>(); private RecyclerView.Adapter mAdapter; private Filter mFilter; private AlphabeticIndexCompat mIndexer; @@ -193,6 +206,13 @@ public class AlphabeticalAppsList { return mSections; } + /** + * Returns fast scroller sections of all the current filtered applications. + */ + public List getFastScrollerSections() { + return mFastScrollerSections; + } + /** * Returns the current filtered list of applications broken down into their sections. */ @@ -321,10 +341,13 @@ public class AlphabeticalAppsList { mFilteredApps.clear(); mSections.clear(); mSectionedFilteredApps.clear(); + mFastScrollerSections.clear(); SectionInfo lastSectionInfo = null; + String lastSectionName = null; + FastScrollSectionInfo lastFastScrollerSectionInfo = null; int position = 0; int appIndex = 0; - int sectionAppIndex = 0; + int numApps = mApps.size(); for (AppInfo info : mApps) { String sectionName = mIndexer.computeSectionName(info.title.toString().trim()); @@ -334,26 +357,29 @@ public class AlphabeticalAppsList { } // Create a new section if necessary - if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) { - lastSectionInfo = new SectionInfo(sectionName); - sectionAppIndex = 0; + if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { + lastSectionName = sectionName; + lastSectionInfo = new SectionInfo(); mSections.add(lastSectionInfo); + lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, + (float) appIndex / numApps); + mFastScrollerSections.add(lastFastScrollerSectionInfo); // Create a new section item, this item is used to break the flow of items in the // list - AdapterItem sectionItem = AdapterItem.asSection(position++, lastSectionInfo); + AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { - lastSectionInfo.sectionItem = sectionItem; + lastSectionInfo.sectionBreakItem = sectionItem; mSectionedFilteredApps.add(sectionItem); } } // Create an app item AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, - sectionAppIndex++, info, appIndex++); - lastSectionInfo.numAppsInSection++; + lastSectionInfo.numApps++, info, appIndex++); if (lastSectionInfo.firstAppItem == null) { lastSectionInfo.firstAppItem = appItem; + lastFastScrollerSectionInfo.appItem = appItem; } mSectionedFilteredApps.add(appItem); mFilteredApps.add(info); @@ -365,9 +391,9 @@ public class AlphabeticalAppsList { int sectionAppCount = 0; for (int i = 0; i < mSections.size(); i++) { SectionInfo section = mSections.get(i); - String mergedSectionName = section.sectionName; - sectionAppCount = section.numAppsInSection; + sectionAppCount = section.numApps; int mergeCount = 1; + // Merge rows if the last app in this section is in a column that is greater than // 0, but less than the min number of apps per row. In addition, apply the // constraint to stop merging if the number of rows in the section is greater than @@ -378,35 +404,25 @@ public class AlphabeticalAppsList { (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); + mSectionedFilteredApps.remove(nextSection.sectionBreakItem); int pos = mSectionedFilteredApps.indexOf(section.firstAppItem); - if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { - // Update the section names for the two sections - for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { - AdapterItem item = mSectionedFilteredApps.get(j); - item.sectionName = mergedSectionName; - item.sectionInfo = section; - } - } // Point the section for these new apps to the merged section - for (int j = pos + section.numAppsInSection; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { + int nextPos = pos + section.numApps; + for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) { AdapterItem item = mSectionedFilteredApps.get(j); item.sectionInfo = section; - item.sectionAppIndex += section.numAppsInSection; + item.sectionAppIndex += section.numApps; } + // 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--; } - section.numAppsInSection += nextSection.numAppsInSection; - sectionAppCount += nextSection.numAppsInSection; + section.numApps += nextSection.numApps; + sectionAppCount += nextSection.numApps; mergeCount++; if (mergeCount >= mMaxAllowableMerges) { break; diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index fb5f6d436..6556cf920 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -254,8 +254,9 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, mFastScrollSectionName.length(), mFastScrollTextBounds); + float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); canvas.drawText(mFastScrollSectionName, - (bgBounds.width() - mFastScrollTextBounds.width()) / 2, + (bgBounds.width() - textWidth) / 2, bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2, mFastScrollTextPaint); canvas.restoreToCount(restoreCount); @@ -285,38 +286,33 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } /** - * Maps the progress (from 0..1) to the position that should be visible + * Maps the touch (from 0..1) to the adapter position that should be visible. */ - private String scrollToPositionAtProgress(float progress) { - List sections = mApps.getSections(); - if (sections.isEmpty()) { + private String scrollToPositionAtProgress(float touchFraction) { + // Ensure that we have any sections + List fastScrollSections = + mApps.getFastScrollerSections(); + if (fastScrollSections.isEmpty()) { return ""; } - // Find the position of the first application in the section that contains the row at the - // current progress - List items = mApps.getAdapterItems(); - int rowAtProgress = (int) (progress * getNumRows()); - int rowCount = 0; - AlphabeticalAppsList.SectionInfo lastSectionInfo = null; - for (AlphabeticalAppsList.SectionInfo section : sections) { - int numRowsInSection = (int) Math.ceil((float) section.numAppsInSection / mNumAppsPerRow); - if (rowCount + numRowsInSection >= rowAtProgress) { - lastSectionInfo = section; + AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0); + for (int i = 1; i < fastScrollSections.size(); i++) { + AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i); + if (lastScrollSection.appRangeFraction <= touchFraction && + touchFraction < scrollSection.appRangeFraction) { break; } - rowCount += numRowsInSection; + lastScrollSection = scrollSection; } - int position = items.indexOf(lastSectionInfo.firstAppItem); // Scroll the position into view, anchored at the top of the screen if possible. We call the // scroll method on the LayoutManager directly since it is not exposed by RecyclerView. LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); stopScroll(); - layoutManager.scrollToPositionWithOffset(position, 0); + layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); - // Return the section name of the row - return lastSectionInfo.sectionName; + return lastScrollSection.sectionName; } /** @@ -392,11 +388,11 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { int appIndex = 0; int rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); - if (appIndex + info.numAppsInSection > position) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + if (appIndex + info.numApps > position) { return rowCount + ((position - appIndex) / mNumAppsPerRow); } - appIndex += info.numAppsInSection; + appIndex += info.numApps; rowCount += numRowsInSection; } return appIndex; @@ -409,7 +405,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { List sections = mApps.getSections(); int rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); rowCount += numRowsInSection; } return rowCount; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 9c05d0d10..993f9c857 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -46,7 +46,6 @@ public class AppsContainerView extends BaseContainerView implements DragSource, View.OnClickListener, View.OnLongClickListener { public static final boolean GRID_MERGE_SECTIONS = true; - public static final boolean GRID_MERGE_SECTION_HEADERS = false; public static final boolean GRID_HIDE_SECTION_HEADERS = false; private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index d83d6c97c..259740c60 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.res.Resources; 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; @@ -86,8 +85,6 @@ class AppsGridAdapter extends RecyclerView.Adapter { private HashMap 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) { @@ -111,26 +108,24 @@ class AppsGridAdapter extends RecyclerView.Adapter { // Draw all the sections for this index String lastSectionName = item.sectionName; - for (int j = item.sectionAppIndex; j < sectionInfo.numAppsInSection;j++, pos++) { + for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) { AlphabeticalAppsList.AdapterItem nextItem = items.get(pos); + String sectionName = nextItem.sectionName; if (nextItem.sectionInfo != sectionInfo) { break; } - if (j > item.sectionAppIndex && nextItem.sectionName.equals(lastSectionName)) { + if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) { continue; } // Find the section code points - getSectionLetters(nextItem.sectionName, mTmpSections, mTmpSectionBounds); - String sectionBegin = mTmpSections[0]; - String sectionEnd = mTmpSections[1]; - PointF sectionBeginBounds = mTmpSectionBounds[0]; - PointF sectionEndBounds = mTmpSectionBounds[1]; + PointF sectionBounds = getAndCacheSectionBounds(sectionName); // Calculate where to draw the section - int sectionBaseline = (int) (viewTopOffset + sectionBeginBounds.y); + int sectionBaseline = (int) (viewTopOffset + sectionBounds.y); int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : mPaddingStart; + x += (int) ((mStartMargin - sectionBounds.x) / 2f); int y = child.getTop() + sectionBaseline; // Determine whether this is the last row with apps in that section, if @@ -139,7 +134,8 @@ class AppsGridAdapter extends RecyclerView.Adapter { 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); + AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos); + boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName); if (!fixedToRow) { y = Math.max(sectionBaseline, y); } @@ -154,25 +150,18 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (FADE_OUT_SECTIONS) { int alpha = 255; if (fixedToRow) { - alpha = Math.min(255, (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); + alpha = Math.min(255, + (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); } mSectionTextPaint.setAlpha(alpha); } - if (sectionEnd != null) { - // If there is a range, draw the range - c.drawText(sectionBegin + "/" + sectionEnd, - x + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, y, - mSectionTextPaint); - } else { - c.drawText(sectionBegin, (int) (x + (mStartMargin / 2f) - (sectionBeginBounds.x / 2f)), y, - mSectionTextPaint); - } + c.drawText(sectionName, x, y, mSectionTextPaint); lastSectionTop = y; - lastSectionHeight = (int) (sectionBeginBounds.y + mSectionHeaderOffset); - lastSectionName = nextItem.sectionName; + lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset); + lastSectionName = sectionName; } - i += (sectionInfo.numAppsInSection - item.sectionAppIndex); + i += (sectionInfo.numApps - item.sectionAppIndex); } } } @@ -184,35 +173,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { } /** - * 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. + * Given a section name, return the bounds of the given section name. */ private PointF getAndCacheSectionBounds(String sectionName) { PointF bounds = mCachedSectionBounds.get(sectionName); -- cgit v1.2.3