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