CellLayout.java revision 472b281d5cb4f5660df981a6c912266b9f5703fe
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
214be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.animation.Animator;
224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.animation.AnimatorListenerAdapter;
2300397b1d9233409d9d6b233b077ae12d09768ce8Chet Haaseimport android.animation.TimeInterpolator;
24de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.animation.ValueAnimator;
25de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.animation.ValueAnimator.AnimatorUpdateListener;
26aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.app.WallpaperManager;
2731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Context;
2879e56263dbcbe85dc434df372bc6e6730aa13477Joe Onoratoimport android.content.res.Resources;
29aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.content.res.TypedArray;
304be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.Bitmap;
31aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.graphics.Canvas;
324be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.Paint;
33de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.graphics.Point;
34de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.graphics.PointF;
3531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Rect;
3631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.RectF;
376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroyimport android.graphics.drawable.Drawable;
3831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.util.AttributeSet;
394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.util.Log;
4031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ContextMenu;
4131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.MotionEvent;
4231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.View;
4331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewDebug;
4431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewGroup;
45aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.Animation;
46150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chungimport android.view.animation.DecelerateInterpolator;
47aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.LayoutAnimationController;
4831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
49f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohenpublic class CellLayout extends ViewGroup implements Dimmable {
50aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    static final String TAG = "CellLayout";
51aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
5231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellWidth;
5331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellHeight;
54aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
55aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mLeftPadding;
56aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mRightPadding;
57aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mTopPadding;
58aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private int mBottomPadding;
59aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
60d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountX;
61d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountY;
6231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mWidthGap;
6431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mHeightGap;
6531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final Rect mRect = new Rect();
678f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    private final RectF mRectF = new RectF();
6831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final CellInfo mCellInfo = new CellInfo();
69aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
70de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    // These are temporary variables to prevent having to allocate a new object just to
71de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
726569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final int[] mTmpCellXY = new int[2];
73de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final int[] mTmpPoint = new int[2];
74de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final PointF mTmpPointF = new PointF();
756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean[][] mOccupied;
7731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
78dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private OnTouchListener mInterceptTouchListener;
79dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mBackgroundAlpha;
81f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private Drawable mBackground;
83f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private Drawable mBackgroundMini;
84f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private Drawable mBackgroundMiniHover;
851262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    private Drawable mBackgroundHover;
863e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private Drawable mBackgroundMiniAcceptsDrops;
873e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private boolean mAcceptsDrops;
881262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
891262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    // If we're actively dragging something over this screen, mHover is true
90a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    private boolean mHover = false;
91dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
92de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final Point mDragCenter = new Point();
936569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
94150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung    // These arrays are used to implement the drag visualization on x-large screens.
954be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    // They are used as circular arrays, indexed by mDragOutlineCurrent.
964be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private Point[] mDragOutlines = new Point[8];
97472b281d5cb4f5660df981a6c912266b9f5703feChet Haase    private float[] mDragOutlineAlphas = new float[mDragOutlines.length];
984be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private InterruptibleInOutAnimator[] mDragOutlineAnims =
994be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            new InterruptibleInOutAnimator[mDragOutlines.length];
100150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
101150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung    // Used as an index into the above 3 arrays; indicates which is the most current value.
1024be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private int mDragOutlineCurrent = 0;
103150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
104de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private Drawable mCrosshairsDrawable = null;
10549250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy    private InterruptibleInOutAnimator mCrosshairsAnimator = null;
106de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private float mCrosshairsVisibility = 0.0f;
107de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
1086569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // When a drag operation is in progress, holds the nearest cell to the touch point
1096569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final int[] mDragCell = new int[2];
11031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
111aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private final WallpaperManager mWallpaperManager;
11231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1134be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private boolean mDragging = false;
1144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
11531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context) {
11631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, null);
11731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
11831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
11931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs) {
12031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, attrs, 0);
12131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
12231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs, int defStyle) {
12431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super(context, attrs, defStyle);
1256569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1266569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
1276569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // the user where a dragged item will land when dropped.
1286569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        setWillNotDraw(false);
129a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka
13031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
13131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
13231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
13331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
134aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
135d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mLeftPadding =
136d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
137d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mRightPadding =
138d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
139d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mTopPadding =
140d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
141d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mBottomPadding =
142d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
143aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
144d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountX = LauncherModel.getCellCountX();
145d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountY = LauncherModel.getCellCountY();
1460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        mOccupied = new boolean[mCountX][mCountY];
14731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        a.recycle();
14931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
15031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setAlwaysDrawnWithCacheEnabled(false);
15131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
152de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        mWallpaperManager = WallpaperManager.getInstance(context);
153de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
154046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        final Resources res = getResources();
155de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
156046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        if (LauncherApplication.isScreenXLarge()) {
157150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            mBackgroundMini = res.getDrawable(R.drawable.mini_home_screen_bg);
158f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            mBackgroundMini.setFilterBitmap(true);
159150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            mBackground = res.getDrawable(R.drawable.home_screen_bg);
160de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            mBackground.setFilterBitmap(true);
161150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            mBackgroundMiniHover = res.getDrawable(R.drawable.mini_home_screen_bg_hover);
162f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            mBackgroundMiniHover.setFilterBitmap(true);
1631262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            mBackgroundHover = res.getDrawable(R.drawable.home_screen_bg_hover);
1641262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            mBackgroundHover.setFilterBitmap(true);
1653e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundMiniAcceptsDrops = res.getDrawable(
1663e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                    R.drawable.mini_home_screen_bg_accepts_drops);
1673e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundMiniAcceptsDrops.setFilterBitmap(true);
168046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        }
169de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
170046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Initialize the data structures used for the drag visualization.
171150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
172046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        mCrosshairsDrawable = res.getDrawable(R.drawable.gardening_crosshairs);
17300397b1d9233409d9d6b233b077ae12d09768ce8Chet Haase        TimeInterpolator interp = new DecelerateInterpolator(2.5f); // Quint ease out
174de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
175046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Set up the animation for fading the crosshairs in and out
176046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        int animDuration = res.getInteger(R.integer.config_crosshairsFadeInTime);
17749250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy        mCrosshairsAnimator = new InterruptibleInOutAnimator(animDuration, 0.0f, 1.0f);
178472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        mCrosshairsAnimator.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
179046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            public void onAnimationUpdate(ValueAnimator animation) {
180046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy                mCrosshairsVisibility = ((Float) animation.getAnimatedValue()).floatValue();
181046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy                CellLayout.this.invalidate();
182046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            }
183046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        });
184472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        mCrosshairsAnimator.getAnimator().setInterpolator(interp);
185046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy
1864be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
1874be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlines[i] = new Point(-1, -1);
188046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        }
189046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy
190046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // When dragging things around the home screens, we show a green outline of
191046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // where the item will land. The outlines gradually fade out, leaving a trail
192046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // behind the drag path.
193046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Set up all the animations that are used to implement this fading.
194046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime);
195472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        final float fromAlphaValue = 0;
196472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        final float toAlphaValue = (float)res.getInteger(R.integer.config_dragOutlineMaxAlpha);
1974be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
1984be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlineAlphas.length; i++) {
1994be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineAlphas[i] = fromAlphaValue;
2004be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
2014be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2024be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlineAnims.length; i++) {
203046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            final InterruptibleInOutAnimator anim =
204046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy                new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
205472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().setInterpolator(interp);
206046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            final int thisIndex = i;
207472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
208de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                public void onAnimationUpdate(ValueAnimator animation) {
2094be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    final Bitmap outline = (Bitmap)anim.getTag();
2104be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2114be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    // If an animation is started and then stopped very quickly, we can still
2124be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    // get spurious updates we've cleared the tag. Guard against this.
2134be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    if (outline == null) {
214fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                        if (false) {
215fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                            Object val = animation.getAnimatedValue();
216fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                            Log.d(TAG, "anim " + thisIndex + " update: " + val +
217fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                                     ", isStopped " + anim.isStopped());
218fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                        }
219fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy
2204be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        // Try to prevent it from continuing to run
2214be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        animation.cancel();
2224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    } else {
223472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                        mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();
2244be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        final int left = mDragOutlines[thisIndex].x;
2254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        final int top = mDragOutlines[thisIndex].y;
2264be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        CellLayout.this.invalidate(left, top,
2274be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                                left + outline.getWidth(), top + outline.getHeight());
2284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    }
229de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                }
230de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            });
2314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // The animation holds a reference to the drag outline bitmap as long is it's
2324be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // running. This way the bitmap can be GCed when the animations are complete.
233472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().addListener(new AnimatorListenerAdapter() {
2344be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                public void onAnimationEnd(Animator animation) {
235472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                    if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {
2364be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        anim.setTag(null);
2374be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    }
2384be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                }
2394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            });
2404be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineAnims[i] = anim;
241de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        }
242a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    }
243a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy
244a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    public void setHover(boolean value) {
245a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        if (mHover != value) {
2463e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mHover = value;
247a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka            invalidate();
248a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
249a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    }
250a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka
2511262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    public void drawChildren(Canvas canvas) {
2521262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        super.dispatchDraw(canvas);
2531262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2541262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
255a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    @Override
2561262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onDraw(Canvas canvas) {
2573e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
2583e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
2593e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're small, we are either drawn normally or in the "accepts drops" state (during
2603e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a drag). However, we also drag the mini hover background *over* one of those two
2613e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // backgrounds
2625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mBackgroundAlpha > 0.0f) {
263f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            Drawable bg;
2641262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            if (getScaleX() < 0.5f) {
2653e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                bg = mAcceptsDrops ? mBackgroundMiniAcceptsDrops : mBackgroundMini;
266f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            } else {
2671262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy                bg = mHover ? mBackgroundHover : mBackground;
268f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            }
2699c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            if (bg != null) {
2709c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                bg.setAlpha((int) (mBackgroundAlpha * 255));
2719c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen                bg.draw(canvas);
2729c4949e12c909d5e01d24386147b1c528015b31bAdam Cohen            }
2733e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            if (mHover && getScaleX() < 0.5f) {
2743e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundMiniHover.setAlpha((int) (mBackgroundAlpha * 255));
2753e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundMiniHover.draw(canvas);
2763e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            }
277a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
27831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
279de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        if (mCrosshairsVisibility > 0.0f) {
280de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int countX = mCountX;
281de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int countY = mCountY;
282de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
283de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final float MAX_ALPHA = 0.4f;
284de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int MAX_VISIBLE_DISTANCE = 600;
285de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final float DISTANCE_MULTIPLIER = 0.002f;
286de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
287de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final Drawable d = mCrosshairsDrawable;
288de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int width = d.getIntrinsicWidth();
289de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int height = d.getIntrinsicHeight();
290de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
291de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            int x = getLeftPadding() - (mWidthGap / 2) - (width / 2);
292de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            for (int col = 0; col <= countX; col++) {
293de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                int y = getTopPadding() - (mHeightGap / 2) - (height / 2);
294de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                for (int row = 0; row <= countY; row++) {
295de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    mTmpPointF.set(x - mDragCenter.x, y - mDragCenter.y);
296de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    float dist = mTmpPointF.length();
297de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    // Crosshairs further from the drag point are more faint
298de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    float alpha = Math.min(MAX_ALPHA,
299de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                            DISTANCE_MULTIPLIER * (MAX_VISIBLE_DISTANCE - dist));
300de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    if (alpha > 0.0f) {
301de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                        d.setBounds(x, y, x + width, y + height);
302de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                        d.setAlpha((int) (alpha * 255 * mCrosshairsVisibility));
303de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                        d.draw(canvas);
304de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    }
305de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                    y += mCellHeight + mHeightGap;
306de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                }
307de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                x += mCellWidth + mWidthGap;
308de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            }
3094be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
310150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
3114be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        final Paint paint = new Paint();
3124be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
313472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            final float alpha = mDragOutlineAlphas[i];
3144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (alpha > 0) {
3154be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                final Point p = mDragOutlines[i];
3164be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
317472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                paint.setAlpha((int)(alpha + .5f));
3184be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                canvas.drawBitmap(b, p.x, p.y, paint);
319150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            }
3206569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
3216569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
3226569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
323f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    public void setDimmableProgress(float progress) {
324f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        for (int i = 0; i < getChildCount(); i++) {
325f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            Dimmable d = (Dimmable) getChildAt(i);
326f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            d.setDimmableProgress(progress);
327f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
328f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
329f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
330f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    public float getDimmableProgress() {
331f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (getChildCount() > 0) {
332f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            return ((Dimmable) getChildAt(0)).getDimmableProgress();
333f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
334f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        return 0.0f;
335f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
336f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
3376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    @Override
33883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    public void cancelLongPress() {
33983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        super.cancelLongPress();
34083f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
34183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        // Cancel long press for all children
34283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        final int count = getChildCount();
34383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        for (int i = 0; i < count; i++) {
34483f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            final View child = getChildAt(i);
34583f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            child.cancelLongPress();
34683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        }
34783f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    }
34883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
349dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void setOnInterceptTouchListener(View.OnTouchListener listener) {
350dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mInterceptTouchListener = listener;
351dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
352dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
35331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountX() {
354d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountX;
35531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
35631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
35731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountY() {
358d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountY;
35931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
36031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
361aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
362aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final LayoutParams lp = params;
363aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
36431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Generate an id for each view, this assumes we have at most 256x256 cells
36531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // per workspace screen
366d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
367aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // If the horizontal or vertical span is set to -1, it is taken to
368aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // mean that it spans the extent of the CellLayout
369d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
370d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
371aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
372aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            child.setId(childId);
37331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
374dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            // We might be in the middle or end of shrinking/fading to a dimmed view
375dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            // Make sure this view's alpha is set the same as all the rest of the views
3765f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            child.setAlpha(getAlpha());
377aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            addView(child, index, lp);
378dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
3790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            markCellsAsOccupiedForView(child);
3800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
381aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return true;
382aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
383aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return false;
38431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
3853e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    public void setAcceptsDrops(boolean acceptsDrops) {
3863e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (mAcceptsDrops != acceptsDrops) {
3873e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mAcceptsDrops = acceptsDrops;
3883e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            invalidate();
3893e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
3903e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
3913e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
3923e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    public boolean getAcceptsDrops() {
3933e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        return mAcceptsDrops;
3943e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
39531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
3970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViews() {
3980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        clearOccupiedCells();
3990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
4020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViewsInLayout() {
4030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        clearOccupiedCells();
4040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
4070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeView(View view) {
4080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
4090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        super.removeView(view);
4100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
4130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewAt(int index) {
4140280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(getChildAt(index));
4150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        super.removeViewAt(index);
4160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
4190280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewInLayout(View view) {
4200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
4210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        super.removeViewInLayout(view);
4220280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
4250280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViews(int start, int count) {
4260280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
4270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            markCellsAsUnoccupiedForView(getChildAt(i));
4280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
4290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        super.removeViews(start, count);
4300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4320280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
4330280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewsInLayout(int start, int count) {
4340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
4350280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            markCellsAsUnoccupiedForView(getChildAt(i));
4360280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
4370280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        super.removeViewsInLayout(start, count);
4380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
4390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
4400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
44131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public void requestChildFocus(View child, View focused) {
44231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.requestChildFocus(child, focused);
44331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (child != null) {
44431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            Rect r = new Rect();
44531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.getDrawingRect(r);
44631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            requestRectangleOnScreen(r);
44731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
44831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
44931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
45031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
45131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onAttachedToWindow() {
45231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onAttachedToWindow();
45331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
45431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
45531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
456af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public void setTagToCellInfoForPoint(int touchX, int touchY) {
45731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo cellInfo = mCellInfo;
458af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final Rect frame = mRect;
459af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int x = touchX + mScrollX;
460af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int y = touchY + mScrollY;
461af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int count = getChildCount();
46231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
463af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        boolean found = false;
464af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        for (int i = count - 1; i >= 0; i--) {
465af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            final View child = getChildAt(i);
466af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
467af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
468af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                child.getHitRect(frame);
469af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                if (frame.contains(x, y)) {
470af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
471af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cell = child;
472af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellX = lp.cellX;
473af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellY = lp.cellY;
474af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanX = lp.cellHSpan;
475af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanY = lp.cellVSpan;
476af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.valid = true;
477af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    found = true;
478af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    break;
47931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
48031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
481af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
482aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
483af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (!found) {
4846569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            final int cellXY[] = mTmpCellXY;
485af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            pointToCellExact(x, y, cellXY);
48631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
487af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cell = null;
488af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellX = cellXY[0];
489af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellY = cellXY[1];
490af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanX = 1;
491af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanY = 1;
4920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX &&
4930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]];
494af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
495af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        setTag(cellInfo);
496af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    }
49731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
498aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
499af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    @Override
500af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public boolean onInterceptTouchEvent(MotionEvent ev) {
501dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
502dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            return true;
503dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
504af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final int action = ev.getAction();
505af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        final CellInfo cellInfo = mCellInfo;
50631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
507af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (action == MotionEvent.ACTION_DOWN) {
508af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
50931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else if (action == MotionEvent.ACTION_UP) {
51031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cell = null;
51131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cellX = -1;
51231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.cellY = -1;
51331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.spanX = 0;
51431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.spanY = 0;
51531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellInfo.valid = false;
51631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            setTag(cellInfo);
51731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
51831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
51931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
52031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
52131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
52231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
52331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellInfo getTag() {
5240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return (CellInfo) super.getTag();
52531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
52631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5276569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
5286569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
5296569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
53031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
53131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = left; x <= right; x++) {
53231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (occupied[x][y]) {
53331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return false;
53431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
53531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
53631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return true;
53731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
53831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
53931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
540aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Given a point, return the cell that strictly encloses that point
54131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
54231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
54331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
54431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
54531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellExact(int x, int y, int[] result) {
546aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int hStartPadding = getLeftPadding();
547aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int vStartPadding = getTopPadding();
54831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
54931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
55031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
55131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
552d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xAxis = mCountX;
553d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yAxis = mCountY;
55431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
55531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] < 0) result[0] = 0;
55631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] >= xAxis) result[0] = xAxis - 1;
55731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] < 0) result[1] = 0;
55831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] >= yAxis) result[1] = yAxis - 1;
55931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
560aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
56131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
56231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that most closely encloses that point
56331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
56431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
56531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
56631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
56731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellRounded(int x, int y, int[] result) {
56831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
56931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
57031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
57131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
57231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a cell coordinate, return the point that represents the upper left corner of that cell
573aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
574aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellX X coordinate of the cell
57531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of the cell
576aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
57731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the point
57831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
57931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void cellToPoint(int cellX, int cellY, int[] result) {
580aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int hStartPadding = getLeftPadding();
581aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int vStartPadding = getTopPadding();
58231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
58331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
58431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
58531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
58631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
58784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellWidth() {
58884f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellWidth;
58984f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
59084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
59184f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellHeight() {
59284f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellHeight;
59384f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
59484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
5951a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getLeftPadding() {
596aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mLeftPadding;
5971a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
5981a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
5991a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getTopPadding() {
600aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mTopPadding;
6011a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
6021a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
6031a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getRightPadding() {
604aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mRightPadding;
6051a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
6061a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
6071a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    int getBottomPadding() {
608aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mBottomPadding;
6091a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy    }
6101a304a1198f2f48be8c6a763b3ce511bd28af811Romain Guy
61131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
61231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
61331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // TODO: currently ignoring padding
614aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
61531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
616aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
617aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
61831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
61931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
620aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
62131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
62231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
62331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
62431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
62531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
62631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
62731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
628d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numWidthGaps = mCountX - 1;
629d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numHeightGaps = mCountY - 1;
630d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
6310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY);
632d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mHeightGap = vSpaceLeft / numHeightGaps;
633d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
6340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX);
635d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mWidthGap = hSpaceLeft / numWidthGaps;
636aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
6375f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        // center it around the min gaps
6385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        int minGap = Math.min(mWidthGap, mHeightGap);
6395f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mWidthGap = mHeightGap = minGap;
6405f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
64131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
64231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
64331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
64431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
64531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            LayoutParams lp = (LayoutParams) child.getLayoutParams();
646aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
647aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    mLeftPadding, mTopPadding);
648aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
6490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
650aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
651aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    MeasureSpec.EXACTLY);
65231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
65331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
65431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
6555f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (widthSpecMode == MeasureSpec.AT_MOST) {
6565f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
6575f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                ((mCountX - 1) * minGap);
6585f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            int newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
6595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                ((mCountY - 1) * minGap);
6605f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            setMeasuredDimension(newWidth, newHeight);
6615f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        } else if (widthSpecMode == MeasureSpec.EXACTLY) {
6625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            setMeasuredDimension(widthSpecSize, heightSpecSize);
6635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        }
66431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
66531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
66631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
66728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int l, int t, int r, int b) {
66831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
66931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
67131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
67231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (child.getVisibility() != GONE) {
67331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
67531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int childLeft = lp.x;
67731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int childTop = lp.y;
67831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
67984f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
68084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                if (lp.dropped) {
68184f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                    lp.dropped = false;
68284f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
6836569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    final int[] cellXY = mTmpCellXY;
68406762ab54d64e84328d427403bb6074dfd0f630cRomain Guy                    getLocationOnScreen(cellXY);
68584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                    mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
68606762ab54d64e84328d427403bb6074dfd0f630cRomain Guy                            cellXY[0] + childLeft + lp.width / 2,
68706762ab54d64e84328d427403bb6074dfd0f630cRomain Guy                            cellXY[1] + childTop + lp.height / 2, 0, null);
68884f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy                }
68931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
69031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
69131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
69231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
69331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
694dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
695dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super.onSizeChanged(w, h, oldw, oldh);
6965f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (mBackground != null) {
6971262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            mBackground.setBounds(0, 0, w, h);
6981262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        }
6991262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        if (mBackgroundHover != null) {
7001262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            mBackgroundHover.setBounds(0, 0, w, h);
701a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
702f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (mBackgroundMiniHover != null) {
7031262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            mBackgroundMiniHover.setBounds(0, 0, w, h);
704f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        }
705f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (mBackgroundMini != null) {
7061262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            mBackgroundMini.setBounds(0, 0, w, h);
707a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
7083e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (mBackgroundMiniAcceptsDrops != null) {
7093e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundMiniAcceptsDrops.setBounds(0, 0, w, h);
7103e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
711dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
712dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
713dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @Override
71431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
71531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int count = getChildCount();
71631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
71731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final View view = getChildAt(i);
71831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            view.setDrawingCacheEnabled(enabled);
71931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Update the drawing caches
720fefa0ce22af9560f1c0f8c84c760c75f34b7b12cAdam Powell            view.buildDrawingCache(true);
72131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
72231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
72331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
72431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
72531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
72631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.setChildrenDrawnWithCacheEnabled(enabled);
72731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
72831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public float getBackgroundAlpha() {
7305f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        return mBackgroundAlpha;
731dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
732dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
7335f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public void setBackgroundAlpha(float alpha) {
7345f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mBackgroundAlpha = alpha;
7350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        invalidate();
736dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
737dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
7385f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    // Need to return true to let the view system know we know how to handle alpha-- this is
7395f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    // because when our children have an alpha of 0.0f, they are still rendering their "dimmed"
7405f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    // versions
7415f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    @Override
7425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    protected boolean onSetAlpha(int alpha) {
7435f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        return true;
7445f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    }
7455f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
7465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public void setAlpha(float alpha) {
7475f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        setChildrenAlpha(alpha);
7485f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        super.setAlpha(alpha);
7495f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    }
7505f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
751dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private void setChildrenAlpha(float alpha) {
7520142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int childCount = getChildCount();
7530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        for (int i = 0; i < childCount; i++) {
754dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            getChildAt(i).setAlpha(alpha);
755dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
756dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
757dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
7580280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private boolean isVacantIgnoring(
7590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int originX, int originY, int spanX, int spanY, View ignoreView) {
7600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        if (ignoreView != null) {
7610280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            markCellsAsUnoccupiedForView(ignoreView);
7620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
76328750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        boolean isVacant = true;
7646569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        for (int i = 0; i < spanY; i++) {
7656569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
76628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                isVacant = false;
76728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                break;
7686569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            }
7696569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
7700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        if (ignoreView != null) {
7710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            markCellsAsOccupiedForView(ignoreView);
7720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
77328750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        return isVacant;
7746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
7756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
7760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private boolean isVacant(int originX, int originY, int spanX, int spanY) {
7770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return isVacantIgnoring(originX, originY, spanX, spanY, null);
7780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
7790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
780440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    public View getChildAt(int x, int y) {
781440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        final int count = getChildCount();
782440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        for (int i = 0; i < count; i++) {
783440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            View child = getChildAt(i);
784440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
785440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
786440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
787440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy                    (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
788440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy                return child;
789440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            }
790440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        }
791440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        return null;
792440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
793440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
7946569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
7958f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * Estimate the size that a child with the given dimensions will take in the layout.
7968f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     */
7978f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    void estimateChildSize(int minWidth, int minHeight, int[] result) {
7988f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        // Assuming it's placed at 0, 0, find where the bottom right cell will land
7998f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        rectToCell(minWidth, minHeight, result);
8008f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy
8018f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        // Then figure out the rect it will occupy
8028f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        cellToRect(0, 0, result[0], result[1], mRectF);
8038f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = (int)mRectF.width();
8048f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = (int)mRectF.height();
8058f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    }
8068f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy
8078f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    /**
8086569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Estimate where the top left cell of the dragged item will land if it is dropped.
8096569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     *
8106569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originX The X value of the top left corner of the item
8116569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originY The Y value of the top left corner of the item
8126569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanX The number of horizontal cells that the item spans
8136569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanY The number of vertical cells that the item spans
8146569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param result The estimated drop cell X and Y.
8156569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
8166569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
817d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countX = mCountX;
818d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countY = mCountY;
8196569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
820a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // pointToCellRounded takes the top left of a cell but will pad that with
821a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // cellWidth/2 and cellHeight/2 when finding the matching cell
822a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        pointToCellRounded(originX, originY, result);
8236569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8246569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // If the item isn't fully on this screen, snap to the edges
8256569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int rightOverhang = result[0] + spanX - countX;
8266569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (rightOverhang > 0) {
8276569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[0] -= rightOverhang; // Snap to right
8286569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
8296569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[0] = Math.max(0, result[0]); // Snap to left
8306569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int bottomOverhang = result[1] + spanY - countY;
8316569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (bottomOverhang > 0) {
8326569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[1] -= bottomOverhang; // Snap to bottom
8336569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
8346569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[1] = Math.max(0, result[1]); // Snap to top
8356569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
8366569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8374be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    void visualizeDropLocation(
8384be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            View v, Bitmap dragOutline, int originX, int originY, int spanX, int spanY) {
8394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
8404be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        final int[] nearest = findNearestVacantArea(originX, originY, spanX, spanY, v, mDragCell);
8414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2));
8426569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
843de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        if (nearest != null) {
8446569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Find the top left corner of the rect the object will occupy
845de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int[] topLeft = mTmpPoint;
846de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            cellToPoint(nearest[0], nearest[1], topLeft);
847de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
8484be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int left = topLeft[0];
8494be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int top = topLeft[1];
8506569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8514be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (v.getParent() instanceof CellLayout) {
8524be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                LayoutParams lp = (LayoutParams) v.getLayoutParams();
8534be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                left += lp.leftMargin;
8544be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                top += lp.topMargin;
8554be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            }
856150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
8574be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // Offsets due to the size difference between the View and the dragOutline
8584be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            left += (v.getWidth() - dragOutline.getWidth()) / 2;
8594be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            top += (v.getHeight() - dragOutline.getHeight()) / 2;
860150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
8614be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int oldIndex = mDragOutlineCurrent;
8624be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final Point lastPoint = mDragOutlines[oldIndex];
8634be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (lastPoint.x != left || lastPoint.y != top) {
8644be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
865150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
8664be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                mDragOutlines[mDragOutlineCurrent].set(left, top);
867150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
8684be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                mDragOutlineAnims[oldIndex].animateOut();
8694be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
8704be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                mDragOutlineAnims[mDragOutlineCurrent].animateIn();
871150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            }
8726569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
87349250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy
87449250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy        // If we are drawing crosshairs, the entire CellLayout needs to be invalidated
87549250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy        if (mCrosshairsDrawable != null) {
87649250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy            invalidate();
87749250ad530385fcdd6072ca54697f670ec503b59Patrick Dubroy        }
8786569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
8796569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
88031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
88170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Find a vacant area that will fit the given bounds nearest the requested
88270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * cell location. Uses Euclidean distance to score multiple vacant areas.
883aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
88451afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelX The X location at which you want to search for a vacant area.
88551afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelY The Y location at which you want to search for a vacant area.
88670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanX Horizontal span of the object.
88770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanY Vertical span of the object.
888de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * @param result Array in which to place the result, or null (in which case a new array will
889de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     *        be allocated)
89070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @return The X, Y cell of a vacant area that can contain this object,
89170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *         nearest the requested location.
89231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
8936a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    int[] findNearestVacantArea(
894de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            int pixelX, int pixelY, int spanX, int spanY, int[] result) {
895de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
8966a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    }
897aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
8986a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    /**
8996a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * Find a vacant area that will fit the given bounds nearest the requested
9006a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * cell location. Uses Euclidean distance to score multiple vacant areas.
9016a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *
9026a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelX The X location at which you want to search for a vacant area.
9036a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelY The Y location at which you want to search for a vacant area.
9046a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanX Horizontal span of the object.
9056a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanY Vertical span of the object.
9066a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param ignoreView Considers space occupied by this view as unoccupied
907de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * @param result Previously returned value to possibly recycle.
9086a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @return The X, Y cell of a vacant area that can contain this object,
9096a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *         nearest the requested location.
9106a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     */
9116a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    int[] findNearestVacantArea(
912de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
913c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
914c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        markCellsAsUnoccupiedForView(ignoreView);
915c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka
91670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Keep track of best-scoring drop area
917de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int[] bestXY = result != null ? result : new int[2];
91870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        double bestDistance = Double.MAX_VALUE;
919aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
920de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countX = mCountX;
921de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countY = mCountY;
922de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final boolean[][] occupied = mOccupied;
923de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
924de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        for (int x = 0; x < countX - (spanX - 1); x++) {
925c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            inner:
926de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            for (int y = 0; y < countY - (spanY - 1); y++) {
927c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                for (int i = 0; i < spanX; i++) {
928c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    for (int j = 0; j < spanY; j++) {
929de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                        if (occupied[x + i][y + j]) {
930c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            // small optimization: we can skip to below the row we just found
931c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            // an occupied cell
932c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            y += j;
933c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                            continue inner;
934c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        }
935c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
936c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
937c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                final int[] cellXY = mTmpCellXY;
938c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                cellToPoint(x, y, cellXY);
939c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka
940c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
941c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        + Math.pow(cellXY[1] - pixelY, 2));
942c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                if (distance <= bestDistance) {
943c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestDistance = distance;
944c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[0] = x;
945c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[1] = y;
946c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
94731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
94831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
949c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
950c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        markCellsAsOccupiedForView(ignoreView);
95131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
952aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        // Return null if no suitable location found
95370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        if (bestDistance < Double.MAX_VALUE) {
95470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return bestXY;
95570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        } else {
95670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return null;
95770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        }
95831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
959aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
9600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean existsEmptyCell() {
9610280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpan(null, 1, 1);
9620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
9630280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
9640280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
9650280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Finds the upper-left coordinate of the first rectangle in the grid that can
9660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
9670280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * then this method will only return coordinates for rectangles that contain the cell
9680280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * (intersectX, intersectY)
9690280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
9700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
9710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
9720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
9730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
9740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
9750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
9760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
9770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
9780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
9790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
9800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
9810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
9820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but ignores any cells occupied by the item "ignoreView"
9830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
9840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
9850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
9860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
9870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
9880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
9890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return
9900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
9910280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
9920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
9930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
9940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
9950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
9960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but if intersectX and intersectY are not -1, then this method will try to
9970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * return coordinates for rectangles that contain the cell [intersectX, intersectY]
9980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
9990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
10000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
10010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
10020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The X coordinate of the cell that we should try to overlap
10030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The Y coordinate of the cell that we should try to overlap
10040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
10050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
10060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
10070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
10080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int intersectX, int intersectY) {
10090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpanThatIntersectsIgnoring(
10100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                cellXY, spanX, spanY, intersectX, intersectY, null);
10110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
10120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
10130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
10140280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * The superset of the above two methods
10150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
10160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
10170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int intersectX, int intersectY, View ignoreView) {
1018c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
1019c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        markCellsAsUnoccupiedForView(ignoreView);
10200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
102128750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        boolean foundCell = false;
10220280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        while (true) {
10230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startX = 0;
10240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
10250280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startX = Math.max(startX, intersectX - (spanX - 1));
10260280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
10270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endX = mCountX - (spanX - 1);
10280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
10290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
10300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
10310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startY = 0;
10320280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
10330280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startY = Math.max(startY, intersectY - (spanY - 1));
10340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
10350280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endY = mCountY - (spanY - 1);
10360280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
10370280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
10380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
10390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
10400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int x = startX; x < endX; x++) {
10410280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                inner:
10420280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                for (int y = startY; y < endY; y++) {
10430280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    for (int i = 0; i < spanX; i++) {
10440280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        for (int j = 0; j < spanY; j++) {
10450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                            if (mOccupied[x + i][y + j]) {
10460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                // small optimization: we can skip to below the row we just found
10470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                // an occupied cell
10480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                y += j;
10490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                continue inner;
10500280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                            }
10510280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        }
10520280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
10530280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    if (cellXY != null) {
10540280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[0] = x;
10550280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[1] = y;
10560280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
105728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    foundCell = true;
105828750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    break;
10590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                }
10600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
10610280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX == -1 && intersectY == -1) {
10620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                break;
10630280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            } else {
10640280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // if we failed to find anything, try again but without any requirements of
10650280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // intersecting
10660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectX = -1;
10670280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectY = -1;
10680280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                continue;
10690280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
10700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
10710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
1072c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
1073c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        markCellsAsOccupiedForView(ignoreView);
107428750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        return foundCell;
10750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
10760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
107731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
10780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Called when drag has left this CellLayout or has been completed (successfully or not)
10796569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
10800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    void onDragExit() {
10814be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // This can actually be called when we aren't in a drag, e.g. when adding a new
10824be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // item to this layout via the customize drawer.
10834be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Guard against that case.
10844be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mDragging) {
10854be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragging = false;
10866569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
10874be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // Invalidate the drag data
10884be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragCell[0] = -1;
10894be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragCell[1] = -1;
1090de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
10914be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            setHover(false);
1092150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
10934be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // Fade out the drag indicators
10944be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (mCrosshairsAnimator != null) {
10954be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                mCrosshairsAnimator.animateOut();
10964be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            }
10974be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
10984be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int prev = mDragOutlineCurrent;
10994be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineAnims[prev].animateOut();
11004be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineCurrent = (prev + 1) % mDragOutlines.length;
11014be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlines[mDragOutlineCurrent].set(-1, -1);
11024be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineAlphas[mDragOutlineCurrent] = 0;
11034be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
11046569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
11056569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
11066569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
1107aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Mark a child as having been dropped.
1108de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * At the beginning of the drag operation, the child may have been on another
1109de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * screen, but it is reparented before this method is called.
111031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
111131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
111231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
1113aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    void onDropChild(View child) {
1114d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        if (child != null) {
1115d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
1116d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            lp.isDragging = false;
111784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy            lp.dropped = true;
1118d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            child.requestLayout();
1119d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        }
11200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        onDragExit();
112131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
112231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
112331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDropAborted(View child) {
112431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (child != null) {
112531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            ((LayoutParams) child.getLayoutParams()).isDragging = false;
112631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
11270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        onDragExit();
112831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
112931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
113031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
113131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Start dragging the specified child
1132aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
113331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dragged
113431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
113531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void onDragChild(View child) {
113631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        LayoutParams lp = (LayoutParams) child.getLayoutParams();
113731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        lp.isDragging = true;
1138de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    }
1139de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
1140de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    /**
1141de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * A drag event has begun over this layout.
1142de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * It may have begun over this layout (in which case onDragChild is called first),
1143de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * or it may have begun on another layout.
1144de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     */
1145de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    void onDragEnter(View dragView) {
1146fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy        if (!mDragging) {
11474be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato//            Log.d(TAG, "Received onDragEnter while drag still active");
1148fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy            // Fade in the drag indicators
1149fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy            if (mCrosshairsAnimator != null) {
1150fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                mCrosshairsAnimator.animateIn();
1151fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy            }
11524be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
11534be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mDragging = true;
115431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1155aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
115631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
115731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes a bounding rectangle for a range of cells
1158aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
115931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of upper left corner expressed as a cell position
116031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of upper left corner expressed as a cell position
1161aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellHSpan Width in cells
116231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellVSpan Height in cells
11636569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param resultRect Rect into which to put the results
116431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
11656569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
116631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
116731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
116831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int widthGap = mWidthGap;
116931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int heightGap = mHeightGap;
1170aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1171aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int hStartPadding = getLeftPadding();
1172aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int vStartPadding = getTopPadding();
1173aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
117431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
117531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
117631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
117731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x = hStartPadding + cellX * (cellWidth + widthGap);
117831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = vStartPadding + cellY * (cellHeight + heightGap);
1179aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
11806569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        resultRect.set(x, y, x + width, y + height);
118131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1182aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
118331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
1184aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Computes the required horizontal and vertical cell spans to always
118531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * fit the given rectangle.
1186aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
118731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param width Width in pixels
118831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param height Height in pixels
11898f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * @param result An array of length 2 in which to store the result (may be null).
119031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
11918f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    public int[] rectToCell(int width, int height, int[] result) {
11929987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka        return rectToCell(getResources(), width, height, result);
11939987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    }
11949987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka
11959987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    public static int[] rectToCell(Resources resources, int width, int height, int[] result) {
119631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always assume we're working with the smallest span to make sure we
119731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // reserve enough space in both orientations.
119879e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
119979e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
120031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int smallerSize = Math.min(actualWidth, actualHeight);
120179e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
120231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always round up to next largest cell
120331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX = (width + smallerSize) / smallerSize;
120431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY = (height + smallerSize) / smallerSize;
120579e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
12068f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        if (result == null) {
12078f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            return new int[] { spanX, spanY };
12088f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        }
12098f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = spanX;
12108f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = spanY;
12118f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        return result;
121231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
121331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
121431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
121531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Find the first vacant cell, if there is one.
121631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
121731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param vacant Holds the x and y coordinate of the vacant cell
121831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanX Horizontal cell span.
121931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanY Vertical cell span.
1220aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
122131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @return True if a vacant cell was found
122231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
122331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
122431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12250280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
122631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
122731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
122831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static boolean findVacantCell(int[] vacant, int spanX, int spanY,
122931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
123031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
123131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int x = 0; x < xCount; x++) {
123231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int y = 0; y < yCount; y++) {
123331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean available = !occupied[x][y];
123431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectout:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
123531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
123631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        available = available && !occupied[i][j];
123731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        if (!available) break out;
123831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
123931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
124031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
124131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (available) {
124231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[0] = x;
124331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[1] = y;
124431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return true;
124531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
124631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
124731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
124831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
124931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
125031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
125131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12526569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
12536569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
12546569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
12556569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    boolean[] getOccupiedCellsFlattened() {
1256d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xCount = mCountX;
1257d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yCount = mCountY;
125831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[][] occupied = mOccupied;
125931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
126031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final boolean[] flat = new boolean[xCount * yCount];
126131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int y = 0; y < yCount; y++) {
126231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int x = 0; x < xCount; x++) {
126331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                flat[y * xCount + x] = occupied[x][y];
126431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
126531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
126631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
126731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return flat;
126831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
126931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void clearOccupiedCells() {
12710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = 0; x < mCountX; x++) {
12720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = 0; y < mCountY; y++) {
12730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                mOccupied[x][y] = false;
127431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
127531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
12760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
127731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void onMove(View view, int newCellX, int newCellY) {
12790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
12800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
12810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
12820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
128331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void markCellsAsOccupiedForView(View view) {
1285c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        if (view == null || view.getParent() != this) return;
12860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
12870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
12880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
12890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
12900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void markCellsAsUnoccupiedForView(View view) {
1291c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        if (view == null || view.getParent() != this) return;
12920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
12930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
12940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
12950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
12960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
12970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
12980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
12990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                mOccupied[x][y] = value;
130031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
130131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
130231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
130331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
130431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
130531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
130631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(getContext(), attrs);
130731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
130831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
130931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
131031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
131131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return p instanceof CellLayout.LayoutParams;
131231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
131331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
131431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
131531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
131631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(p);
131731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
131831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1319aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public static class CellLayoutAnimationController extends LayoutAnimationController {
1320aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public CellLayoutAnimationController(Animation animation, float delay) {
1321aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(animation, delay);
1322aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
1323aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1324aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        @Override
1325aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        protected long getDelayForView(View view) {
1326aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return (int) (Math.random() * 150);
1327aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
1328aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
1329aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
133031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
133131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
133231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Horizontal location of the item in the grid.
133331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
133431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
133531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellX;
133631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
133731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
133831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Vertical location of the item in the grid.
133931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
134031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
134131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellY;
134231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
134331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
134431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned horizontally by the item.
134531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
134631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
134731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellHSpan;
134831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
134931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
135031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned vertically by the item.
135131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
135231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
135331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellVSpan;
1354aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
135531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
135631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Is this item currently being dragged
135731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
135831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public boolean isDragging;
135931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
136031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // X coordinate of the view in the layout.
136131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
136231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x;
136331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Y coordinate of the view in the layout.
136431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
136531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y;
136631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
136784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        boolean dropped;
1368fcb9e7144e58614f5ae0e9b272fb7ce040848c67Romain Guy
136931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(Context c, AttributeSet attrs) {
137031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(c, attrs);
137131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
137231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
137331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
137431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
137531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(ViewGroup.LayoutParams source) {
137631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(source);
137731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
137831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
137931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1380aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1381aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public LayoutParams(LayoutParams source) {
1382aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(source);
1383aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellX = source.cellX;
1384aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellY = source.cellY;
1385aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellHSpan = source.cellHSpan;
1386aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellVSpan = source.cellVSpan;
1387aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
1388aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
138931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
13908f19cdd62f6e2be05e3890916eabd11317ae1bc2Romain Guy            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
139131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellX = cellX;
139231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellY = cellY;
139331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellHSpan = cellHSpan;
139431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellVSpan = cellVSpan;
139531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
139631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
139731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
139831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int hStartPadding, int vStartPadding) {
1399aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
140031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellHSpan = cellHSpan;
140131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellVSpan = cellVSpan;
140231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellX = cellX;
140331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int myCellY = cellY;
1404aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
140531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
140631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    leftMargin - rightMargin;
140731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
140831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    topMargin - bottomMargin;
140931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
141031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
141131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
141231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1413aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1414aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public String toString() {
1415aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "(" + this.cellX + ", " + this.cellY + ")";
1416aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
141731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
141831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14190280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // This class stores info for two purposes:
14200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
14210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    its spanX, spanY, and the screen it is on
14220280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 2. When long clicking on an empty cell in a CellLayout, we save information about the
14230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
14240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    the CellLayout that was long clicked
142531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static final class CellInfo implements ContextMenu.ContextMenuInfo {
142631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        View cell;
1427a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellX = -1;
1428a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellY = -1;
142931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX;
143031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY;
143131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int screen;
143231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean valid;
143331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
143431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
143531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public String toString() {
1436aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1437aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    + ", x=" + cellX + ", y=" + cellY + "]";
143831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
143931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
144031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
1441