diff options
Diffstat (limited to 'src/com/cyanogenmod/trebuchet/PagedViewCellLayout.java')
-rw-r--r-- | src/com/cyanogenmod/trebuchet/PagedViewCellLayout.java | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/src/com/cyanogenmod/trebuchet/PagedViewCellLayout.java b/src/com/cyanogenmod/trebuchet/PagedViewCellLayout.java new file mode 100644 index 000000000..5eaf92694 --- /dev/null +++ b/src/com/cyanogenmod/trebuchet/PagedViewCellLayout.java @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.trebuchet; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup; + +import com.cyanogenmod.trebuchet.R; + +/** + * An abstraction of the original CellLayout which supports laying out items + * which span multiple cells into a grid-like layout. Also supports dimming + * to give a preview of its contents. + */ +public class PagedViewCellLayout extends ViewGroup implements Page { + static final String TAG = "PagedViewCellLayout"; + + private int mCellCountX; + private int mCellCountY; + private int mOriginalCellWidth; + private int mOriginalCellHeight; + private int mCellWidth; + private int mCellHeight; + private int mOriginalWidthGap; + private int mOriginalHeightGap; + private int mWidthGap; + private int mHeightGap; + private int mMaxGap; + protected PagedViewCellLayoutChildren mChildren; + + public PagedViewCellLayout(Context context) { + this(context, null); + } + + public PagedViewCellLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + setAlwaysDrawnWithCacheEnabled(false); + + // setup default cell parameters + Resources resources = context.getResources(); + mOriginalCellWidth = mCellWidth = + resources.getDimensionPixelSize(R.dimen.apps_customize_cell_width); + mOriginalCellHeight = mCellHeight = + resources.getDimensionPixelSize(R.dimen.apps_customize_cell_height); + mCellCountX = LauncherModel.getCellCountX(); + mCellCountY = LauncherModel.getCellCountY(); + mOriginalWidthGap = mOriginalHeightGap = mWidthGap = mHeightGap = -1; + mMaxGap = resources.getDimensionPixelSize(R.dimen.apps_customize_max_gap); + + mChildren = new PagedViewCellLayoutChildren(context); + mChildren.setCellDimensions(mCellWidth, mCellHeight); + mChildren.setGap(mWidthGap, mHeightGap); + + addView(mChildren); + } + + public int getCellWidth() { + return mCellWidth; + } + + public int getCellHeight() { + return mCellHeight; + } + + void destroyHardwareLayers() { + // called when a page is no longer visible (triggered by loadAssociatedPages -> + // removeAllViewsOnPage) + setLayerType(LAYER_TYPE_NONE, null); + } + + void createHardwareLayers() { + // called when a page is visible (triggered by loadAssociatedPages -> syncPageItems) + setLayerType(LAYER_TYPE_HARDWARE, null); + } + + @Override + public void cancelLongPress() { + super.cancelLongPress(); + + // Cancel long press for all children + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + child.cancelLongPress(); + } + } + + public boolean addViewToCellLayout(View child, int index, int childId, + PagedViewCellLayout.LayoutParams params) { + final PagedViewCellLayout.LayoutParams lp = params; + + // Generate an id for each view, this assumes we have at most 256x256 cells + // per workspace screen + if (lp.cellX >= 0 && lp.cellX <= (mCellCountX - 1) && + lp.cellY >= 0 && (lp.cellY <= mCellCountY - 1)) { + // If the horizontal or vertical span is set to -1, it is taken to + // mean that it spans the extent of the CellLayout + if (lp.cellHSpan < 0) lp.cellHSpan = mCellCountX; + if (lp.cellVSpan < 0) lp.cellVSpan = mCellCountY; + + child.setId(childId); + mChildren.addView(child, index, lp); + + return true; + } + return false; + } + + @Override + public void removeAllViewsOnPage() { + mChildren.removeAllViews(); + destroyHardwareLayers(); + } + + @Override + public void removeViewOnPageAt(int index) { + mChildren.removeViewAt(index); + } + + /** + * Clears all the key listeners for the individual icons. + */ + public void resetChildrenOnKeyListeners() { + int childCount = mChildren.getChildCount(); + for (int j = 0; j < childCount; ++j) { + mChildren.getChildAt(j).setOnKeyListener(null); + } + } + + @Override + public int getPageChildCount() { + return mChildren.getChildCount(); + } + + public PagedViewCellLayoutChildren getChildrenLayout() { + return mChildren; + } + + @Override + public View getChildOnPageAt(int i) { + return mChildren.getChildAt(i); + } + + @Override + public int indexOfChildOnPage(View v) { + return mChildren.indexOfChild(v); + } + + public int getCellCountX() { + return mCellCountX; + } + + public int getCellCountY() { + return mCellCountY; + } + + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { + throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions"); + } + + int numWidthGaps = mCellCountX - 1; + int numHeightGaps = mCellCountY - 1; + + if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) { + int hSpace = widthSpecSize - getPaddingLeft() - getPaddingRight(); + int vSpace = heightSpecSize - getPaddingTop() - getPaddingBottom(); + int hFreeSpace = hSpace - (mCellCountX * mOriginalCellWidth); + int vFreeSpace = vSpace - (mCellCountY * mOriginalCellHeight); + mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0); + mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0); + + mChildren.setGap(mWidthGap, mHeightGap); + } else { + mWidthGap = mOriginalWidthGap; + mHeightGap = mOriginalHeightGap; + } + + // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY + int newWidth = widthSpecSize; + int newHeight = heightSpecSize; + if (widthSpecMode == MeasureSpec.AT_MOST) { + newWidth = getPaddingLeft() + getPaddingRight() + (mCellCountX * mCellWidth) + + ((mCellCountX - 1) * mWidthGap); + newHeight = getPaddingTop() + getPaddingBottom() + (mCellCountY * mCellHeight) + + ((mCellCountY - 1) * mHeightGap); + setMeasuredDimension(newWidth, newHeight); + } + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + int childWidthMeasureSpec = + MeasureSpec.makeMeasureSpec(newWidth - getPaddingLeft() - + getPaddingRight(), MeasureSpec.EXACTLY); + int childheightMeasureSpec = + MeasureSpec.makeMeasureSpec(newHeight - getPaddingTop() - + getPaddingBottom(), MeasureSpec.EXACTLY); + child.measure(childWidthMeasureSpec, childheightMeasureSpec); + } + + setMeasuredDimension(newWidth, newHeight); + } + + int getContentWidth() { + return getWidthBeforeFirstLayout() + getPaddingLeft() + getPaddingRight(); + } + + int getContentHeight() { + if (mCellCountY > 0) { + return mCellCountY * mCellHeight + (mCellCountY - 1) * Math.max(0, mHeightGap); + } + return 0; + } + + int getWidthBeforeFirstLayout() { + if (mCellCountX > 0) { + return mCellCountX * mCellWidth + (mCellCountX - 1) * Math.max(0, mWidthGap); + } + return 0; + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + child.layout(getPaddingLeft(), getPaddingTop(), + r - l - getPaddingRight(), b - t - getPaddingBottom()); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean result = super.onTouchEvent(event); + int count = getPageChildCount(); + if (count > 0) { + // We only intercept the touch if we are tapping in empty space after the final row + View child = getChildOnPageAt(count - 1); + int bottom = child.getBottom(); + int numRows = (int) Math.ceil((float) getPageChildCount() / getCellCountX()); + if (numRows < getCellCountY()) { + // Add a little bit of buffer if there is room for another row + bottom += mCellHeight / 2; + } + result = result || (event.getY() < bottom); + } + return result; + } + + public void enableCenteredContent(boolean enabled) { + mChildren.enableCenteredContent(enabled); + } + + @Override + protected void setChildrenDrawingCacheEnabled(boolean enabled) { + mChildren.setChildrenDrawingCacheEnabled(enabled); + } + + public void setCellCount(int xCount, int yCount) { + mCellCountX = xCount; + mCellCountY = yCount; + requestLayout(); + } + + public void setGap(int widthGap, int heightGap) { + mOriginalWidthGap = mWidthGap = widthGap; + mOriginalHeightGap = mHeightGap = heightGap; + mChildren.setGap(widthGap, heightGap); + } + + public int[] getCellCountForDimensions(int width, int height) { + // Always assume we're working with the smallest span to make sure we + // reserve enough space in both orientations + int smallerSize = Math.min(mCellWidth, mCellHeight); + + // Always round up to next largest cell + int spanX = (width + smallerSize) / smallerSize; + int spanY = (height + smallerSize) / smallerSize; + + return new int[] { spanX, spanY }; + } + + /** + * Start dragging the specified child + * + * @param child The child that is being dragged + */ + void onDragChild(View child) { + PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); + lp.isDragging = true; + } + + /** + * Estimates the number of cells that the specified width would take up. + */ + public int estimateCellHSpan(int width) { + // We don't show the next/previous pages any more, so we use the full width, minus the + // padding + int availWidth = width - (getPaddingLeft() + getPaddingRight()); + + // We know that we have to fit N cells with N-1 width gaps, so we just juggle to solve for N + int n = Math.max(1, (availWidth + mWidthGap) / (mCellWidth + mWidthGap)); + + // We don't do anything fancy to determine if we squeeze another row in. + return n; + } + + /** + * Estimates the number of cells that the specified height would take up. + */ + public int estimateCellVSpan(int height) { + // The space for a page is the height - top padding (current page) - bottom padding (current + // page) + int availHeight = height - (getPaddingTop() + getPaddingBottom()); + + // We know that we have to fit N cells with N-1 height gaps, so we juggle to solve for N + int n = Math.max(1, (availHeight + mHeightGap) / (mCellHeight + mHeightGap)); + + // We don't do anything fancy to determine if we squeeze another row in. + return n; + } + + /** Returns an estimated center position of the cell at the specified index */ + public int[] estimateCellPosition(int x, int y) { + return new int[] { + getPaddingLeft() + (x * mCellWidth) + (x * mWidthGap) + (mCellWidth / 2), + getPaddingTop() + (y * mCellHeight) + (y * mHeightGap) + (mCellHeight / 2) + }; + } + + public void calculateCellCount(int width, int height, int maxCellCountX, int maxCellCountY) { + mCellCountX = Math.min(maxCellCountX, estimateCellHSpan(width)); + mCellCountY = Math.min(maxCellCountY, estimateCellVSpan(height)); + requestLayout(); + } + + /** + * Estimates the width that the number of hSpan cells will take up. + */ + public int estimateCellWidth(int hSpan) { + // TODO: we need to take widthGap into effect + return hSpan * mCellWidth; + } + + /** + * Estimates the height that the number of vSpan cells will take up. + */ + public int estimateCellHeight(int vSpan) { + // TODO: we need to take heightGap into effect + return vSpan * mCellHeight; + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return new PagedViewCellLayout.LayoutParams(getContext(), attrs); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof PagedViewCellLayout.LayoutParams; + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new PagedViewCellLayout.LayoutParams(p); + } + + public static class LayoutParams extends ViewGroup.MarginLayoutParams { + /** + * Horizontal location of the item in the grid. + */ + @ViewDebug.ExportedProperty + public int cellX; + + /** + * Vertical location of the item in the grid. + */ + @ViewDebug.ExportedProperty + public int cellY; + + /** + * Number of cells spanned horizontally by the item. + */ + @ViewDebug.ExportedProperty + public int cellHSpan; + + /** + * Number of cells spanned vertically by the item. + */ + @ViewDebug.ExportedProperty + public int cellVSpan; + + /** + * Is this item currently being dragged + */ + public boolean isDragging; + + // a data object that you can bind to this layout params + private Object mTag; + + // X coordinate of the view in the layout. + @ViewDebug.ExportedProperty + int x; + // Y coordinate of the view in the layout. + @ViewDebug.ExportedProperty + int y; + + public LayoutParams() { + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + cellHSpan = 1; + cellVSpan = 1; + } + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + cellHSpan = 1; + cellVSpan = 1; + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + cellHSpan = 1; + cellVSpan = 1; + } + + public LayoutParams(LayoutParams source) { + super(source); + this.cellX = source.cellX; + this.cellY = source.cellY; + this.cellHSpan = source.cellHSpan; + this.cellVSpan = source.cellVSpan; + } + + public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) { + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + this.cellX = cellX; + this.cellY = cellY; + this.cellHSpan = cellHSpan; + this.cellVSpan = cellVSpan; + } + + public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap, + int hStartPadding, int vStartPadding) { + + final int myCellHSpan = cellHSpan; + final int myCellVSpan = cellVSpan; + final int myCellX = cellX; + final int myCellY = cellY; + + width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) - + leftMargin - rightMargin; + height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) - + topMargin - bottomMargin; + + if (LauncherApplication.isScreenLarge()) { + x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin; + y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin; + } else { + x = myCellX * (cellWidth + widthGap) + leftMargin; + y = myCellY * (cellHeight + heightGap) + topMargin; + } + } + + public Object getTag() { + return mTag; + } + + public void setTag(Object tag) { + mTag = tag; + } + + public String toString() { + return "(" + this.cellX + ", " + this.cellY + ", " + + this.cellHSpan + ", " + this.cellVSpan + ")"; + } + } +} + +interface Page { + public int getPageChildCount(); + public View getChildOnPageAt(int i); + public void removeAllViewsOnPage(); + public void removeViewOnPageAt(int i); + public int indexOfChildOnPage(View v); +} |