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
194be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.animation.Animator;
20629758ff081a354e43aa409159211210ee4ee85aMichael Jurkaimport android.animation.AnimatorListenerAdapter;
2150e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keelyimport android.animation.AnimatorSet;
2200397b1d9233409d9d6b233b077ae12d09768ce8Chet Haaseimport android.animation.TimeInterpolator;
23de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.animation.ValueAnimator;
24de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.animation.ValueAnimator.AnimatorUpdateListener;
2531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Context;
2679e56263dbcbe85dc434df372bc6e6730aa13477Joe Onoratoimport android.content.res.Resources;
27aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.content.res.TypedArray;
284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.Bitmap;
29aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.graphics.Canvas;
300dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynnimport android.graphics.Color;
314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.Paint;
32de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroyimport android.graphics.Point;
33b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohenimport android.graphics.PorterDuff;
34b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohenimport android.graphics.PorterDuffXfermode;
3531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Rect;
36482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohenimport android.graphics.drawable.ColorDrawable;
376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroyimport android.graphics.drawable.Drawable;
38b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohenimport android.graphics.drawable.NinePatchDrawable;
391462de39f01cec0ba785386532719cb0207dd827Adam Cohenimport android.os.Parcelable;
4031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.util.AttributeSet;
414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.util.Log;
421462de39f01cec0ba785386532719cb0207dd827Adam Cohenimport android.util.SparseArray;
4331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.MotionEvent;
4431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.View;
4531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewDebug;
4631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewGroup;
47aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.Animation;
48150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chungimport android.view.animation.DecelerateInterpolator;
49aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.LayoutAnimationController;
5031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
516639687cd67bab1aeef2a75e5c6bc458b20dc082Adam Cohenimport com.android.launcher.R;
5269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohenimport com.android.launcher2.FolderIcon.FolderRingAnimator;
538e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy
5469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohenimport java.util.ArrayList;
55c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohenimport java.util.Arrays;
56f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohenimport java.util.Collections;
57f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohenimport java.util.Comparator;
58bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohenimport java.util.HashMap;
59d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohenimport java.util.Stack;
60c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen
61bdb5c5342adc550559fd723af461e53248f2fba8Michael Jurkapublic class CellLayout extends ViewGroup {
62aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    static final String TAG = "CellLayout";
63aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
642acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen    private Launcher mLauncher;
6531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellWidth;
6631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellHeight;
67aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
68d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountX;
69d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountY;
7031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
71234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen    private int mOriginalWidthGap;
72234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen    private int mOriginalHeightGap;
7331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mWidthGap;
7431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mHeightGap;
754b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung    private int mMaxGap;
76ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    private boolean mScrollingTransformsDirty = false;
7731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final Rect mRect = new Rect();
7931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final CellInfo mCellInfo = new CellInfo();
80aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
81de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    // These are temporary variables to prevent having to allocate a new object just to
82de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
830be025d64c1f84138fe430a58875886e66aae767Winson Chung    private final int[] mTmpXY = new int[2];
84de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final int[] mTmpPoint = new int[2];
8569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    int[] mTempLocation = new int[2];
866569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean[][] mOccupied;
88482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    boolean[][] mTmpOccupied;
89d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    private boolean mLastDownOnOccupiedCell = false;
9031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
91dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private OnTouchListener mInterceptTouchListener;
92dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
9369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>();
94c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    private int[] mFolderLeaveBehindCell = {-1, -1};
9569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
96b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private int mForegroundAlpha = 0;
975f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mBackgroundAlpha;
981b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    private float mBackgroundAlphaMultiplier = 1.0f;
99f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
10033945b21544bc98381df17726a3537c292d8c985Michael Jurka    private Drawable mNormalBackground;
10133945b21544bc98381df17726a3537c292d8c985Michael Jurka    private Drawable mActiveGlowBackground;
102b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Drawable mOverScrollForegroundDrawable;
103b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Drawable mOverScrollLeft;
104b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Drawable mOverScrollRight;
10518014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka    private Rect mBackgroundRect;
106b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Rect mForegroundRect;
107b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private int mForegroundPadding;
10833945b21544bc98381df17726a3537c292d8c985Michael Jurka
10933945b21544bc98381df17726a3537c292d8c985Michael Jurka    // If we're actively dragging something over this screen, mIsDragOverlapping is true
11033945b21544bc98381df17726a3537c292d8c985Michael Jurka    private boolean mIsDragOverlapping = false;
111de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final Point mDragCenter = new Point();
1126569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
113150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung    // These arrays are used to implement the drag visualization on x-large screens.
1144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    // They are used as circular arrays, indexed by mDragOutlineCurrent.
115d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private Rect[] mDragOutlines = new Rect[4];
116472b281d5cb4f5660df981a6c912266b9f5703feChet Haase    private float[] mDragOutlineAlphas = new float[mDragOutlines.length];
1174be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private InterruptibleInOutAnimator[] mDragOutlineAnims =
1184be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            new InterruptibleInOutAnimator[mDragOutlines.length];
119150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
120150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung    // Used as an index into the above 3 arrays; indicates which is the most current value.
1214be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private int mDragOutlineCurrent = 0;
1228e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy    private final Paint mDragOutlinePaint = new Paint();
123150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
12496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    private BubbleTextView mPressedOrFocusedIcon;
12596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
126482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
127482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            HashMap<CellLayout.LayoutParams, Animator>();
12819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private HashMap<View, ReorderHintAnimation>
12919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mShakeAnimators = new HashMap<View, ReorderHintAnimation>();
13019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
13119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private boolean mItemPlacementDirty = false;
132bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1336569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // When a drag operation is in progress, holds the nearest cell to the touch point
1346569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final int[] mDragCell = new int[2];
13531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1364be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private boolean mDragging = false;
1374be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
138ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    private TimeInterpolator mEaseOutInterpolator;
139a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    private ShortcutAndWidgetContainer mShortcutsAndWidgets;
140ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy
1410dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    private boolean mIsHotseat = false;
142307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    private float mHotseatScale = 1f;
1430dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
144482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_DRAG_OVER = 0;
145482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_ON_DROP = 1;
146482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_ON_DROP_EXTERNAL = 2;
147482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_ACCEPT_DROP = 3;
14819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private static final boolean DESTRUCTIVE_REORDER = false;
149482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
150482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
151a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static final int LANDSCAPE = 0;
152a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static final int PORTRAIT = 1;
153a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen
1547bdfc9700b1cad043c04c757f134db1bf3df00daAdam Cohen    private static final float REORDER_HINT_MAGNITUDE = 0.12f;
15519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private static final int REORDER_ANIMATION_DURATION = 150;
15619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private float mReorderHintAnimationMagnitude;
15719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
158482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private ArrayList<View> mIntersectingViews = new ArrayList<View>();
159482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private Rect mOccupiedRect = new Rect();
160482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private int[] mDirectionVector = new int[2];
16119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    int[] mPreviousReorderDirection = new int[2];
162b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen    private static final int INVALID_DIRECTION = -100;
163c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen    private DropTarget.DragEnforcer mDragEnforcer;
164482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1658a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy    private final static PorterDuffXfermode sAddBlendMode =
1668a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy            new PorterDuffXfermode(PorterDuff.Mode.ADD);
167ca99383daef92fed673de22126875cb485be494fMichael Jurka    private final static Paint sPaint = new Paint();
1688a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy
16931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context) {
17031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, null);
17131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
17231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
17331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs) {
17431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, attrs, 0);
17531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
17631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
17731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs, int defStyle) {
17831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super(context, attrs, defStyle);
1798b805b17158886035b38261eb611d8641701ae43Michael Jurka        mDragEnforcer = new DropTarget.DragEnforcer(context);
1806569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1816569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
1826569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // the user where a dragged item will land when dropped.
1836569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        setWillNotDraw(false);
184ce3cbd145b4222779abae32869da8dd3c2aefb67Romain Guy        setClipToPadding(false);
1852acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen        mLauncher = (Launcher) context;
186a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka
18731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
18831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
189f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
190f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
191234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        mWidthGap = mOriginalWidthGap = a.getDimensionPixelSize(R.styleable.CellLayout_widthGap, 0);
192234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        mHeightGap = mOriginalHeightGap = a.getDimensionPixelSize(R.styleable.CellLayout_heightGap, 0);
1934b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        mMaxGap = a.getDimensionPixelSize(R.styleable.CellLayout_maxGap, 0);
194d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountX = LauncherModel.getCellCountX();
195d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountY = LauncherModel.getCellCountY();
1960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        mOccupied = new boolean[mCountX][mCountY];
197482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mTmpOccupied = new boolean[mCountX][mCountY];
1985b53f29f809a0dad4a1e0717ba610ce737ee0f43Adam Cohen        mPreviousReorderDirection[0] = INVALID_DIRECTION;
1995b53f29f809a0dad4a1e0717ba610ce737ee0f43Adam Cohen        mPreviousReorderDirection[1] = INVALID_DIRECTION;
20031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
20131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        a.recycle();
20231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
20331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setAlwaysDrawnWithCacheEnabled(false);
20431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
205046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        final Resources res = getResources();
206307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        mHotseatScale = (res.getInteger(R.integer.hotseat_item_scale_percentage) / 100f);
207de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
208967289b6d5fec77f5c381d11ffb2319f3bb5e737Winson Chung        mNormalBackground = res.getDrawable(R.drawable.homescreen_blue_normal_holo);
209dea74b7d12b0fcd50bfdb4274f9867ba76d75238Winson Chung        mActiveGlowBackground = res.getDrawable(R.drawable.homescreen_blue_strong_holo);
210b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung
211b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left);
212b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right);
213b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundPadding =
214b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen                res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding);
215b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung
21619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mReorderHintAnimationMagnitude = (REORDER_HINT_MAGNITUDE *
21719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                res.getDimensionPixelSize(R.dimen.app_icon_size));
21819f3792523fe2d55ea791a9286398a6120920690Adam Cohen
219b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        mNormalBackground.setFilterBitmap(true);
220b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        mActiveGlowBackground.setFilterBitmap(true);
221de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
222046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Initialize the data structures used for the drag visualization.
223150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
224ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out
225de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
226046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy
227b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung        mDragCell[0] = mDragCell[1] = -1;
2284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
229d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            mDragOutlines[i] = new Rect(-1, -1, -1, -1);
230046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        }
231046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy
232046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // When dragging things around the home screens, we show a green outline of
233046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // where the item will land. The outlines gradually fade out, leaving a trail
234046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // behind the drag path.
235046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Set up all the animations that are used to implement this fading.
236046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime);
237472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        final float fromAlphaValue = 0;
238472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        final float toAlphaValue = (float)res.getInteger(R.integer.config_dragOutlineMaxAlpha);
2394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2408e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy        Arrays.fill(mDragOutlineAlphas, fromAlphaValue);
2414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2424be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlineAnims.length; i++) {
243046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            final InterruptibleInOutAnimator anim =
244f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                new InterruptibleInOutAnimator(this, duration, fromAlphaValue, toAlphaValue);
245ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy            anim.getAnimator().setInterpolator(mEaseOutInterpolator);
246046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            final int thisIndex = i;
247472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
248de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                public void onAnimationUpdate(ValueAnimator animation) {
2494be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    final Bitmap outline = (Bitmap)anim.getTag();
2504be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2514be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    // If an animation is started and then stopped very quickly, we can still
2524be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    // get spurious updates we've cleared the tag. Guard against this.
2534be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    if (outline == null) {
2543a9fcedbcd235372cd2ab64f825a0b5b3937f59eMichael Jurka                        @SuppressWarnings("all") // suppress dead code warning
2553a9fcedbcd235372cd2ab64f825a0b5b3937f59eMichael Jurka                        final boolean debug = false;
2563a9fcedbcd235372cd2ab64f825a0b5b3937f59eMichael Jurka                        if (debug) {
257fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                            Object val = animation.getAnimatedValue();
258fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                            Log.d(TAG, "anim " + thisIndex + " update: " + val +
259fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                                     ", isStopped " + anim.isStopped());
260fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                        }
2614be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        // Try to prevent it from continuing to run
2624be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        animation.cancel();
2634be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    } else {
264472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                        mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();
265d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        CellLayout.this.invalidate(mDragOutlines[thisIndex]);
2664be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    }
267de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                }
268de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            });
2694be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // The animation holds a reference to the drag outline bitmap as long is it's
2704be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // running. This way the bitmap can be GCed when the animations are complete.
271472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().addListener(new AnimatorListenerAdapter() {
2723c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                @Override
2734be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                public void onAnimationEnd(Animator animation) {
274472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                    if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {
2754be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        anim.setTag(null);
2764be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    }
2774be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                }
2784be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            });
2794be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineAnims[i] = anim;
280de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        }
281ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy
28218014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mBackgroundRect = new Rect();
283b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundRect = new Rect();
284bea15195346bab3c52b0156e92f2b71f0811b210Michael Jurka
285a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context);
2862374abfda3e53f84e005df8923170308e4df8c03Adam Cohen        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
2872374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                mCountX);
2882374abfda3e53f84e005df8923170308e4df8c03Adam Cohen
289a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        addView(mShortcutsAndWidgets);
29018014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka    }
29118014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka
292f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    static int widthInPortrait(Resources r, int numCells) {
293f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // We use this method from Workspace to figure out how many rows/columns Launcher should
294f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // have. We ignore the left/right padding on CellLayout because it turns out in our design
295f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // the padding extends outside the visible screen size, but it looked fine anyway.
296f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        int cellWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);
2974b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),
2984b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                r.getDimensionPixelSize(R.dimen.workspace_height_gap));
299f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3004b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        return  minGap * (numCells - 1) + cellWidth * numCells;
301f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    }
302f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
303f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    static int heightInLandscape(Resources r, int numCells) {
304f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // We use this method from Workspace to figure out how many rows/columns Launcher should
305f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // have. We ignore the left/right padding on CellLayout because it turns out in our design
306f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // the padding extends outside the visible screen size, but it looked fine anyway.
307f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        int cellHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height);
3084b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),
3094b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                r.getDimensionPixelSize(R.dimen.workspace_height_gap));
310f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3114b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        return minGap * (numCells - 1) + cellHeight * numCells;
312f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    }
313f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3142801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public void enableHardwareLayers() {
315ca99383daef92fed673de22126875cb485be494fMichael Jurka        mShortcutsAndWidgets.setLayerType(LAYER_TYPE_HARDWARE, sPaint);
316d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    }
317d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka
318d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    public void disableHardwareLayers() {
319ca99383daef92fed673de22126875cb485be494fMichael Jurka        mShortcutsAndWidgets.setLayerType(LAYER_TYPE_NONE, sPaint);
320d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    }
321d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka
322d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    public void buildHardwareLayer() {
323d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka        mShortcutsAndWidgets.buildLayer();
3242801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
3252801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
326307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    public float getChildrenScale() {
327307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        return mIsHotseat ? mHotseatScale : 1.0f;
328307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    }
329307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
3302801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public void setGridSize(int x, int y) {
3312801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mCountX = x;
3322801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mCountY = y;
3332801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mOccupied = new boolean[mCountX][mCountY];
334482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mTmpOccupied = new boolean[mCountX][mCountY];
3357fbec10b36818f100b631f3d73fe1ad5360975aaAdam Cohen        mTempRectStack.clear();
3362374abfda3e53f84e005df8923170308e4df8c03Adam Cohen        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
3372374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                mCountX);
33876fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen        requestLayout();
3392801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
3402801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
3412374abfda3e53f84e005df8923170308e4df8c03Adam Cohen    // Set whether or not to invert the layout horizontally if the layout is in RTL mode.
3422374abfda3e53f84e005df8923170308e4df8c03Adam Cohen    public void setInvertIfRtl(boolean invert) {
3432374abfda3e53f84e005df8923170308e4df8c03Adam Cohen        mShortcutsAndWidgets.setInvertIfRtl(invert);
3442374abfda3e53f84e005df8923170308e4df8c03Adam Cohen    }
3452374abfda3e53f84e005df8923170308e4df8c03Adam Cohen
34696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    private void invalidateBubbleTextView(BubbleTextView icon) {
34796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        final int padding = icon.getPressedOrFocusedBackgroundPadding();
3484b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        invalidate(icon.getLeft() + getPaddingLeft() - padding,
3494b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getTop() + getPaddingTop() - padding,
3504b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getRight() + getPaddingLeft() + padding,
3514b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getBottom() + getPaddingTop() + padding);
35296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    }
35396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
354b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    void setOverScrollAmount(float r, boolean left) {
355b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (left && mOverScrollForegroundDrawable != mOverScrollLeft) {
356b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable = mOverScrollLeft;
357b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        } else if (!left && mOverScrollForegroundDrawable != mOverScrollRight) {
358b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable = mOverScrollRight;
359b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
360b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
361b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundAlpha = (int) Math.round((r * 255));
362b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollForegroundDrawable.setAlpha(mForegroundAlpha);
363b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        invalidate();
364b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
365b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
36696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    void setPressedOrFocusedIcon(BubbleTextView icon) {
36796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
36896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // requires an expanded clip rect (due to the glow's blur radius)
36996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        BubbleTextView oldIcon = mPressedOrFocusedIcon;
37096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        mPressedOrFocusedIcon = icon;
37196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (oldIcon != null) {
37296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            invalidateBubbleTextView(oldIcon);
37396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
37496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (mPressedOrFocusedIcon != null) {
37596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            invalidateBubbleTextView(mPressedOrFocusedIcon);
37696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
37796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    }
37896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
37933945b21544bc98381df17726a3537c292d8c985Michael Jurka    void setIsDragOverlapping(boolean isDragOverlapping) {
38033945b21544bc98381df17726a3537c292d8c985Michael Jurka        if (mIsDragOverlapping != isDragOverlapping) {
38133945b21544bc98381df17726a3537c292d8c985Michael Jurka            mIsDragOverlapping = isDragOverlapping;
38233945b21544bc98381df17726a3537c292d8c985Michael Jurka            invalidate();
38333945b21544bc98381df17726a3537c292d8c985Michael Jurka        }
38433945b21544bc98381df17726a3537c292d8c985Michael Jurka    }
38533945b21544bc98381df17726a3537c292d8c985Michael Jurka
38633945b21544bc98381df17726a3537c292d8c985Michael Jurka    boolean getIsDragOverlapping() {
38733945b21544bc98381df17726a3537c292d8c985Michael Jurka        return mIsDragOverlapping;
38833945b21544bc98381df17726a3537c292d8c985Michael Jurka    }
38933945b21544bc98381df17726a3537c292d8c985Michael Jurka
390ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected void setOverscrollTransformsDirty(boolean dirty) {
391ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        mScrollingTransformsDirty = dirty;
392ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    }
393ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
394ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected void resetOverscrollTransforms() {
395ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        if (mScrollingTransformsDirty) {
396ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setOverscrollTransformsDirty(false);
397ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setTranslationX(0);
398ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setRotationY(0);
399ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            // It doesn't matter if we pass true or false here, the important thing is that we
400ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            // pass 0, which results in the overscroll drawable not being drawn any more.
401ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setOverScrollAmount(0, false);
402ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setPivotX(getMeasuredWidth() / 2);
403ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setPivotY(getMeasuredHeight() / 2);
404ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        }
405ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    }
406ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
407307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    public void scaleRect(Rect r, float scale) {
408307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        if (scale != 1.0f) {
409307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.left = (int) (r.left * scale + 0.5f);
410307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.top = (int) (r.top * scale + 0.5f);
411307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.right = (int) (r.right * scale + 0.5f);
412307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.bottom = (int) (r.bottom * scale + 0.5f);
413307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        }
414307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    }
415307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
416307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    Rect temp = new Rect();
417307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    void scaleRectAboutCenter(Rect in, Rect out, float scale) {
418307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        int cx = in.centerX();
419307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        int cy = in.centerY();
420307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        out.set(in);
421307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        out.offset(-cx, -cy);
422307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        scaleRect(out, scale);
423307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        out.offset(cx, cy);
424307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    }
425307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
426a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    @Override
4271262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onDraw(Canvas canvas) {
4283e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
4293e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
4303e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're small, we are either drawn normally or in the "accepts drops" state (during
4313e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a drag). However, we also drag the mini hover background *over* one of those two
4323e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // backgrounds
433b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        if (mBackgroundAlpha > 0.0f) {
434f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            Drawable bg;
43533945b21544bc98381df17726a3537c292d8c985Michael Jurka
43633945b21544bc98381df17726a3537c292d8c985Michael Jurka            if (mIsDragOverlapping) {
43733945b21544bc98381df17726a3537c292d8c985Michael Jurka                // In the mini case, we draw the active_glow bg *over* the active background
438bdf78559f223ac11e01e3311edd5a48a80383e1eMichael Jurka                bg = mActiveGlowBackground;
439f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            } else {
440bdf78559f223ac11e01e3311edd5a48a80383e1eMichael Jurka                bg = mNormalBackground;
441f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            }
44233945b21544bc98381df17726a3537c292d8c985Michael Jurka
44333945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
44433945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.setBounds(mBackgroundRect);
44533945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.draw(canvas);
446a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
44731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4488e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy        final Paint paint = mDragOutlinePaint;
4494be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
450472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            final float alpha = mDragOutlineAlphas[i];
4514be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (alpha > 0) {
452d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                final Rect r = mDragOutlines[i];
453307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                scaleRectAboutCenter(r, temp, getChildrenScale());
4544be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
455472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                paint.setAlpha((int)(alpha + .5f));
456307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                canvas.drawBitmap(b, null, temp, paint);
457150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            }
4586569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
45996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
46096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
46196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // requires an expanded clip rect (due to the glow's blur radius)
46296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (mPressedOrFocusedIcon != null) {
46396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            final int padding = mPressedOrFocusedIcon.getPressedOrFocusedBackgroundPadding();
46496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            final Bitmap b = mPressedOrFocusedIcon.getPressedOrFocusedBackground();
46596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            if (b != null) {
46696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy                canvas.drawBitmap(b,
4674b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                        mPressedOrFocusedIcon.getLeft() + getPaddingLeft() - padding,
4684b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                        mPressedOrFocusedIcon.getTop() + getPaddingTop() - padding,
46996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy                        null);
47096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            }
47196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
47269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
473482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (DEBUG_VISUALIZE_OCCUPIED) {
474482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int[] pt = new int[2];
475482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            ColorDrawable cd = new ColorDrawable(Color.RED);
476e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            cd.setBounds(0, 0,  mCellWidth, mCellHeight);
477482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int i = 0; i < mCountX; i++) {
478482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                for (int j = 0; j < mCountY; j++) {
479482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    if (mOccupied[i][j]) {
480482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        cellToPoint(i, j, pt);
481482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.save();
482482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.translate(pt[0], pt[1]);
483482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        cd.draw(canvas);
484482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.restore();
485482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    }
486482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
487482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
488482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
489482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
490850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn        int previewOffset = FolderRingAnimator.sPreviewSize;
491850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn
49269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        // The folder outer / inner ring image(s)
49369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        for (int i = 0; i < mFolderOuterRings.size(); i++) {
49469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            FolderRingAnimator fra = mFolderOuterRings.get(i);
49569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
49669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            // Draw outer ring
49769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            Drawable d = FolderRingAnimator.sSharedOuterRingDrawable;
49869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int width = (int) fra.getOuterRingSize();
49969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int height = width;
50069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
50169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
50269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int centerX = mTempLocation[0] + mCellWidth / 2;
503850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            int centerY = mTempLocation[1] + previewOffset / 2;
50469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
50569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.save();
50669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.translate(centerX - width / 2, centerY - height / 2);
50769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.setBounds(0, 0, width, height);
50869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.draw(canvas);
50969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.restore();
51069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
51169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            // Draw inner ring
51269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d = FolderRingAnimator.sSharedInnerRingDrawable;
51369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            width = (int) fra.getInnerRingSize();
51469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            height = width;
51569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
51669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
51769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            centerX = mTempLocation[0] + mCellWidth / 2;
518850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            centerY = mTempLocation[1] + previewOffset / 2;
51969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.save();
52069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.translate(centerX - width / 2, centerY - width / 2);
52169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.setBounds(0, 0, width, height);
52269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.draw(canvas);
52369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.restore();
52469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        }
525c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
526c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        if (mFolderLeaveBehindCell[0] >= 0 && mFolderLeaveBehindCell[1] >= 0) {
527c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            Drawable d = FolderIcon.sSharedFolderLeaveBehind;
528c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int width = d.getIntrinsicWidth();
529c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int height = d.getIntrinsicHeight();
530c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
531c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            cellToPoint(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1], mTempLocation);
532c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int centerX = mTempLocation[0] + mCellWidth / 2;
533850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            int centerY = mTempLocation[1] + previewOffset / 2;
534c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
535c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.save();
536c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.translate(centerX - width / 2, centerY - width / 2);
537c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            d.setBounds(0, 0, width, height);
538c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            d.draw(canvas);
539c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.restore();
540c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        }
54169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    }
54269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
543b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    @Override
544b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected void dispatchDraw(Canvas canvas) {
545b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        super.dispatchDraw(canvas);
546b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (mForegroundAlpha > 0) {
547b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable.setBounds(mForegroundRect);
548b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            Paint p = ((NinePatchDrawable) mOverScrollForegroundDrawable).getPaint();
5498a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy            p.setXfermode(sAddBlendMode);
550b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable.draw(canvas);
551b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            p.setXfermode(null);
552b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
553b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
554b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
55569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    public void showFolderAccept(FolderRingAnimator fra) {
55669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        mFolderOuterRings.add(fra);
55769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    }
55869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
55969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    public void hideFolderAccept(FolderRingAnimator fra) {
56069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        if (mFolderOuterRings.contains(fra)) {
56169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            mFolderOuterRings.remove(fra);
56269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        }
56369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        invalidate();
5646569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
5656569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
566c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    public void setFolderLeaveBehindCell(int x, int y) {
567c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[0] = x;
568c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[1] = y;
569c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        invalidate();
570c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    }
571c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
572c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    public void clearFolderLeaveBehind() {
573c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[0] = -1;
574c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[1] = -1;
575c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        invalidate();
576c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    }
577c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
5786569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    @Override
579e6235dd225404239b55c459245543f3302326112Michael Jurka    public boolean shouldDelayChildPressedState() {
580e6235dd225404239b55c459245543f3302326112Michael Jurka        return false;
581e6235dd225404239b55c459245543f3302326112Michael Jurka    }
582e6235dd225404239b55c459245543f3302326112Michael Jurka
5831462de39f01cec0ba785386532719cb0207dd827Adam Cohen    public void restoreInstanceState(SparseArray<Parcelable> states) {
5841462de39f01cec0ba785386532719cb0207dd827Adam Cohen        dispatchRestoreInstanceState(states);
5851462de39f01cec0ba785386532719cb0207dd827Adam Cohen    }
5861462de39f01cec0ba785386532719cb0207dd827Adam Cohen
587e6235dd225404239b55c459245543f3302326112Michael Jurka    @Override
58883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    public void cancelLongPress() {
58983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        super.cancelLongPress();
59083f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
59183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        // Cancel long press for all children
59283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        final int count = getChildCount();
59383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        for (int i = 0; i < count; i++) {
59483f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            final View child = getChildAt(i);
59583f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            child.cancelLongPress();
59683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        }
59783f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    }
59883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
599dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void setOnInterceptTouchListener(View.OnTouchListener listener) {
600dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mInterceptTouchListener = listener;
601dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
602dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
60331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountX() {
604d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountX;
60531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
60631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
60731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountY() {
608d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountY;
60931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
61031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6110dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    public void setIsHotseat(boolean isHotseat) {
6120dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        mIsHotseat = isHotseat;
6130dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    }
6140dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
6150dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
616850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            boolean markCells) {
617aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final LayoutParams lp = params;
618aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
619de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn        // Hotseat icons - remove text
6200dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        if (child instanceof BubbleTextView) {
6210dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn            BubbleTextView bubbleChild = (BubbleTextView) child;
6220dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
623de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            Resources res = getResources();
624de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            if (mIsHotseat) {
625de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn                bubbleChild.setTextColor(res.getColor(android.R.color.transparent));
626de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            } else {
627de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn                bubbleChild.setTextColor(res.getColor(R.color.workspace_icon_text_color));
6280dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn            }
6290dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        }
6300dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
631307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        child.setScaleX(getChildrenScale());
632307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        child.setScaleY(getChildrenScale());
633307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
63431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Generate an id for each view, this assumes we have at most 256x256 cells
63531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // per workspace screen
636d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
637aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // If the horizontal or vertical span is set to -1, it is taken to
638aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // mean that it spans the extent of the CellLayout
639d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
640d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
641aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
642aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            child.setId(childId);
64331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
644a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.addView(child, index, lp);
645dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
646f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka            if (markCells) markCellsAsOccupiedForView(child);
6470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
648aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return true;
649aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
650aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return false;
65131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
6523e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
65331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
6540280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViews() {
6550280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        clearOccupiedCells();
656a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeAllViews();
6570280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6580280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViewsInLayout() {
661a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (mShortcutsAndWidgets.getChildCount() > 0) {
6627cfc2825c3a1029f962d2fc387ae2eaa85b51798Michael Jurka            clearOccupiedCells();
663a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.removeAllViewsInLayout();
6647cfc2825c3a1029f962d2fc387ae2eaa85b51798Michael Jurka        }
6650280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
667f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    public void removeViewWithoutMarkingCells(View view) {
668a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeView(view);
669f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    }
670f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka
6710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeView(View view) {
6730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
674a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeView(view);
6750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewAt(int index) {
679a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(index));
680a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewAt(index);
6810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewInLayout(View view) {
6850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
686a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewInLayout(view);
6870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViews(int start, int count) {
6910280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
692a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
6930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
694a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViews(start, count);
6950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewsInLayout(int start, int count) {
6990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
700a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
7010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
702a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewsInLayout(start, count);
703abded66084680bb31cc7ea403c88f44f79a3c884Michael Jurka    }
704abded66084680bb31cc7ea403c88f44f79a3c884Michael Jurka
70531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
70631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onAttachedToWindow() {
70731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onAttachedToWindow();
70831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
70931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
71031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
711af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public void setTagToCellInfoForPoint(int touchX, int touchY) {
71231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo cellInfo = mCellInfo;
713eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung        Rect frame = mRect;
7148b805b17158886035b38261eb611d8641701ae43Michael Jurka        final int x = touchX + getScrollX();
7158b805b17158886035b38261eb611d8641701ae43Michael Jurka        final int y = touchY + getScrollY();
716a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        final int count = mShortcutsAndWidgets.getChildCount();
71731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
718af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        boolean found = false;
719af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        for (int i = count - 1; i >= 0; i--) {
720a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            final View child = mShortcutsAndWidgets.getChildAt(i);
721d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
722af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
7231b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen            if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) &&
7241b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen                    lp.isLockedToGrid) {
725af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                child.getHitRect(frame);
7260be025d64c1f84138fe430a58875886e66aae767Winson Chung
727eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                float scale = child.getScaleX();
728eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                frame = new Rect(child.getLeft(), child.getTop(), child.getRight(),
729eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                        child.getBottom());
7300be025d64c1f84138fe430a58875886e66aae767Winson Chung                // The child hit rect is relative to the CellLayoutChildren parent, so we need to
7310be025d64c1f84138fe430a58875886e66aae767Winson Chung                // offset that by this CellLayout's padding to test an (x,y) point that is relative
7320be025d64c1f84138fe430a58875886e66aae767Winson Chung                // to this view.
7338b805b17158886035b38261eb611d8641701ae43Michael Jurka                frame.offset(getPaddingLeft(), getPaddingTop());
734eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                frame.inset((int) (frame.width() * (1f - scale) / 2),
735eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                        (int) (frame.height() * (1f - scale) / 2));
7360be025d64c1f84138fe430a58875886e66aae767Winson Chung
737af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                if (frame.contains(x, y)) {
738af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cell = child;
739af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellX = lp.cellX;
740af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellY = lp.cellY;
741af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanX = lp.cellHSpan;
742af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanY = lp.cellVSpan;
743af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    found = true;
744af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    break;
74531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
74631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
747af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
748aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
749d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka        mLastDownOnOccupiedCell = found;
750d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka
751af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (!found) {
7520be025d64c1f84138fe430a58875886e66aae767Winson Chung            final int cellXY[] = mTmpXY;
753af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            pointToCellExact(x, y, cellXY);
75431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
755af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cell = null;
756af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellX = cellXY[0];
757af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellY = cellXY[1];
758af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanX = 1;
759af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanY = 1;
760af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
761af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        setTag(cellInfo);
762af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    }
76331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
764af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    @Override
765af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public boolean onInterceptTouchEvent(MotionEvent ev) {
766c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // First we clear the tag to ensure that on every touch down we start with a fresh slate,
767c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // even in the case where we return early. Not clearing here was causing bugs whereby on
768c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // long-press we'd end up picking up an item from a previous drag operation.
769c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        final int action = ev.getAction();
770c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
771c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        if (action == MotionEvent.ACTION_DOWN) {
772c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen            clearTagCellInfo();
773c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        }
774c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
775dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
776dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            return true;
777dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
77831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
779af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (action == MotionEvent.ACTION_DOWN) {
780af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
78131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
782eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung
78331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
78431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
78531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
786c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen    private void clearTagCellInfo() {
787c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        final CellInfo cellInfo = mCellInfo;
788c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cell = null;
789c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cellX = -1;
790c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cellY = -1;
791c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.spanX = 0;
792c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.spanY = 0;
793c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        setTag(cellInfo);
794c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen    }
795c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
79631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellInfo getTag() {
7970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return (CellInfo) super.getTag();
79831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
79931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
8006569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
801aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Given a point, return the cell that strictly encloses that point
80231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
80331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
80431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
80531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
80631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellExact(int x, int y, int[] result) {
8074b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
8084b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
80931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
81031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
81131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
81231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
813d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xAxis = mCountX;
814d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yAxis = mCountY;
81531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
81631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] < 0) result[0] = 0;
81731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] >= xAxis) result[0] = xAxis - 1;
81831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] < 0) result[1] = 0;
81931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] >= yAxis) result[1] = yAxis - 1;
82031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
821aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
82231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
82331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that most closely encloses that point
82431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
82531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
82631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
82731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
82831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellRounded(int x, int y, int[] result) {
82931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
83031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
83131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
83231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
83331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a cell coordinate, return the point that represents the upper left corner of that cell
834aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
835aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellX X coordinate of the cell
83631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of the cell
837aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
83831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the point
83931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
84031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void cellToPoint(int cellX, int cellY, int[] result) {
8414b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
8424b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
84331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
84431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
84531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
84631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
84731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
848e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    /**
849482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Given a cell coordinate, return the point that represents the center of the cell
850e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     *
851e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param cellX X coordinate of the cell
852e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param cellY Y coordinate of the cell
853e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     *
854e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param result Array of 2 ints to hold the x and y coordinate of the point
855e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     */
856e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    void cellToCenterPoint(int cellX, int cellY, int[] result) {
85747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        regionToCenterPoint(cellX, cellY, 1, 1, result);
85847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
85947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
86047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    /**
86147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * Given a cell coordinate and span return the point that represents the center of the regio
86247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *
86347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellX X coordinate of the cell
86447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellY Y coordinate of the cell
86547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *
86647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param result Array of 2 ints to hold the x and y coordinate of the point
86747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     */
86847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
8694b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
8704b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
87147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) +
87247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                (spanX * mCellWidth + (spanX - 1) * mWidthGap) / 2;
87347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) +
87447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                (spanY * mCellHeight + (spanY - 1) * mHeightGap) / 2;
875e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    }
876e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen
87719f3792523fe2d55ea791a9286398a6120920690Adam Cohen     /**
87819f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * Given a cell coordinate and span fills out a corresponding pixel rect
87919f3792523fe2d55ea791a9286398a6120920690Adam Cohen     *
88019f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param cellX X coordinate of the cell
88119f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param cellY Y coordinate of the cell
88219f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param result Rect in which to write the result
88319f3792523fe2d55ea791a9286398a6120920690Adam Cohen     */
88419f3792523fe2d55ea791a9286398a6120920690Adam Cohen     void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) {
88519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int hStartPadding = getPaddingLeft();
88619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int vStartPadding = getPaddingTop();
88719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int left = hStartPadding + cellX * (mCellWidth + mWidthGap);
88819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int top = vStartPadding + cellY * (mCellHeight + mHeightGap);
88919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        result.set(left, top, left + (spanX * mCellWidth + (spanX - 1) * mWidthGap),
89019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                top + (spanY * mCellHeight + (spanY - 1) * mHeightGap));
89119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
89219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
893482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public float getDistanceFromCell(float x, float y, int[] cell) {
894482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        cellToCenterPoint(cell[0], cell[1], mTmpPoint);
895482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        float distance = (float) Math.sqrt( Math.pow(x - mTmpPoint[0], 2) +
896482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                Math.pow(y - mTmpPoint[1], 2));
897482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return distance;
898482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
899482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
90084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellWidth() {
90184f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellWidth;
90284f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
90384f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
90484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellHeight() {
90584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellHeight;
90684f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
90784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
908d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    int getWidthGap() {
909d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        return mWidthGap;
910d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    }
911d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
912d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    int getHeightGap() {
913d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        return mHeightGap;
914d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    }
915d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
9167f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen    Rect getContentRect(Rect r) {
9177f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        if (r == null) {
9187f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            r = new Rect();
9197f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
9207f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        int left = getPaddingLeft();
9217f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        int top = getPaddingTop();
9228b805b17158886035b38261eb611d8641701ae43Michael Jurka        int right = left + getWidth() - getPaddingLeft() - getPaddingRight();
9238b805b17158886035b38261eb611d8641701ae43Michael Jurka        int bottom = top + getHeight() - getPaddingTop() - getPaddingBottom();
9247f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        r.set(left, top, right, bottom);
9257f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        return r;
9267f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen    }
9277f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
928a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static void getMetrics(Rect metrics, Resources res, int measureWidth, int measureHeight,
929a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int countX, int countY, int orientation) {
930a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int numWidthGaps = countX - 1;
931a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int numHeightGaps = countY - 1;
932f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
933f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int widthGap;
934f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int heightGap;
935f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int cellWidth;
936f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int cellHeight;
937f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingLeft;
938f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingRight;
939f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingTop;
940f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingBottom;
941f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
942a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int maxGap = res.getDimensionPixelSize(R.dimen.workspace_max_gap);
943f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        if (orientation == LANDSCAPE) {
944f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellWidth = res.getDimensionPixelSize(R.dimen.workspace_cell_width_land);
945f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellHeight = res.getDimensionPixelSize(R.dimen.workspace_cell_height_land);
946f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            widthGap = res.getDimensionPixelSize(R.dimen.workspace_width_gap_land);
947f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            heightGap = res.getDimensionPixelSize(R.dimen.workspace_height_gap_land);
948f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingLeft = res.getDimensionPixelSize(R.dimen.cell_layout_left_padding_land);
949f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingRight = res.getDimensionPixelSize(R.dimen.cell_layout_right_padding_land);
950f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingTop = res.getDimensionPixelSize(R.dimen.cell_layout_top_padding_land);
951f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingBottom = res.getDimensionPixelSize(R.dimen.cell_layout_bottom_padding_land);
952f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        } else {
953f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            // PORTRAIT
954f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellWidth = res.getDimensionPixelSize(R.dimen.workspace_cell_width_port);
955f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellHeight = res.getDimensionPixelSize(R.dimen.workspace_cell_height_port);
956f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            widthGap = res.getDimensionPixelSize(R.dimen.workspace_width_gap_port);
957f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            heightGap = res.getDimensionPixelSize(R.dimen.workspace_height_gap_port);
958f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingLeft = res.getDimensionPixelSize(R.dimen.cell_layout_left_padding_port);
959f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingRight = res.getDimensionPixelSize(R.dimen.cell_layout_right_padding_port);
960f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingTop = res.getDimensionPixelSize(R.dimen.cell_layout_top_padding_port);
961f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingBottom = res.getDimensionPixelSize(R.dimen.cell_layout_bottom_padding_port);
962f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        }
963f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
964f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        if (widthGap < 0 || heightGap < 0) {
965f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int hSpace = measureWidth - paddingLeft - paddingRight;
966f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int vSpace = measureHeight - paddingTop - paddingBottom;
967a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int hFreeSpace = hSpace - (countX * cellWidth);
968a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int vFreeSpace = vSpace - (countY * cellHeight);
969a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            widthGap = Math.min(maxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
970a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            heightGap = Math.min(maxGap, numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
971f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        }
972f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        metrics.set(cellWidth, cellHeight, widthGap, heightGap);
973f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen    }
974f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
97531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
97631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
97731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
978aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
979aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
98031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
98131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
982aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
98331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
98431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
98531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
98631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
987d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numWidthGaps = mCountX - 1;
988d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numHeightGaps = mCountY - 1;
989d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
990234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
991dd13e3d0f9925b7bb80c37e21d039aab4fa7e7a1Michael Jurka            int hSpace = widthSpecSize - getPaddingLeft() - getPaddingRight();
992dd13e3d0f9925b7bb80c37e21d039aab4fa7e7a1Michael Jurka            int vSpace = heightSpecSize - getPaddingTop() - getPaddingBottom();
993f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int hFreeSpace = hSpace - (mCountX * mCellWidth);
994f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int vFreeSpace = vSpace - (mCountY * mCellHeight);
9954b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung            mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
9964b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung            mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
9972374abfda3e53f84e005df8923170308e4df8c03Adam Cohen            mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
9982374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                    mCountX);
999234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        } else {
1000234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen            mWidthGap = mOriginalWidthGap;
1001234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen            mHeightGap = mOriginalHeightGap;
1002ece7f5b3b55cab646941123e03589241a61678e2Winson Chung        }
10035f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
10048c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY
10058c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int newWidth = widthSpecSize;
10068c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int newHeight = heightSpecSize;
10078c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        if (widthSpecMode == MeasureSpec.AT_MOST) {
10088b805b17158886035b38261eb611d8641701ae43Michael Jurka            newWidth = getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
10098c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka                ((mCountX - 1) * mWidthGap);
10108b805b17158886035b38261eb611d8641701ae43Michael Jurka            newHeight = getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
10118c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka                ((mCountY - 1) * mHeightGap);
10128c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            setMeasuredDimension(newWidth, newHeight);
10138c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        }
101431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10158c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int count = getChildCount();
101631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
101731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
10188b805b17158886035b38261eb611d8641701ae43Michael Jurka            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth - getPaddingLeft() -
10198b805b17158886035b38261eb611d8641701ae43Michael Jurka                    getPaddingRight(), MeasureSpec.EXACTLY);
10208b805b17158886035b38261eb611d8641701ae43Michael Jurka            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight - getPaddingTop() -
10218b805b17158886035b38261eb611d8641701ae43Michael Jurka                    getPaddingBottom(), MeasureSpec.EXACTLY);
102231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
102331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
10248c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        setMeasuredDimension(newWidth, newHeight);
102531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
102631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
102731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
102828750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int l, int t, int r, int b) {
102931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
103031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
10318c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            View child = getChildAt(i);
10328b805b17158886035b38261eb611d8641701ae43Michael Jurka            child.layout(getPaddingLeft(), getPaddingTop(),
10338b805b17158886035b38261eb611d8641701ae43Michael Jurka                    r - l - getPaddingRight(), b - t - getPaddingBottom());
103431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
103531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
103631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
103731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
1038dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1039dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super.onSizeChanged(w, h, oldw, oldh);
104018014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mBackgroundRect.set(0, 0, w, h);
1041b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundRect.set(mForegroundPadding, mForegroundPadding,
1042215b416c1f7cf0234b777155b823637a0902739fAdam Cohen                w - mForegroundPadding, h - mForegroundPadding);
1043dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1044dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1045dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @Override
104631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
1047a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setChildrenDrawingCacheEnabled(enabled);
104831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
104931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
105031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
105131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
1052a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setChildrenDrawnWithCacheEnabled(enabled);
105331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
105431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10555f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public float getBackgroundAlpha() {
10565f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        return mBackgroundAlpha;
1057dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1058dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
10591b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    public void setBackgroundAlphaMultiplier(float multiplier) {
1060a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka        if (mBackgroundAlphaMultiplier != multiplier) {
1061a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka            mBackgroundAlphaMultiplier = multiplier;
1062a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka            invalidate();
1063a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka        }
10641b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    }
10651b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen
1066ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen    public float getBackgroundAlphaMultiplier() {
1067ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen        return mBackgroundAlphaMultiplier;
1068ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen    }
1069ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen
10705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public void setBackgroundAlpha(float alpha) {
1071afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka        if (mBackgroundAlpha != alpha) {
1072afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka            mBackgroundAlpha = alpha;
1073afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka            invalidate();
1074afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka        }
1075dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1076dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1077a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    public void setShortcutAndWidgetAlpha(float alpha) {
10780142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int childCount = getChildCount();
10790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        for (int i = 0; i < childCount; i++) {
1080dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            getChildAt(i).setAlpha(alpha);
1081dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
1082dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1083dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1084a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
1085a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (getChildCount() > 0) {
1086a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            return (ShortcutAndWidgetContainer) getChildAt(0);
1087a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        }
1088a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        return null;
1089a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    }
1090a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka
1091440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    public View getChildAt(int x, int y) {
1092a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        return mShortcutsAndWidgets.getChildAt(x, y);
1093440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
1094440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
109576fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen    public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
1096482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int delay, boolean permanent, boolean adjustOccupied) {
1097a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
1098482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean[][] occupied = mOccupied;
1099482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!permanent) {
1100482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            occupied = mTmpOccupied;
1101482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1102482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
110319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (clc.indexOfChild(child) != -1) {
1104bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1105bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            final ItemInfo info = (ItemInfo) child.getTag();
1106bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1107bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            // We cancel any existing animations
1108bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            if (mReorderAnimators.containsKey(lp)) {
1109bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                mReorderAnimators.get(lp).cancel();
1110bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                mReorderAnimators.remove(lp);
1111bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            }
1112bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1113482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int oldX = lp.x;
1114482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int oldY = lp.y;
1115482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (adjustOccupied) {
1116482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[lp.cellX][lp.cellY] = false;
1117482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[cellX][cellY] = true;
1118482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1119bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            lp.isLockedToGrid = true;
1120482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (permanent) {
1121482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.cellX = info.cellX = cellX;
1122482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.cellY = info.cellY = cellY;
1123482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else {
1124482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.tmpCellX = cellX;
1125482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.tmpCellY = cellY;
1126482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1127bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            clc.setupLp(lp);
1128bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            lp.isLockedToGrid = false;
1129482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int newX = lp.x;
1130482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int newY = lp.y;
1131bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
113276fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            lp.x = oldX;
113376fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            lp.y = oldY;
113476fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen
1135482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // Exit early if we're not actually moving the view
1136482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (oldX == newX && oldY == newY) {
1137482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.isLockedToGrid = true;
1138482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return true;
1139482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1140482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1141f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
1142482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.setDuration(duration);
1143482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mReorderAnimators.put(lp, va);
1144482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1145482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.addUpdateListener(new AnimatorUpdateListener() {
1146482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                @Override
1147bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationUpdate(ValueAnimator animation) {
1148482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    float r = ((Float) animation.getAnimatedValue()).floatValue();
114919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    lp.x = (int) ((1 - r) * oldX + r * newX);
115019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    lp.y = (int) ((1 - r) * oldY + r * newY);
11516b8a02d63a5d9cab8209381993e37db6a6afb753Adam Cohen                    child.requestLayout();
1152bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1153bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            });
1154482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.addListener(new AnimatorListenerAdapter() {
1155bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                boolean cancelled = false;
1156bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationEnd(Animator animation) {
1157bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // If the animation was cancelled, it means that another animation
1158bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // has interrupted this one, and we don't want to lock the item into
1159bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // place just yet.
1160bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    if (!cancelled) {
1161bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                        lp.isLockedToGrid = true;
1162482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        child.requestLayout();
1163bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    }
1164bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    if (mReorderAnimators.containsKey(lp)) {
1165bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                        mReorderAnimators.remove(lp);
1166bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    }
1167bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1168bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationCancel(Animator animation) {
1169bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    cancelled = true;
1170bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1171bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            });
1172482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.setStartDelay(delay);
1173482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.start();
1174bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            return true;
1175bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen        }
1176bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen        return false;
1177bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen    }
1178bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
11796569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
11806569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Estimate where the top left cell of the dragged item will land if it is dropped.
11816569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     *
11826569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originX The X value of the top left corner of the item
11836569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originY The Y value of the top left corner of the item
11846569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanX The number of horizontal cells that the item spans
11856569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanY The number of vertical cells that the item spans
11866569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param result The estimated drop cell X and Y.
11876569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
11886569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
1189d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countX = mCountX;
1190d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countY = mCountY;
11916569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1192a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // pointToCellRounded takes the top left of a cell but will pad that with
1193a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // cellWidth/2 and cellHeight/2 when finding the matching cell
1194a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        pointToCellRounded(originX, originY, result);
11956569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
11966569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // If the item isn't fully on this screen, snap to the edges
11976569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int rightOverhang = result[0] + spanX - countX;
11986569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (rightOverhang > 0) {
11996569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[0] -= rightOverhang; // Snap to right
12006569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
12016569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[0] = Math.max(0, result[0]); // Snap to left
12026569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int bottomOverhang = result[1] + spanY - countY;
12036569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (bottomOverhang > 0) {
12046569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[1] -= bottomOverhang; // Snap to bottom
12056569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
12066569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[1] = Math.max(0, result[1]); // Snap to top
12076569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
12086569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1209482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY, int cellX,
1210482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int cellY, int spanX, int spanY, boolean resize, Point dragOffset, Rect dragRegion) {
121108ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        final int oldDragCellX = mDragCell[0];
121208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        final int oldDragCellY = mDragCell[1];
1213482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1214b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung        if (v != null && dragOffset == null) {
1215a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2));
1216a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        } else {
1217a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mDragCenter.set(originX, originY);
1218a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        }
12196569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
12202801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        if (dragOutline == null && v == null) {
12212801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen            return;
12222801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        }
12232801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
1224482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (cellX != oldDragCellX || cellY != oldDragCellY) {
1225482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mDragCell[0] = cellX;
1226482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mDragCell[1] = cellY;
12276569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Find the top left corner of the rect the object will occupy
1228de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int[] topLeft = mTmpPoint;
1229482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            cellToPoint(cellX, cellY, topLeft);
1230de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
12314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int left = topLeft[0];
12324be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int top = topLeft[1];
12336569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1234b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung            if (v != null && dragOffset == null) {
123599e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // When drawing the drag outline, it did not account for margin offsets
123699e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // added by the view's parent.
123799e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
123899e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                left += lp.leftMargin;
123999e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                top += lp.topMargin;
124099e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen
124199e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // Offsets due to the size difference between the View and the dragOutline.
124299e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // There is a size difference to account for the outer blur, which may lie
124399e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // outside the bounds of the view.
1244a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                top += (v.getHeight() - dragOutline.getHeight()) / 2;
1245ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                // We center about the x axis
1246ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1247ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                        - dragOutline.getWidth()) / 2;
12486639687cd67bab1aeef2a75e5c6bc458b20dc082Adam Cohen            } else {
1249b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                if (dragOffset != null && dragRegion != null) {
1250b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // Center the drag region *horizontally* in the cell and apply a drag
1251b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // outline offset
1252b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1253b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                             - dragRegion.width()) / 2;
1254b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    top += dragOffset.y;
1255b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                } else {
1256b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // Center the drag outline in the cell
1257b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1258b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                            - dragOutline.getWidth()) / 2;
1259b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
1260b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                            - dragOutline.getHeight()) / 2;
1261b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                }
1262a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            }
12634be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int oldIndex = mDragOutlineCurrent;
126408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[oldIndex].animateOut();
126508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
1266d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            Rect r = mDragOutlines[mDragOutlineCurrent];
1267d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
1268d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            if (resize) {
1269482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                cellToRect(cellX, cellY, spanX, spanY, r);
1270d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            }
1271150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
127208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
127308ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[mDragOutlineCurrent].animateIn();
12746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
12756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
12766569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1277e0310965022e7a1adb7ad489505d404186608689Adam Cohen    public void clearDragOutlines() {
1278e0310965022e7a1adb7ad489505d404186608689Adam Cohen        final int oldIndex = mDragOutlineCurrent;
1279e0310965022e7a1adb7ad489505d404186608689Adam Cohen        mDragOutlineAnims[oldIndex].animateOut();
1280d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        mDragCell[0] = mDragCell[1] = -1;
1281e0310965022e7a1adb7ad489505d404186608689Adam Cohen    }
1282e0310965022e7a1adb7ad489505d404186608689Adam Cohen
128331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
128470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Find a vacant area that will fit the given bounds nearest the requested
128570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * cell location. Uses Euclidean distance to score multiple vacant areas.
1286aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
128751afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelX The X location at which you want to search for a vacant area.
128851afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelY The Y location at which you want to search for a vacant area.
128970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanX Horizontal span of the object.
129070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanY Vertical span of the object.
1291de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * @param result Array in which to place the result, or null (in which case a new array will
1292de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     *        be allocated)
129370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @return The X, Y cell of a vacant area that can contain this object,
129470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *         nearest the requested location.
129531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
1296d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
1297d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int[] result) {
1298de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
12996a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    }
1300aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
13016a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    /**
13026a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * Find a vacant area that will fit the given bounds nearest the requested
13036a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * cell location. Uses Euclidean distance to score multiple vacant areas.
13046a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *
13056a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelX The X location at which you want to search for a vacant area.
13066a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelY The Y location at which you want to search for a vacant area.
1307d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
1308d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
1309d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
1310d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
1311d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1312d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *        be allocated)
1313d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1314d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
1315d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
1316d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
1317d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int spanY, int[] result, int[] resultSpan) {
1318d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        return findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null,
1319d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                result, resultSpan);
1320d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1321d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1322d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
1323d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1324d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
1325d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
1326d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
1327d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
13286a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanX Horizontal span of the object.
13296a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanY Vertical span of the object.
1330df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreOccupied If true, the result can be an occupied cell
1331df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1332df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *        be allocated)
13336a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @return The X, Y cell of a vacant area that can contain this object,
13346a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *         nearest the requested location.
13356a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     */
1336df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView,
1337df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            boolean ignoreOccupied, int[] result) {
1338d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY,
1339482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                spanX, spanY, ignoreView, ignoreOccupied, result, null, mOccupied);
1340d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1341d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1342d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private final Stack<Rect> mTempRectStack = new Stack<Rect>();
1343d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private void lazyInitTempRectStack() {
1344d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        if (mTempRectStack.isEmpty()) {
1345d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            for (int i = 0; i < mCountX * mCountY; i++) {
1346d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                mTempRectStack.push(new Rect());
1347d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            }
1348d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1349d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1350482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1351d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private void recycleTempRects(Stack<Rect> used) {
1352d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        while (!used.isEmpty()) {
1353d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            mTempRectStack.push(used.pop());
1354d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1355d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1356d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1357d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
1358d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1359d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
1360d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
1361d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
1362d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
1363d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
1364d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
1365d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
1366d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
1367d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param ignoreOccupied If true, the result can be an occupied cell
1368d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1369d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *        be allocated)
1370d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1371d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
1372d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
1373d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
1374482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            View ignoreView, boolean ignoreOccupied, int[] result, int[] resultSpan,
1375482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            boolean[][] occupied) {
1376d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        lazyInitTempRectStack();
1377c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
1378482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(ignoreView, occupied);
1379c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka
1380e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
1381e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // to the center of the item, but we are searching based on the top-left cell, so
1382e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // we translate the point over to correspond to the top-left.
1383e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
1384e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
1385e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen
138670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Keep track of best-scoring drop area
1387de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int[] bestXY = result != null ? result : new int[2];
138870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        double bestDistance = Double.MAX_VALUE;
1389d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        final Rect bestRect = new Rect(-1, -1, -1, -1);
1390d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        final Stack<Rect> validRegions = new Stack<Rect>();
1391aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1392de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countX = mCountX;
1393de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countY = mCountY;
1394de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
1395d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        if (minSpanX <= 0 || minSpanY <= 0 || spanX <= 0 || spanY <= 0 ||
1396d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                spanX < minSpanX || spanY < minSpanY) {
1397d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            return bestXY;
1398d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1399d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1400d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        for (int y = 0; y < countY - (minSpanY - 1); y++) {
1401c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            inner:
1402d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            for (int x = 0; x < countX - (minSpanX - 1); x++) {
1403d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                int ySize = -1;
1404d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                int xSize = -1;
1405df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                if (ignoreOccupied) {
1406d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // First, let's see if this thing fits anywhere
1407d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    for (int i = 0; i < minSpanX; i++) {
1408d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        for (int j = 0; j < minSpanY; j++) {
1409df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                            if (occupied[x + i][y + j]) {
1410df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                                continue inner;
1411df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                            }
1412c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        }
1413c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
1414d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    xSize = minSpanX;
1415d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    ySize = minSpanY;
1416d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1417d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // We know that the item will fit at _some_ acceptable size, now let's see
1418d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // how big we can make it. We'll alternate between incrementing x and y spans
1419d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // until we hit a limit.
1420d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean incX = true;
1421d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean hitMaxX = xSize >= spanX;
1422d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean hitMaxY = ySize >= spanY;
1423d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    while (!(hitMaxX && hitMaxY)) {
1424d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        if (incX && !hitMaxX) {
1425d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            for (int j = 0; j < ySize; j++) {
1426d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                if (x + xSize > countX -1 || occupied[x + xSize][y + j]) {
1427d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    // We can't move out horizontally
1428d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    hitMaxX = true;
1429d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                }
1430d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1431d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            if (!hitMaxX) {
1432d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                xSize++;
1433d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1434d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        } else if (!hitMaxY) {
1435d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            for (int i = 0; i < xSize; i++) {
1436d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                if (y + ySize > countY - 1 || occupied[x + i][y + ySize]) {
1437d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    // We can't move out vertically
1438d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    hitMaxY = true;
1439d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                }
1440d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1441d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            if (!hitMaxY) {
1442d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                ySize++;
1443d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1444d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        }
1445d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        hitMaxX |= xSize >= spanX;
1446d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        hitMaxY |= ySize >= spanY;
1447d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        incX = !incX;
1448d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1449d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    incX = true;
1450d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    hitMaxX = xSize >= spanX;
1451d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    hitMaxY = ySize >= spanY;
1452c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
14530be025d64c1f84138fe430a58875886e66aae767Winson Chung                final int[] cellXY = mTmpXY;
1454e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen                cellToCenterPoint(x, y, cellXY);
1455c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka
1456d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // We verify that the current rect is not a sub-rect of any of our previous
1457d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // candidates. In this case, the current rect is disqualified in favour of the
1458d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // containing rect.
1459d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                Rect currentRect = mTempRectStack.pop();
1460d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                currentRect.set(x, y, x + xSize, y + ySize);
1461d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                boolean contained = false;
1462d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                for (Rect r : validRegions) {
1463d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    if (r.contains(currentRect)) {
1464d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        contained = true;
1465d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        break;
1466d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1467d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                }
1468d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                validRegions.push(currentRect);
1469c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
1470c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        + Math.pow(cellXY[1] - pixelY, 2));
1471482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1472d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                if ((distance <= bestDistance && !contained) ||
1473d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        currentRect.contains(bestRect)) {
1474c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestDistance = distance;
1475c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[0] = x;
1476c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[1] = y;
1477d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    if (resultSpan != null) {
1478d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        resultSpan[0] = xSize;
1479d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        resultSpan[1] = ySize;
1480d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1481d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    bestRect.set(currentRect);
1482c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
148331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
148431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1485c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
1486482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(ignoreView, occupied);
148731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1488c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        // Return -1, -1 if no suitable location found
1489c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        if (bestDistance == Double.MAX_VALUE) {
1490c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen            bestXY[0] = -1;
1491c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen            bestXY[1] = -1;
149270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        }
1493d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        recycleTempRects(validRegions);
1494c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        return bestXY;
149531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1496aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1497482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     /**
1498482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1499482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * cell location, and will also weigh in a suggested direction vector of the
1500482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * desired location. This method computers distance based on unit grid distances,
1501482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * not pixel distances.
1502482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *
150347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellX The X cell nearest to which you want to search for a vacant area.
150447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellY The Y cell nearest which you want to search for a vacant area.
1505482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param spanX Horizontal span of the object.
1506482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param spanY Vertical span of the object.
150747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param direction The favored direction in which the views should move from x, y
150847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param exactDirectionOnly If this parameter is true, then only solutions where the direction
150947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *        matches exactly. Otherwise we find the best matching direction.
151047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param occoupied The array which represents which cells in the CellLayout are occupied
151147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param blockOccupied The array which represents which cells in the specified block (cellX,
151247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *        cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
1513482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1514482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *        be allocated)
1515482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1516482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *         nearest the requested location.
1517482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     */
1518482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
151947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            boolean[][] occupied, boolean blockOccupied[][], int[] result) {
1520482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Keep track of best-scoring drop area
1521482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int[] bestXY = result != null ? result : new int[2];
1522482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        float bestDistance = Float.MAX_VALUE;
1523482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int bestDirectionScore = Integer.MIN_VALUE;
1524482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1525482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int countX = mCountX;
1526482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int countY = mCountY;
1527482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1528482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int y = 0; y < countY - (spanY - 1); y++) {
1529482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            inner:
1530482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int x = 0; x < countX - (spanX - 1); x++) {
1531482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                // First, let's see if this thing fits anywhere
1532482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                for (int i = 0; i < spanX; i++) {
1533482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    for (int j = 0; j < spanY; j++) {
153447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
1535482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                            continue inner;
1536482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        }
1537482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    }
1538482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1539482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1540482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                float distance = (float)
1541482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
1542482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                int[] curDirection = mTmpPoint;
154347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                computeDirectionVector(x - cellX, y - cellY, curDirection);
154447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                // The direction score is just the dot product of the two candidate direction
154547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                // and that passed in.
1546482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                int curDirectionScore = direction[0] * curDirection[0] +
1547482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        direction[1] * curDirection[1];
154847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean exactDirectionOnly = false;
154947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean directionMatches = direction[0] == curDirection[0] &&
155047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        direction[0] == curDirection[0];
155147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                if ((directionMatches || !exactDirectionOnly) &&
155247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        Float.compare(distance,  bestDistance) < 0 || (Float.compare(distance,
1553482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
1554482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestDistance = distance;
1555482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestDirectionScore = curDirectionScore;
1556482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestXY[0] = x;
1557482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestXY[1] = y;
1558482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1559482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1560482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1561482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1562482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Return -1, -1 if no suitable location found
1563482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (bestDistance == Float.MAX_VALUE) {
1564482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            bestXY[0] = -1;
1565482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            bestXY[1] = -1;
1566482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1567482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return bestXY;
1568482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1569482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1570482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
15718baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int[] direction, ItemConfiguration currentState) {
15728baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        CellAndSpan c = currentState.map.get(v);
1573482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean success = false;
15748baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1575482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
1576482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
15778baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        findNearestArea(c.x, c.y, c.spanX, c.spanY, direction, mTmpOccupied, null, mTempLocation);
1578482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1579482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
15808baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            c.x = mTempLocation[0];
15818baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            c.y = mTempLocation[1];
1582482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            success = true;
1583482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
15848baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1585482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return success;
1586482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1587482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1588f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen    /**
1589f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * This helper class defines a cluster of views. It helps with defining complex edges
1590f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * of the cluster and determining how those edges interact with other views. The edges
1591f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * essentially define a fine-grained boundary around the cluster of views -- like a more
1592f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * precise version of a bounding box.
1593f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     */
1594f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen    private class ViewCluster {
1595f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int LEFT = 0;
1596f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int TOP = 1;
1597f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int RIGHT = 2;
1598f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int BOTTOM = 3;
1599f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1600f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ArrayList<View> views;
1601f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ItemConfiguration config;
1602f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        Rect boundingRect = new Rect();
1603f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1604f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] leftEdge = new int[mCountY];
1605f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] rightEdge = new int[mCountY];
1606f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] topEdge = new int[mCountX];
1607f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] bottomEdge = new int[mCountX];
1608f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean leftEdgeDirty, rightEdgeDirty, topEdgeDirty, bottomEdgeDirty, boundingRectDirty;
160947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1610f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        @SuppressWarnings("unchecked")
1611f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
1612f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            this.views = (ArrayList<View>) views.clone();
1613f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            this.config = config;
1614f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            resetEdges();
1615f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
161647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1617f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void resetEdges() {
1618f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (int i = 0; i < mCountX; i++) {
1619f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                topEdge[i] = -1;
1620f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                bottomEdge[i] = -1;
1621f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1622f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (int i = 0; i < mCountY; i++) {
1623f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                leftEdge[i] = -1;
1624f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                rightEdge[i] = -1;
1625f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1626f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            leftEdgeDirty = true;
1627f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            rightEdgeDirty = true;
1628f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            bottomEdgeDirty = true;
1629f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            topEdgeDirty = true;
1630f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            boundingRectDirty = true;
1631f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1632f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1633f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void computeEdge(int which, int[] edge) {
1634f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int count = views.size();
1635f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (int i = 0; i < count; i++) {
1636f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan cs = config.map.get(views.get(i));
1637f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                switch (which) {
1638f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case LEFT:
1639f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int left = cs.x;
1640f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
1641f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (left < edge[j] || edge[j] < 0) {
1642f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = left;
1643f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1644f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1645f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1646f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case RIGHT:
1647f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int right = cs.x + cs.spanX;
1648f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
1649f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (right > edge[j]) {
1650f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = right;
1651f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1652f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1653f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1654f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case TOP:
1655f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int top = cs.y;
1656f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
1657f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (top < edge[j] || edge[j] < 0) {
1658f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = top;
1659f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1660f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1661f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1662f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case BOTTOM:
1663f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int bottom = cs.y + cs.spanY;
1664f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
1665f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (bottom > edge[j]) {
1666f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = bottom;
1667f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1668f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1669f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1670f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1671f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
167247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
167347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1674f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean isViewTouchingEdge(View v, int whichEdge) {
1675f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            CellAndSpan cs = config.map.get(v);
167647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1677f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int[] edge = getEdge(whichEdge);
1678f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1679f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            switch (whichEdge) {
1680f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case LEFT:
1681f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
1682f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.x + cs.spanX) {
1683f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
168447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        }
168547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    }
1686f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1687f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case RIGHT:
1688f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
1689f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.x) {
1690f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
1691f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1692f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1693f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1694f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case TOP:
1695f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
1696f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.y + cs.spanY) {
1697f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
1698f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1699f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1700f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1701f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case BOTTOM:
1702f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
1703f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.y) {
1704f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
1705f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1706f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1707f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1708f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1709f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return false;
1710f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1711f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1712f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void shift(int whichEdge, int delta) {
1713f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: views) {
1714f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan c = config.map.get(v);
1715f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                switch (whichEdge) {
1716f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case LEFT:
1717f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.x -= delta;
1718f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1719f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case RIGHT:
1720f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.x += delta;
1721f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1722f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case TOP:
1723f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.y -= delta;
1724f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1725f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case BOTTOM:
1726f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    default:
1727f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.y += delta;
1728f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1729f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1730f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1731f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            resetEdges();
1732f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1733f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1734f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public void addView(View v) {
1735f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            views.add(v);
1736f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            resetEdges();
1737f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1738f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1739f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public Rect getBoundingRect() {
1740f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (boundingRectDirty) {
1741f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                boolean first = true;
1742f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                for (View v: views) {
1743f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    CellAndSpan c = config.map.get(v);
1744f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    if (first) {
1745f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        boundingRect.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
1746f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        first = false;
1747f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    } else {
1748a56dc1077e09faca984a59b861c5dc6364ae2f21Adam Cohen                        boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
1749a56dc1077e09faca984a59b861c5dc6364ae2f21Adam Cohen                    }
175047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
175147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
1752f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return boundingRect;
1753f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1754f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1755f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getEdge(int which) {
1756f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            switch (which) {
1757f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case LEFT:
1758f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getLeftEdge();
1759f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case RIGHT:
1760f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getRightEdge();
1761f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case TOP:
1762f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getTopEdge();
1763f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case BOTTOM:
1764f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                default:
1765f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getBottomEdge();
1766f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1767f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1768f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1769f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getLeftEdge() {
1770f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (leftEdgeDirty) {
1771f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(LEFT, leftEdge);
1772f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1773f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return leftEdge;
1774f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1775f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1776f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getRightEdge() {
1777f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (rightEdgeDirty) {
1778f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(RIGHT, rightEdge);
1779f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1780f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return rightEdge;
1781f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1782f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1783f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getTopEdge() {
1784f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (topEdgeDirty) {
1785f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(TOP, topEdge);
1786f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1787f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return topEdge;
1788f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1789f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1790f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getBottomEdge() {
1791f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (bottomEdgeDirty) {
1792f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(BOTTOM, bottomEdge);
1793f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1794f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return bottomEdge;
1795f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1796f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1797f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        PositionComparator comparator = new PositionComparator();
1798f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        class PositionComparator implements Comparator<View> {
1799f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int whichEdge = 0;
1800f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            public int compare(View left, View right) {
1801f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan l = config.map.get(left);
1802f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan r = config.map.get(right);
1803f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                switch (whichEdge) {
1804f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case LEFT:
1805f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return (r.x + r.spanX) - (l.x + l.spanX);
1806f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case RIGHT:
1807f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return l.x - r.x;
1808f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case TOP:
1809f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return (r.y + r.spanY) - (l.y + l.spanY);
1810f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case BOTTOM:
1811f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    default:
1812f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return l.y - r.y;
1813f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1814f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1815f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1816f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1817f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public void sortConfigurationForEdgePush(int edge) {
1818f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            comparator.whichEdge = edge;
1819f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            Collections.sort(config.sortedViews, comparator);
182047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
182147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
182247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1823f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen    private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
1824f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int[] direction, View dragView, ItemConfiguration currentState) {
1825f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1826f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ViewCluster cluster = new ViewCluster(views, currentState);
1827f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        Rect clusterRect = cluster.getBoundingRect();
1828f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int whichEdge;
1829f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int pushDistance;
1830f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean fail = false;
1831e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
1832f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Determine the edge of the cluster that will be leading the push and how far
1833f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // the cluster must be shifted.
1834f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (direction[0] < 0) {
1835f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.LEFT;
1836f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
1837e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen        } else if (direction[0] > 0) {
1838f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.RIGHT;
1839f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
1840f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        } else if (direction[1] < 0) {
1841f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.TOP;
1842f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
1843f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        } else {
1844f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.BOTTOM;
1845f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
1846f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1847f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1848f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Break early for invalid push distance.
1849f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (pushDistance <= 0) {
1850f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return false;
1851f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1852f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1853f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Mark the occupied state as false for the group of views we want to move.
1854f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
1855f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            CellAndSpan c = currentState.map.get(v);
1856f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1857f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1858f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1859f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // We save the current configuration -- if we fail to find a solution we will revert
1860f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // to the initial state. The process of finding a solution modifies the configuration
1861f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // in place, hence the need for revert in the failure case.
1862f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        currentState.save();
1863f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1864f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // The pushing algorithm is simplified by considering the views in the order in which
1865f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // they would be pushed by the cluster. For example, if the cluster is leading with its
1866f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // left edge, we consider sort the views by their right edge, from right to left.
1867f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        cluster.sortConfigurationForEdgePush(whichEdge);
1868f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1869f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        while (pushDistance > 0 && !fail) {
1870f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: currentState.sortedViews) {
1871f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                // For each view that isn't in the cluster, we see if the leading edge of the
1872f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                // cluster is contacting the edge of that view. If so, we add that view to the
1873f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                // cluster.
1874f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                if (!cluster.views.contains(v) && v != dragView) {
1875f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    if (cluster.isViewTouchingEdge(v, whichEdge)) {
1876f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        LayoutParams lp = (LayoutParams) v.getLayoutParams();
1877f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (!lp.canReorder) {
1878f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            // The push solution includes the all apps button, this is not viable.
1879f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            fail = true;
1880f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            break;
1881f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1882f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        cluster.addView(v);
1883f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        CellAndSpan c = currentState.map.get(v);
1884f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1885f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        // Adding view to cluster, mark it as not occupied.
1886f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1887f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1888f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1889f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1890f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance--;
1891f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1892f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // The cluster has been completed, now we move the whole thing over in the appropriate
1893f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // direction.
1894f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            cluster.shift(whichEdge, 1);
1895e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen        }
1896e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
1897f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean foundSolution = false;
1898f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        clusterRect = cluster.getBoundingRect();
1899f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1900f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Due to the nature of the algorithm, the only check required to verify a valid solution
1901f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // is to ensure that completed shifted cluster lies completely within the cell layout.
1902f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
1903f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                clusterRect.bottom <= mCountY) {
1904f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            foundSolution = true;
1905f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        } else {
1906f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            currentState.restore();
1907f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1908e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
1909f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // In either case, we set the occupied array as marked for the location of the views
1910f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: cluster.views) {
1911f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            CellAndSpan c = currentState.map.get(v);
1912f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1913e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen        }
1914f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1915f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        return foundSolution;
1916e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen    }
1917e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
19188baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
1919f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int[] direction, View dragView, ItemConfiguration currentState) {
192047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (views.size() == 0) return true;
192147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
192247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean success = false;
192347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        Rect boundingRect = null;
19248baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We construct a rect which represents the entire group of views passed in
192547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        for (View v: views) {
19268baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
192747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            if (boundingRect == null) {
19288baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                boundingRect = new Rect(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
192947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            } else {
19308baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
193147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
193247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
193347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
19348baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Mark the occupied state as false for the group of views we want to move.
1935f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
19368baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
19378baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
193847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
193947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
194047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
194147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int top = boundingRect.top;
194247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int left = boundingRect.left;
19438baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We mark more precisely which parts of the bounding rect are truly occupied, allowing
1944a56dc1077e09faca984a59b861c5dc6364ae2f21Adam Cohen        // for interlocking.
1945f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
19468baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
19478baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x - left, c.y - top, c.spanX, c.spanY, blockOccupied, true);
194847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
194947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
195047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
195147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1952f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
1953f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
195447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
19558baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // If we successfuly found a location by pushing the block of views, we commit it
195647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
19578baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int deltaX = mTempLocation[0] - boundingRect.left;
19588baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int deltaY = mTempLocation[1] - boundingRect.top;
1959f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: views) {
19608baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                CellAndSpan c = currentState.map.get(v);
19618baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c.x += deltaX;
19628baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c.y += deltaY;
196347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
196447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            success = true;
196547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
1966482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
19678baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // In either case, we set the occupied array as marked for the location of the views
1968f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
19698baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
19708baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1971482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1972482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return success;
1973482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1974482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1975482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void markCellsForRect(Rect r, boolean[][] occupied, boolean value) {
1976482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(r.left, r.top, r.width(), r.height(), occupied, value);
1977482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1978482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
19794abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // This method tries to find a reordering solution which satisfies the push mechanic by trying
19804abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // to push items in each of the cardinal directions, in an order based on the direction vector
19814abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // passed.
19824abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
19834abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int[] direction, View ignoreView, ItemConfiguration solution) {
19844abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
19854abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If the direction vector has two non-zero components, we try pushing
19864abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // separately in each of the components.
19874abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int temp = direction[1];
19884abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = 0;
1989f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1990f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
19914abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
19924abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
19934abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
19944abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = temp;
19954abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[0];
19964abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = 0;
1997f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1998f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
19994abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20004abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20014abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20024abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Revert the direction
20034abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
20044abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20054abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Now we try pushing in each component of the opposite direction
20064abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20074abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20084abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[1];
20094abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = 0;
2010f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20114abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20124abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20134abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20144abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20154abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = temp;
20164abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[0];
20174abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = 0;
2018f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20194abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20204abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20214abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20224abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // revert the direction
20234abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
20244abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20254abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20264abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20274abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        } else {
20284abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If the direction vector has a single non-zero component, we push first in the
20294abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // direction of the vector
2030f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20314abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20324abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20334abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20344abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Then we try the opposite direction
20354abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20364abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
2037f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20384abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20394abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20404abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20414abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Switch the direction back
20424abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20434abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20444abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20454abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If we have failed to find a push solution with the above, then we try
20464abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // to find a solution by pushing along the perpendicular axis.
20474abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20484abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Swap the components
20494abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int temp = direction[1];
20504abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = direction[0];
20514abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
2052f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20534abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20544abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20554abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20564abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20574abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Then we try the opposite direction
20584abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20594abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
2060f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20614abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20624abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20634abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20644abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Switch the direction back
20654abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20664abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20674abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20684abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Swap the components back
20694abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[1];
20704abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = direction[0];
20714abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
20724abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        }
20734abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        return false;
20744abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    }
20754abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
2076482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
20778baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            View ignoreView, ItemConfiguration solution) {
2078e3e03bcd313ba8060f2832b6a16dea6fd6d532eaWinson Chung        // Return early if get invalid cell positions
2079e3e03bcd313ba8060f2832b6a16dea6fd6d532eaWinson Chung        if (cellX < 0 || cellY < 0) return false;
2080482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
20818baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        mIntersectingViews.clear();
2082482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
2083482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
20848baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Mark the desired location of the view currently being dragged.
2085482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (ignoreView != null) {
20868baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(ignoreView);
208719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (c != null) {
208819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                c.x = cellX;
208919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                c.y = cellY;
209019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2091482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2092482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
2093482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        Rect r1 = new Rect();
20948baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (View child: solution.map.keySet()) {
2095482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == ignoreView) continue;
20968baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
2097482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
20988baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
2099482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (Rect.intersects(r0, r1)) {
2100482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                if (!lp.canReorder) {
2101482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    return false;
2102482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2103482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mIntersectingViews.add(child);
2104482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2105482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
210647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
21074abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // First we try to find a solution which respects the push mechanic. That is,
21084abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // we try to find a solution such that no displaced item travels through another item
21094abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // without also displacing that item.
21104abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
211119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                solution)) {
211247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            return true;
211347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
211447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
21154abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // Next we try moving the views as a block, but without requiring the push mechanic.
2116f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
211719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                solution)) {
2118482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            return true;
2119482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
212047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
2121482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Ok, they couldn't move as a block, let's move them individually
2122482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (View v : mIntersectingViews) {
21238baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
2124482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return false;
2125482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2126482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2127482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return true;
2128482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2129482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2130482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    /*
2131482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
2132482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * the provided point and the provided cell
2133482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     */
213447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
2135482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        double angle = Math.atan(((float) deltaY) / deltaX);
2136482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2137482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result[0] = 0;
2138482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result[1] = 0;
2139482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (Math.abs(Math.cos(angle)) > 0.5f) {
2140482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = (int) Math.signum(deltaX);
2141482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2142482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (Math.abs(Math.sin(angle)) > 0.5f) {
2143482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[1] = (int) Math.signum(deltaY);
2144482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2145482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2146482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
21478baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private void copyOccupiedArray(boolean[][] occupied) {
21488baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (int i = 0; i < mCountX; i++) {
21498baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            for (int j = 0; j < mCountY; j++) {
21508baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                occupied[i][j] = mOccupied[i][j];
21518baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            }
21528baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        }
21538baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    }
21548baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
2155482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    ItemConfiguration simpleSwap(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
2156482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) {
21578baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Copy the current state into the solution. This solution will be manipulated as necessary.
21588baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        copyCurrentStateToSolution(solution, false);
21598baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Copy the current occupied array into the temporary occupied array. This array will be
21608baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // manipulated as necessary to find a solution.
21618baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        copyOccupiedArray(mTmpOccupied);
2162482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2163482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // We find the nearest cell into which we would place the dragged item, assuming there's
2164482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // nothing in its way.
2165482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int result[] = new int[2];
2166482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
2167482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2168482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean success = false;
2169482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // First we try the exact nearest position of the item being dragged,
2170482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // we will then want to try to move this around to other neighbouring positions
21718baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
21728baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                solution);
2173482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2174482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!success) {
2175482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
2176482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // x, then 1 in y etc.
2177482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (spanX > minSpanX && (minSpanY == spanY || decX)) {
2178482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY, direction,
2179482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        dragView, false, solution);
2180482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else if (spanY > minSpanY) {
2181482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1, direction,
2182482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        dragView, true, solution);
2183482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2184482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = false;
2185482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2186482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = true;
2187482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewX = result[0];
2188482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewY = result[1];
2189482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanX = spanX;
2190482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanY = spanY;
2191482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2192482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return solution;
2193482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2194482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2195482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
2196a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2197482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2198a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
2199482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
22008baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c;
2201482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (temp) {
22028baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
2203482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else {
22048baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
2205482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2206f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            solution.add(child, c);
2207482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2208482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2209482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2210482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
2211482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2212482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2213482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mTmpOccupied[i][j] = false;
2214482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2215482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2216482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2217a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2218482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2219a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
2220482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == dragView) continue;
2221482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
22228baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
22238baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (c != null) {
22248baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.tmpCellX = c.x;
22258baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.tmpCellY = c.y;
22268baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.cellHSpan = c.spanX;
22278baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.cellVSpan = c.spanY;
22288baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
2229482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2230482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2231482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
2232482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                solution.dragViewSpanY, mTmpOccupied, true);
2233482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2234482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2235482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
2236482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            commitDragView) {
2237482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2238482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean[][] occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
2239482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2240482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2241482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[i][j] = false;
2242482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2243482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2244482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2245a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2246482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2247a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
2248482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == dragView) continue;
22498baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
22508baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (c != null) {
225119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                animateChildToPosition(child, c.x, c.y, REORDER_ANIMATION_DURATION, 0,
225219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        DESTRUCTIVE_REORDER, false);
22538baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                markCellsForView(c.x, c.y, c.spanX, c.spanY, occupied, true);
2254482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2255482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2256482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (commitDragView) {
2257482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
2258482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    solution.dragViewSpanY, occupied, true);
2259482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2260482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2261482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
226219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // This method starts or changes the reorder hint animations
226319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void beginOrAdjustHintAnimations(ItemConfiguration solution, View dragView, int delay) {
226419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int childCount = mShortcutsAndWidgets.getChildCount();
226519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < childCount; i++) {
226619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
226719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (child == dragView) continue;
226819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            CellAndSpan c = solution.map.get(child);
226919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
227019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (c != null) {
227119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                ReorderHintAnimation rha = new ReorderHintAnimation(child, lp.cellX, lp.cellY,
227219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        c.x, c.y, c.spanX, c.spanY);
2273d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                rha.animate();
227419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
227519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
227619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
227719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
227819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // Class which represents the reorder hint animations. These animations show that an item is
227919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // in a temporary state, and hint at where the item will return to.
228019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    class ReorderHintAnimation {
228119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        View child;
2282d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalDeltaX;
2283d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalDeltaY;
2284d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initDeltaX;
2285d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initDeltaY;
2286d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalScale;
2287d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initScale;
228850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        private static final int DURATION = 300;
2289e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen        Animator a;
229019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
229119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        public ReorderHintAnimation(View child, int cellX0, int cellY0, int cellX1, int cellY1,
229219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                int spanX, int spanY) {
229319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint);
229419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int x0 = mTmpPoint[0];
229519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int y0 = mTmpPoint[1];
229619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint);
229719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int x1 = mTmpPoint[0];
229819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int y1 = mTmpPoint[1];
229919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int dX = x1 - x0;
230019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int dY = y1 - y0;
2301d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalDeltaX = 0;
2302d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalDeltaY = 0;
230319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (dX == dY && dX == 0) {
230419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            } else {
230519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (dY == 0) {
2306d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaX = - Math.signum(dX) * mReorderHintAnimationMagnitude;
230719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else if (dX == 0) {
2308d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaY = - Math.signum(dY) * mReorderHintAnimationMagnitude;
230919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else {
231019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    double angle = Math.atan( (float) (dY) / dX);
2311d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaX = (int) (- Math.signum(dX) *
2312fe41ac641bdef7ea96dcbac59b4f3abdbdff6cfeAdam Cohen                            Math.abs(Math.cos(angle) * mReorderHintAnimationMagnitude));
2313d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaY = (int) (- Math.signum(dY) *
2314fe41ac641bdef7ea96dcbac59b4f3abdbdff6cfeAdam Cohen                            Math.abs(Math.sin(angle) * mReorderHintAnimationMagnitude));
231519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
231619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2317d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initDeltaX = child.getTranslationX();
2318d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initDeltaY = child.getTranslationY();
2319307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            finalScale = getChildrenScale() - 4.0f / child.getWidth();
2320d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initScale = child.getScaleX();
232119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            this.child = child;
232219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
232319f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2324d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        void animate() {
232519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (mShakeAnimators.containsKey(child)) {
232619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                ReorderHintAnimation oldAnimation = mShakeAnimators.get(child);
2327d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                oldAnimation.cancel();
232819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mShakeAnimators.remove(child);
2329e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                if (finalDeltaX == 0 && finalDeltaY == 0) {
2330e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                    completeAnimationImmediately();
2331e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                    return;
2332e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                }
233319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2334d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            if (finalDeltaX == 0 && finalDeltaY == 0) {
233519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                return;
233619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2337f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
2338e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            a = va;
233919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.setRepeatMode(ValueAnimator.REVERSE);
234019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.setRepeatCount(ValueAnimator.INFINITE);
23417bdfc9700b1cad043c04c757f134db1bf3df00daAdam Cohen            va.setDuration(DURATION);
2342d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            va.setStartDelay((int) (Math.random() * 60));
234319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.addUpdateListener(new AnimatorUpdateListener() {
234419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                @Override
234519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                public void onAnimationUpdate(ValueAnimator animation) {
234619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    float r = ((Float) animation.getAnimatedValue()).floatValue();
2347d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float x = r * finalDeltaX + (1 - r) * initDeltaX;
2348d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float y = r * finalDeltaY + (1 - r) * initDeltaY;
234919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    child.setTranslationX(x);
235019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    child.setTranslationY(y);
2351d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float s = r * finalScale + (1 - r) * initScale;
235250e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely                    child.setScaleX(s);
235350e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely                    child.setScaleY(s);
235419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
235519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            });
235619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.addListener(new AnimatorListenerAdapter() {
235719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                public void onAnimationRepeat(Animator animation) {
235819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    // We make sure to end only after a full period
2359d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initDeltaX = 0;
2360d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initDeltaY = 0;
2361307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                    initScale = getChildrenScale();
236219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
236319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            });
236419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mShakeAnimators.put(child, this);
236519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.start();
236619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
236719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2368d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        private void cancel() {
2369e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            if (a != null) {
2370e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                a.cancel();
2371e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            }
237219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
2373e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen
237450e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        private void completeAnimationImmediately() {
2375e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            if (a != null) {
2376e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                a.cancel();
2377e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            }
237850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely
23792ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka            AnimatorSet s = LauncherAnimUtils.createAnimatorSet();
2380e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            a = s;
238150e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.playTogether(
2382307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                LauncherAnimUtils.ofFloat(child, "scaleX", getChildrenScale()),
2383307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                LauncherAnimUtils.ofFloat(child, "scaleY", getChildrenScale()),
23842ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "translationX", 0f),
23852ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "translationY", 0f)
238650e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            );
238750e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.setDuration(REORDER_ANIMATION_DURATION);
238850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
238950e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.start();
239050e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        }
239119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
239219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
239319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void completeAndClearReorderHintAnimations() {
239419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (ReorderHintAnimation a: mShakeAnimators.values()) {
239550e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            a.completeAnimationImmediately();
239619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
239719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mShakeAnimators.clear();
239819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
239919f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2400482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void commitTempPlacement() {
2401482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2402482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2403482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mOccupied[i][j] = mTmpOccupied[i][j];
2404482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2405482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2406a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2407482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2408ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
2409ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
2410ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            ItemInfo info = (ItemInfo) child.getTag();
24112acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            // We do a null check here because the item info can be null in the case of the
24122acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            // AllApps button in the hotseat.
24132acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            if (info != null) {
2414487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                if (info.cellX != lp.tmpCellX || info.cellY != lp.tmpCellY ||
2415487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                        info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan) {
2416487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                    info.requiresDbUpdate = true;
2417487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                }
24182acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen                info.cellX = lp.cellX = lp.tmpCellX;
24192acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen                info.cellY = lp.cellY = lp.tmpCellY;
2420bebf042666cffe52039b875a549a582abd78a431Adam Cohen                info.spanX = lp.cellHSpan;
2421bebf042666cffe52039b875a549a582abd78a431Adam Cohen                info.spanY = lp.cellVSpan;
24222acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            }
2423482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
24242acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen        mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
2425482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2426482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2427482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void setUseTempCoords(boolean useTempCoords) {
2428a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2429482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2430a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
2431482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            lp.useTmpCoords = useTempCoords;
2432482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2433482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2434482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2435482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY,
2436482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int spanX, int spanY, View dragView, ItemConfiguration solution) {
2437482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int[] result = new int[2];
2438482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int[] resultSpan = new int[2];
2439482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null, result,
2440482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                resultSpan);
2441482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (result[0] >= 0 && result[1] >= 0) {
2442482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            copyCurrentStateToSolution(solution, false);
2443482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewX = result[0];
2444482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewY = result[1];
2445482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanX = resultSpan[0];
2446482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanY = resultSpan[1];
2447482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = true;
2448482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2449482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = false;
2450482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2451482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return solution;
2452482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2453482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2454482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void prepareChildForDrag(View child) {
2455482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(child);
2456482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2457482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
245819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    /* This seems like it should be obvious and straight-forward, but when the direction vector
245919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    needs to match with the notion of the dragView pushing other views, we have to employ
246019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    a slightly more subtle notion of the direction vector. The question is what two points is
246119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    the vector between? The center of the dragView and its desired destination? Not quite, as
246219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    this doesn't necessarily coincide with the interaction of the dragView and items occupying
246319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    those cells. Instead we use some heuristics to often lock the vector to up, down, left
246419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    or right, which helps make pushing feel right.
246519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    */
246619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
246719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            int spanY, View dragView, int[] resultDirection) {
246819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int[] targetDestination = new int[2];
246919f3792523fe2d55ea791a9286398a6120920690Adam Cohen
247019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
247119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect dragRect = new Rect();
247219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        regionToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
247319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
247419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
247519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect dropRegionRect = new Rect();
247619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
247719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                dragView, dropRegionRect, mIntersectingViews);
247819f3792523fe2d55ea791a9286398a6120920690Adam Cohen
247919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int dropRegionSpanX = dropRegionRect.width();
248019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int dropRegionSpanY = dropRegionRect.height();
248119f3792523fe2d55ea791a9286398a6120920690Adam Cohen
248219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        regionToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
248319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                dropRegionRect.height(), dropRegionRect);
248419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
248519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
248619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
248719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
248819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (dropRegionSpanX == mCountX || spanX == mCountX) {
248919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            deltaX = 0;
249019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
249119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (dropRegionSpanY == mCountY || spanY == mCountY) {
249219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            deltaY = 0;
249319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
249419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
249519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (deltaX == 0 && deltaY == 0) {
249619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            // No idea what to do, give a random direction.
249719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            resultDirection[0] = 1;
249819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            resultDirection[1] = 0;
249919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        } else {
250019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            computeDirectionVector(deltaX, deltaY, resultDirection);
250119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
250219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
250319f3792523fe2d55ea791a9286398a6120920690Adam Cohen
250419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // For a given cell and span, fetch the set of views intersecting the region.
250519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
250619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
250719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (boundingRect != null) {
250819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
250919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
251019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        intersectingViews.clear();
251119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
251219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect r1 = new Rect();
251319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int count = mShortcutsAndWidgets.getChildCount();
251419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < count; i++) {
251519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
251619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (child == dragView) continue;
251719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
251819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
251919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (Rect.intersects(r0, r1)) {
252019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mIntersectingViews.add(child);
252119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (boundingRect != null) {
252219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    boundingRect.union(r1);
252319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
252419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
252519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
252619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
252719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
252819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
252919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View dragView, int[] result) {
253019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
253119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
253219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mIntersectingViews);
253319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        return !mIntersectingViews.isEmpty();
253419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
253519f3792523fe2d55ea791a9286398a6120920690Adam Cohen
253619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    void revertTempState() {
253719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (!isItemPlacementDirty() || DESTRUCTIVE_REORDER) return;
253819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int count = mShortcutsAndWidgets.getChildCount();
253919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < count; i++) {
254019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
254119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
254219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
254319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                lp.tmpCellX = lp.cellX;
254419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                lp.tmpCellY = lp.cellY;
254519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION,
254619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        0, false, false);
254719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
254819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
254919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        completeAndClearReorderHintAnimations();
255019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        setItemPlacementDirty(false);
255119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
255219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2553bebf042666cffe52039b875a549a582abd78a431Adam Cohen    boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
2554bebf042666cffe52039b875a549a582abd78a431Adam Cohen            View dragView, int[] direction, boolean commit) {
2555bebf042666cffe52039b875a549a582abd78a431Adam Cohen        int[] pixelXY = new int[2];
2556bebf042666cffe52039b875a549a582abd78a431Adam Cohen        regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
2557bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2558bebf042666cffe52039b875a549a582abd78a431Adam Cohen        // First we determine if things have moved enough to cause a different layout
2559bebf042666cffe52039b875a549a582abd78a431Adam Cohen        ItemConfiguration swapSolution = simpleSwap(pixelXY[0], pixelXY[1], spanX, spanY,
2560bebf042666cffe52039b875a549a582abd78a431Adam Cohen                 spanX,  spanY, direction, dragView,  true,  new ItemConfiguration());
2561bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2562bebf042666cffe52039b875a549a582abd78a431Adam Cohen        setUseTempCoords(true);
2563bebf042666cffe52039b875a549a582abd78a431Adam Cohen        if (swapSolution != null && swapSolution.isSolution) {
2564bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
2565bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // committing anything or animating anything as we just want to determine if a solution
2566bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // exists
2567bebf042666cffe52039b875a549a582abd78a431Adam Cohen            copySolutionToTempState(swapSolution, dragView);
2568bebf042666cffe52039b875a549a582abd78a431Adam Cohen            setItemPlacementDirty(true);
2569bebf042666cffe52039b875a549a582abd78a431Adam Cohen            animateItemsToSolution(swapSolution, dragView, commit);
2570bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2571bebf042666cffe52039b875a549a582abd78a431Adam Cohen            if (commit) {
2572bebf042666cffe52039b875a549a582abd78a431Adam Cohen                commitTempPlacement();
2573bebf042666cffe52039b875a549a582abd78a431Adam Cohen                completeAndClearReorderHintAnimations();
2574bebf042666cffe52039b875a549a582abd78a431Adam Cohen                setItemPlacementDirty(false);
2575bebf042666cffe52039b875a549a582abd78a431Adam Cohen            } else {
2576bebf042666cffe52039b875a549a582abd78a431Adam Cohen                beginOrAdjustHintAnimations(swapSolution, dragView,
2577bebf042666cffe52039b875a549a582abd78a431Adam Cohen                        REORDER_ANIMATION_DURATION);
2578bebf042666cffe52039b875a549a582abd78a431Adam Cohen            }
2579bebf042666cffe52039b875a549a582abd78a431Adam Cohen            mShortcutsAndWidgets.requestLayout();
2580bebf042666cffe52039b875a549a582abd78a431Adam Cohen        }
2581bebf042666cffe52039b875a549a582abd78a431Adam Cohen        return swapSolution.isSolution;
2582bebf042666cffe52039b875a549a582abd78a431Adam Cohen    }
2583bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2584482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    int[] createArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
2585482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            View dragView, int[] result, int resultSpan[], int mode) {
2586482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // First we determine if things have moved enough to cause a different layout
258747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
2588482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2589482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (resultSpan == null) {
2590482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan = new int[2];
2591482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2592482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
259319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // When we are checking drop validity or actually dropping, we don't recompute the
259419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // direction vector, since we want the solution to match the preview, and it's possible
259519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // that the exact position of the item has changed to result in a new reordering outcome.
2596b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen        if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
2597b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen               && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
259819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mDirectionVector[0] = mPreviousReorderDirection[0];
259919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mDirectionVector[1] = mPreviousReorderDirection[1];
260019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            // We reset this vector after drop
2601b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen            if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
2602b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen                mPreviousReorderDirection[0] = INVALID_DIRECTION;
2603b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen                mPreviousReorderDirection[1] = INVALID_DIRECTION;
260419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
260519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        } else {
260619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
260719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mPreviousReorderDirection[0] = mDirectionVector[0];
260819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mPreviousReorderDirection[1] = mDirectionVector[1];
260919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
261019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2611482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration swapSolution = simpleSwap(pixelX, pixelY, minSpanX, minSpanY,
2612482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                 spanX,  spanY, mDirectionVector, dragView,  true,  new ItemConfiguration());
2613482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2614482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // We attempt the approach which doesn't shuffle views at all
2615482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
2616482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                minSpanY, spanX, spanY, dragView, new ItemConfiguration());
2617482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2618482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration finalSolution = null;
2619482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
2620482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            finalSolution = swapSolution;
2621482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else if (noShuffleSolution.isSolution) {
2622482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            finalSolution = noShuffleSolution;
2623482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2624482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2625482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean foundSolution = true;
2626482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!DESTRUCTIVE_REORDER) {
2627482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            setUseTempCoords(true);
2628482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2629482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2630482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (finalSolution != null) {
2631482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = finalSolution.dragViewX;
2632482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[1] = finalSolution.dragViewY;
2633482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan[0] = finalSolution.dragViewSpanX;
2634482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan[1] = finalSolution.dragViewSpanY;
2635482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2636482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
2637482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // committing anything or animating anything as we just want to determine if a solution
2638482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // exists
2639482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
2640482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                if (!DESTRUCTIVE_REORDER) {
2641482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    copySolutionToTempState(finalSolution, dragView);
2642482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2643482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                setItemPlacementDirty(true);
2644482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP);
2645482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
264619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (!DESTRUCTIVE_REORDER &&
264719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
2648482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    commitTempPlacement();
264919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    completeAndClearReorderHintAnimations();
265019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    setItemPlacementDirty(false);
265119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else {
265219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    beginOrAdjustHintAnimations(finalSolution, dragView,
265319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                            REORDER_ANIMATION_DURATION);
2654482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2655482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2656482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2657482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            foundSolution = false;
2658482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
2659482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2660482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2661482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if ((mode == MODE_ON_DROP || !foundSolution) && !DESTRUCTIVE_REORDER) {
2662482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            setUseTempCoords(false);
2663482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2664482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2665a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.requestLayout();
2666482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return result;
2667482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2668482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
266919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    void setItemPlacementDirty(boolean dirty) {
267019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mItemPlacementDirty = dirty;
2671482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
267219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    boolean isItemPlacementDirty() {
267319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        return mItemPlacementDirty;
2674482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2675482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2676482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private class ItemConfiguration {
26778baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
2678f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
2679f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ArrayList<View> sortedViews = new ArrayList<View>();
2680482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean isSolution = false;
2681482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
2682482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2683f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void save() {
2684f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // Copy current state into savedMap
2685f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: map.keySet()) {
2686f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                map.get(v).copy(savedMap.get(v));
2687f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
2688f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2689f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2690f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void restore() {
2691f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // Restore current state from savedMap
2692f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: savedMap.keySet()) {
2693f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                savedMap.get(v).copy(map.get(v));
2694f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
2695f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2696f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2697f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void add(View v, CellAndSpan cs) {
2698f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            map.put(v, cs);
2699f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            savedMap.put(v, new CellAndSpan());
2700f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            sortedViews.add(v);
2701f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2702f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2703482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int area() {
2704482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            return dragViewSpanX * dragViewSpanY;
2705482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
27068baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    }
27078baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
27088baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private class CellAndSpan {
27098baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        int x, y;
27108baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        int spanX, spanY;
27118baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
2712f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public CellAndSpan() {
2713f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2714f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2715f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public void copy(CellAndSpan copy) {
2716f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.x = x;
2717f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.y = y;
2718f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.spanX = spanX;
2719f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.spanY = spanY;
2720f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2721f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
27228baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        public CellAndSpan(int x, int y, int spanX, int spanY) {
27238baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.x = x;
27248baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.y = y;
27258baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.spanX = spanX;
27268baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.spanY = spanY;
2727482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2728f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2729f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public String toString() {
2730f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return "(" + x + ", " + y + ": " + spanX + ", " + spanY + ")";
2731f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2732f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2733482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2734482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2735df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    /**
2736df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
2737df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2738df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *
2739df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2740df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2741df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanX Horizontal span of the object.
2742df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanY Vertical span of the object.
2743df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2744df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Previously returned value to possibly recycle.
2745df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2746df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *         nearest the requested location.
2747df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     */
2748df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestVacantArea(
2749df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
2750df0353815c629fc678824b07a234b89a1ff94208Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY, ignoreView, true, result);
2751df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    }
2752df0353815c629fc678824b07a234b89a1ff94208Adam Cohen
2753df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    /**
2754d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
2755d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2756d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
2757d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2758d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2759d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
2760d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
2761d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
2762d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
2763d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2764d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Previously returned value to possibly recycle.
2765d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2766d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
2767d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
2768d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY,
2769d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int spanX, int spanY, View ignoreView, int[] result, int[] resultSpan) {
2770482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, ignoreView, true,
2771482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                result, resultSpan, mOccupied);
2772d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
2773d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
2774d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
2775df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * Find a starting cell position that will fit the given bounds nearest the requested
2776df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2777df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *
2778df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2779df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2780df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanX Horizontal span of the object.
2781df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanY Vertical span of the object.
2782df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2783df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Previously returned value to possibly recycle.
2784df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2785df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *         nearest the requested location.
2786df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     */
2787df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestArea(
2788df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            int pixelX, int pixelY, int spanX, int spanY, int[] result) {
2789df0353815c629fc678824b07a234b89a1ff94208Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY, null, false, result);
2790df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    }
2791df0353815c629fc678824b07a234b89a1ff94208Adam Cohen
27920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean existsEmptyCell() {
27930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpan(null, 1, 1);
27940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
27950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
27960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
27970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Finds the upper-left coordinate of the first rectangle in the grid that can
27980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
27990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * then this method will only return coordinates for rectangles that contain the cell
28000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * (intersectX, intersectY)
28010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
28030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
28040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
28050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
28060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
28080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
2810482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied);
28110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
28130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
28140280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but ignores any cells occupied by the item "ignoreView"
28150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
28170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
28180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
28190280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
28200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
28210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return
28220280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
2824482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1,
2825482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                ignoreView, mOccupied);
28260280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
28280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
28290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but if intersectX and intersectY are not -1, then this method will try to
28300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * return coordinates for rectangles that contain the cell [intersectX, intersectY]
28310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28320280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
28330280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
28340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
28350280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The X coordinate of the cell that we should try to overlap
28360280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The Y coordinate of the cell that we should try to overlap
28370280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
28390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
28410280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int intersectX, int intersectY) {
28420280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpanThatIntersectsIgnoring(
2843482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                cellXY, spanX, spanY, intersectX, intersectY, null, mOccupied);
28440280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
28460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
28470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * The superset of the above two methods
28480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
2850482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int intersectX, int intersectY, View ignoreView, boolean occupied[][]) {
2851c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
2852482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(ignoreView, occupied);
28530280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
285428750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        boolean foundCell = false;
28550280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        while (true) {
28560280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startX = 0;
28570280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
28580280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startX = Math.max(startX, intersectX - (spanX - 1));
28590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endX = mCountX - (spanX - 1);
28610280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
28620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
28630280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28640280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startY = 0;
28650280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
28660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startY = Math.max(startY, intersectY - (spanY - 1));
28670280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28680280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endY = mCountY - (spanY - 1);
28690280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
28700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
28710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2873bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung            for (int y = startY; y < endY && !foundCell; y++) {
28740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                inner:
2875bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                for (int x = startX; x < endX; x++) {
28760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    for (int i = 0; i < spanX; i++) {
28770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        for (int j = 0; j < spanY; j++) {
2878482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                            if (occupied[x + i][y + j]) {
2879bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                                // small optimization: we can skip to after the column we just found
28800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                // an occupied cell
2881bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                                x += i;
28820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                continue inner;
28830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                            }
28840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        }
28850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
28860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    if (cellXY != null) {
28870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[0] = x;
28880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[1] = y;
28890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
289028750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    foundCell = true;
289128750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    break;
28920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                }
28930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX == -1 && intersectY == -1) {
28950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                break;
28960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            } else {
28970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // if we failed to find anything, try again but without any requirements of
28980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // intersecting
28990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectX = -1;
29000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectY = -1;
29010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                continue;
29020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
29030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
29040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2905c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
2906482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(ignoreView, occupied);
290728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        return foundCell;
29080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
29090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
291031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2911c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * A drag event has begun over this layout.
2912c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * It may have begun over this layout (in which case onDragChild is called first),
2913c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * or it may have begun on another layout.
2914c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     */
2915c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    void onDragEnter() {
2916c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen        mDragEnforcer.onDragEnter();
2917c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung        mDragging = true;
2918c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    }
2919c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung
2920c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    /**
29210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Called when drag has left this CellLayout or has been completed (successfully or not)
29226569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
29230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    void onDragExit() {
2924c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen        mDragEnforcer.onDragExit();
29254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // This can actually be called when we aren't in a drag, e.g. when adding a new
29264be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // item to this layout via the customize drawer.
29274be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Guard against that case.
29284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mDragging) {
29294be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragging = false;
29304be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
293108ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy
293208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        // Invalidate the drag data
2933d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        mDragCell[0] = mDragCell[1] = -1;
293408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragOutlineAnims[mDragOutlineCurrent].animateOut();
293508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragOutlineCurrent = (mDragOutlineCurrent + 1) % mDragOutlineAnims.length;
293619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        revertTempState();
293733945b21544bc98381df17726a3537c292d8c985Michael Jurka        setIsDragOverlapping(false);
29386569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
29396569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
29406569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
2941aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Mark a child as having been dropped.
2942de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * At the beginning of the drag operation, the child may have been on another
2943ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy     * screen, but it is re-parented before this method is called.
294431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
294531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
294631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
2947716b51e030f9c6ed34af2b947760e46a280c65a6Adam Cohen    void onDropChild(View child) {
2948d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        if (child != null) {
2949d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
295084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy            lp.dropped = true;
2951d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            child.requestLayout();
2952d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        }
295331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
295431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
295531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
295631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes a bounding rectangle for a range of cells
2957aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
295831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of upper left corner expressed as a cell position
295931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of upper left corner expressed as a cell position
2960aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellHSpan Width in cells
296131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellVSpan Height in cells
29626569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param resultRect Rect into which to put the results
296331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
2964d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, Rect resultRect) {
296531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
296631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
296731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int widthGap = mWidthGap;
296831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int heightGap = mHeightGap;
2969aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
29704b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
29714b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
2972aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
297331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
297431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
297531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
297631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x = hStartPadding + cellX * (cellWidth + widthGap);
297731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = vStartPadding + cellY * (cellHeight + heightGap);
2978aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
29796569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        resultRect.set(x, y, x + width, y + height);
298031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
2981aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
298231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2983aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Computes the required horizontal and vertical cell spans to always
298431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * fit the given rectangle.
2985aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
298631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param width Width in pixels
298731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param height Height in pixels
29888f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * @param result An array of length 2 in which to store the result (may be null).
298931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
29908f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    public int[] rectToCell(int width, int height, int[] result) {
29919987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka        return rectToCell(getResources(), width, height, result);
29929987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    }
29939987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka
29949987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    public static int[] rectToCell(Resources resources, int width, int height, int[] result) {
299531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always assume we're working with the smallest span to make sure we
299631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // reserve enough space in both orientations.
299779e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
299879e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
299931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int smallerSize = Math.min(actualWidth, actualHeight);
300079e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
300131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always round up to next largest cell
300254c725cc294cd43ed0650179bfae64a622547660Winson Chung        int spanX = (int) Math.ceil(width / (float) smallerSize);
300354c725cc294cd43ed0650179bfae64a622547660Winson Chung        int spanY = (int) Math.ceil(height / (float) smallerSize);
300479e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
30058f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        if (result == null) {
30068f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            return new int[] { spanX, spanY };
30078f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        }
30088f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = spanX;
30098f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = spanY;
30108f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        return result;
301131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
301231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3013f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka    public int[] cellSpansToSize(int hSpans, int vSpans) {
3014f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        int[] size = new int[2];
3015f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        size[0] = hSpans * mCellWidth + (hSpans - 1) * mWidthGap;
3016f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        size[1] = vSpans * mCellHeight + (vSpans - 1) * mHeightGap;
3017f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        return size;
3018f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka    }
3019f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka
302031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
3021047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy     * Calculate the grid spans needed to fit given item
3022047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy     */
3023047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    public void calculateSpans(ItemInfo info) {
3024047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        final int minWidth;
3025047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        final int minHeight;
3026047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy
3027047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        if (info instanceof LauncherAppWidgetInfo) {
3028047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minWidth = ((LauncherAppWidgetInfo) info).minWidth;
3029047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minHeight = ((LauncherAppWidgetInfo) info).minHeight;
3030047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        } else if (info instanceof PendingAddWidgetInfo) {
3031610ded21c79ae3da4c105c2d1921a30732468a69Svet Ganov            minWidth = ((PendingAddWidgetInfo) info).info.minWidth;
3032610ded21c79ae3da4c105c2d1921a30732468a69Svet Ganov            minHeight = ((PendingAddWidgetInfo) info).info.minHeight;
3033047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        } else {
3034047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            // It's not a widget, so it must be 1x1
3035047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            info.spanX = info.spanY = 1;
3036047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            return;
3037047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        }
3038047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        int[] spans = rectToCell(minWidth, minHeight, null);
3039047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        info.spanX = spans[0];
3040047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        info.spanY = spans[1];
3041047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    }
3042047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy
3043047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    /**
304431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Find the first vacant cell, if there is one.
304531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
304631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param vacant Holds the x and y coordinate of the vacant cell
304731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanX Horizontal cell span.
304831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanY Vertical cell span.
3049aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
305031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @return True if a vacant cell was found
305131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
305231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
305331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30540280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
305531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
305631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
305731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static boolean findVacantCell(int[] vacant, int spanX, int spanY,
305831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
305931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30602801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        for (int y = 0; y < yCount; y++) {
30612801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen            for (int x = 0; x < xCount; x++) {
306231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean available = !occupied[x][y];
306331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectout:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
306431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
306531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        available = available && !occupied[i][j];
306631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        if (!available) break out;
306731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
306831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
306931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
307031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (available) {
307131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[0] = x;
307231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[1] = y;
307331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return true;
307431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
307531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
307631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
307731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
307831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
307931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
308031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void clearOccupiedCells() {
30820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = 0; x < mCountX; x++) {
30830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = 0; y < mCountY; y++) {
30840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                mOccupied[x][y] = false;
308531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
308631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
30870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
308831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3089d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) {
30900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
3091482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(newCellX, newCellY, newSpanX, newSpanY, mOccupied, true);
30920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
309331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3094d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    public void markCellsAsOccupiedForView(View view) {
3095482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(view, mOccupied);
3096482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
3097482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void markCellsAsOccupiedForView(View view, boolean[][] occupied) {
3098a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
30990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
3100482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, true);
31010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
31020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
3103d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    public void markCellsAsUnoccupiedForView(View view) {
3104482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(view, mOccupied);
3105482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
3106482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void markCellsAsUnoccupiedForView(View view, boolean occupied[][]) {
3107a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
31080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
3109482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, false);
31100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
31110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
3112482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied,
3113482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            boolean value) {
3114482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (cellX < 0 || cellY < 0) return;
31150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
31160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
3117482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[x][y] = value;
311831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
311931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
312031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
312131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
31222801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public int getDesiredWidth() {
31238b805b17158886035b38261eb611d8641701ae43Michael Jurka        return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
31242801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen                (Math.max((mCountX - 1), 0) * mWidthGap);
31252801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
31262801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
31272801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public int getDesiredHeight()  {
31288b805b17158886035b38261eb611d8641701ae43Michael Jurka        return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
31292801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen                (Math.max((mCountY - 1), 0) * mHeightGap);
31302801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
31312801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
313266d72178af91d455700875635473be942bc90e54Michael Jurka    public boolean isOccupied(int x, int y) {
313366d72178af91d455700875635473be942bc90e54Michael Jurka        if (x < mCountX && y < mCountY) {
313466d72178af91d455700875635473be942bc90e54Michael Jurka            return mOccupied[x][y];
313566d72178af91d455700875635473be942bc90e54Michael Jurka        } else {
313666d72178af91d455700875635473be942bc90e54Michael Jurka            throw new RuntimeException("Position exceeds the bound of this CellLayout");
313766d72178af91d455700875635473be942bc90e54Michael Jurka        }
313866d72178af91d455700875635473be942bc90e54Michael Jurka    }
313966d72178af91d455700875635473be942bc90e54Michael Jurka
314031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
314131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
314231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(getContext(), attrs);
314331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
314431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
314531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
314631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
314731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return p instanceof CellLayout.LayoutParams;
314831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
314931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
315031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
315131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
315231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(p);
315331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
315431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3155aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public static class CellLayoutAnimationController extends LayoutAnimationController {
3156aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public CellLayoutAnimationController(Animation animation, float delay) {
3157aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(animation, delay);
3158aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
3159aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
3160aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        @Override
3161aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        protected long getDelayForView(View view) {
3162aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return (int) (Math.random() * 150);
3163aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
3164aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
3165aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
316631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
316731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
316831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Horizontal location of the item in the grid.
316931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
317031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
317131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellX;
317231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
317331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
317431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Vertical location of the item in the grid.
317531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
317631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
317731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellY;
317831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
317931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
3180482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Temporary horizontal location of the item in the grid during reorder
3181482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3182482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public int tmpCellX;
3183482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
3184482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
3185482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Temporary vertical location of the item in the grid during reorder
3186482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3187482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public int tmpCellY;
3188482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
3189482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
3190482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Indicates that the temporary coordinates should be used to layout the items
3191482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3192482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public boolean useTmpCoords;
3193482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
3194482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
319531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned horizontally by the item.
319631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
319731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
319831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellHSpan;
319931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
320031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
320131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned vertically by the item.
320231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
320331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
320431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellVSpan;
3205aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
32061b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen        /**
32071b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         * Indicates whether the item will set its x, y, width and height parameters freely,
32081b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
32091b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         */
3210d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        public boolean isLockedToGrid = true;
3211d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
3212482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
3213482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Indicates whether this item can be reordered. Always true except in the case of the
3214482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * the AllApps button.
3215482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3216482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public boolean canReorder = true;
3217482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
321831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // X coordinate of the view in the layout.
321931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
322031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x;
322131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Y coordinate of the view in the layout.
322231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
322331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y;
322431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
322584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        boolean dropped;
3226fcb9e7144e58614f5ae0e9b272fb7ce040848c67Romain Guy
322731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(Context c, AttributeSet attrs) {
322831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(c, attrs);
322931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
323031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
323131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
323231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
323331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(ViewGroup.LayoutParams source) {
323431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(source);
323531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
323631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
323731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
3238aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
3239aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public LayoutParams(LayoutParams source) {
3240aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(source);
3241aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellX = source.cellX;
3242aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellY = source.cellY;
3243aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellHSpan = source.cellHSpan;
3244aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellVSpan = source.cellVSpan;
3245aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
3246aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
324731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
32488f19cdd62f6e2be05e3890916eabd11317ae1bc2Romain Guy            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
324931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellX = cellX;
325031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellY = cellY;
325131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellHSpan = cellHSpan;
325231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellVSpan = cellVSpan;
325331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
325431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
32552374abfda3e53f84e005df8923170308e4df8c03Adam Cohen        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
32562374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                boolean invertHorizontally, int colCount) {
3257d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            if (isLockedToGrid) {
3258d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                final int myCellHSpan = cellHSpan;
3259d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                final int myCellVSpan = cellVSpan;
32602374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                int myCellX = useTmpCoords ? tmpCellX : cellX;
32612374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                int myCellY = useTmpCoords ? tmpCellY : cellY;
32622374abfda3e53f84e005df8923170308e4df8c03Adam Cohen
32632374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                if (invertHorizontally) {
32642374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                    myCellX = colCount - myCellX - cellHSpan;
32652374abfda3e53f84e005df8923170308e4df8c03Adam Cohen                }
32661b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen
3267d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
3268d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                        leftMargin - rightMargin;
3269d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
3270d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                        topMargin - bottomMargin;
3271eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                x = (int) (myCellX * (cellWidth + widthGap) + leftMargin);
3272eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                y = (int) (myCellY * (cellHeight + heightGap) + topMargin);
3273d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            }
3274d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        }
3275d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
3276aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public String toString() {
3277aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "(" + this.cellX + ", " + this.cellY + ")";
3278aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
32797f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32807f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setWidth(int width) {
32817f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.width = width;
32827f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32837f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32847f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getWidth() {
32857f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return width;
32867f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32877f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32887f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setHeight(int height) {
32897f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.height = height;
32907f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32917f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32927f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getHeight() {
32937f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return height;
32947f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32957f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32967f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setX(int x) {
32977f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.x = x;
32987f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32997f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
33007f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getX() {
33017f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return x;
33027f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
33037f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
33047f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setY(int y) {
33057f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.y = y;
33067f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
33077f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
33087f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getY() {
33097f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return y;
33107f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
331131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
331231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
33130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // This class stores info for two purposes:
33140280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
33150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    its spanX, spanY, and the screen it is on
33160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 2. When long clicking on an empty cell in a CellLayout, we save information about the
33170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
33180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    the CellLayout that was long clicked
3319e5fb0f27bca7afb996258a7163c76ca7390d7bffMichael Jurka    static final class CellInfo {
332031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        View cell;
3321a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellX = -1;
3322a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellY = -1;
332331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX;
332431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY;
332531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int screen;
33263d503fbd9468fb2b9fa645f4f7b91e11229edbfaWinson Chung        long container;
332731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
332831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
332931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public String toString() {
3330aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "Cell[view=" + (cell == null ? "null" : cell.getClass())
3331aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    + ", x=" + cellX + ", y=" + cellY + "]";
333231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
333331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
3334d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka
3335d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    public boolean lastDownOnOccupiedCell() {
3336d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka        return mLastDownOnOccupiedCell;
3337d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    }
333831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
3339