/* * 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} & {@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 ArrayList previewItemsForPage(int page, List contents) { ArrayList 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; } }