CellLayout.java revision d94533d04a5f8f5485f106d10af60169857ea899
131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project/*
231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * you may not use this file except in compliance with the License.
631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * You may obtain a copy of the License at
731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
1031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * See the License for the specific language governing permissions and
1431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * limitations under the License.
1531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project */
1631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
17a5902524d4403885eb4c50360bf3465c6be796efJoe Onoratopackage com.android.launcher2;
1831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Context;
2031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.res.TypedArray;
2131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Rect;
2231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.RectF;
2331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.util.AttributeSet;
2431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ContextMenu;
2531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.MotionEvent;
2631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.View;
2731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewDebug;
2831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewGroup;
2931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport java.util.ArrayList;
3131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectpublic class CellLayout extends ViewGroup {
3331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private boolean mPortrait;
3431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellWidth;
3631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellHeight;
3731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mLongAxisStartPadding;
3931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mLongAxisEndPadding;
4031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mShortAxisStartPadding;
4231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mShortAxisEndPadding;
4331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mShortAxisCells;
4531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mLongAxisCells;
4631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mWidthGap;
4831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mHeightGap;
4931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final Rect mRect = new Rect();
5131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final CellInfo mCellInfo = new CellInfo();
5231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int[] mCellXY = new int[2];
5431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean[][] mOccupied;
5631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private RectF mDragRect = new RectF();
5831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private boolean mDirtyTag;
6031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context) {
6231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, null);
6331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
6431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs) {
6631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, attrs, 0);
6731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
6831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs, int defStyle) {
7031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super(context, attrs, defStyle);
7131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
7231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
7431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
7531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLongAxisStartPadding =
7731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
7831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLongAxisEndPadding =
7931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
8031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mShortAxisStartPadding =
8131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
8231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mShortAxisEndPadding =
8331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
8431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
8531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
8631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
8731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
8831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        a.recycle();
8931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setAlwaysDrawnWithCacheEnabled(false);
9131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (mOccupied == null) {
9331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (mPortrait) {
9431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
9531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
9631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
9731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
9831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
9931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
10031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    @Override
10283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    public void cancelLongPress() {
10383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        super.cancelLongPress();
10483f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
10583f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        // Cancel long press for all children
10683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        final int count = getChildCount();
10783f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        for (int i = 0; i < count; i++) {
10883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            final View child = getChildAt(i);
10983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            child.cancelLongPress();
11083f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        }
11183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    }
11283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
11331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountX() {
11431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return mPortrait ? mShortAxisCells : mLongAxisCells;
11531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
11631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
11731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountY() {
11831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return mPortrait ? mLongAxisCells : mShortAxisCells;
11931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
12031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
12231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public void addView(View child, int index, ViewGroup.LayoutParams params) {
12331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Generate an id for each view, this assumes we have at most 256x256 cells
12431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // per workspace screen
12531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final LayoutParams cellParams = (LayoutParams) params;
12631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        child.setId(((getId() & 0xFF) << 16) |
12731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                (cellParams.cellX & 0xFF) << 8 | (cellParams.cellY & 0xFF));
12831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.addView(child, index, params);
13031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
13131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
13231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
13331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public void requestChildFocus(View child, View focused) {
13431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.requestChildFocus(child, focused);
13531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (child != null) {
13631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            Rect r = new Rect();
13731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.getDrawingRect(r);
13831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            requestRectangleOnScreen(r);
13931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
14031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
14131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
14331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onAttachedToWindow() {
14431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onAttachedToWindow();
14531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
14631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
14731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
14931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean onInterceptTouchEvent(MotionEvent ev) {
15031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int action = ev.getAction();
15131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo cellInfo = mCellInfo;
15231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
15331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (action == MotionEvent.ACTION_DOWN) {
15431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final Rect frame = mRect;
15531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int x = (int) ev.getX() + mScrollX;
15631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int y = (int) ev.getY() + mScrollY;
15731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int count = getChildCount();
15831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
15931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            boolean found = false;
16031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int i = count - 1; i >= 0; i--) {
16131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                final View child = getChildAt(i);
16231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
16331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
16431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    child.getHitRect(frame);
16531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    if (frame.contains(x, y)) {
16631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
16731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellInfo.cell = child;
16831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellInfo.cellX = lp.cellX;
16931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellInfo.cellY = lp.cellY;
17031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellInfo.spanX = lp.cellHSpan;
17131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellInfo.spanY = lp.cellVSpan;
17231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellInfo.valid = true;
17331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        found = true;
17431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        mDirtyTag = false;
17531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        break;
17631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
17731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
17831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
17931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
18031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (!found) {
18131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int cellXY[] = mCellXY;
18231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                pointToCellExact(x, y, cellXY);
18331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
18431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                final boolean portrait = mPortrait;
18531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
18631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
18731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
18831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                final boolean[][] occupied = mOccupied;
18970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                findOccupiedCells(xCount, yCount, occupied, null);
19031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellInfo.cell = null;
19231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellInfo.cellX = cellXY[0];
19331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellInfo.cellY = cellXY[1];
19431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellInfo.spanX = 1;
19531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellInfo.spanY = 1;
19631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
19731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
19831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // Instead of finding the interesting vacant cells here, wait until a
20031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // caller invokes getTag() to retrieve the result. Finding the vacant
20131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // cells is a bit expensive and can generate many new objects, it's
20231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // therefore better to defer it until we know we actually need it.
20331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
20431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mDirtyTag = true;
20531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
20631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            setTag(cellInfo);
20731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else if (action == MotionEvent.ACTION_UP) {
20831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cell = null;
20931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cellX = -1;
21031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cellY = -1;
21131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.spanX = 0;
21231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.spanY = 0;
21331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.valid = false;
21431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mDirtyTag = false;
21531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            setTag(cellInfo);
21631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
21731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
21831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
21931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
22031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
22131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
22231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellInfo getTag() {
22331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo info = (CellInfo) super.getTag();
22431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (mDirtyTag && info.valid) {
22531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final boolean portrait = mPortrait;
22631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
22731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
22831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
22931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final boolean[][] occupied = mOccupied;
23070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            findOccupiedCells(xCount, yCount, occupied, null);
23131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
23231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
23331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
23431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mDirtyTag = false;
23531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
23631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return info;
23731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
23831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
23931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
24031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
24131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
24231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
24331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
24431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
24531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
24631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.clearVacantCells();
24731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
24831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (occupied[x][y]) {
24931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return;
25031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
25131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
25231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.current.set(x, y, x, y);
25331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
25431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
25531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
25631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
25731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
25831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            CellInfo cellInfo) {
25931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
26031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        addVacantCell(current, cellInfo);
26131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
26231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (current.left > 0) {
26331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
26431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.left--;
26531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                findVacantCell(current, xCount, yCount, occupied, cellInfo);
26631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.left++;
26731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
26831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
26931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (current.right < xCount - 1) {
27131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
27231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.right++;
27331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                findVacantCell(current, xCount, yCount, occupied, cellInfo);
27431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.right--;
27531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
27631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
27731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (current.top > 0) {
27931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
28031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.top--;
28131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                findVacantCell(current, xCount, yCount, occupied, cellInfo);
28231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.top++;
28331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
28431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
28531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
28631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (current.bottom < yCount - 1) {
28731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
28831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.bottom++;
28931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                findVacantCell(current, xCount, yCount, occupied, cellInfo);
29031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                current.bottom--;
29131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
29231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
29331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
29431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
29531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static void addVacantCell(Rect current, CellInfo cellInfo) {
29631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
29731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cell.cellX = current.left;
29831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cell.cellY = current.top;
29931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cell.spanX = current.right - current.left + 1;
30031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cell.spanY = current.bottom - current.top + 1;
30131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (cell.spanX > cellInfo.maxVacantSpanX) {
30231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.maxVacantSpanX = cell.spanX;
30331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.maxVacantSpanXSpanY = cell.spanY;
30431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
30531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (cell.spanY > cellInfo.maxVacantSpanY) {
30631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.maxVacantSpanY = cell.spanY;
30731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.maxVacantSpanYSpanX = cell.spanX;
30831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
30931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.vacantCells.add(cell);
31031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
31131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
31231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
31331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int y = top; y <= bottom; y++) {
31431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (occupied[x][y]) {
31531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return false;
31631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
31731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
31831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return true;
31931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
32031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
32131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
32231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = left; x <= right; x++) {
32331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (occupied[x][y]) {
32431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return false;
32531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
32631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
32731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return true;
32831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
32931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
33070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    CellInfo findAllVacantCells(boolean[] occupiedCells, View ignoreView) {
33131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean portrait = mPortrait;
33231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
33331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
33431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
33531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean[][] occupied = mOccupied;
33631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
33731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (occupiedCells != null) {
33831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
33931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                for (int x = 0; x < xCount; x++) {
34031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    occupied[x][y] = occupiedCells[y * xCount + x];
34131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
34231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
34331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else {
34470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            findOccupiedCells(xCount, yCount, occupied, ignoreView);
34531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
34631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
34731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        CellInfo cellInfo = new CellInfo();
34831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
34931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.cellX = -1;
35031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.cellY = -1;
35131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.spanY = 0;
35231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.spanX = 0;
35331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
35431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
35531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
35631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
35731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.screen = mCellInfo.screen;
35831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
35931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        Rect current = cellInfo.current;
36031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
36131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = 0; x < xCount; x++) {
36231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
36331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (!occupied[x][y]) {
36431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    current.set(x, y, x, y);
36531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    findVacantCell(current, xCount, yCount, occupied, cellInfo);
36631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    occupied[x][y] = true;
36731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
36831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
36931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
37031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
37131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.valid = cellInfo.vacantCells.size() > 0;
37231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
37331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Assume the caller will perform their own cell searching, otherwise we
37431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // risk causing an unnecessary rebuild after findCellForSpan()
37531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
37631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return cellInfo;
37731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
37831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
37931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
38031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that strictly encloses that point
38131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
38231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
38331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
38431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
38531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellExact(int x, int y, int[] result) {
38631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean portrait = mPortrait;
38731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
38831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
38931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
39031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
39231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
39331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
39531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
39631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] < 0) result[0] = 0;
39831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] >= xAxis) result[0] = xAxis - 1;
39931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] < 0) result[1] = 0;
40031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] >= yAxis) result[1] = yAxis - 1;
40131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
40231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
40331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
40431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that most closely encloses that point
40531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
40631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
40731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
40831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
40931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellRounded(int x, int y, int[] result) {
41031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
41131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
41231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
41331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
41431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a cell coordinate, return the point that represents the upper left corner of that cell
41531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
41631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of the cell
41731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of the cell
41831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
41931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the point
42031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
42131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void cellToPoint(int cellX, int cellY, int[] result) {
42231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean portrait = mPortrait;
42331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
42431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
42531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
42631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
42731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
42831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
42931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
43031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
43131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
43231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
43331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
43431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // TODO: currently ignoring padding
43531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
43631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
43731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
43831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
43931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
44031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
44131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
44231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
44331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
44431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
44531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
44631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int shortAxisCells = mShortAxisCells;
44731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int longAxisCells = mLongAxisCells;
44831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int longAxisStartPadding = mLongAxisStartPadding;
44931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int longAxisEndPadding = mLongAxisEndPadding;
45031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int shortAxisStartPadding = mShortAxisStartPadding;
45131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int shortAxisEndPadding = mShortAxisEndPadding;
45231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
45331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
45431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
45531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mPortrait = heightSpecSize > widthSpecSize;
45631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
45731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int numShortGaps = shortAxisCells - 1;
45831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int numLongGaps = longAxisCells - 1;
45931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
46031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (mPortrait) {
46131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding
46231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    - (cellHeight * longAxisCells);
46331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mHeightGap = vSpaceLeft / numLongGaps;
46431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
46531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding
46631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    - (cellWidth * shortAxisCells);
46731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (numShortGaps > 0) {
46831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mWidthGap = hSpaceLeft / numShortGaps;
46931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
47031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mWidthGap = 0;
47131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
47231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else {
47331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding
47431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    - (cellWidth * longAxisCells);
47531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mWidthGap = hSpaceLeft / numLongGaps;
47631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
47731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding
47831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    - (cellHeight * shortAxisCells);
47931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (numShortGaps > 0) {
48031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mHeightGap = vSpaceLeft / numShortGaps;
48131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
48231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mHeightGap = 0;
48331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
48431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
48531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
48631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
48731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
48831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
48931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
49031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            LayoutParams lp = (LayoutParams) child.getLayoutParams();
49131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
49231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (mPortrait) {
49331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding,
49431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        longAxisStartPadding);
49531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
49631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding,
49731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        shortAxisStartPadding);
49831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
49931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
50031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
50131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int childheightMeasureSpec =
50231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
50331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
50431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
50531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
50631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setMeasuredDimension(widthSpecSize, heightSpecSize);
50731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
50831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
50931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
51031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onLayout(boolean changed, int l, int t, int r, int b) {
51131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
51231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
51331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
51431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
51531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (child.getVisibility() != GONE) {
51631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
51731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
51831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
51931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int childLeft = lp.x;
52031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int childTop = lp.y;
52131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
52231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
52331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
52431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
52531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
52631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
52731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
52831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int count = getChildCount();
52931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
53031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final View view = getChildAt(i);
53131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            view.setDrawingCacheEnabled(enabled);
53231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Update the drawing caches
533806b0f3fbf02171c691b1f9d48e51a47760565b3Romain Guy            view.buildDrawingCache(true);
53431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
53531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
53631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
53731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
53831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
53931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.setChildrenDrawnWithCacheEnabled(enabled);
54031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
54131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
54231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
54370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Find a vacant area that will fit the given bounds nearest the requested
54470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * cell location. Uses Euclidean distance to score multiple vacant areas.
54570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *
54651afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelX The X location at which you want to search for a vacant area.
54751afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelY The Y location at which you want to search for a vacant area.
54870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanX Horizontal span of the object.
54970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanY Vertical span of the object.
55070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param vacantCells Pre-computed set of vacant cells to search.
55170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param recycle Previously returned value to possibly recycle.
55270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @return The X, Y cell of a vacant area that can contain this object,
55370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *         nearest the requested location.
55431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
55570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
55670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            CellInfo vacantCells, int[] recycle) {
55770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
55870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Keep track of best-scoring drop area
55970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        final int[] bestXY = recycle != null ? recycle : new int[2];
56070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        final int[] cellXY = mCellXY;
56170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        double bestDistance = Double.MAX_VALUE;
56270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
56370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Bail early if vacant cells aren't valid
56470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        if (!vacantCells.valid) {
56570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return null;
56631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
56731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
56870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Look across all vacant cells for best fit
56970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        final int size = vacantCells.vacantCells.size();
57070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        for (int i = 0; i < size; i++) {
57170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i);
57270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
57370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            // Reject if vacant cell isn't our exact size
57470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            if (cell.spanX != spanX || cell.spanY != spanY) {
57531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                continue;
57631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
57770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
57870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            // Score is center distance from requested pixel
57970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            cellToPoint(cell.cellX, cell.cellY, cellXY);
58070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
58170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) +
58270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                    Math.pow(cellXY[1] - pixelY, 2));
58370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            if (distance <= bestDistance) {
58470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                bestDistance = distance;
58570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                bestXY[0] = cell.cellX;
58670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                bestXY[1] = cell.cellY;
58731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
58831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
58931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
59070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Return null if no suitable location found
59170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        if (bestDistance < Double.MAX_VALUE) {
59270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return bestXY;
59370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        } else {
59470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return null;
59570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        }
59631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
59770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
59831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
59931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Drop a child at the specified position
60031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
60131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
60270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param targetXY Destination area to move to
60331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
60470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    void onDropChild(View child, int[] targetXY) {
605d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        if (child != null) {
606d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
607d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            lp.cellX = targetXY[0];
608d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            lp.cellY = targetXY[1];
609d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            lp.isDragging = false;
610d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            mDragRect.setEmpty();
611d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            child.requestLayout();
612d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            invalidate();
613d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        }
61431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
61531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
61631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDropAborted(View child) {
61731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (child != null) {
61831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            ((LayoutParams) child.getLayoutParams()).isDragging = false;
61931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            invalidate();
62031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
62131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDragRect.setEmpty();
62231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
62331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
62431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
62531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Start dragging the specified child
62631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
62731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dragged
62831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
62931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDragChild(View child) {
63031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        LayoutParams lp = (LayoutParams) child.getLayoutParams();
63131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        lp.isDragging = true;
63231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDragRect.setEmpty();
63331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
63431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
63531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
63631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Drag a child over the specified position
63731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
63831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
63931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX The child's new x cell location
64031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY The child's new y cell location
64131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
64231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDragOverChild(View child, int cellX, int cellY) {
64331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int[] cellXY = mCellXY;
64431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellRounded(cellX, cellY, cellXY);
64531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        LayoutParams lp = (LayoutParams) child.getLayoutParams();
64631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
64731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        invalidate();
64831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
64931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
65031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
65131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes a bounding rectangle for a range of cells
65231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
65331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of upper left corner expressed as a cell position
65431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of upper left corner expressed as a cell position
65531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellHSpan Width in cells
65631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellVSpan Height in cells
65731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param dragRect Rectnagle into which to put the results
65831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
65931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) {
66031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean portrait = mPortrait;
66131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
66231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
66331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int widthGap = mWidthGap;
66431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int heightGap = mHeightGap;
66531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
66631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
66731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
66831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
66931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
67031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
67131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x = hStartPadding + cellX * (cellWidth + widthGap);
67331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = vStartPadding + cellY * (cellHeight + heightGap);
67431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        dragRect.set(x, y, x + width, y + height);
67631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
67731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
67931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes the required horizontal and vertical cell spans to always
68031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * fit the given rectangle.
68131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
68231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param width Width in pixels
68331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param height Height in pixels
68431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
68531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public int[] rectToCell(int width, int height) {
68631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always assume we're working with the smallest span to make sure we
68731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // reserve enough space in both orientations.
68831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int actualWidth = mCellWidth + mWidthGap;
68931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int actualHeight = mCellHeight + mHeightGap;
69031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int smallerSize = Math.min(actualWidth, actualHeight);
69131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
69231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always round up to next largest cell
69331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX = (width + smallerSize) / smallerSize;
69431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY = (height + smallerSize) / smallerSize;
69531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new int[] { spanX, spanY };
69631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
69731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
69831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
69931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Find the first vacant cell, if there is one.
70031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
70131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param vacant Holds the x and y coordinate of the vacant cell
70231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanX Horizontal cell span.
70331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanY Vertical cell span.
70431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
70531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @return True if a vacant cell was found
70631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
70731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
70831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean portrait = mPortrait;
70931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
71031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
71131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[][] occupied = mOccupied;
71231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
71370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        findOccupiedCells(xCount, yCount, occupied, null);
71431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
71531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
71631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
71731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
71831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static boolean findVacantCell(int[] vacant, int spanX, int spanY,
71931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
72031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
72131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = 0; x < xCount; x++) {
72231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
72331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean available = !occupied[x][y];
72431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectout:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
72531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
72631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        available = available && !occupied[i][j];
72731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        if (!available) break out;
72831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
72931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
73031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
73131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (available) {
73231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[0] = x;
73331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[1] = y;
73431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return true;
73531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
73631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
73731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
73831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
73931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
74031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
74131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
74231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean[] getOccupiedCells() {
74331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean portrait = mPortrait;
74431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
74531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
74631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[][] occupied = mOccupied;
74731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
74870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        findOccupiedCells(xCount, yCount, occupied, null);
74931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
75031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[] flat = new boolean[xCount * yCount];
75131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int y = 0; y < yCount; y++) {
75231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int x = 0; x < xCount; x++) {
75331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                flat[y * xCount + x] = occupied[x][y];
75431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
75531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
75631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
75731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return flat;
75831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
75931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
76070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
76131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = 0; x < xCount; x++) {
76231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
76331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                occupied[x][y] = false;
76431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
76531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
76631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
76731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
76831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
76931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
77070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            if (child instanceof Folder || child.equals(ignoreView)) {
77131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                continue;
77231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
77331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            LayoutParams lp = (LayoutParams) child.getLayoutParams();
77431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
77531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
77631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
77731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    occupied[x][y] = true;
77831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
77931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
78031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
78131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
78231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
78331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
78431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
78531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(getContext(), attrs);
78631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
78731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
78831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
78931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
79031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return p instanceof CellLayout.LayoutParams;
79131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
79231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
79331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
79431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
79531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(p);
79631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
79731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
79831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
79931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
80031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Horizontal location of the item in the grid.
80131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
80231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
80331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellX;
80431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
80531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
80631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Vertical location of the item in the grid.
80731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
80831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
80931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellY;
81031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
81131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
81231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned horizontally by the item.
81331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
81431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
81531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellHSpan;
81631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
81731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
81831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned vertically by the item.
81931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
82031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
82131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellVSpan;
82231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
82331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
82431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Is this item currently being dragged
82531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
82631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public boolean isDragging;
82731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
82831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // X coordinate of the view in the layout.
82931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
83031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x;
83131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Y coordinate of the view in the layout.
83231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
83331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y;
83431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
83531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(Context c, AttributeSet attrs) {
83631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(c, attrs);
83731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
83831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
83931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
84031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
84131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(ViewGroup.LayoutParams source) {
84231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(source);
84331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
84431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
84531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
84631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
84731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
84831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
84931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellX = cellX;
85031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellY = cellY;
85131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellHSpan = cellHSpan;
85231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellVSpan = cellVSpan;
85331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
85431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
85531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
85631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int hStartPadding, int vStartPadding) {
85731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
85831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellHSpan = cellHSpan;
85931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellVSpan = cellVSpan;
86031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellX = cellX;
86131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellY = cellY;
86231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
86331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
86431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    leftMargin - rightMargin;
86531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
86631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    topMargin - bottomMargin;
86731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
86831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
86931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
87031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
87131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
87231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
87331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static final class CellInfo implements ContextMenu.ContextMenuInfo {
87431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
87531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * See View.AttachInfo.InvalidateInfo for futher explanations about
87631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * the recycling mechanism. In this case, we recycle the vacant cells
87731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * instances because up to several hundreds can be instanciated when
87831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * the user long presses an empty cell.
87931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
88031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        static final class VacantCell {
88131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int cellX;
88231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int cellY;
88331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int spanX;
88431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int spanY;
88531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
88631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
88731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // like a reasonable compromise given the size of a VacantCell and
88831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // the fact that the user is not likely to touch an empty 4x4 grid
88931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // very often
89031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            private static final int POOL_LIMIT = 100;
89131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            private static final Object sLock = new Object();
89231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
89331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            private static int sAcquiredCount = 0;
89431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            private static VacantCell sRoot;
89531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
89631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            private VacantCell next;
89731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
89831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            static VacantCell acquire() {
89931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                synchronized (sLock) {
90031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    if (sRoot == null) {
90131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        return new VacantCell();
90231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
90331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
90431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    VacantCell info = sRoot;
90531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    sRoot = info.next;
90631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    sAcquiredCount--;
90731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
90831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return info;
90931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
91031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
91131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
91231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            void release() {
91331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                synchronized (sLock) {
91431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    if (sAcquiredCount < POOL_LIMIT) {
91531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        sAcquiredCount++;
91631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        next = sRoot;
91731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        sRoot = this;
91831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
91931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
92031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
92131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
92231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            @Override
92331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            public String toString() {
92431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
92531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        ", spanY=" + spanY + "]";
92631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
92731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
92831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
92931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        View cell;
93031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int cellX;
93131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int cellY;
93231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX;
93331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY;
93431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int screen;
93531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean valid;
93631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
93731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
93831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int maxVacantSpanX;
93931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int maxVacantSpanXSpanY;
94031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int maxVacantSpanY;
94131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int maxVacantSpanYSpanX;
94231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final Rect current = new Rect();
94331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9444c58c485d8c02f8ca7e8b4d93140440f6a3a5131Romain Guy        void clearVacantCells() {
94531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final ArrayList<VacantCell> list = vacantCells;
94631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int count = list.size();
94731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
94831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int i = 0; i < count; i++) list.get(i).release();
94931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
95031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            list.clear();
95131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
95231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
95331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
95431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (cellX < 0 || cellY < 0) {
95531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
95631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
95731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                clearVacantCells();
95831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return;
95931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
96031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
96131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final boolean[][] unflattened = new boolean[xCount][yCount];
96231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
96331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                for (int x = 0; x < xCount; x++) {
96431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    unflattened[x][y] = occupied[y * xCount + x];
96531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
96631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
96731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
96831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
96931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
97031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
97131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * This method can be called only once! Calling #findVacantCellsFromOccupied will
97231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * restore the ability to call this method.
97331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *
97431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Finds the upper-left coordinate of the first rectangle in the grid that can
97531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * hold a cell of the specified dimensions.
97631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *
97731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @param cellXY The array that will contain the position of a vacant cell if such a cell
97831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *               can be found.
97931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @param spanX The horizontal span of the cell we want to find.
98031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @param spanY The vertical span of the cell we want to find.
98131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *
98231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @return True if a vacant cell of the specified dimension was found, false otherwise.
98331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
98431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
9854c58c485d8c02f8ca7e8b4d93140440f6a3a5131Romain Guy            return findCellForSpan(cellXY, spanX, spanY, true);
9864c58c485d8c02f8ca7e8b4d93140440f6a3a5131Romain Guy        }
9874c58c485d8c02f8ca7e8b4d93140440f6a3a5131Romain Guy
9884c58c485d8c02f8ca7e8b4d93140440f6a3a5131Romain Guy        boolean findCellForSpan(int[] cellXY, int spanX, int spanY, boolean clear) {
98931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final ArrayList<VacantCell> list = vacantCells;
99031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int count = list.size();
99131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
99231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            boolean found = false;
99331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
99431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (this.spanX >= spanX && this.spanY >= spanY) {
99531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellXY[0] = cellX;
99631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellXY[1] = cellY;
99731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                found = true;
99831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
99931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
100031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Look for an exact match first
100131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int i = 0; i < count; i++) {
100231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                VacantCell cell = list.get(i);
100331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (cell.spanX == spanX && cell.spanY == spanY) {
100431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    cellXY[0] = cell.cellX;
100531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    cellXY[1] = cell.cellY;
100631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    found = true;
100731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    break;
100831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
100931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
101031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
101131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Look for the first cell large enough
101231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int i = 0; i < count; i++) {
101331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                VacantCell cell = list.get(i);
101431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (cell.spanX >= spanX && cell.spanY >= spanY) {
101531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    cellXY[0] = cell.cellX;
101631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    cellXY[1] = cell.cellY;
101731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    found = true;
101831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    break;
101931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
102031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
102131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10224c58c485d8c02f8ca7e8b4d93140440f6a3a5131Romain Guy            if (clear) clearVacantCells();
102331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
102431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return found;
102531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
102631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
102731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
102831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public String toString() {
102931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX +
103031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    ", y=" + cellY + "]";
103131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
103231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
103331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
103431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
103531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1036