Refactoring some folder binding logic:

> Moving grid calcutation in a separate class
> Moving content saving logic to folder instead of relying on item bind

Bug: 139051851
Change-Id: I81b226dbebe13652482a767c992e8cc8f4f35a60
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 09fb244..976ccd5 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2702,6 +2702,14 @@
             }
         }
 
+        /**
+         * Sets the position to the provided point
+         */
+        public void setXY(Point point) {
+            cellX = point.x;
+            cellY = point.y;
+        }
+
         public String toString() {
             return "(" + this.cellX + ", " + this.cellY + ")";
         }
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index b747d62..67d5ab0 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -50,9 +50,9 @@
     /**
      * The apps and shortcuts
      */
-    public ArrayList<WorkspaceItemInfo> contents = new ArrayList<WorkspaceItemInfo>();
+    public ArrayList<WorkspaceItemInfo> contents = new ArrayList<>();
 
-    ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
+    private ArrayList<FolderListener> mListeners = new ArrayList<>();
 
     public FolderInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
@@ -72,10 +72,10 @@
      * Add an app or shortcut for a specified rank.
      */
     public void add(WorkspaceItemInfo item, int rank, boolean animate) {
-        rank = Utilities.boundToRange(rank, 0, contents.size());
+        rank = Utilities.boundToRange(rank, 0, contents.size() + 1);
         contents.add(rank, item);
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onAdd(item, rank);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onAdd(item, rank);
         }
         itemsChanged(animate);
     }
@@ -87,16 +87,16 @@
      */
     public void remove(WorkspaceItemInfo item, boolean animate) {
         contents.remove(item);
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onRemove(item);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onRemove(item);
         }
         itemsChanged(animate);
     }
 
     public void setTitle(CharSequence title) {
         this.title = title;
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onTitleChanged(title);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onTitleChanged(title);
         }
     }
 
@@ -105,26 +105,25 @@
         super.onAddToDatabase(writer);
         writer.put(LauncherSettings.Favorites.TITLE, title)
                 .put(LauncherSettings.Favorites.OPTIONS, options);
-
     }
 
     public void addListener(FolderListener listener) {
-        listeners.add(listener);
+        mListeners.add(listener);
     }
 
     public void removeListener(FolderListener listener) {
-        listeners.remove(listener);
+        mListeners.remove(listener);
     }
 
     public void itemsChanged(boolean animate) {
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onItemsChanged(animate);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onItemsChanged(animate);
         }
     }
 
     public void prepareAutoUpdate() {
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).prepareAutoUpdate();
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).prepareAutoUpdate();
         }
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c262e16..75d48ee 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -79,6 +79,8 @@
 import android.view.animation.OvershootInterpolator;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
@@ -94,8 +96,8 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -110,7 +112,6 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.states.InternalStateHandler;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.ItemClickHandler;
@@ -158,8 +159,6 @@
 import java.util.List;
 import java.util.function.Predicate;
 
-import androidx.annotation.Nullable;
-
 /**
  * Default launcher application.
  */
@@ -605,10 +604,9 @@
         if (info.container >= 0) {
             View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
             if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) {
-                FolderIconPreviewVerifier verifier =
-                        new FolderIconPreviewVerifier(getDeviceProfile().inv);
-                verifier.setFolderInfo((FolderInfo) folderIcon.getTag());
-                if (verifier.isItemInPreview(info.rank)) {
+                if (new FolderGridOrganizer(getDeviceProfile().inv)
+                        .setFolderInfo((FolderInfo) folderIcon.getTag())
+                        .isItemInPreview(info.rank)) {
                     folderIcon.invalidate();
                 }
             }
@@ -1725,8 +1723,6 @@
         return true;
     }
 
-
-
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3be91d4..812f444 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,9 +67,9 @@
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -3102,7 +3102,7 @@
             ItemInfo info = (ItemInfo) item.getTag();
             if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
                 FolderIcon folder = (FolderIcon) item;
-                ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                ArrayList<View> folderChildren = folder.getFolder().getIconsInReadingOrder();
                 // map over all the children in the folder
                 final int childCount = folderChildren.size();
                 for (int childIdx = 0; childIdx < childCount; childIdx++) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2ef6d70..1b57449 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -385,6 +385,7 @@
         mInfo = info;
         ArrayList<WorkspaceItemInfo> children = info.contents;
         Collections.sort(children, ITEM_POS_COMPARATOR);
+        updateItemLocationsInDatabaseBatch();
         mContent.bindItems(children);
 
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
@@ -822,9 +823,9 @@
         }
     }
 
+    @Override
     public void onDropCompleted(final View target, final DragObject d,
             final boolean success) {
-
         if (success) {
             if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
                 replaceFolderWithFinalItem();
@@ -834,9 +835,9 @@
             WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
             View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
                     ? mCurrentDragView : mContent.createNewView(info);
-            ArrayList<View> views = getItemsInReadingOrder();
+            ArrayList<View> views = getIconsInReadingOrder();
             views.add(info.rank, icon);
-            mContent.arrangeChildren(views, views.size());
+            mContent.arrangeChildren(views);
             mItemsInvalidated = true;
 
             try (SuppressInfoChanges s = new SuppressInfoChanges()) {
@@ -874,16 +875,21 @@
     }
 
     private void updateItemLocationsInDatabaseBatch() {
-        ArrayList<View> list = getItemsInReadingOrder();
-        ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
-        for (int i = 0; i < list.size(); i++) {
-            View v = list.get(i);
-            ItemInfo info = (ItemInfo) v.getTag();
-            info.rank = i;
-            items.add(info);
+        FolderGridOrganizer verifier = new FolderGridOrganizer(
+                mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+
+        ArrayList<ItemInfo> items = new ArrayList<>();
+        int total = mInfo.contents.size();
+        for (int i = 0; i < total; i++) {
+            WorkspaceItemInfo itemInfo = mInfo.contents.get(i);
+            if (verifier.updateRankAndPos(itemInfo, i)) {
+                items.add(itemInfo);
+            }
         }
 
-        mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+        if (!items.isEmpty()) {
+            mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+        }
     }
 
     public void notifyDrop() {
@@ -1021,17 +1027,7 @@
      * Rearranges the children based on their rank.
      */
     public void rearrangeChildren() {
-        rearrangeChildren(-1);
-    }
-
-    /**
-     * Rearranges the children based on their rank.
-     * @param itemCount if greater than the total children count, empty spaces are left at the end,
-     * otherwise it is ignored.
-     */
-    public void rearrangeChildren(int itemCount) {
-        ArrayList<View> views = getItemsInReadingOrder();
-        mContent.arrangeChildren(views, Math.max(itemCount, views.size()));
+        mContent.arrangeChildren(getIconsInReadingOrder());
         mItemsInvalidated = true;
     }
 
@@ -1124,6 +1120,7 @@
         }
     }
 
+    @Override
     public void onDrop(DragObject d, DragOptions options) {
         // If the icon was dropped while the page was being scrolled, we need to compute
         // the target location again such that the icon is placed of the final page.
@@ -1171,12 +1168,6 @@
                 // before creating the view, so that WorkspaceItemInfo is updated appropriately.
                 mLauncher.getModelWriter().addOrMoveItemInDatabase(
                         si, mInfo.id, 0, si.cellX, si.cellY);
-
-                // We only need to update the locations if it doesn't get handled in
-                // #onDropCompleted.
-                if (d.dragSource != this) {
-                    updateItemLocationsInDatabaseBatch();
-                }
                 mIsExternalDrag = false;
             } else {
                 currentDragView = mCurrentDragView;
@@ -1203,7 +1194,13 @@
 
             // Temporarily suppress the listener, as we did all the work already here.
             try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-                mInfo.add(si, false);
+                mInfo.add(si, mEmptyCellRank, false);
+            }
+
+            // We only need to update the locations if it doesn't get handled in
+            // #onDropCompleted.
+            if (d.dragSource != this) {
+                updateItemLocationsInDatabaseBatch();
             }
         }
 
@@ -1226,22 +1223,29 @@
     // to correspond to the animation of the icon back into the folder. This is
     public void hideItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
-        v.setVisibility(INVISIBLE);
+        if (v != null) {
+            v.setVisibility(INVISIBLE);
+        }
     }
     public void showItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
-        v.setVisibility(VISIBLE);
+        if (v != null) {
+            v.setVisibility(VISIBLE);
+        }
     }
 
     @Override
     public void onAdd(WorkspaceItemInfo item, int rank) {
-        View view = mContent.createAndAddViewForRank(item, rank);
+        FolderGridOrganizer verifier = new FolderGridOrganizer(
+                mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+        verifier.updateRankAndPos(item, rank);
         mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
                 item.cellY);
+        updateItemLocationsInDatabaseBatch();
 
-        ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
-        items.add(rank, view);
-        mContent.arrangeChildren(items, items.size());
+        ArrayList<View> items = new ArrayList<>(getIconsInReadingOrder());
+        items.add(rank, mContent.createAndAddViewForRank(item, rank));
+        mContent.arrangeChildren(items);
         mItemsInvalidated = true;
     }
 
@@ -1264,13 +1268,7 @@
     }
 
     private View getViewForInfo(final WorkspaceItemInfo item) {
-        return mContent.iterateOverItems(new ItemOperator() {
-
-            @Override
-            public boolean evaluate(ItemInfo info, View view) {
-                return info == item;
-            }
-        });
+        return mContent.iterateOverItems((info, view) -> info == item);
     }
 
     @Override
@@ -1286,7 +1284,10 @@
     public void onTitleChanged(CharSequence title) {
     }
 
-    public ArrayList<View> getItemsInReadingOrder() {
+    /**
+     * Returns the sorted list of all the icons in the folder
+     */
+    public ArrayList<View> getIconsInReadingOrder() {
         if (mItemsInvalidated) {
             mItemsInReadingOrder.clear();
             mContent.iterateOverItems(new ItemOperator() {
@@ -1303,7 +1304,7 @@
     }
 
     public List<BubbleTextView> getItemsOnPage(int page) {
-        ArrayList<View> allItems = getItemsInReadingOrder();
+        ArrayList<View> allItems = getIconsInReadingOrder();
         int lastPage = mContent.getPageCount() - 1;
         int totalItemsInFolder = allItems.size();
         int itemsPerPage = mContent.itemsPerPage();
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 962f215..9eb0693 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -79,7 +79,7 @@
     private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
 
     private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-
+    private final FolderGridOrganizer mPreviewVerifier;
 
     public FolderAnimationManager(Folder folder, boolean isOpening) {
         mFolder = folder;
@@ -91,6 +91,7 @@
 
         mContext = folder.getContext();
         mLauncher = folder.mLauncher;
+        mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
 
         mIsOpening = isOpening;
 
@@ -113,7 +114,7 @@
     public AnimatorSet getAnimator() {
         final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
         ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
-        final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems();
+        final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
 
         // Match position of the FolderIcon
         final Rect folderIconPos = new Rect();
@@ -226,15 +227,22 @@
     }
 
     /**
+     * Returns the list of "preview items" on {@param page}.
+     */
+    private List<BubbleTextView> getPreviewIconsOnPage(int page) {
+        return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
+                .previewItemsForPage(page, mFolder.getIconsInReadingOrder());
+    }
+
+    /**
      * Animate the items on the current page.
      */
     private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
             int previewItemOffsetX, int previewItemOffsetY) {
         ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
         boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
-        final List<BubbleTextView> itemsInPreview = isOnFirstPage
-                ? mFolderIcon.getPreviewItems()
-                : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage());
+        final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(
+                isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage());
         final int numItemsInPreview = itemsInPreview.size();
         final int numItemsInFirstPagePreview = isOnFirstPage
                 ? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW;
diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java
new file mode 100644
index 0000000..9d14a5f
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2019 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.folder;
+
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
+import android.graphics.Point;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for managing item positions in a folder based on rank
+ */
+public class FolderGridOrganizer {
+
+    private final Point mPoint = new Point();
+    private final int mMaxCountX;
+    private final int mMaxCountY;
+    private final int mMaxItemsPerPage;
+
+    private int mNumItemsInFolder;
+    private int mCountX;
+    private int mCountY;
+    private boolean mDisplayingUpperLeftQuadrant = false;
+
+    /**
+     * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
+     */
+    public FolderGridOrganizer(InvariantDeviceProfile profile) {
+        mMaxCountX = profile.numFolderColumns;
+        mMaxCountY = profile.numFolderRows;
+        mMaxItemsPerPage = mMaxCountX * mMaxCountY;
+    }
+
+    /**
+     * Updates the organizer with the provided folder info
+     */
+    public FolderGridOrganizer setFolderInfo(FolderInfo info) {
+        return setContentSize(info.contents.size());
+    }
+
+    /**
+     * Updates the organizer to reflect the content size
+     */
+    public FolderGridOrganizer setContentSize(int contentSize) {
+        if (contentSize != mNumItemsInFolder) {
+            calculateGridSize(contentSize);
+
+            mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW;
+            mNumItemsInFolder = contentSize;
+        }
+        return this;
+    }
+
+    public int getCountX() {
+        return mCountX;
+    }
+
+    public int getCountY() {
+        return mCountY;
+    }
+
+    public int getMaxItemsPerPage() {
+        return mMaxItemsPerPage;
+    }
+
+    /**
+     * Calculates the grid size such that {@param count} items can fit in the grid.
+     * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
+     * maintaining the restrictions of {@link #mMaxCountX} &amp; {@link #mMaxCountY}.
+     */
+    private void calculateGridSize(int count) {
+        boolean done;
+        int gridCountX = mCountX;
+        int gridCountY = mCountY;
+
+        if (count >= mMaxItemsPerPage) {
+            gridCountX = mMaxCountX;
+            gridCountY = mMaxCountY;
+            done = true;
+        } else {
+            done = false;
+        }
+
+        while (!done) {
+            int oldCountX = gridCountX;
+            int oldCountY = gridCountY;
+            if (gridCountX * gridCountY < count) {
+                // Current grid is too small, expand it
+                if ((gridCountX <= gridCountY || gridCountY == mMaxCountY)
+                        && gridCountX < mMaxCountX) {
+                    gridCountX++;
+                } else if (gridCountY < mMaxCountY) {
+                    gridCountY++;
+                }
+                if (gridCountY == 0) gridCountY++;
+            } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
+                gridCountY = Math.max(0, gridCountY - 1);
+            } else if ((gridCountX - 1) * gridCountY >= count) {
+                gridCountX = Math.max(0, gridCountX - 1);
+            }
+            done = gridCountX == oldCountX && gridCountY == oldCountY;
+        }
+
+        mCountX = gridCountX;
+        mCountY = gridCountY;
+    }
+
+    /**
+     * Updates the item's cellX, cellY and rank corresponding to the provided rank.
+     * @return true if there was any change
+     */
+    public boolean updateRankAndPos(ItemInfo item, int rank) {
+        Point pos = getPosForRank(rank);
+        if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) {
+            item.rank = rank;
+            item.cellX = pos.x;
+            item.cellY = pos.y;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the position of the item in the grid
+     */
+    public Point getPosForRank(int rank) {
+        int pagePos = rank % mMaxItemsPerPage;
+        mPoint.x = pagePos % mCountX;
+        mPoint.y = pagePos / mCountX;
+        return mPoint;
+    }
+
+    /**
+     * Returns the preview items for the provided pageNo using the full list of contents
+     */
+    public <T, R extends T> ArrayList<R> previewItemsForPage(int page, List<T> contents) {
+        ArrayList<R> result = new ArrayList<>();
+        int itemsPerPage = mCountX * mCountY;
+        int start = itemsPerPage * page;
+        int end = Math.min(start + itemsPerPage, contents.size());
+
+        for (int i = start, rank = 0; i < end; i++, rank++) {
+            if (isItemInPreview(page, rank)) {
+                result.add((R) contents.get(i));
+            }
+
+            if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns whether the item with rank is in the default Folder icon preview.
+     */
+    public boolean isItemInPreview(int rank) {
+        return isItemInPreview(0, rank);
+    }
+
+    /**
+     * @param page The page the item is on.
+     * @param rank The rank of the item.
+     * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
+     */
+    public boolean isItemInPreview(int page, int rank) {
+        // First page items are laid out such that the first 4 items are always in the upper
+        // left quadrant. For all other pages, we need to check the row and col.
+        if (page > 0 || mDisplayingUpperLeftQuadrant) {
+            int col = rank % mCountX;
+            int row = rank / mCountX;
+            return col < 2 && row < 2;
+        }
+        return rank < MAX_NUM_ITEMS_IN_PREVIEW;
+    }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 250169c..686684d 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -95,7 +95,7 @@
     PreviewBackground mBackground = new PreviewBackground();
     private boolean mBackgroundIsVisible = true;
 
-    FolderIconPreviewVerifier mPreviewVerifier;
+    FolderGridOrganizer mPreviewVerifier;
     ClippedFolderIconLayoutRule mPreviewLayoutRule;
     private PreviewItemManager mPreviewItemManager;
     private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
@@ -212,7 +212,7 @@
 
     private void setFolder(Folder folder) {
         mFolder = folder;
-        mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+        mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
         mPreviewVerifier.setFolderInfo(mFolder.getInfo());
         updatePreviewItems(false);
     }
@@ -230,11 +230,7 @@
     }
 
     public void addItem(WorkspaceItemInfo item) {
-        addItem(item, true);
-    }
-
-    public void addItem(WorkspaceItemInfo item, boolean animate) {
-        mInfo.add(item, animate);
+        mInfo.add(item, true);
     }
 
     public void removeItem(WorkspaceItemInfo item, boolean animate) {
@@ -294,8 +290,7 @@
     }
 
     private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect,
-            float scaleRelativeToDragLayer, int index,
-            boolean itemReturnedOnFailedDrop) {
+            float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) {
         item.cellX = -1;
         item.cellY = -1;
 
@@ -327,9 +322,9 @@
             boolean itemAdded = false;
             if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
                 List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
-                addItem(item, false);
+                mInfo.add(item, index, false);
                 mCurrentPreviewItems.clear();
-                mCurrentPreviewItems.addAll(getPreviewItems());
+                mCurrentPreviewItems.addAll(getPreviewIconsOnPage(0));
 
                 if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
                     for (int i = 0; i < mCurrentPreviewItems.size(); ++i) {
@@ -349,13 +344,13 @@
             }
 
             if (!itemAdded) {
-                addItem(item);
+                mInfo.add(item, index, true);
             }
 
             int[] center = new int[2];
             float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
-            center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
-            center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
+            center[0] = Math.round(scaleRelativeToDragLayer * center[0]);
+            center[1] = Math.round(scaleRelativeToDragLayer * center[1]);
 
             to.offset(center[0] - animateView.getMeasuredWidth() / 2,
                     center[1] - animateView.getMeasuredHeight() / 2);
@@ -396,7 +391,8 @@
             item = (WorkspaceItemInfo) d.dragInfo;
         }
         mFolder.notifyDrop();
-        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
+        onDrop(item, d.dragView, null, 1.0f,
+                itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(),
                 itemReturnedOnFailedDrop);
     }
 
@@ -493,8 +489,7 @@
             mBackground.drawBackground(canvas);
         }
 
-        if (mFolder == null) return;
-        if (mFolder.getItemCount() == 0 && !mAnimating) return;
+        if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
 
         final int saveCount = canvas.save();
         canvas.clipPath(mBackground.getClipPath());
@@ -536,31 +531,11 @@
     }
 
     /**
-     * Returns the list of preview items displayed in the icon.
-     */
-    public List<BubbleTextView> getPreviewItems() {
-        return getPreviewItemsOnPage(0);
-    }
-
-    /**
      * Returns the list of "preview items" on {@param page}.
      */
-    public List<BubbleTextView> getPreviewItemsOnPage(int page) {
-        mPreviewVerifier.setFolderInfo(mFolder.getInfo());
-
-        List<BubbleTextView> itemsToDisplay = new ArrayList<>();
-        List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page);
-        int numItems = itemsOnPage.size();
-        for (int rank = 0; rank < numItems; ++rank) {
-            if (mPreviewVerifier.isItemInPreview(page, rank)) {
-                itemsToDisplay.add(itemsOnPage.get(rank));
-            }
-
-            if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
-                break;
-            }
-        }
-        return itemsToDisplay;
+    public List<BubbleTextView> getPreviewIconsOnPage(int page) {
+        return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
+                .previewItemsForPage(page, mFolder.getIconsInReadingOrder());
     }
 
     @Override
@@ -578,7 +553,7 @@
     private void updatePreviewItems(boolean animate) {
         mPreviewItemManager.updatePreviewItems(animate);
         mCurrentPreviewItems.clear();
-        mCurrentPreviewItems.addAll(getPreviewItems());
+        mCurrentPreviewItems.addAll(getPreviewIconsOnPage(0));
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
deleted file mode 100644
index 4c84e35..0000000
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2017 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.folder;
-
-import android.util.Log;
-
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InvariantDeviceProfile;
-
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-
-/**
- * Verifies whether an item in a Folder is displayed in the FolderIcon preview.
- */
-public class FolderIconPreviewVerifier {
-
-    private static final String TAG = "FolderPreviewVerifier";
-
-    private final int mMaxGridCountX;
-    private final int mMaxGridCountY;
-    private final int mMaxItemsPerPage;
-    private final int[] mGridSize = new int[] { 1, 1 };
-
-    private int mNumItemsInFolder;
-    private int mGridCountX;
-    private boolean mDisplayingUpperLeftQuadrant = false;
-
-    /**
-     * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
-     */
-    public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
-        mMaxGridCountX = profile.numFolderColumns;
-        mMaxGridCountY = profile.numFolderRows;
-        mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
-    }
-
-    public void setFolderInfo(FolderInfo info) {
-        int numItemsInFolder = info.contents.size();
-        if (numItemsInFolder != mNumItemsInFolder) {
-            FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
-                    mMaxGridCountY, mMaxItemsPerPage, mGridSize);
-            mGridCountX = mGridSize[0];
-
-            mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
-            mNumItemsInFolder = numItemsInFolder;
-        }
-    }
-
-    /**
-     * Returns whether the item with {@param rank} is in the default Folder icon preview.
-     */
-    public boolean isItemInPreview(int rank) {
-        return isItemInPreview(0, rank);
-    }
-
-    /**
-     * @param page The page the item is on.
-     * @param rank The rank of the item.
-     * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
-     */
-    public boolean isItemInPreview(int page, int rank) {
-        if (mGridSize[0] == 1) {
-            Log.w(TAG, "setFolderInfo not called before checking if item is in preview.");
-        }
-
-        // First page items are laid out such that the first 4 items are always in the upper
-        // left quadrant. For all other pages, we need to check the row and col.
-        if (page > 0 || mDisplayingUpperLeftQuadrant) {
-            int col = rank % mGridCountX;
-            int row = rank / mGridCountX;
-            return col < 2 && row < 2;
-        }
-        return rank < MAX_NUM_ITEMS_IN_PREVIEW;
-    }
-}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 57105e7..3e00cae 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -38,9 +38,9 @@
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
@@ -50,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.function.ToIntFunction;
 
 public class FolderPagedView extends PagedView<PageIndicatorDots> {
 
@@ -73,12 +74,7 @@
 
     @Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>();
 
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private final int mMaxCountX;
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private final int mMaxCountY;
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private final int mMaxItemsPerPage;
+    private final FolderGridOrganizer mOrganizer;
 
     private int mAllocatedContentSize;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -91,10 +87,7 @@
     public FolderPagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
         InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
-        mMaxCountX = profile.numFolderColumns;
-        mMaxCountY = profile.numFolderRows;
-
-        mMaxItemsPerPage = mMaxCountX * mMaxCountY;
+        mOrganizer = new FolderGridOrganizer(profile);
 
         mInflater = LayoutInflater.from(context);
 
@@ -111,57 +104,13 @@
     }
 
     /**
-     * Calculates the grid size such that {@param count} items can fit in the grid.
-     * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
-     * maintaining the restrictions of {@link #mMaxCountX} &amp; {@link #mMaxCountY}.
-     */
-    public static void calculateGridSize(int count, int countX, int countY, int maxCountX,
-            int maxCountY, int maxItemsPerPage, int[] out) {
-        boolean done;
-        int gridCountX = countX;
-        int gridCountY = countY;
-
-        if (count >= maxItemsPerPage) {
-            gridCountX = maxCountX;
-            gridCountY = maxCountY;
-            done = true;
-        } else {
-            done = false;
-        }
-
-        while (!done) {
-            int oldCountX = gridCountX;
-            int oldCountY = gridCountY;
-            if (gridCountX * gridCountY < count) {
-                // Current grid is too small, expand it
-                if ((gridCountX <= gridCountY || gridCountY == maxCountY)
-                        && gridCountX < maxCountX) {
-                    gridCountX++;
-                } else if (gridCountY < maxCountY) {
-                    gridCountY++;
-                }
-                if (gridCountY == 0) gridCountY++;
-            } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
-                gridCountY = Math.max(0, gridCountY - 1);
-            } else if ((gridCountX - 1) * gridCountY >= count) {
-                gridCountX = Math.max(0, gridCountX - 1);
-            }
-            done = gridCountX == oldCountX && gridCountY == oldCountY;
-        }
-
-        out[0] = gridCountX;
-        out[1] = gridCountY;
-    }
-
-    /**
      * Sets up the grid size such that {@param count} items can fit in the grid.
      */
-    public void setupContentDimensions(int count) {
+    private void setupContentDimensions(int count) {
         mAllocatedContentSize = count;
-        calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage,
-                sTmpArray);
-        mGridCountX = sTmpArray[0];
-        mGridCountY = sTmpArray[1];
+        mOrganizer.setContentSize(count);
+        mGridCountX = mOrganizer.getCountX();
+        mGridCountY = mOrganizer.getCountY();
 
         // Update grid size
         for (int i = getPageCount() - 1; i >= 0; i--) {
@@ -183,13 +132,13 @@
         for (WorkspaceItemInfo item : items) {
             icons.add(createNewView(item));
         }
-        arrangeChildren(icons, icons.size(), false);
+        arrangeChildren(icons);
     }
 
     public void allocateSpaceForRank(int rank) {
-        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
+        ArrayList<View> views = new ArrayList<>(mFolder.getIconsInReadingOrder());
         views.add(rank, null);
-        arrangeChildren(views, views.size(), false);
+        arrangeChildren(views);
     }
 
     /**
@@ -199,7 +148,7 @@
     public int allocateRankForNewItem() {
         int rank = getItemCount();
         allocateSpaceForRank(rank);
-        setCurrentPage(rank / mMaxItemsPerPage);
+        setCurrentPage(rank / mOrganizer.getMaxItemsPerPage());
         return rank;
     }
 
@@ -215,16 +164,10 @@
      * related attributes. It assumes that {@param item} is already attached to the view.
      */
     public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
-        int pagePos = rank % mMaxItemsPerPage;
-        int pageNo = rank / mMaxItemsPerPage;
-
-        item.rank = rank;
-        item.cellX = pagePos % mGridCountX;
-        item.cellY = pagePos / mGridCountX;
+        int pageNo = rank / mOrganizer.getMaxItemsPerPage();
 
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
-        lp.cellX = item.cellX;
-        lp.cellY = item.cellY;
+        lp.setXY(mOrganizer.getPosForRank(rank));
         getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
     }
 
@@ -295,16 +238,10 @@
      * page.
      *
      * @param list the ordered list of children.
-     * @param itemCount if greater than the total children count, empty spaces are left
-     * at the end, otherwise it is ignored.
-     *
      */
-    public void arrangeChildren(ArrayList<View> list, int itemCount) {
-        arrangeChildren(list, itemCount, true);
-    }
-
     @SuppressLint("RtlHardcoded")
-    private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
+    public void arrangeChildren(ArrayList<View> list) {
+        int itemCount = list.size();
         ArrayList<CellLayout> pages = new ArrayList<>();
         for (int i = 0; i < getChildCount(); i++) {
             CellLayout page = (CellLayout) getChildAt(i);
@@ -317,15 +254,12 @@
         CellLayout currentPage = null;
 
         int position = 0;
-        int newX, newY, rank;
+        int rank = 0;
 
-        FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
-                Launcher.getLauncher(getContext()).getDeviceProfile().inv);
-        verifier.setFolderInfo(mFolder.getInfo());
-        rank = 0;
+        mOrganizer.setFolderInfo(mFolder.getInfo());
         for (int i = 0; i < itemCount; i++) {
             View v = list.size() > i ? list.get(i) : null;
-            if (currentPage == null || position >= mMaxItemsPerPage) {
+            if (currentPage == null || position >= mOrganizer.getMaxItemsPerPage()) {
                 // Next page
                 if (pageItr.hasNext()) {
                     currentPage = pageItr.next();
@@ -337,28 +271,16 @@
 
             if (v != null) {
                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
-                newX = position % mGridCountX;
-                newY = position / mGridCountX;
                 ItemInfo info = (ItemInfo) v.getTag();
-                if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
-                    info.cellX = newX;
-                    info.cellY = newY;
-                    info.rank = rank;
-                    if (saveChanges) {
-                        mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info,
-                                mFolder.mInfo.id, 0, info.cellX, info.cellY);
-                    }
-                }
-                lp.cellX = info.cellX;
-                lp.cellY = info.cellY;
+                lp.setXY(mOrganizer.getPosForRank(rank));
                 currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
 
-                if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
+                if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) {
                     ((BubbleTextView) v).verifyHighRes();
                 }
             }
 
-            rank ++;
+            rank++;
             position++;
         }
 
@@ -398,7 +320,7 @@
             return 0;
         }
         return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount()
-                + lastPageIndex * mMaxItemsPerPage;
+                + lastPageIndex * mOrganizer.getMaxItemsPerPage();
     }
 
     /**
@@ -412,31 +334,28 @@
             sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
         }
         return Math.min(mAllocatedContentSize - 1,
-                pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
+                pageIndex * mOrganizer.getMaxItemsPerPage()
+                        + sTmpArray[1] * mGridCountX + sTmpArray[0]);
     }
 
     public View getFirstItem() {
-        if (getChildCount() < 1) {
-            return null;
-        }
-        ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
-        if (mGridCountX > 0) {
-            return currContainer.getChildAt(0, 0);
-        } else {
-            return currContainer.getChildAt(0);
-        }
+        return getViewInCurrentPage(c -> 0);
     }
 
     public View getLastItem() {
+        return getViewInCurrentPage(c -> c.getChildCount() - 1);
+    }
+
+    private View getViewInCurrentPage(ToIntFunction<ShortcutAndWidgetContainer> rankProvider) {
         if (getChildCount() < 1) {
             return null;
         }
-        ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
-        int lastRank = currContainer.getChildCount() - 1;
+        ShortcutAndWidgetContainer container = getCurrentCellLayout().getShortcutsAndWidgets();
+        int rank = rankProvider.applyAsInt(container);
         if (mGridCountX > 0) {
-            return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+            return container.getChildAt(rank % mGridCountX, rank / mGridCountX);
         } else {
-            return currContainer.getChildAt(lastRank);
+            return container.getChildAt(rank);
         }
     }
 
@@ -517,7 +436,7 @@
     }
 
     public boolean rankOnCurrentPage(int rank) {
-        int p = rank / mMaxItemsPerPage;
+        int p = rank / mOrganizer.getMaxItemsPerPage();
         return p == getNextPage();
     }
 
@@ -563,15 +482,16 @@
 
         // Animation only happens on the current page.
         int pageToAnimate = getNextPage();
+        int maxItemsPerPage = mOrganizer.getMaxItemsPerPage();
 
-        int pageT = target / mMaxItemsPerPage;
-        int pagePosT = target % mMaxItemsPerPage;
+        int pageT = target / maxItemsPerPage;
+        int pagePosT = target % maxItemsPerPage;
 
         if (pageT != pageToAnimate) {
             Log.e(TAG, "Cannot animate when the target cell is invisible");
         }
-        int pagePosE = empty % mMaxItemsPerPage;
-        int pageE = empty / mMaxItemsPerPage;
+        int pagePosE = empty % maxItemsPerPage;
+        int pageE = empty / maxItemsPerPage;
 
         int startPos, endPos;
         int moveStart, moveEnd;
@@ -588,7 +508,7 @@
             if (pageE < pageToAnimate) {
                 moveStart = empty;
                 // Instantly move the first item in the current page.
-                moveEnd = pageToAnimate * mMaxItemsPerPage;
+                moveEnd = pageToAnimate * maxItemsPerPage;
                 // Animate the 2nd item in the current page, as the first item was already moved to
                 // the last page.
                 startPos = 0;
@@ -606,10 +526,10 @@
                 // Move the items immediately.
                 moveStart = empty;
                 // Instantly move the last item in the current page.
-                moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1;
+                moveEnd = (pageToAnimate + 1) * maxItemsPerPage - 1;
 
                 // Animations start with the second last item in the page
-                startPos = mMaxItemsPerPage - 1;
+                startPos = maxItemsPerPage - 1;
             } else {
                 moveStart = moveEnd = -1;
                 startPos = pagePosE;
@@ -621,8 +541,8 @@
         // Instant moving views.
         while (moveStart != moveEnd) {
             int rankToMove = moveStart + direction;
-            int p = rankToMove / mMaxItemsPerPage;
-            int pagePos = rankToMove % mMaxItemsPerPage;
+            int p = rankToMove / maxItemsPerPage;
+            int pagePos = rankToMove % maxItemsPerPage;
             int x = pagePos % mGridCountX;
             int y = pagePos / mGridCountX;
 
@@ -667,9 +587,6 @@
         for (int i = startPos; i != endPos; i += direction) {
             int nextPos = i + direction;
             View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX);
-            if (v != null) {
-                ((ItemInfo) v.getTag()).rank -= direction;
-            }
             if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX,
                     REORDER_ANIMATION_DURATION, delay, true, true)) {
                 delay += delayAmount;
@@ -679,6 +596,6 @@
     }
 
     public int itemsPerPage() {
-        return mMaxItemsPerPage;
+        return mOrganizer.getMaxItemsPerPage();
     }
 }
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 49763ba..2ac6bf4 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -30,15 +30,15 @@
 import android.view.View;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-
 /**
  * Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}.
  */
@@ -200,7 +200,7 @@
     }
 
     void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
-        List<BubbleTextView> items = mIcon.getPreviewItemsOnPage(page);
+        List<BubbleTextView> items = mIcon.getPreviewIconsOnPage(page);
         int prevNumItems = params.size();
 
         // We adjust the size of the list to match the number of items in the preview.
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 7a58363..54249dc 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -57,7 +57,7 @@
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
 import com.android.launcher3.icons.IconCache;
@@ -744,8 +744,8 @@
             }
 
             // Sort the folder items, update ranks, and make sure all preview items are high res.
-            FolderIconPreviewVerifier verifier =
-                    new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+            FolderGridOrganizer verifier =
+                    new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
             for (FolderInfo folder : mBgDataModel.folders) {
                 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
                 verifier.setFolderInfo(folder);
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index babbcdd..6cd2b2d 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -17,6 +17,7 @@
 
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
+
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -60,7 +61,7 @@
         if (info.container >= 0) {
             Folder folder = Folder.getOpen(launcher);
             if (folder != null) {
-                if (!folder.getItemsInReadingOrder().contains(v)) {
+                if (!folder.getIconsInReadingOrder().contains(v)) {
                     folder.close(true);
                 } else {
                     folder.startDrag(v, dragOptions);