• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.launcher2;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.util.AttributeSet;
22 import android.util.Log;
23 import android.view.MotionEvent;
24 import android.view.View;
25 import android.view.ViewDebug;
26 import android.view.ViewGroup;
27 
28 import com.android.launcher.R;
29 
30 /**
31  * An abstraction of the original CellLayout which supports laying out items
32  * which span multiple cells into a grid-like layout.  Also supports dimming
33  * to give a preview of its contents.
34  */
35 public class PagedViewCellLayout extends ViewGroup implements Page {
36     static final String TAG = "PagedViewCellLayout";
37 
38     private int mCellCountX;
39     private int mCellCountY;
40     private int mOriginalCellWidth;
41     private int mOriginalCellHeight;
42     private int mCellWidth;
43     private int mCellHeight;
44     private int mOriginalWidthGap;
45     private int mOriginalHeightGap;
46     private int mWidthGap;
47     private int mHeightGap;
48     private int mMaxGap;
49     protected PagedViewCellLayoutChildren mChildren;
50 
PagedViewCellLayout(Context context)51     public PagedViewCellLayout(Context context) {
52         this(context, null);
53     }
54 
PagedViewCellLayout(Context context, AttributeSet attrs)55     public PagedViewCellLayout(Context context, AttributeSet attrs) {
56         this(context, attrs, 0);
57     }
58 
PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle)59     public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) {
60         super(context, attrs, defStyle);
61 
62         setAlwaysDrawnWithCacheEnabled(false);
63 
64         // setup default cell parameters
65         Resources resources = context.getResources();
66         mOriginalCellWidth = mCellWidth =
67             resources.getDimensionPixelSize(R.dimen.apps_customize_cell_width);
68         mOriginalCellHeight = mCellHeight =
69             resources.getDimensionPixelSize(R.dimen.apps_customize_cell_height);
70         mCellCountX = LauncherModel.getCellCountX();
71         mCellCountY = LauncherModel.getCellCountY();
72         mOriginalHeightGap = mOriginalHeightGap = mWidthGap = mHeightGap = -1;
73         mMaxGap = resources.getDimensionPixelSize(R.dimen.apps_customize_max_gap);
74 
75         mChildren = new PagedViewCellLayoutChildren(context);
76         mChildren.setCellDimensions(mCellWidth, mCellHeight);
77         mChildren.setGap(mWidthGap, mHeightGap);
78 
79         addView(mChildren);
80     }
81 
getCellWidth()82     public int getCellWidth() {
83         return mCellWidth;
84     }
85 
getCellHeight()86     public int getCellHeight() {
87         return mCellHeight;
88     }
89 
90     @Override
setAlpha(float alpha)91     public void setAlpha(float alpha) {
92         mChildren.setAlpha(alpha);
93     }
94 
destroyHardwareLayers()95     void destroyHardwareLayers() {
96         // called when a page is no longer visible (triggered by loadAssociatedPages ->
97         // removeAllViewsOnPage)
98         mChildren.destroyHardwareLayer();
99     }
100 
createHardwareLayers()101     void createHardwareLayers() {
102         // called when a page is visible (triggered by loadAssociatedPages -> syncPageItems)
103         mChildren.createHardwareLayer();
104     }
105 
106     @Override
cancelLongPress()107     public void cancelLongPress() {
108         super.cancelLongPress();
109 
110         // Cancel long press for all children
111         final int count = getChildCount();
112         for (int i = 0; i < count; i++) {
113             final View child = getChildAt(i);
114             child.cancelLongPress();
115         }
116     }
117 
addViewToCellLayout(View child, int index, int childId, PagedViewCellLayout.LayoutParams params)118     public boolean addViewToCellLayout(View child, int index, int childId,
119             PagedViewCellLayout.LayoutParams params) {
120         final PagedViewCellLayout.LayoutParams lp = params;
121 
122         // Generate an id for each view, this assumes we have at most 256x256 cells
123         // per workspace screen
124         if (lp.cellX >= 0 && lp.cellX <= (mCellCountX - 1) &&
125                 lp.cellY >= 0 && (lp.cellY <= mCellCountY - 1)) {
126             // If the horizontal or vertical span is set to -1, it is taken to
127             // mean that it spans the extent of the CellLayout
128             if (lp.cellHSpan < 0) lp.cellHSpan = mCellCountX;
129             if (lp.cellVSpan < 0) lp.cellVSpan = mCellCountY;
130 
131             child.setId(childId);
132             mChildren.addView(child, index, lp);
133 
134             if (child instanceof PagedViewIcon) {
135                 PagedViewIcon pagedViewIcon = (PagedViewIcon) child;
136                 pagedViewIcon.disableCache();
137             }
138             return true;
139         }
140         return false;
141     }
142 
143     @Override
removeAllViewsOnPage()144     public void removeAllViewsOnPage() {
145         mChildren.removeAllViews();
146         destroyHardwareLayers();
147     }
148 
149     @Override
removeViewOnPageAt(int index)150     public void removeViewOnPageAt(int index) {
151         mChildren.removeViewAt(index);
152     }
153 
154     @Override
getPageChildCount()155     public int getPageChildCount() {
156         return mChildren.getChildCount();
157     }
158 
getChildrenLayout()159     public PagedViewCellLayoutChildren getChildrenLayout() {
160         return mChildren;
161     }
162 
163     @Override
getChildOnPageAt(int i)164     public View getChildOnPageAt(int i) {
165         return mChildren.getChildAt(i);
166     }
167 
168     @Override
indexOfChildOnPage(View v)169     public int indexOfChildOnPage(View v) {
170         return mChildren.indexOfChild(v);
171     }
172 
getCellCountX()173     public int getCellCountX() {
174         return mCellCountX;
175     }
176 
getCellCountY()177     public int getCellCountY() {
178         return mCellCountY;
179     }
180 
onMeasure(int widthMeasureSpec, int heightMeasureSpec)181     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
182         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
183         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
184 
185         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
186         int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
187 
188         if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
189             throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
190         }
191 
192         int numWidthGaps = mCellCountX - 1;
193         int numHeightGaps = mCellCountY - 1;
194 
195         if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
196             int hSpace = widthSpecSize - mPaddingLeft - mPaddingRight;
197             int vSpace = heightSpecSize - mPaddingTop - mPaddingBottom;
198             int hFreeSpace = hSpace - (mCellCountX * mOriginalCellWidth);
199             int vFreeSpace = vSpace - (mCellCountY * mOriginalCellHeight);
200             mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
201             mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
202 
203             mChildren.setGap(mWidthGap, mHeightGap);
204         } else {
205             mWidthGap = mOriginalWidthGap;
206             mHeightGap = mOriginalHeightGap;
207         }
208 
209         // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY
210         int newWidth = widthSpecSize;
211         int newHeight = heightSpecSize;
212         if (widthSpecMode == MeasureSpec.AT_MOST) {
213             newWidth = mPaddingLeft + mPaddingRight + (mCellCountX * mCellWidth) +
214                 ((mCellCountX - 1) * mWidthGap);
215             newHeight = mPaddingTop + mPaddingBottom + (mCellCountY * mCellHeight) +
216                 ((mCellCountY - 1) * mHeightGap);
217             setMeasuredDimension(newWidth, newHeight);
218         }
219 
220         final int count = getChildCount();
221         for (int i = 0; i < count; i++) {
222             View child = getChildAt(i);
223             int childWidthMeasureSpec =
224                 MeasureSpec.makeMeasureSpec(newWidth - mPaddingLeft -
225                         mPaddingRight, MeasureSpec.EXACTLY);
226             int childheightMeasureSpec =
227                 MeasureSpec.makeMeasureSpec(newHeight - mPaddingTop -
228                         mPaddingBottom, MeasureSpec.EXACTLY);
229             child.measure(childWidthMeasureSpec, childheightMeasureSpec);
230         }
231 
232         setMeasuredDimension(newWidth, newHeight);
233     }
234 
getContentWidth()235     int getContentWidth() {
236         return getWidthBeforeFirstLayout() + mPaddingLeft + mPaddingRight;
237     }
238 
getContentHeight()239     int getContentHeight() {
240         if (mCellCountY > 0) {
241             return mCellCountY * mCellHeight + (mCellCountY - 1) * Math.max(0, mHeightGap);
242         }
243         return 0;
244     }
245 
getWidthBeforeFirstLayout()246     int getWidthBeforeFirstLayout() {
247         if (mCellCountX > 0) {
248             return mCellCountX * mCellWidth + (mCellCountX - 1) * Math.max(0, mWidthGap);
249         }
250         return 0;
251     }
252 
253     @Override
onLayout(boolean changed, int l, int t, int r, int b)254     protected void onLayout(boolean changed, int l, int t, int r, int b) {
255         int count = getChildCount();
256         for (int i = 0; i < count; i++) {
257             View child = getChildAt(i);
258             child.layout(mPaddingLeft, mPaddingTop,
259                 r - l - mPaddingRight, b - t - mPaddingBottom);
260         }
261     }
262 
263     @Override
onTouchEvent(MotionEvent event)264     public boolean onTouchEvent(MotionEvent event) {
265         boolean result = super.onTouchEvent(event);
266         int count = getPageChildCount();
267         if (count > 0) {
268             // We only intercept the touch if we are tapping in empty space after the final row
269             View child = getChildOnPageAt(count - 1);
270             int bottom = child.getBottom();
271             int numRows = (int) Math.ceil((float) getPageChildCount() / getCellCountX());
272             if (numRows < getCellCountY()) {
273                 // Add a little bit of buffer if there is room for another row
274                 bottom += mCellHeight / 2;
275             }
276             result = result || (event.getY() < bottom);
277         }
278         return result;
279     }
280 
enableCenteredContent(boolean enabled)281     public void enableCenteredContent(boolean enabled) {
282         mChildren.enableCenteredContent(enabled);
283     }
284 
285     @Override
setChildrenDrawingCacheEnabled(boolean enabled)286     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
287         mChildren.setChildrenDrawingCacheEnabled(enabled);
288     }
289 
setCellCount(int xCount, int yCount)290     public void setCellCount(int xCount, int yCount) {
291         mCellCountX = xCount;
292         mCellCountY = yCount;
293         requestLayout();
294     }
295 
setGap(int widthGap, int heightGap)296     public void setGap(int widthGap, int heightGap) {
297         mWidthGap = widthGap;
298         mHeightGap = heightGap;
299         mChildren.setGap(widthGap, heightGap);
300     }
301 
getCellCountForDimensions(int width, int height)302     public int[] getCellCountForDimensions(int width, int height) {
303         // Always assume we're working with the smallest span to make sure we
304         // reserve enough space in both orientations
305         int smallerSize = Math.min(mCellWidth, mCellHeight);
306 
307         // Always round up to next largest cell
308         int spanX = (width + smallerSize) / smallerSize;
309         int spanY = (height + smallerSize) / smallerSize;
310 
311         return new int[] { spanX, spanY };
312     }
313 
314     /**
315      * Start dragging the specified child
316      *
317      * @param child The child that is being dragged
318      */
onDragChild(View child)319     void onDragChild(View child) {
320         PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
321         lp.isDragging = true;
322     }
323 
324     /**
325      * Estimates the number of cells that the specified width would take up.
326      */
estimateCellHSpan(int width)327     public int estimateCellHSpan(int width) {
328         // The space for a page assuming that we want to show half of a column of the previous and
329         // next pages is the width - left padding (current & next page) - right padding (previous &
330         // current page) - half cell width (for previous and next pages)
331         int availWidth = (int) (width - (2 * mPaddingLeft + 2 * mPaddingRight));
332 
333         // We know that we have to fit N cells with N-1 width gaps, so we just juggle to solve for N
334         int n = Math.max(1, (availWidth + mWidthGap) / (mCellWidth + mWidthGap));
335 
336         // We don't do anything fancy to determine if we squeeze another row in.
337         return n;
338     }
339 
340     /**
341      * Estimates the number of cells that the specified height would take up.
342      */
estimateCellVSpan(int height)343     public int estimateCellVSpan(int height) {
344         // The space for a page is the height - top padding (current page) - bottom padding (current
345         // page)
346         int availHeight = height - (mPaddingTop + mPaddingBottom);
347 
348         // We know that we have to fit N cells with N-1 height gaps, so we juggle to solve for N
349         int n = Math.max(1, (availHeight + mHeightGap) / (mCellHeight + mHeightGap));
350 
351         // We don't do anything fancy to determine if we squeeze another row in.
352         return n;
353     }
354 
355     /** Returns an estimated center position of the cell at the specified index */
estimateCellPosition(int x, int y)356     public int[] estimateCellPosition(int x, int y) {
357         return new int[] {
358                 mPaddingLeft + (x * mCellWidth) + (x * mWidthGap) + (mCellWidth / 2),
359                 mPaddingTop + (y * mCellHeight) + (y * mHeightGap) + (mCellHeight / 2)
360         };
361     }
362 
calculateCellCount(int width, int height, int maxCellCountX, int maxCellCountY)363     public void calculateCellCount(int width, int height, int maxCellCountX, int maxCellCountY) {
364         mCellCountX = Math.min(maxCellCountX, estimateCellHSpan(width));
365         mCellCountY = Math.min(maxCellCountY, estimateCellVSpan(height));
366         requestLayout();
367     }
368 
369     /**
370      * Estimates the width that the number of hSpan cells will take up.
371      */
estimateCellWidth(int hSpan)372     public int estimateCellWidth(int hSpan) {
373         // TODO: we need to take widthGap into effect
374         return hSpan * mCellWidth;
375     }
376 
377     /**
378      * Estimates the height that the number of vSpan cells will take up.
379      */
estimateCellHeight(int vSpan)380     public int estimateCellHeight(int vSpan) {
381         // TODO: we need to take heightGap into effect
382         return vSpan * mCellHeight;
383     }
384 
385     @Override
generateLayoutParams(AttributeSet attrs)386     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
387         return new PagedViewCellLayout.LayoutParams(getContext(), attrs);
388     }
389 
390     @Override
checkLayoutParams(ViewGroup.LayoutParams p)391     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
392         return p instanceof PagedViewCellLayout.LayoutParams;
393     }
394 
395     @Override
generateLayoutParams(ViewGroup.LayoutParams p)396     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
397         return new PagedViewCellLayout.LayoutParams(p);
398     }
399 
400     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
401         /**
402          * Horizontal location of the item in the grid.
403          */
404         @ViewDebug.ExportedProperty
405         public int cellX;
406 
407         /**
408          * Vertical location of the item in the grid.
409          */
410         @ViewDebug.ExportedProperty
411         public int cellY;
412 
413         /**
414          * Number of cells spanned horizontally by the item.
415          */
416         @ViewDebug.ExportedProperty
417         public int cellHSpan;
418 
419         /**
420          * Number of cells spanned vertically by the item.
421          */
422         @ViewDebug.ExportedProperty
423         public int cellVSpan;
424 
425         /**
426          * Is this item currently being dragged
427          */
428         public boolean isDragging;
429 
430         // a data object that you can bind to this layout params
431         private Object mTag;
432 
433         // X coordinate of the view in the layout.
434         @ViewDebug.ExportedProperty
435         int x;
436         // Y coordinate of the view in the layout.
437         @ViewDebug.ExportedProperty
438         int y;
439 
LayoutParams()440         public LayoutParams() {
441             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
442             cellHSpan = 1;
443             cellVSpan = 1;
444         }
445 
LayoutParams(Context c, AttributeSet attrs)446         public LayoutParams(Context c, AttributeSet attrs) {
447             super(c, attrs);
448             cellHSpan = 1;
449             cellVSpan = 1;
450         }
451 
LayoutParams(ViewGroup.LayoutParams source)452         public LayoutParams(ViewGroup.LayoutParams source) {
453             super(source);
454             cellHSpan = 1;
455             cellVSpan = 1;
456         }
457 
LayoutParams(LayoutParams source)458         public LayoutParams(LayoutParams source) {
459             super(source);
460             this.cellX = source.cellX;
461             this.cellY = source.cellY;
462             this.cellHSpan = source.cellHSpan;
463             this.cellVSpan = source.cellVSpan;
464         }
465 
LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan)466         public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
467             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
468             this.cellX = cellX;
469             this.cellY = cellY;
470             this.cellHSpan = cellHSpan;
471             this.cellVSpan = cellVSpan;
472         }
473 
setup(int cellWidth, int cellHeight, int widthGap, int heightGap, int hStartPadding, int vStartPadding)474         public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
475                 int hStartPadding, int vStartPadding) {
476 
477             final int myCellHSpan = cellHSpan;
478             final int myCellVSpan = cellVSpan;
479             final int myCellX = cellX;
480             final int myCellY = cellY;
481 
482             width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
483                     leftMargin - rightMargin;
484             height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
485                     topMargin - bottomMargin;
486 
487             if (LauncherApplication.isScreenLarge()) {
488                 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
489                 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
490             } else {
491                 x = myCellX * (cellWidth + widthGap) + leftMargin;
492                 y = myCellY * (cellHeight + heightGap) + topMargin;
493             }
494         }
495 
getTag()496         public Object getTag() {
497             return mTag;
498         }
499 
setTag(Object tag)500         public void setTag(Object tag) {
501             mTag = tag;
502         }
503 
toString()504         public String toString() {
505             return "(" + this.cellX + ", " + this.cellY + ", " +
506                 this.cellHSpan + ", " + this.cellVSpan + ")";
507         }
508     }
509 }
510 
511 interface Page {
getPageChildCount()512     public int getPageChildCount();
getChildOnPageAt(int i)513     public View getChildOnPageAt(int i);
removeAllViewsOnPage()514     public void removeAllViewsOnPage();
removeViewOnPageAt(int i)515     public void removeViewOnPageAt(int i);
indexOfChildOnPage(View v)516     public int indexOfChildOnPage(View v);
517 }
518