/**
   * Updates the set of filtered apps with the current filter. At this point, we expect
   * mCachedSectionNames to have been calculated for the set of all apps in mApps.
   */
  private void updateAdapterItems() {
    SectionInfo lastSectionInfo = null;
    String lastSectionName = null;
    FastScrollSectionInfo lastFastScrollerSectionInfo = null;
    int position = 0;
    int appIndex = 0;

    // Prepare to update the list of sections, filtered apps, etc.
    mFilteredApps.clear();
    mFastScrollerSections.clear();
    mAdapterItems.clear();
    mSections.clear();

    if (DEBUG_PREDICTIONS) {
      if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
        mPredictedAppComponents.add(
            new ComponentKey(mApps.get(0).componentName, UserHandleCompat.myUserHandle()));
        mPredictedAppComponents.add(
            new ComponentKey(mApps.get(0).componentName, UserHandleCompat.myUserHandle()));
        mPredictedAppComponents.add(
            new ComponentKey(mApps.get(0).componentName, UserHandleCompat.myUserHandle()));
        mPredictedAppComponents.add(
            new ComponentKey(mApps.get(0).componentName, UserHandleCompat.myUserHandle()));
      }
    }

    // Process the predicted app components
    mPredictedApps.clear();
    if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
      for (ComponentKey ck : mPredictedAppComponents) {
        AppInfo info = mComponentToAppMap.get(ck);
        if (info != null) {
          mPredictedApps.add(info);
        } else {
          if (LauncherAppState.isDogfoodBuild()) {
            Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
          }
        }
        // Stop at the number of predicted apps
        if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
          break;
        }
      }

      if (!mPredictedApps.isEmpty()) {
        // Add a section for the predictions
        lastSectionInfo = new SectionInfo();
        lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
        AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
        mSections.add(lastSectionInfo);
        mFastScrollerSections.add(lastFastScrollerSectionInfo);
        mAdapterItems.add(sectionItem);

        // Add the predicted app items
        for (AppInfo info : mPredictedApps) {
          AdapterItem appItem =
              AdapterItem.asPredictedApp(
                  position++, lastSectionInfo, "", lastSectionInfo.numApps++, info, appIndex++);
          if (lastSectionInfo.firstAppItem == null) {
            lastSectionInfo.firstAppItem = appItem;
            lastFastScrollerSectionInfo.fastScrollToItem = appItem;
          }
          mAdapterItems.add(appItem);
          mFilteredApps.add(info);
        }
      }
    }

    // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
    // ordered set of sections
    for (AppInfo info : getFiltersAppInfos()) {
      String sectionName = getAndUpdateCachedSectionName(info.title);

      // Create a new section if the section names do not match
      if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
        lastSectionName = sectionName;
        lastSectionInfo = new SectionInfo();
        lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
        mSections.add(lastSectionInfo);
        mFastScrollerSections.add(lastFastScrollerSectionInfo);

        // Create a new section item to break the flow of items in the list
        if (!hasFilter()) {
          AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
          mAdapterItems.add(sectionItem);
        }
      }

      // Create an app item
      AdapterItem appItem =
          AdapterItem.asApp(
              position++,
              lastSectionInfo,
              sectionName,
              lastSectionInfo.numApps++,
              info,
              appIndex++);
      if (lastSectionInfo.firstAppItem == null) {
        lastSectionInfo.firstAppItem = appItem;
        lastFastScrollerSectionInfo.fastScrollToItem = appItem;
      }
      mAdapterItems.add(appItem);
      mFilteredApps.add(info);
    }

    // Append the search market item if we are currently searching
    if (hasFilter()) {
      if (hasNoFilteredResults()) {
        mAdapterItems.add(AdapterItem.asEmptySearch(position++));
      } else {
        mAdapterItems.add(AdapterItem.asDivider(position++));
      }
      mAdapterItems.add(AdapterItem.asMarketSearch(position++));
    }

    // Merge multiple sections together as requested by the merge strategy for this device
    mergeSections();

    if (mNumAppsPerRow != 0) {
      // Update the number of rows in the adapter after we do all the merging (otherwise, we
      // would have to shift the values again)
      int numAppsInSection = 0;
      int numAppsInRow = 0;
      int rowIndex = -1;
      for (AdapterItem item : mAdapterItems) {
        item.rowIndex = 0;
        if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) {
          numAppsInSection = 0;
        } else if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE
            || item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
          if (numAppsInSection % mNumAppsPerRow == 0) {
            numAppsInRow = 0;
            rowIndex++;
          }
          item.rowIndex = rowIndex;
          item.rowAppIndex = numAppsInRow;
          numAppsInSection++;
          numAppsInRow++;
        }
      }
      mNumAppRowsInAdapter = rowIndex + 1;

      // Pre-calculate all the fast scroller fractions
      switch (mFastScrollDistributionMode) {
        case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION:
          float rowFraction = 1f / mNumAppRowsInAdapter;
          for (FastScrollSectionInfo info : mFastScrollerSections) {
            AdapterItem item = info.fastScrollToItem;
            if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE
                && item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
              info.touchFraction = 0f;
              continue;
            }

            float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow);
            info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
          }
          break;
        case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS:
          float perSectionTouchFraction = 1f / mFastScrollerSections.size();
          float cumulativeTouchFraction = 0f;
          for (FastScrollSectionInfo info : mFastScrollerSections) {
            AdapterItem item = info.fastScrollToItem;
            if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE
                && item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
              info.touchFraction = 0f;
              continue;
            }
            info.touchFraction = cumulativeTouchFraction;
            cumulativeTouchFraction += perSectionTouchFraction;
          }
          break;
      }
    }

    // Refresh the recycler view
    if (mAdapter != null) {
      mAdapter.notifyDataSetChanged();
    }
  }