CellLayout.java revision c28de51eedb26848abf9245ddd19e021d30be318
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
196569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroyimport com.android.launcher.R;
20aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
21aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.app.WallpaperManager;
2231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Context;
2379e56263dbcbe85dc434df372bc6e6730aa13477Joe Onoratoimport android.content.res.Resources;
24aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.content.res.TypedArray;
25dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurkaimport android.graphics.Bitmap;
26aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.graphics.Canvas;
27dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurkaimport android.graphics.Matrix;
28dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurkaimport android.graphics.Paint;
29dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurkaimport android.graphics.PorterDuff;
3031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Rect;
3131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.RectF;
326569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroyimport android.graphics.drawable.Drawable;
3331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.util.AttributeSet;
3431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ContextMenu;
3531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.MotionEvent;
3631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.View;
3731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewDebug;
3831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewGroup;
39dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurkaimport android.view.View.OnTouchListener;
40aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.Animation;
41aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.LayoutAnimationController;
4231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
436569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroyimport java.util.ArrayList;
446569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroyimport java.util.Arrays;
45edcce099c98a6c40d10109ac092ab50f9d2668f3Romain Guy
4631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectpublic class CellLayout extends ViewGroup {
47aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    static final String TAG = "CellLayout";
48dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    // we make the dimmed bitmap smaller than the screen itself for memory + perf reasons
49dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    static final float DIMMED_BITMAP_SCALE = 0.25f;
50aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
5131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private boolean mPortrait;
5231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellWidth;
5431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellHeight;
55aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
56aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mLeftPadding;
57aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mRightPadding;
58aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mTopPadding;
59aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mBottomPadding;
60aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
61d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountX;
62d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountY;
6331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mWidthGap;
6531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mHeightGap;
6631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final Rect mRect = new Rect();
688f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    private final RectF mRectF = new RectF();
6931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final CellInfo mCellInfo = new CellInfo();
70aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
716569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // This is a temporary variable to prevent having to allocate a new object just to
726569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
736569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final int[] mTmpCellXY = new int[2];
746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean[][] mOccupied;
7631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
77dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private OnTouchListener mInterceptTouchListener;
78dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
79dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    // this is what the home screen fades to when it shrinks
80dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    //   (ie in all apps and in home screen customize mode)
81dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private Bitmap mDimmedBitmap;
82dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private Canvas mDimmedBitmapCanvas;
83dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private float mDimmedBitmapAlpha;
84dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private boolean mDimmedBitmapDirty;
85dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private final Paint mDimmedBitmapPaint = new Paint();
86dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private final Rect mLayoutRect = new Rect();
87dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private final Rect mDimmedBitmapRect = new Rect();
88dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
896569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final RectF mDragRect = new RectF();
906569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
916569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // When dragging, used to indicate a vacant drop location
926569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private Drawable mVacantDrawable;
936569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
946569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // When dragging, used to indicate an occupied drop location
956569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private Drawable mOccupiedDrawable;
966569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
976569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
986569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private Drawable mDragRectDrawable;
996569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1006569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // When a drag operation is in progress, holds the nearest cell to the touch point
1016569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final int[] mDragCell = new int[2];
10231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private boolean mDirtyTag;
104f8bbd34d7d4705b49db202be92b920196bc7ea38Mike Cleron    private boolean mLastDownOnOccupiedCell = false;
105aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
106aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private final WallpaperManager mWallpaperManager;
10731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context) {
10931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, null);
11031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
11131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
11231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs) {
11331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, attrs, 0);
11431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
11531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
11631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs, int defStyle) {
11731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super(context, attrs, defStyle);
1186569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1196569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
1206569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // the user where a dragged item will land when dropped.
1216569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        setWillNotDraw(false);
1226569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
1236569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
1246569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
12531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
12631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
12831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
129aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
130d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mLeftPadding =
131d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
132d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mRightPadding =
133d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
134d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mTopPadding =
135d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
136d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mBottomPadding =
137d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
138aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
139d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountX = LauncherModel.getCellCountX();
140d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountY = LauncherModel.getCellCountY();
14131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        a.recycle();
14331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setAlwaysDrawnWithCacheEnabled(false);
14531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14684f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        mWallpaperManager = WallpaperManager.getInstance(getContext());
147dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
148dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapPaint.setFilterBitmap(true);
149a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    }
150a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy
151a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    @Override
152a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    public void dispatchDraw(Canvas canvas) {
153a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy        super.dispatchDraw(canvas);
15431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
15531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
15683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    @Override
1576569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    protected void onDraw(Canvas canvas) {
1586569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (!mDragRect.isEmpty()) {
1596569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            mDragRectDrawable.setBounds(
1606569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    (int)mDragRect.left,
1616569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    (int)mDragRect.top,
1626569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    (int)mDragRect.right,
1636569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    (int)mDragRect.bottom);
1646569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            mDragRectDrawable.draw(canvas);
1656569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
166dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mDimmedBitmap != null && mDimmedBitmapAlpha > 0.0f) {
167dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            if (mDimmedBitmapDirty) {
168dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                updateDimmedBitmap();
169dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                mDimmedBitmapDirty = false;
170dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            }
171dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            mDimmedBitmapPaint.setAlpha((int) (mDimmedBitmapAlpha * 255));
172dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
173dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            canvas.drawBitmap(mDimmedBitmap, mDimmedBitmapRect, mLayoutRect, mDimmedBitmapPaint);
174dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
1756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
1766569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1776569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    @Override
17883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    public void cancelLongPress() {
17983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        super.cancelLongPress();
18083f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
18183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        // Cancel long press for all children
18283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        final int count = getChildCount();
18383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        for (int i = 0; i < count; i++) {
18483f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            final View child = getChildAt(i);
18583f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            child.cancelLongPress();
18683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        }
18783f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    }
18883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
189dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void setOnInterceptTouchListener(View.OnTouchListener listener) {
190dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mInterceptTouchListener = listener;
191dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
192dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
19331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountX() {
194d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountX;
19531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
19631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountY() {
198d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountY;
19931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
20031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
201aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    // Takes canonical layout parameters
202aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
203aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final LayoutParams lp = params;
204aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
20531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Generate an id for each view, this assumes we have at most 256x256 cells
20631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // per workspace screen
207d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
208aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // If the horizontal or vertical span is set to -1, it is taken to
209aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // mean that it spans the extent of the CellLayout
210d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
211d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
212aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
213aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            child.setId(childId);
21431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
215dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            // We might be in the middle or end of shrinking/fading to a dimmed view
216dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            // Make sure this view's alpha is set the same as all the rest of the views
217dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            child.setAlpha(1.0f - mDimmedBitmapAlpha);
218dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
219aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            addView(child, index, lp);
220dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
221dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            // next time we draw the dimmed bitmap we need to update it
222dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            mDimmedBitmapDirty = true;
223aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return true;
224aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
225aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return false;
22631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
22731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
22831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
229dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void removeView(View view) {
230dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super.removeView(view);
231dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapDirty = true;
232dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
233dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
234dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @Override
23531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public void requestChildFocus(View child, View focused) {
23631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.requestChildFocus(child, focused);
23731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (child != null) {
23831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            Rect r = new Rect();
23931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.getDrawingRect(r);
24031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            requestRectangleOnScreen(r);
24131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
24231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
24331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
24431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
24531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onAttachedToWindow() {
24631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onAttachedToWindow();
24731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
24831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
24931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
250af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public void setTagToCellInfoForPoint(int touchX, int touchY) {
25131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo cellInfo = mCellInfo;
252af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final Rect frame = mRect;
253af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int x = touchX + mScrollX;
254af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int y = touchY + mScrollY;
255af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int count = getChildCount();
25631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
257af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        boolean found = false;
258af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        for (int i = count - 1; i >= 0; i--) {
259af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            final View child = getChildAt(i);
260af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
261af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
262af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                child.getHitRect(frame);
263af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                if (frame.contains(x, y)) {
264af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
265af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cell = child;
266af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellX = lp.cellX;
267af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellY = lp.cellY;
268af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanX = lp.cellHSpan;
269af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanY = lp.cellVSpan;
270c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    cellInfo.intersectX = lp.cellX;
271c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    cellInfo.intersectY = lp.cellY;
272af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.valid = true;
273af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    found = true;
274af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    mDirtyTag = false;
275af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    break;
27631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
27731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
278af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
279aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
280af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        mLastDownOnOccupiedCell = found;
28131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
282af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (!found) {
2836569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            final int cellXY[] = mTmpCellXY;
284af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            pointToCellExact(x, y, cellXY);
28531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
286af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            final boolean portrait = mPortrait;
287d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            final int xCount = mCountX;
288d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            final int yCount = mCountY;
28931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
290af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            final boolean[][] occupied = mOccupied;
2916569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            findOccupiedCells(xCount, yCount, occupied, null, true);
29231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
293af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cell = null;
294af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellX = cellXY[0];
295af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellY = cellXY[1];
296af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanX = 1;
297af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanY = 1;
298c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            cellInfo.intersectX = cellXY[0];
299c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            cellInfo.intersectY = cellXY[1];
300af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
301af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
302af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
303af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            // Instead of finding the interesting vacant cells here, wait until a
304af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            // caller invokes getTag() to retrieve the result. Finding the vacant
305af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            // cells is a bit expensive and can generate many new objects, it's
306af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            // therefore better to defer it until we know we actually need it.
307af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
308af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            mDirtyTag = true;
309af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
310af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        setTag(cellInfo);
311af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    }
31231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
313aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
314af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    @Override
315af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public boolean onInterceptTouchEvent(MotionEvent ev) {
316dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
317dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            return true;
318dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
319af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int action = ev.getAction();
320af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final CellInfo cellInfo = mCellInfo;
32131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
322af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (action == MotionEvent.ACTION_DOWN) {
323af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
32431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else if (action == MotionEvent.ACTION_UP) {
32531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cell = null;
32631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cellX = -1;
32731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cellY = -1;
32831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.spanX = 0;
32931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.spanY = 0;
33031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.valid = false;
33131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mDirtyTag = false;
33231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            setTag(cellInfo);
33331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
33431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
33531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
33631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
33731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
33831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
33931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellInfo getTag() {
34031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo info = (CellInfo) super.getTag();
34131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (mDirtyTag && info.valid) {
342d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            final int xCount = mCountX;
343d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            final int yCount = mCountY;
34431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
34531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final boolean[][] occupied = mOccupied;
3466569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            findOccupiedCells(xCount, yCount, occupied, null, true);
34731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
348c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            info.updateOccupiedCells(occupied, mCountX, mCountY);
34931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
35031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mDirtyTag = false;
35131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
35231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return info;
35331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
35431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3556569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
3566569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive.
3576569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
35831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
35931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int y = top; y <= bottom; y++) {
36031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (occupied[x][y]) {
36131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return false;
36231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
36331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
36431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return true;
36531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
36631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3676569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
3686569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
3696569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
37031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
37131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = left; x <= right; x++) {
37231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (occupied[x][y]) {
37331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return false;
37431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
37531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
37631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return true;
37731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
37831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
379c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka    CellInfo updateOccupiedCells(boolean[] occupiedCells, View ignoreView) {
380d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xCount = mCountX;
381d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yCount = mCountY;
38231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
38331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean[][] occupied = mOccupied;
38431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
38531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (occupiedCells != null) {
38631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
38731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                for (int x = 0; x < xCount; x++) {
38831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    occupied[x][y] = occupiedCells[y * xCount + x];
38931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
39031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
39131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else {
3926569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            findOccupiedCells(xCount, yCount, occupied, ignoreView, true);
39331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
39431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        CellInfo cellInfo = new CellInfo();
39631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.cellX = -1;
39831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.cellY = -1;
399c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        cellInfo.intersectX = -1;
400c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        cellInfo.intersectY = -1;
40131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.spanY = 0;
40231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.spanX = 0;
40331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellInfo.screen = mCellInfo.screen;
40431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
405c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        cellInfo.updateOccupiedCells(occupied, mCountX, mCountY);
40631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
407c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        cellInfo.valid = cellInfo.existsEmptyCell();
40831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
40931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Assume the caller will perform their own cell searching, otherwise we
41031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // risk causing an unnecessary rebuild after findCellForSpan()
411aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
41231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return cellInfo;
41331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
41431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
41531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
416aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Given a point, return the cell that strictly encloses that point
41731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
41831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
41931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
42031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
42131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellExact(int x, int y, int[] result) {
422aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int hStartPadding = getLeftPadding();
423aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int vStartPadding = getTopPadding();
42431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
42531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
42631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
42731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
428d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xAxis = mCountX;
429d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yAxis = mCountY;
43031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
43131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] < 0) result[0] = 0;
43231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] >= xAxis) result[0] = xAxis - 1;
43331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] < 0) result[1] = 0;
43431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] >= yAxis) result[1] = yAxis - 1;
43531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
436aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
43731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
43831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that most closely encloses that point
43931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
44031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
44131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
44231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
44331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellRounded(int x, int y, int[] result) {
44431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
44531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
44631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
44731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
44831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a cell coordinate, return the point that represents the upper left corner of that cell
449aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
450aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellX X coordinate of the cell
45131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of the cell
452aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
45331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the point
45431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
45531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void cellToPoint(int cellX, int cellY, int[] result) {
456aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int hStartPadding = getLeftPadding();
457aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int vStartPadding = getTopPadding();
45831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
45931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
46031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
46131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
46231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
46384f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellWidth() {
46484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellWidth;
46584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
46684f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
46784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellHeight() {
46884f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellHeight;
46984f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
47084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
4711a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getLeftPadding() {
472aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mLeftPadding;
4731a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
4741a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
4751a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getTopPadding() {
476aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mTopPadding;
4771a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
4781a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
4791a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getRightPadding() {
480aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mRightPadding;
4811a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
4821a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
4831a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getBottomPadding() {
484aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mBottomPadding;
4851a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
4861a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
48731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
48831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
48931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // TODO: currently ignoring padding
490aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
49131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
492aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
493aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
49431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
49531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
496aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
49731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
49831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
49931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
50031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
50131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
50231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
50331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
504d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        if (mOccupied == null) {
505d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            mOccupied = new boolean[mCountX][mCountY];
506aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
507aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
508d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numWidthGaps = mCountX - 1;
509d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numHeightGaps = mCountY - 1;
510d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
511d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int vSpaceLeft = heightSpecSize - mTopPadding
512d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen                - mBottomPadding - (cellHeight * mCountY);
513d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mHeightGap = vSpaceLeft / numHeightGaps;
514d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
515d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int hSpaceLeft = widthSpecSize - mLeftPadding
516d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen                - mRightPadding - (cellWidth * mCountX);
517d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mWidthGap = hSpaceLeft / numWidthGaps;
518aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
51931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
52031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
52131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
52231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
52331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            LayoutParams lp = (LayoutParams) child.getLayoutParams();
524aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
525aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    mLeftPadding, mTopPadding);
526aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
527aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
528aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    MeasureSpec.EXACTLY);
529aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
530aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    MeasureSpec.EXACTLY);
53131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
53231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
53331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
53431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
53531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setMeasuredDimension(widthSpecSize, heightSpecSize);
53631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
53731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
53831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
53931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onLayout(boolean changed, int l, int t, int r, int b) {
54031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
54131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
54231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
54331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
54431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (child.getVisibility() != GONE) {
54531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
54631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
54731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
54831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int childLeft = lp.x;
54931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int childTop = lp.y;
55031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
55184f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
55284f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                if (lp.dropped) {
55384f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                    lp.dropped = false;
55484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
5556569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    final int[] cellXY = mTmpCellXY;
55606762ab54d64e84328d427403bb6074dfd0f630cRomain Guy                    getLocationOnScreen(cellXY);
55784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                    mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
55806762ab54d64e84328d427403bb6074dfd0f630cRomain Guy                            cellXY[0] + childLeft + lp.width / 2,
55906762ab54d64e84328d427403bb6074dfd0f630cRomain Guy                            cellXY[1] + childTop + lp.height / 2, 0, null);
56084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                }
56131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
56231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
56331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
56431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
56531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
566dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
567dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super.onSizeChanged(w, h, oldw, oldh);
568dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mLayoutRect.set(0, 0, w, h);
569dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapRect.set(0, 0, (int) (DIMMED_BITMAP_SCALE * w), (int) (DIMMED_BITMAP_SCALE * h));
570dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
571dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
572dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @Override
57331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
57431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int count = getChildCount();
57531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
57631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final View view = getChildAt(i);
57731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            view.setDrawingCacheEnabled(enabled);
57831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Update the drawing caches
579fefa0ce22af9560f1c0f8c84c760c75f34b7b12cAdam Powell            view.buildDrawingCache(true);
58031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
58131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
58231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
58331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
58431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
58531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.setChildrenDrawnWithCacheEnabled(enabled);
58631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
58731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
588dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public float getDimmedBitmapAlpha() {
589dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        return mDimmedBitmapAlpha;
590dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
591dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
592dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void setDimmedBitmapAlpha(float alpha) {
593dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // If we're dimming the screen after it was not dimmed, refresh
594dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // to allow for updated widgets. We don't continually refresh it
595dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // after this point, however, as an optimization
596dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mDimmedBitmapAlpha == 0.0f && alpha > 0.0f) {
597dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            updateDimmedBitmap();
598dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
599dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapAlpha = alpha;
600dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
601dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
602dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
603dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private void setChildrenAlpha(float alpha) {
604dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        for (int i = 0; i < getChildCount(); i++) {
605dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            getChildAt(i).setAlpha(alpha);
606dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
607dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
608dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
609dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void updateDimmedBitmap() {
610dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mDimmedBitmap == null) {
611dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            mDimmedBitmap = Bitmap.createBitmap((int) (getWidth() * DIMMED_BITMAP_SCALE),
612dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                    (int) (getHeight() * DIMMED_BITMAP_SCALE), Bitmap.Config.ARGB_8888);
613dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            mDimmedBitmapCanvas = new Canvas(mDimmedBitmap);
614dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            mDimmedBitmapCanvas.scale(DIMMED_BITMAP_SCALE, DIMMED_BITMAP_SCALE);
615dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
616dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // clear the canvas
617dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
618dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
619dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // draw the screen into the bitmap
620dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // just for drawing to the bitmap, make all the items on the screen opaque
621dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        setChildrenAlpha(1.0f);
622dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        dispatchDraw(mDimmedBitmapCanvas);
623dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
624dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
625dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // make the bitmap 'dimmed' ie colored regions are dark grey,
626dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // the rest is light grey
627dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // We draw grey to the whole bitmap, but filter where we draw based on
628dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // what regions are transparent or not (SRC_OUT), causing the intended effect
629dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
630dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // First, draw light grey everywhere in the background (currently transparent) regions
631dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // This will leave the regions with the widgets as mostly transparent
632dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapCanvas.drawColor(0xCCCCCCCC, PorterDuff.Mode.SRC_OUT);
633dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // Now, fill the the remaining transparent regions with dark grey
634dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mDimmedBitmapCanvas.drawColor(0xCC333333, PorterDuff.Mode.SRC_OUT);
635dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
636dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
6376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private boolean isVacant(int originX, int originY, int spanX, int spanY) {
6386569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        for (int i = 0; i < spanY; i++) {
6396569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
6406569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                return false;
6416569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            }
6426569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
6436569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        return true;
6446569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
6456569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
646440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    public View getChildAt(int x, int y) {
647440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        final int count = getChildCount();
648440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        for (int i = 0; i < count; i++) {
649440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            View child = getChildAt(i);
650440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
651440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
652440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
653440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy                    (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
654440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy                return child;
655440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            }
656440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        }
657440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        return null;
658440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
659440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
6606569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
6618f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * Estimate the size that a child with the given dimensions will take in the layout.
6628f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     */
6638f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    void estimateChildSize(int minWidth, int minHeight, int[] result) {
6648f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        // Assuming it's placed at 0, 0, find where the bottom right cell will land
6658f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        rectToCell(minWidth, minHeight, result);
6668f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy
6678f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        // Then figure out the rect it will occupy
6688f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        cellToRect(0, 0, result[0], result[1], mRectF);
6698f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = (int)mRectF.width();
6708f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = (int)mRectF.height();
6718f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    }
6728f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy
6738f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    /**
6746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Estimate where the top left cell of the dragged item will land if it is dropped.
6756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     *
6766569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originX The X value of the top left corner of the item
6776569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originY The Y value of the top left corner of the item
6786569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanX The number of horizontal cells that the item spans
6796569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanY The number of vertical cells that the item spans
6806569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param result The estimated drop cell X and Y.
6816569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
6826569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
683d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countX = mCountX;
684d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countY = mCountY;
6856569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
686976ebec9e31643d3513f9f0de2b433d9aa186ce9Patrick Dubroy        // originX and originY give the top left of the cell, but pointToCellRounded
687976ebec9e31643d3513f9f0de2b433d9aa186ce9Patrick Dubroy        // compares center-to-center, so pass in the middle coordinates
688976ebec9e31643d3513f9f0de2b433d9aa186ce9Patrick Dubroy        pointToCellRounded(originX + (mCellWidth / 2), originY + (mCellHeight / 2), result);
6896569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
6906569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // If the item isn't fully on this screen, snap to the edges
6916569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int rightOverhang = result[0] + spanX - countX;
6926569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (rightOverhang > 0) {
6936569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[0] -= rightOverhang; // Snap to right
6946569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
6956569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[0] = Math.max(0, result[0]); // Snap to left
6966569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int bottomOverhang = result[1] + spanY - countY;
6976569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (bottomOverhang > 0) {
6986569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[1] -= bottomOverhang; // Snap to bottom
6996569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
7006569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[1] = Math.max(0, result[1]); // Snap to top
7016569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
7026569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7036569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
7046569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        final int[] originCell = mDragCell;
7056569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        final int[] cellXY = mTmpCellXY;
7066569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        estimateDropCell(originX, originY, spanX, spanY, cellXY);
7076569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7086569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // Only recalculate the bounding rect when necessary
7096569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (!Arrays.equals(cellXY, originCell)) {
7106569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            originCell[0] = cellXY[0];
7116569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            originCell[1] = cellXY[1];
7126569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7136569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Find the top left corner of the rect the object will occupy
7146569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            final int[] topLeft = mTmpCellXY;
7156569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            cellToPoint(originCell[0], originCell[1], topLeft);
7166569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            final int left = topLeft[0];
7176569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            final int top = topLeft[1];
7186569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7196569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Now find the bottom right
7206569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            final int[] bottomRight = mTmpCellXY;
7216569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
7226569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            bottomRight[0] += mCellWidth;
7236569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            bottomRight[1] += mCellHeight;
7246569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
725d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            final int countX = mCountX;
726d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            final int countY = mCountY;
7276569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // TODO: It's not necessary to do this every time, but it's not especially expensive
7286569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            findOccupiedCells(countX, countY, mOccupied, view, false);
7296569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7306569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY);
7316569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
7326569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7336569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // mDragRect will be rendered in onDraw()
7346569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
7356569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            invalidate();
7366569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
7376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
7386569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
73931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
74070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Find a vacant area that will fit the given bounds nearest the requested
74170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * cell location. Uses Euclidean distance to score multiple vacant areas.
742aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
74351afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelX The X location at which you want to search for a vacant area.
74451afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelY The Y location at which you want to search for a vacant area.
74570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanX Horizontal span of the object.
74670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanY Vertical span of the object.
74770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param vacantCells Pre-computed set of vacant cells to search.
74870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param recycle Previously returned value to possibly recycle.
74970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @return The X, Y cell of a vacant area that can contain this object,
75070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *         nearest the requested location.
75131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
75270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
75370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            CellInfo vacantCells, int[] recycle) {
754aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
75570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Keep track of best-scoring drop area
75670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        final int[] bestXY = recycle != null ? recycle : new int[2];
75770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        double bestDistance = Double.MAX_VALUE;
758aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
759c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        for (int x = 0; x < mCountX - (spanX - 1); x++) {
760c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            inner:
761c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            for (int y = 0; y < mCountY - (spanY - 1); y++) {
762c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                for (int i = 0; i < spanX; i++) {
763c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    for (int j = 0; j < spanY; j++) {
764c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        if (mOccupied[x + i][y + j]) {
765c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            // small optimization: we can skip to below the row we just found
766c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            // an occupied cell
767c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            y += j;
768c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            continue inner;
769c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        }
770c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
771c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
772c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                final int[] cellXY = mTmpCellXY;
773c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                cellToPoint(x, y, cellXY);
774c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka
775c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
776c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        + Math.pow(cellXY[1] - pixelY, 2));
777c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                if (distance <= bestDistance) {
778c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestDistance = distance;
779c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[0] = x;
780c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[1] = y;
781c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
78231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
78331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
78431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
785aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        // Return null if no suitable location found
78670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        if (bestDistance < Double.MAX_VALUE) {
78770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return bestXY;
78870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        } else {
78970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return null;
79070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        }
79131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
792aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
79331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
7946569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Called when a drag and drop operation has finished (successfully or not).
7956569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
7966569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void onDragComplete() {
7976569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // Invalidate the drag data
7986569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mDragCell[0] = -1;
7996569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mDragCell[1] = -1;
8006569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8016569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mDragRect.setEmpty();
8026569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        invalidate();
8036569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
8046569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8056569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
806aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Mark a child as having been dropped.
80731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
80831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
80931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
810aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    void onDropChild(View child) {
811d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        if (child != null) {
812d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
813d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            lp.isDragging = false;
81484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy            lp.dropped = true;
815d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            mDragRect.setEmpty();
816d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            child.requestLayout();
817d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        }
8186569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        onDragComplete();
81931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
82031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
82131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDropAborted(View child) {
82231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (child != null) {
82331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            ((LayoutParams) child.getLayoutParams()).isDragging = false;
82431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
8256569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        onDragComplete();
82631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
82731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
82831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
82931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Start dragging the specified child
830aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
83131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dragged
83231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
83331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDragChild(View child) {
83431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        LayoutParams lp = (LayoutParams) child.getLayoutParams();
83531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        lp.isDragging = true;
83631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDragRect.setEmpty();
83731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
838aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
83931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
84031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes a bounding rectangle for a range of cells
841aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
84231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of upper left corner expressed as a cell position
84331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of upper left corner expressed as a cell position
844aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellHSpan Width in cells
84531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellVSpan Height in cells
8466569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param resultRect Rect into which to put the results
84731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
8486569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
84931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
85031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
85131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int widthGap = mWidthGap;
85231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int heightGap = mHeightGap;
853aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
854aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int hStartPadding = getLeftPadding();
855aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int vStartPadding = getTopPadding();
856aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
85731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
85831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
85931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
86031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x = hStartPadding + cellX * (cellWidth + widthGap);
86131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = vStartPadding + cellY * (cellHeight + heightGap);
862aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
8636569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        resultRect.set(x, y, x + width, y + height);
86431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
865aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
86631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
867aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Computes the required horizontal and vertical cell spans to always
86831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * fit the given rectangle.
869aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
87031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param width Width in pixels
87131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param height Height in pixels
8728f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * @param result An array of length 2 in which to store the result (may be null).
87331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
8748f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    public int[] rectToCell(int width, int height, int[] result) {
87531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always assume we're working with the smallest span to make sure we
87631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // reserve enough space in both orientations.
87779e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        final Resources resources = getResources();
87879e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
87979e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
88031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int smallerSize = Math.min(actualWidth, actualHeight);
88179e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
88231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always round up to next largest cell
88331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX = (width + smallerSize) / smallerSize;
88431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY = (height + smallerSize) / smallerSize;
88579e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
8868f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        if (result == null) {
8878f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            return new int[] { spanX, spanY };
8888f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        }
8898f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = spanX;
8908f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = spanY;
8918f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        return result;
89231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
89331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
89431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
89531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Find the first vacant cell, if there is one.
89631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
89731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param vacant Holds the x and y coordinate of the vacant cell
89831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanX Horizontal cell span.
89931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanY Vertical cell span.
900aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
90131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @return True if a vacant cell was found
90231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
90331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
904d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xCount = mCountX;
905d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yCount = mCountY;
90631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[][] occupied = mOccupied;
90731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9086569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        findOccupiedCells(xCount, yCount, occupied, null, true);
90931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
91031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
91131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
91231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
91331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static boolean findVacantCell(int[] vacant, int spanX, int spanY,
91431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
91531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
91631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = 0; x < xCount; x++) {
91731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
91831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean available = !occupied[x][y];
91931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectout:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
92031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
92131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        available = available && !occupied[i][j];
92231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        if (!available) break out;
92331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
92431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
92531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
92631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (available) {
92731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[0] = x;
92831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[1] = y;
92931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return true;
93031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
93131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
93231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
93331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
93431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
93531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
93631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
9386569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
9396569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
9406569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    boolean[] getOccupiedCellsFlattened() {
941d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xCount = mCountX;
942d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yCount = mCountY;
94331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[][] occupied = mOccupied;
94431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9456569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        findOccupiedCells(xCount, yCount, occupied, null, true);
94631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
94731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[] flat = new boolean[xCount * yCount];
94831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int y = 0; y < yCount; y++) {
94931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int x = 0; x < xCount; x++) {
95031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                flat[y * xCount + x] = occupied[x][y];
95131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
95231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
95331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
95431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return flat;
95531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
95631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9576569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
9586569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Update the array of occupied cells.
9596569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param ignoreView If non-null, the space occupied by this View is treated as vacant
9606569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant
9616569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
9626569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private void findOccupiedCells(
9636569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) {
9646569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
96531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = 0; x < xCount; x++) {
96631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
96731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                occupied[x][y] = false;
96831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
96931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
97031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
97131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
97231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
97331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
9746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) {
97531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                continue;
97631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
97731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            LayoutParams lp = (LayoutParams) child.getLayoutParams();
97831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
97931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
98031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
98131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    occupied[x][y] = true;
98231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
98331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
98431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
98531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
98631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
98731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
98831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
98931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(getContext(), attrs);
99031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
99131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
99231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
99331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
99431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return p instanceof CellLayout.LayoutParams;
99531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
99631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
99731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
99831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
99931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(p);
100031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
100131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1002aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public static class CellLayoutAnimationController extends LayoutAnimationController {
1003aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public CellLayoutAnimationController(Animation animation, float delay) {
1004aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(animation, delay);
1005aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
1006aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1007aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        @Override
1008aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        protected long getDelayForView(View view) {
1009aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return (int) (Math.random() * 150);
1010aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
1011aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
1012aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
101331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
101431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
101531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Horizontal location of the item in the grid.
101631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
101731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
101831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellX;
101931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
102031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
102131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Vertical location of the item in the grid.
102231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
102331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
102431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellY;
102531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
102631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
102731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned horizontally by the item.
102831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
102931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
103031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellHSpan;
103131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
103231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
103331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned vertically by the item.
103431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
103531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
103631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellVSpan;
1037aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
103831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
103931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Is this item currently being dragged
104031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
104131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public boolean isDragging;
104231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
104331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // X coordinate of the view in the layout.
104431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
104531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x;
104631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Y coordinate of the view in the layout.
104731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
104831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y;
104931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
105084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        boolean dropped;
1051fcb9e7144e58614f5ae0e9b272fb7ce040848c67Romain Guy
105231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(Context c, AttributeSet attrs) {
105331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(c, attrs);
105431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
105531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
105631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
105731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
105831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(ViewGroup.LayoutParams source) {
105931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(source);
106031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
106131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
106231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1063aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1064aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public LayoutParams(LayoutParams source) {
1065aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(source);
1066aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellX = source.cellX;
1067aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellY = source.cellY;
1068aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellHSpan = source.cellHSpan;
1069aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellVSpan = source.cellVSpan;
1070aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
1071aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
107231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
10738f19cdd62f6e2be05e3890916eabd11317ae1bc2Romain Guy            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
107431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellX = cellX;
107531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellY = cellY;
107631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellHSpan = cellHSpan;
107731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellVSpan = cellVSpan;
107831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
107931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
108031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
108131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int hStartPadding, int vStartPadding) {
1082aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
108331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellHSpan = cellHSpan;
108431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellVSpan = cellVSpan;
108531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellX = cellX;
108631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellY = cellY;
1087aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
108831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
108931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    leftMargin - rightMargin;
109031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
109131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    topMargin - bottomMargin;
109231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
109331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
109431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
109531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1096aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1097aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public String toString() {
1098aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "(" + this.cellX + ", " + this.cellY + ")";
1099aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
110031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
110131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
110231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static final class CellInfo implements ContextMenu.ContextMenuInfo {
1103c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        private boolean[][] mOccupied;
1104c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        private int mCountX;
1105c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        private int mCountY;
110631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        View cell;
110731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int cellX;
110831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int cellY;
1109c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        // intersectX and intersectY constrain the results of findCellForSpan; any empty space
1110c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        // it results must include this point (unless intersectX and intersectY are -1)
1111c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        int intersectX;
1112c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        int intersectY;
111331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX;
111431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY;
111531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int screen;
111631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean valid;
111731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1118c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        void updateOccupiedCells(boolean[][] occupied, int xCount, int yCount) {
1119c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            mOccupied = occupied.clone();
1120c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            mCountX = xCount;
1121c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            mCountY = yCount;
112231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
112331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1124c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        void updateOccupiedCells(boolean[] occupied, int xCount, int yCount) {
1125c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            if (mOccupied == null || mCountX != xCount || mCountY != yCount) {
1126c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                mOccupied = new boolean[xCount][yCount];
112731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
1128c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            mCountX = xCount;
1129c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            mCountY = yCount;
113031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
113131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                for (int x = 0; x < xCount; x++) {
1132c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    mOccupied[x][y] = occupied[y * xCount + x];
113331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
113431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
113531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
113631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1137c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        boolean existsEmptyCell() {
1138c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            return findCellForSpan(null, 1, 1);
1139c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka        }
114031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
114131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Finds the upper-left coordinate of the first rectangle in the grid that can
1142c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka         * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
1143c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka         * then this method will only return coordinates for rectangles that contain the cell
1144c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka         * (intersectX, intersectY)
114531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *
114631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @param cellXY The array that will contain the position of a vacant cell if such a cell
114731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *               can be found.
114831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @param spanX The horizontal span of the cell we want to find.
114931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @param spanY The vertical span of the cell we want to find.
115031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         *
115131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * @return True if a vacant cell of the specified dimension was found, false otherwise.
115231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
115331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
11540e26059548e429e5d1c973bebe4c561bead2926fMichael Jurka            // return the span represented by the CellInfo only there is no view there
11550e26059548e429e5d1c973bebe4c561bead2926fMichael Jurka            //   (this.cell == null) and there is enough space
115631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1157c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) {
1158c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                if (cellXY != null) {
1159c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    cellXY[0] = cellX;
1160c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    cellXY[1] = cellY;
116131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
1162c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                return true;
116331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
116431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1165c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            int startX = 0;
1166c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            if (intersectX >= 0) {
1167c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                startX = Math.max(startX, intersectX - (spanX - 1));
116831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
1169c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            int endX = mCountX - (spanX - 1);
1170c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            if (intersectX >= 0) {
1171c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                endX = Math.min(endX, intersectX + (spanX - 1));
1172c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            }
1173c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            int startY = 0;
1174c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            if (intersectY >= 0) {
1175c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                startY = Math.max(startY, intersectY - (spanY - 1));
1176c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            }
1177c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            int endY = mCountY - (spanY - 1);
1178c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            if (intersectY >= 0) {
1179c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                endY = Math.min(endY, intersectY + (spanY - 1));
1180aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            }
118131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1182c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            for (int x = startX; x < endX + 1; x++) {
1183c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                inner:
1184c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                for (int y = startY; y < endY; y++) {
1185c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    for (int i = 0; i < spanX; i++) {
1186c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        for (int j = 0; j < spanY; j++) {
1187c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            if (mOccupied[x + i][y + j]) {
1188c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                                // small optimization: we can skip to below the row we just found
1189c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                                // an occupied cell
1190c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                                y += j;
1191c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                                continue inner;
1192c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            }
1193c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        }
1194c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
1195c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    if (cellXY != null) {
1196c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        cellXY[0] = x;
1197c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        cellXY[1] = y;
1198c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
1199c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    return true;
1200c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
1201c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            }
1202c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            return false;
120331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
120431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
120531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
120631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public String toString() {
1207aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1208aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    + ", x=" + cellX + ", y=" + cellY + "]";
120931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
121031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1211f8bbd34d7d4705b49db202be92b920196bc7ea38Mike Cleron
1212f8bbd34d7d4705b49db202be92b920196bc7ea38Mike Cleron    public boolean lastDownOnOccupiedCell() {
1213f8bbd34d7d4705b49db202be92b920196bc7ea38Mike Cleron        return mLastDownOnOccupiedCell;
1214f8bbd34d7d4705b49db202be92b920196bc7ea38Mike Cleron    }
121531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
1216