1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.folder; 18 19 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; 20 21 import android.graphics.Point; 22 23 import com.android.launcher3.DeviceProfile; 24 import com.android.launcher3.model.data.FolderInfo; 25 import com.android.launcher3.model.data.ItemInfo; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Utility class for managing item positions in a folder based on rank 32 */ 33 public class FolderGridOrganizer { 34 35 private final Point mPoint = new Point(); 36 private final int mMaxCountX; 37 private final int mMaxCountY; 38 private final int mMaxItemsPerPage; 39 40 private int mNumItemsInFolder; 41 private int mCountX; 42 private int mCountY; 43 private boolean mDisplayingUpperLeftQuadrant = false; 44 private static final int PREVIEW_MAX_ROWS = 2; 45 private static final int PREVIEW_MAX_COLUMNS = 2; 46 47 /** 48 * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. 49 */ FolderGridOrganizer(DeviceProfile profile)50 public FolderGridOrganizer(DeviceProfile profile) { 51 mMaxCountX = profile.numFolderColumns; 52 mMaxCountY = profile.numFolderRows; 53 mMaxItemsPerPage = mMaxCountX * mMaxCountY; 54 } 55 56 /** 57 * Updates the organizer with the provided folder info 58 */ setFolderInfo(FolderInfo info)59 public FolderGridOrganizer setFolderInfo(FolderInfo info) { 60 return setContentSize(info.getContents().size()); 61 } 62 63 /** 64 * Updates the organizer to reflect the content size 65 */ setContentSize(int contentSize)66 public FolderGridOrganizer setContentSize(int contentSize) { 67 if (contentSize != mNumItemsInFolder) { 68 calculateGridSize(contentSize); 69 70 mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW; 71 mNumItemsInFolder = contentSize; 72 } 73 return this; 74 } 75 getCountX()76 public int getCountX() { 77 return mCountX; 78 } 79 getCountY()80 public int getCountY() { 81 return mCountY; 82 } 83 getMaxItemsPerPage()84 public int getMaxItemsPerPage() { 85 return mMaxItemsPerPage; 86 } 87 88 /** 89 * Calculates the grid size such that {@param count} items can fit in the grid. 90 * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while 91 * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. 92 */ calculateGridSize(int count)93 private void calculateGridSize(int count) { 94 boolean done; 95 int gridCountX = mCountX; 96 int gridCountY = mCountY; 97 98 if (count >= mMaxItemsPerPage) { 99 gridCountX = mMaxCountX; 100 gridCountY = mMaxCountY; 101 done = true; 102 } else { 103 done = false; 104 } 105 106 while (!done) { 107 int oldCountX = gridCountX; 108 int oldCountY = gridCountY; 109 if (gridCountX * gridCountY < count) { 110 // Current grid is too small, expand it 111 if ((gridCountX <= gridCountY || gridCountY == mMaxCountY) 112 && gridCountX < mMaxCountX) { 113 gridCountX++; 114 } else if (gridCountY < mMaxCountY) { 115 gridCountY++; 116 } 117 if (gridCountY == 0) gridCountY++; 118 } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) { 119 gridCountY = Math.max(0, gridCountY - 1); 120 } else if ((gridCountX - 1) * gridCountY >= count) { 121 gridCountX = Math.max(0, gridCountX - 1); 122 } 123 done = gridCountX == oldCountX && gridCountY == oldCountY; 124 } 125 126 mCountX = gridCountX; 127 mCountY = gridCountY; 128 } 129 130 /** 131 * Updates the item's cellX, cellY and rank corresponding to the provided rank. 132 * 133 * @return true if there was any change 134 */ updateRankAndPos(ItemInfo item, int rank)135 public boolean updateRankAndPos(ItemInfo item, int rank) { 136 Point pos = getPosForRank(rank); 137 if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) { 138 item.rank = rank; 139 item.cellX = pos.x; 140 item.cellY = pos.y; 141 return true; 142 } 143 return false; 144 } 145 146 /** 147 * Returns the position of the item in the grid 148 */ getPosForRank(int rank)149 public Point getPosForRank(int rank) { 150 int pagePos = rank % mMaxItemsPerPage; 151 mPoint.x = pagePos % mCountX; 152 mPoint.y = pagePos / mCountX; 153 return mPoint; 154 } 155 156 /** 157 * Returns the preview items for the provided pageNo using the full list of contents 158 */ previewItemsForPage(int page, List<T> contents)159 public <T, R extends T> ArrayList<R> previewItemsForPage(int page, List<T> contents) { 160 ArrayList<R> result = new ArrayList<>(); 161 int itemsPerPage = mCountX * mCountY; 162 int start = itemsPerPage * page; 163 int end = Math.min(start + itemsPerPage, contents.size()); 164 165 for (int i = start, rank = 0; i < end; i++, rank++) { 166 if (isItemInPreview(page, rank)) { 167 result.add((R) contents.get(i)); 168 } 169 170 if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) { 171 break; 172 } 173 } 174 return result; 175 } 176 177 /** 178 * Returns whether the item with rank is in the default Folder icon preview. 179 */ isItemInPreview(int rank)180 public boolean isItemInPreview(int rank) { 181 return isItemInPreview(0, rank); 182 } 183 184 /** 185 * @param page The page the item is on. 186 * @param rank The rank of the item. 187 * @return True iff the icon is in the 2x2 upper left quadrant of the Folder. 188 */ isItemInPreview(int page, int rank)189 public boolean isItemInPreview(int page, int rank) { 190 // First page items are laid out such that the first 4 items are always in the upper 191 // left quadrant. For all other pages, we need to check the row and col. 192 if (page > 0 || mDisplayingUpperLeftQuadrant) { 193 int col = rank % mCountX; 194 int row = rank / mCountX; 195 return col < PREVIEW_MAX_COLUMNS && row < PREVIEW_MAX_ROWS; 196 } 197 return rank < MAX_NUM_ITEMS_IN_PREVIEW; 198 } 199 } 200