blob: 4be82ed8d3b1ac6b844432c0e7c4bea388423b8d [file] [log] [blame]
/*
* 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.InvariantDeviceProfile;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.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;
}
}