blob: e6e9199e837526a20e92dfd115ee789efa50d590 [file] [log] [blame]
Sunny Goyalc3a609f2015-02-26 17:43:50 -08001package com.android.launcher3;
2
3import android.content.Context;
4import android.util.AttributeSet;
5import android.view.LayoutInflater;
6import android.view.View;
7
Sunny Goyalbc753352015-03-05 09:40:44 -08008import com.android.launcher3.Workspace.ItemOperator;
9
Sunny Goyalc3a609f2015-02-26 17:43:50 -080010import java.util.ArrayList;
11
Sunny Goyalbc753352015-03-05 09:40:44 -080012public class FolderCellLayout extends CellLayout implements Folder.FolderContent {
Sunny Goyalc3a609f2015-02-26 17:43:50 -080013
14 private static final int REORDER_ANIMATION_DURATION = 230;
15 private static final int START_VIEW_REORDER_DELAY = 30;
16 private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
17
18 private static final int[] sTempPosArray = new int[2];
19
20 private final FolderKeyEventListener mKeyListener = new FolderKeyEventListener();
21 private final LayoutInflater mInflater;
22 private final IconCache mIconCache;
23
24 private final int mMaxCountX;
25 private final int mMaxCountY;
26 private final int mMaxNumItems;
27
28 // Indicates the last number of items used to set up the grid size
29 private int mAllocatedContentSize;
30
31 private Folder mFolder;
32 private FocusIndicatorView mFocusIndicatorView;
33
34 public FolderCellLayout(Context context) {
35 this(context, null);
36 }
37
38 public FolderCellLayout(Context context, AttributeSet attrs) {
39 this(context, attrs, 0);
40 }
41
42 public FolderCellLayout(Context context, AttributeSet attrs, int defStyle) {
43 super(context, attrs, defStyle);
44
45 LauncherAppState app = LauncherAppState.getInstance();
46 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
47 mMaxCountX = (int) grid.numColumns;
48 mMaxCountY = (int) grid.numRows;
49 mMaxNumItems = mMaxCountX * mMaxCountY;
50
51 mInflater = LayoutInflater.from(context);
52 mIconCache = app.getIconCache();
Sunny Goyalbc753352015-03-05 09:40:44 -080053
54 setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
55 getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
56 setInvertIfRtl(true);
Sunny Goyalc3a609f2015-02-26 17:43:50 -080057 }
58
Sunny Goyalbc753352015-03-05 09:40:44 -080059 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -080060 public void setFolder(Folder folder) {
61 mFolder = folder;
62 mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
63 }
64
65 /**
66 * Sets up the grid size such that {@param count} items can fit in the grid.
67 * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
68 * maintaining the restrictions of {@link #mMaxCountX} &amp; {@link #mMaxCountY}.
69 */
70 private void setupContentDimensions(int count) {
71 mAllocatedContentSize = count;
72 int countX = getCountX();
73 int countY = getCountY();
74 boolean done = false;
75
76 while (!done) {
77 int oldCountX = countX;
78 int oldCountY = countY;
79 if (countX * countY < count) {
80 // Current grid is too small, expand it
81 if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) {
82 countX++;
83 } else if (countY < mMaxCountY) {
84 countY++;
85 }
86 if (countY == 0) countY++;
87 } else if ((countY - 1) * countX >= count && countY >= countX) {
88 countY = Math.max(0, countY - 1);
89 } else if ((countX - 1) * countY >= count) {
90 countX = Math.max(0, countX - 1);
91 }
92 done = countX == oldCountX && countY == oldCountY;
93 }
94 setGridSize(countX, countY);
95 }
96
Sunny Goyalbc753352015-03-05 09:40:44 -080097 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -080098 public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
99 ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
100 setupContentDimensions(Math.min(items.size(), mMaxNumItems));
101
102 int countX = getCountX();
103 int rank = 0;
104 for (ShortcutInfo item : items) {
105 if (rank >= mMaxNumItems) {
106 extra.add(item);
107 continue;
108 }
109
110 item.rank = rank;
111 item.cellX = rank % countX;
112 item.cellY = rank / countX;
113 addNewView(item);
114 rank++;
115 }
116 return extra;
117 }
118
Sunny Goyalbc753352015-03-05 09:40:44 -0800119 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800120 public int allocateNewLastItemRank() {
121 int rank = getItemCount();
122 mFolder.rearrangeChildren(rank + 1);
123 return rank;
124 }
125
Sunny Goyalbc753352015-03-05 09:40:44 -0800126 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800127 public View createAndAddViewForRank(ShortcutInfo item, int rank) {
128 updateItemXY(item, rank);
129 return addNewView(item);
130 }
131
Sunny Goyalbc753352015-03-05 09:40:44 -0800132 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800133 public void addViewForRank(View view, ShortcutInfo item, int rank) {
134 updateItemXY(item, rank);
135 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
136 lp.cellX = item.cellX;
137 lp.cellY = item.cellY;
Sunny Goyal82e861d2015-03-05 14:56:29 -0800138 addViewToCellLayout(view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800139 }
140
141 /**
142 * Updates the item cellX and cellY position
143 */
144 private void updateItemXY(ShortcutInfo item, int rank) {
145 item.rank = rank;
146 int countX = getCountX();
147 item.cellX = rank % countX;
148 item.cellY = rank / countX;
149 }
150
151 private View addNewView(ShortcutInfo item) {
152 final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
153 R.layout.folder_application, getShortcutsAndWidgets(), false);
154 textView.applyFromShortcutInfo(item, mIconCache, false);
155 textView.setOnClickListener(mFolder);
156 textView.setOnLongClickListener(mFolder);
157 textView.setOnFocusChangeListener(mFocusIndicatorView);
158 textView.setOnKeyListener(mKeyListener);
159
160 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(
161 item.cellX, item.cellY, item.spanX, item.spanY);
Sunny Goyal82e861d2015-03-05 14:56:29 -0800162 addViewToCellLayout(textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800163 return textView;
164 }
165
166 /**
167 * Refer {@link #findNearestArea(int, int, int, int, View, boolean, int[])}
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800168 */
Sunny Goyalbc753352015-03-05 09:40:44 -0800169 @Override
170 public int findNearestArea(int pixelX, int pixelY) {
171 findNearestArea(pixelX, pixelY, 1, 1, null, false, sTempPosArray);
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800172 if (mFolder.isLayoutRtl()) {
173 sTempPosArray[0] = getCountX() - sTempPosArray[0] - 1;
174 }
175
176 // Convert this position to rank.
177 return Math.min(mAllocatedContentSize - 1,
178 sTempPosArray[1] * getCountX() + sTempPosArray[0]);
179 }
180
Sunny Goyalbc753352015-03-05 09:40:44 -0800181 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800182 public boolean isFull() {
183 return getItemCount() >= mMaxNumItems;
184 }
185
Sunny Goyalbc753352015-03-05 09:40:44 -0800186 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800187 public int getItemCount() {
188 return getShortcutsAndWidgets().getChildCount();
189 }
190
Sunny Goyalbc753352015-03-05 09:40:44 -0800191 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800192 public void arrangeChildren(ArrayList<View> list, int itemCount) {
193 setupContentDimensions(itemCount);
194 removeAllViews();
195
196 int newX, newY;
197 int rank = 0;
198 int countX = getCountX();
199 for (View v : list) {
200 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
201 newX = rank % countX;
202 newY = rank / countX;
203 ItemInfo info = (ItemInfo) v.getTag();
204 if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
205 info.cellX = newX;
206 info.cellY = newY;
207 info.rank = rank;
208 LauncherModel.addOrMoveItemInDatabase(getContext(), info,
209 mFolder.mInfo.id, 0, info.cellX, info.cellY);
210 }
211 lp.cellX = info.cellX;
212 lp.cellY = info.cellY;
213 rank ++;
Sunny Goyal82e861d2015-03-05 14:56:29 -0800214 addViewToCellLayout(v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800215 }
216 }
217
Sunny Goyalbc753352015-03-05 09:40:44 -0800218 @Override
219 public View iterateOverItems(ItemOperator op) {
220 for (int j = 0; j < getCountY(); j++) {
221 for (int i = 0; i < getCountX(); i++) {
222 View v = getChildAt(i, j);
223 if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) {
224 return v;
225 }
226 }
227 }
228 return null;
229 }
230
231 @Override
232 public String getAccessibilityDescription() {
233 return String.format(getContext().getString(R.string.folder_opened),
234 getCountX(), getCountY());
235 }
236
237 @Override
238 public void setFocusOnFirstChild() {
239 View firstChild = getChildAt(0, 0);
240 if (firstChild != null) {
241 firstChild.requestFocus();
242 }
243 }
244
245 @Override
246 public View getLastItem() {
Sunny Goyal82e861d2015-03-05 14:56:29 -0800247 int lastRank = getShortcutsAndWidgets().getChildCount() - 1;
248 return getShortcutsAndWidgets().getChildAt(lastRank % getCountX(), lastRank / getCountX());
Sunny Goyalbc753352015-03-05 09:40:44 -0800249 }
250
251 @Override
Sunny Goyalc3a609f2015-02-26 17:43:50 -0800252 public void realTimeReorder(int empty, int target) {
253 boolean wrap;
254 int startX;
255 int endX;
256 int startY;
257 int delay = 0;
258 float delayAmount = START_VIEW_REORDER_DELAY;
259
260 int countX = getCountX();
261 int emptyX = empty % getCountX();
262 int emptyY = empty / countX;
263
264 int targetX = target % countX;
265 int targetY = target / countX;
266
267 if (target > empty) {
268 wrap = emptyX == countX - 1;
269 startY = wrap ? emptyY + 1 : emptyY;
270 for (int y = startY; y <= targetY; y++) {
271 startX = y == emptyY ? emptyX + 1 : 0;
272 endX = y < targetY ? countX - 1 : targetX;
273 for (int x = startX; x <= endX; x++) {
274 View v = getChildAt(x,y);
275 if (animateChildToPosition(v, emptyX, emptyY,
276 REORDER_ANIMATION_DURATION, delay, true, true)) {
277 emptyX = x;
278 emptyY = y;
279 delay += delayAmount;
280 delayAmount *= VIEW_REORDER_DELAY_FACTOR;
281 }
282 }
283 }
284 } else {
285 wrap = emptyX == 0;
286 startY = wrap ? emptyY - 1 : emptyY;
287 for (int y = startY; y >= targetY; y--) {
288 startX = y == emptyY ? emptyX - 1 : countX - 1;
289 endX = y > targetY ? 0 : targetX;
290 for (int x = startX; x >= endX; x--) {
291 View v = getChildAt(x,y);
292 if (animateChildToPosition(v, emptyX, emptyY,
293 REORDER_ANIMATION_DURATION, delay, true, true)) {
294 emptyX = x;
295 emptyY = y;
296 delay += delayAmount;
297 delayAmount *= VIEW_REORDER_DELAY_FACTOR;
298 }
299 }
300 }
301 }
302 }
303}