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.FolderInfo; 24 import com.android.launcher3.InvariantDeviceProfile; 25 import com.android.launcher3.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 45 /** 46 * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. 47 */ FolderGridOrganizer(InvariantDeviceProfile profile)48 public FolderGridOrganizer(InvariantDeviceProfile profile) { 49 mMaxCountX = profile.numFolderColumns; 50 mMaxCountY = profile.numFolderRows; 51 mMaxItemsPerPage = mMaxCountX * mMaxCountY; 52 } 53 54 /** 55 * Updates the organizer with the provided folder info 56 */ setFolderInfo(FolderInfo info)57 public FolderGridOrganizer setFolderInfo(FolderInfo info) { 58 return setContentSize(info.contents.size()); 59 } 60 61 /** 62 * Updates the organizer to reflect the content size 63 */ setContentSize(int contentSize)64 public FolderGridOrganizer setContentSize(int contentSize) { 65 if (contentSize != mNumItemsInFolder) { 66 calculateGridSize(contentSize); 67 68 mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW; 69 mNumItemsInFolder = contentSize; 70 } 71 return this; 72 } 73 getCountX()74 public int getCountX() { 75 return mCountX; 76 } 77 getCountY()78 public int getCountY() { 79 return mCountY; 80 } 81 getMaxItemsPerPage()82 public int getMaxItemsPerPage() { 83 return mMaxItemsPerPage; 84 } 85 86 /** 87 * Calculates the grid size such that {@param count} items can fit in the grid. 88 * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while 89 * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. 90 */ calculateGridSize(int count)91 private void calculateGridSize(int count) { 92 boolean done; 93 int gridCountX = mCountX; 94 int gridCountY = mCountY; 95 96 if (count >= mMaxItemsPerPage) { 97 gridCountX = mMaxCountX; 98 gridCountY = mMaxCountY; 99 done = true; 100 } else { 101 done = false; 102 } 103 104 while (!done) { 105 int oldCountX = gridCountX; 106 int oldCountY = gridCountY; 107 if (gridCountX * gridCountY < count) { 108 // Current grid is too small, expand it 109 if ((gridCountX <= gridCountY || gridCountY == mMaxCountY) 110 && gridCountX < mMaxCountX) { 111 gridCountX++; 112 } else if (gridCountY < mMaxCountY) { 113 gridCountY++; 114 } 115 if (gridCountY == 0) gridCountY++; 116 } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) { 117 gridCountY = Math.max(0, gridCountY - 1); 118 } else if ((gridCountX - 1) * gridCountY >= count) { 119 gridCountX = Math.max(0, gridCountX - 1); 120 } 121 done = gridCountX == oldCountX && gridCountY == oldCountY; 122 } 123 124 mCountX = gridCountX; 125 mCountY = gridCountY; 126 } 127 128 /** 129 * Updates the item's cellX, cellY and rank corresponding to the provided rank. 130 * @return true if there was any change 131 */ updateRankAndPos(ItemInfo item, int rank)132 public boolean updateRankAndPos(ItemInfo item, int rank) { 133 Point pos = getPosForRank(rank); 134 if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) { 135 item.rank = rank; 136 item.cellX = pos.x; 137 item.cellY = pos.y; 138 return true; 139 } 140 return false; 141 } 142 143 /** 144 * Returns the position of the item in the grid 145 */ getPosForRank(int rank)146 public Point getPosForRank(int rank) { 147 int pagePos = rank % mMaxItemsPerPage; 148 mPoint.x = pagePos % mCountX; 149 mPoint.y = pagePos / mCountX; 150 return mPoint; 151 } 152 153 /** 154 * Returns the preview items for the provided pageNo using the full list of contents 155 */ previewItemsForPage(int page, List<T> contents)156 public <T, R extends T> ArrayList<R> previewItemsForPage(int page, List<T> contents) { 157 ArrayList<R> result = new ArrayList<>(); 158 int itemsPerPage = mCountX * mCountY; 159 int start = itemsPerPage * page; 160 int end = Math.min(start + itemsPerPage, contents.size()); 161 162 for (int i = start, rank = 0; i < end; i++, rank++) { 163 if (isItemInPreview(page, rank)) { 164 result.add((R) contents.get(i)); 165 } 166 167 if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) { 168 break; 169 } 170 } 171 return result; 172 } 173 174 /** 175 * Returns whether the item with rank is in the default Folder icon preview. 176 */ isItemInPreview(int rank)177 public boolean isItemInPreview(int rank) { 178 return isItemInPreview(0, rank); 179 } 180 181 /** 182 * @param page The page the item is on. 183 * @param rank The rank of the item. 184 * @return True iff the icon is in the 2x2 upper left quadrant of the Folder. 185 */ isItemInPreview(int page, int rank)186 public boolean isItemInPreview(int page, int rank) { 187 // First page items are laid out such that the first 4 items are always in the upper 188 // left quadrant. For all other pages, we need to check the row and col. 189 if (page > 0 || mDisplayingUpperLeftQuadrant) { 190 int col = rank % mCountX; 191 int row = rank / mCountX; 192 return col < 2 && row < 2; 193 } 194 return rank < MAX_NUM_ITEMS_IN_PREVIEW; 195 } 196 } 197