2019-08-07 09:24:26 -07:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2020-04-06 15:11:17 -07:00
|
|
|
import com.android.launcher3.model.data.FolderInfo;
|
|
|
|
|
import com.android.launcher3.model.data.ItemInfo;
|
2019-08-07 09:24:26 -07:00
|
|
|
|
|
|
|
|
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 <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;
|
|
|
|
|
}
|
|
|
|
|
}
|