CellLayout.java revision f1ad608c28c79c8e9b83d83ce9154f1b7284f412
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);
286a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
287a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        addView(mShortcutsAndWidgets);
28818014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka    }
28918014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka
290f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    static int widthInPortrait(Resources r, int numCells) {
291f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // We use this method from Workspace to figure out how many rows/columns Launcher should
292f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // have. We ignore the left/right padding on CellLayout because it turns out in our design
293f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // the padding extends outside the visible screen size, but it looked fine anyway.
294f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        int cellWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);
2954b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),
2964b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                r.getDimensionPixelSize(R.dimen.workspace_height_gap));
297f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
2984b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        return  minGap * (numCells - 1) + cellWidth * numCells;
299f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    }
300f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
301f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    static int heightInLandscape(Resources r, int numCells) {
302f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // We use this method from Workspace to figure out how many rows/columns Launcher should
303f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // have. We ignore the left/right padding on CellLayout because it turns out in our design
304f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // the padding extends outside the visible screen size, but it looked fine anyway.
305f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        int cellHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height);
3064b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),
3074b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                r.getDimensionPixelSize(R.dimen.workspace_height_gap));
308f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3094b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        return minGap * (numCells - 1) + cellHeight * numCells;
310f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    }
311f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3122801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public void enableHardwareLayers() {
313ca99383daef92fed673de22126875cb485be494fMichael Jurka        mShortcutsAndWidgets.setLayerType(LAYER_TYPE_HARDWARE, sPaint);
314d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    }
315d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka
316d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    public void disableHardwareLayers() {
317ca99383daef92fed673de22126875cb485be494fMichael Jurka        mShortcutsAndWidgets.setLayerType(LAYER_TYPE_NONE, sPaint);
318d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    }
319d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka
320d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    public void buildHardwareLayer() {
321d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka        mShortcutsAndWidgets.buildLayer();
3222801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
3232801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
324307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    public float getChildrenScale() {
325307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        return mIsHotseat ? mHotseatScale : 1.0f;
326307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    }
327307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
3282801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public void setGridSize(int x, int y) {
3292801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mCountX = x;
3302801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mCountY = y;
3312801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mOccupied = new boolean[mCountX][mCountY];
332482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mTmpOccupied = new boolean[mCountX][mCountY];
3337fbec10b36818f100b631f3d73fe1ad5360975aaAdam Cohen        mTempRectStack.clear();
33476fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen        requestLayout();
3352801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
3362801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
33796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    private void invalidateBubbleTextView(BubbleTextView icon) {
33896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        final int padding = icon.getPressedOrFocusedBackgroundPadding();
3394b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        invalidate(icon.getLeft() + getPaddingLeft() - padding,
3404b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getTop() + getPaddingTop() - padding,
3414b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getRight() + getPaddingLeft() + padding,
3424b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getBottom() + getPaddingTop() + padding);
34396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    }
34496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
345b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    void setOverScrollAmount(float r, boolean left) {
346b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (left && mOverScrollForegroundDrawable != mOverScrollLeft) {
347b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable = mOverScrollLeft;
348b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        } else if (!left && mOverScrollForegroundDrawable != mOverScrollRight) {
349b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable = mOverScrollRight;
350b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
351b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
352b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundAlpha = (int) Math.round((r * 255));
353b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollForegroundDrawable.setAlpha(mForegroundAlpha);
354b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        invalidate();
355b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
356b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
35796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    void setPressedOrFocusedIcon(BubbleTextView icon) {
35896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
35996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // requires an expanded clip rect (due to the glow's blur radius)
36096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        BubbleTextView oldIcon = mPressedOrFocusedIcon;
36196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        mPressedOrFocusedIcon = icon;
36296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (oldIcon != null) {
36396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            invalidateBubbleTextView(oldIcon);
36496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
36596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (mPressedOrFocusedIcon != null) {
36696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            invalidateBubbleTextView(mPressedOrFocusedIcon);
36796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
36896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    }
36996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
37033945b21544bc98381df17726a3537c292d8c985Michael Jurka    void setIsDragOverlapping(boolean isDragOverlapping) {
37133945b21544bc98381df17726a3537c292d8c985Michael Jurka        if (mIsDragOverlapping != isDragOverlapping) {
37233945b21544bc98381df17726a3537c292d8c985Michael Jurka            mIsDragOverlapping = isDragOverlapping;
37333945b21544bc98381df17726a3537c292d8c985Michael Jurka            invalidate();
37433945b21544bc98381df17726a3537c292d8c985Michael Jurka        }
37533945b21544bc98381df17726a3537c292d8c985Michael Jurka    }
37633945b21544bc98381df17726a3537c292d8c985Michael Jurka
37733945b21544bc98381df17726a3537c292d8c985Michael Jurka    boolean getIsDragOverlapping() {
37833945b21544bc98381df17726a3537c292d8c985Michael Jurka        return mIsDragOverlapping;
37933945b21544bc98381df17726a3537c292d8c985Michael Jurka    }
38033945b21544bc98381df17726a3537c292d8c985Michael Jurka
381ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected void setOverscrollTransformsDirty(boolean dirty) {
382ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        mScrollingTransformsDirty = dirty;
383ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    }
384ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
385ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected void resetOverscrollTransforms() {
386ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        if (mScrollingTransformsDirty) {
387ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setOverscrollTransformsDirty(false);
388ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setTranslationX(0);
389ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setRotationY(0);
390ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            // It doesn't matter if we pass true or false here, the important thing is that we
391ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            // pass 0, which results in the overscroll drawable not being drawn any more.
392ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setOverScrollAmount(0, false);
393ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setPivotX(getMeasuredWidth() / 2);
394ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setPivotY(getMeasuredHeight() / 2);
395ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        }
396ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    }
397ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
398307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    public void scaleRect(Rect r, float scale) {
399307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        if (scale != 1.0f) {
400307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.left = (int) (r.left * scale + 0.5f);
401307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.top = (int) (r.top * scale + 0.5f);
402307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.right = (int) (r.right * scale + 0.5f);
403307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            r.bottom = (int) (r.bottom * scale + 0.5f);
404307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        }
405307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    }
406307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
407307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    Rect temp = new Rect();
408307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    void scaleRectAboutCenter(Rect in, Rect out, float scale) {
409307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        int cx = in.centerX();
410307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        int cy = in.centerY();
411307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        out.set(in);
412307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        out.offset(-cx, -cy);
413307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        scaleRect(out, scale);
414307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        out.offset(cx, cy);
415307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen    }
416307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
417a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    @Override
4181262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onDraw(Canvas canvas) {
4193e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
4203e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
4213e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're small, we are either drawn normally or in the "accepts drops" state (during
4223e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a drag). However, we also drag the mini hover background *over* one of those two
4233e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // backgrounds
424b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        if (mBackgroundAlpha > 0.0f) {
425f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            Drawable bg;
42633945b21544bc98381df17726a3537c292d8c985Michael Jurka
42733945b21544bc98381df17726a3537c292d8c985Michael Jurka            if (mIsDragOverlapping) {
42833945b21544bc98381df17726a3537c292d8c985Michael Jurka                // In the mini case, we draw the active_glow bg *over* the active background
429bdf78559f223ac11e01e3311edd5a48a80383e1eMichael Jurka                bg = mActiveGlowBackground;
430f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            } else {
431bdf78559f223ac11e01e3311edd5a48a80383e1eMichael Jurka                bg = mNormalBackground;
432f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            }
43333945b21544bc98381df17726a3537c292d8c985Michael Jurka
43433945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
43533945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.setBounds(mBackgroundRect);
43633945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.draw(canvas);
437a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
43831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4398e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy        final Paint paint = mDragOutlinePaint;
4404be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
441472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            final float alpha = mDragOutlineAlphas[i];
4424be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (alpha > 0) {
443d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                final Rect r = mDragOutlines[i];
444307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                scaleRectAboutCenter(r, temp, getChildrenScale());
4454be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
446472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                paint.setAlpha((int)(alpha + .5f));
447307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                canvas.drawBitmap(b, null, temp, paint);
448150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            }
4496569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
45096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
45196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
45296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // requires an expanded clip rect (due to the glow's blur radius)
45396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (mPressedOrFocusedIcon != null) {
45496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            final int padding = mPressedOrFocusedIcon.getPressedOrFocusedBackgroundPadding();
45596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            final Bitmap b = mPressedOrFocusedIcon.getPressedOrFocusedBackground();
45696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            if (b != null) {
45796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy                canvas.drawBitmap(b,
4584b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                        mPressedOrFocusedIcon.getLeft() + getPaddingLeft() - padding,
4594b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                        mPressedOrFocusedIcon.getTop() + getPaddingTop() - padding,
46096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy                        null);
46196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            }
46296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
46369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
464482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (DEBUG_VISUALIZE_OCCUPIED) {
465482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int[] pt = new int[2];
466482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            ColorDrawable cd = new ColorDrawable(Color.RED);
467e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            cd.setBounds(0, 0,  mCellWidth, mCellHeight);
468482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int i = 0; i < mCountX; i++) {
469482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                for (int j = 0; j < mCountY; j++) {
470482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    if (mOccupied[i][j]) {
471482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        cellToPoint(i, j, pt);
472482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.save();
473482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.translate(pt[0], pt[1]);
474482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        cd.draw(canvas);
475482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.restore();
476482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    }
477482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
478482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
479482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
480482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
481850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn        int previewOffset = FolderRingAnimator.sPreviewSize;
482850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn
48369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        // The folder outer / inner ring image(s)
48469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        for (int i = 0; i < mFolderOuterRings.size(); i++) {
48569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            FolderRingAnimator fra = mFolderOuterRings.get(i);
48669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
48769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            // Draw outer ring
48869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            Drawable d = FolderRingAnimator.sSharedOuterRingDrawable;
48969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int width = (int) fra.getOuterRingSize();
49069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int height = width;
49169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
49269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
49369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int centerX = mTempLocation[0] + mCellWidth / 2;
494850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            int centerY = mTempLocation[1] + previewOffset / 2;
49569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
49669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.save();
49769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.translate(centerX - width / 2, centerY - height / 2);
49869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.setBounds(0, 0, width, height);
49969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.draw(canvas);
50069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.restore();
50169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
50269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            // Draw inner ring
50369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d = FolderRingAnimator.sSharedInnerRingDrawable;
50469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            width = (int) fra.getInnerRingSize();
50569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            height = width;
50669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
50769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
50869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            centerX = mTempLocation[0] + mCellWidth / 2;
509850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            centerY = mTempLocation[1] + previewOffset / 2;
51069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.save();
51169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.translate(centerX - width / 2, centerY - width / 2);
51269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.setBounds(0, 0, width, height);
51369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.draw(canvas);
51469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.restore();
51569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        }
516c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
517c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        if (mFolderLeaveBehindCell[0] >= 0 && mFolderLeaveBehindCell[1] >= 0) {
518c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            Drawable d = FolderIcon.sSharedFolderLeaveBehind;
519c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int width = d.getIntrinsicWidth();
520c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int height = d.getIntrinsicHeight();
521c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
522c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            cellToPoint(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1], mTempLocation);
523c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int centerX = mTempLocation[0] + mCellWidth / 2;
524850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            int centerY = mTempLocation[1] + previewOffset / 2;
525c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
526c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.save();
527c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.translate(centerX - width / 2, centerY - width / 2);
528c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            d.setBounds(0, 0, width, height);
529c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            d.draw(canvas);
530c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.restore();
531c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        }
53269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    }
53369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
534b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    @Override
535b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected void dispatchDraw(Canvas canvas) {
536b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        super.dispatchDraw(canvas);
537b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (mForegroundAlpha > 0) {
538b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable.setBounds(mForegroundRect);
539b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            Paint p = ((NinePatchDrawable) mOverScrollForegroundDrawable).getPaint();
5408a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy            p.setXfermode(sAddBlendMode);
541b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable.draw(canvas);
542b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            p.setXfermode(null);
543b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
544b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
545b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
54669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    public void showFolderAccept(FolderRingAnimator fra) {
54769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        mFolderOuterRings.add(fra);
54869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    }
54969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
55069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    public void hideFolderAccept(FolderRingAnimator fra) {
55169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        if (mFolderOuterRings.contains(fra)) {
55269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            mFolderOuterRings.remove(fra);
55369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        }
55469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        invalidate();
5556569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
5566569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
557c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    public void setFolderLeaveBehindCell(int x, int y) {
558c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[0] = x;
559c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[1] = y;
560c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        invalidate();
561c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    }
562c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
563c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    public void clearFolderLeaveBehind() {
564c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[0] = -1;
565c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[1] = -1;
566c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        invalidate();
567c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    }
568c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
5696569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    @Override
570e6235dd225404239b55c459245543f3302326112Michael Jurka    public boolean shouldDelayChildPressedState() {
571e6235dd225404239b55c459245543f3302326112Michael Jurka        return false;
572e6235dd225404239b55c459245543f3302326112Michael Jurka    }
573e6235dd225404239b55c459245543f3302326112Michael Jurka
5741462de39f01cec0ba785386532719cb0207dd827Adam Cohen    public void restoreInstanceState(SparseArray<Parcelable> states) {
5751462de39f01cec0ba785386532719cb0207dd827Adam Cohen        dispatchRestoreInstanceState(states);
5761462de39f01cec0ba785386532719cb0207dd827Adam Cohen    }
5771462de39f01cec0ba785386532719cb0207dd827Adam Cohen
578e6235dd225404239b55c459245543f3302326112Michael Jurka    @Override
57983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    public void cancelLongPress() {
58083f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        super.cancelLongPress();
58183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
58283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        // Cancel long press for all children
58383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        final int count = getChildCount();
58483f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        for (int i = 0; i < count; i++) {
58583f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            final View child = getChildAt(i);
58683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            child.cancelLongPress();
58783f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        }
58883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    }
58983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
590dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void setOnInterceptTouchListener(View.OnTouchListener listener) {
591dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mInterceptTouchListener = listener;
592dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
593dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
59431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountX() {
595d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountX;
59631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
59731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
59831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountY() {
599d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountY;
60031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
60131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6020dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    public void setIsHotseat(boolean isHotseat) {
6030dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        mIsHotseat = isHotseat;
6040dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    }
6050dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
6060dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
607850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            boolean markCells) {
608aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final LayoutParams lp = params;
609aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
610de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn        // Hotseat icons - remove text
6110dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        if (child instanceof BubbleTextView) {
6120dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn            BubbleTextView bubbleChild = (BubbleTextView) child;
6130dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
614de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            Resources res = getResources();
615de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            if (mIsHotseat) {
616de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn                bubbleChild.setTextColor(res.getColor(android.R.color.transparent));
617de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            } else {
618de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn                bubbleChild.setTextColor(res.getColor(R.color.workspace_icon_text_color));
6190dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn            }
6200dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        }
6210dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
622307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        child.setScaleX(getChildrenScale());
623307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen        child.setScaleY(getChildrenScale());
624307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen
62531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Generate an id for each view, this assumes we have at most 256x256 cells
62631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // per workspace screen
627d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
628aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // If the horizontal or vertical span is set to -1, it is taken to
629aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // mean that it spans the extent of the CellLayout
630d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
631d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
632aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
633aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            child.setId(childId);
63431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
635a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.addView(child, index, lp);
636dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
637f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka            if (markCells) markCellsAsOccupiedForView(child);
6380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
639aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return true;
640aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
641aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return false;
64231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
6433e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
64431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
6450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViews() {
6460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        clearOccupiedCells();
647a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeAllViews();
6480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6500280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6510280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViewsInLayout() {
652a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (mShortcutsAndWidgets.getChildCount() > 0) {
6537cfc2825c3a1029f962d2fc387ae2eaa85b51798Michael Jurka            clearOccupiedCells();
654a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.removeAllViewsInLayout();
6557cfc2825c3a1029f962d2fc387ae2eaa85b51798Michael Jurka        }
6560280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6570280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
658f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    public void removeViewWithoutMarkingCells(View view) {
659a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeView(view);
660f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    }
661f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka
6620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6630280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeView(View view) {
6640280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
665a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeView(view);
6660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6670280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6680280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6690280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewAt(int index) {
670a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(index));
671a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewAt(index);
6720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewInLayout(View view) {
6760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
677a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewInLayout(view);
6780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViews(int start, int count) {
6820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
683a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
6840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
685a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViews(start, count);
6860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewsInLayout(int start, int count) {
6900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
691a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
6920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
693a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewsInLayout(start, count);
694abded66084680bb31cc7ea403c88f44f79a3c884Michael Jurka    }
695abded66084680bb31cc7ea403c88f44f79a3c884Michael Jurka
69631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
69731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onAttachedToWindow() {
69831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onAttachedToWindow();
69931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
70031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
70131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
702af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public void setTagToCellInfoForPoint(int touchX, int touchY) {
70331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo cellInfo = mCellInfo;
704eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung        Rect frame = mRect;
7058b805b17158886035b38261eb611d8641701ae43Michael Jurka        final int x = touchX + getScrollX();
7068b805b17158886035b38261eb611d8641701ae43Michael Jurka        final int y = touchY + getScrollY();
707a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        final int count = mShortcutsAndWidgets.getChildCount();
70831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
709af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        boolean found = false;
710af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        for (int i = count - 1; i >= 0; i--) {
711a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            final View child = mShortcutsAndWidgets.getChildAt(i);
712d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
713af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
7141b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen            if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) &&
7151b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen                    lp.isLockedToGrid) {
716af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                child.getHitRect(frame);
7170be025d64c1f84138fe430a58875886e66aae767Winson Chung
718eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                float scale = child.getScaleX();
719eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                frame = new Rect(child.getLeft(), child.getTop(), child.getRight(),
720eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                        child.getBottom());
7210be025d64c1f84138fe430a58875886e66aae767Winson Chung                // The child hit rect is relative to the CellLayoutChildren parent, so we need to
7220be025d64c1f84138fe430a58875886e66aae767Winson Chung                // offset that by this CellLayout's padding to test an (x,y) point that is relative
7230be025d64c1f84138fe430a58875886e66aae767Winson Chung                // to this view.
7248b805b17158886035b38261eb611d8641701ae43Michael Jurka                frame.offset(getPaddingLeft(), getPaddingTop());
725eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                frame.inset((int) (frame.width() * (1f - scale) / 2),
726eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                        (int) (frame.height() * (1f - scale) / 2));
7270be025d64c1f84138fe430a58875886e66aae767Winson Chung
728af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                if (frame.contains(x, y)) {
729af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cell = child;
730af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellX = lp.cellX;
731af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellY = lp.cellY;
732af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanX = lp.cellHSpan;
733af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanY = lp.cellVSpan;
734af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    found = true;
735af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    break;
73631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
73731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
738af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
739aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
740d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka        mLastDownOnOccupiedCell = found;
741d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka
742af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (!found) {
7430be025d64c1f84138fe430a58875886e66aae767Winson Chung            final int cellXY[] = mTmpXY;
744af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            pointToCellExact(x, y, cellXY);
74531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
746af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cell = null;
747af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellX = cellXY[0];
748af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellY = cellXY[1];
749af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanX = 1;
750af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanY = 1;
751af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
752af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        setTag(cellInfo);
753af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    }
75431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
755af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    @Override
756af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public boolean onInterceptTouchEvent(MotionEvent ev) {
757c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // First we clear the tag to ensure that on every touch down we start with a fresh slate,
758c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // even in the case where we return early. Not clearing here was causing bugs whereby on
759c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // long-press we'd end up picking up an item from a previous drag operation.
760c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        final int action = ev.getAction();
761c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
762c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        if (action == MotionEvent.ACTION_DOWN) {
763c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen            clearTagCellInfo();
764c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        }
765c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
766dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
767dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            return true;
768dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
76931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
770af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (action == MotionEvent.ACTION_DOWN) {
771af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
77231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
773eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung
77431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
77531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
77631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
777c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen    private void clearTagCellInfo() {
778c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        final CellInfo cellInfo = mCellInfo;
779c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cell = null;
780c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cellX = -1;
781c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cellY = -1;
782c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.spanX = 0;
783c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.spanY = 0;
784c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        setTag(cellInfo);
785c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen    }
786c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
78731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellInfo getTag() {
7880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return (CellInfo) super.getTag();
78931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
79031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7916569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
792aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Given a point, return the cell that strictly encloses that point
79331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
79431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
79531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
79631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
79731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellExact(int x, int y, int[] result) {
7984b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
7994b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
80031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
80131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
80231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
80331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
804d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xAxis = mCountX;
805d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yAxis = mCountY;
80631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
80731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] < 0) result[0] = 0;
80831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] >= xAxis) result[0] = xAxis - 1;
80931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] < 0) result[1] = 0;
81031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] >= yAxis) result[1] = yAxis - 1;
81131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
812aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
81331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
81431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that most closely encloses that point
81531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
81631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
81731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
81831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
81931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellRounded(int x, int y, int[] result) {
82031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
82131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
82231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
82331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
82431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a cell coordinate, return the point that represents the upper left corner of that cell
825aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
826aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellX X coordinate of the cell
82731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of the cell
828aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
82931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the point
83031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
83131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void cellToPoint(int cellX, int cellY, int[] result) {
8324b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
8334b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
83431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
83531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
83631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
83731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
83831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
839e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    /**
840482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Given a cell coordinate, return the point that represents the center of the cell
841e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     *
842e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param cellX X coordinate of the cell
843e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param cellY Y coordinate of the cell
844e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     *
845e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param result Array of 2 ints to hold the x and y coordinate of the point
846e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     */
847e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    void cellToCenterPoint(int cellX, int cellY, int[] result) {
84847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        regionToCenterPoint(cellX, cellY, 1, 1, result);
84947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
85047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
85147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    /**
85247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * Given a cell coordinate and span return the point that represents the center of the regio
85347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *
85447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellX X coordinate of the cell
85547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellY Y coordinate of the cell
85647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *
85747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param result Array of 2 ints to hold the x and y coordinate of the point
85847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     */
85947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
8604b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
8614b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
86247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) +
86347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                (spanX * mCellWidth + (spanX - 1) * mWidthGap) / 2;
86447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) +
86547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                (spanY * mCellHeight + (spanY - 1) * mHeightGap) / 2;
866e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    }
867e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen
86819f3792523fe2d55ea791a9286398a6120920690Adam Cohen     /**
86919f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * Given a cell coordinate and span fills out a corresponding pixel rect
87019f3792523fe2d55ea791a9286398a6120920690Adam Cohen     *
87119f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param cellX X coordinate of the cell
87219f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param cellY Y coordinate of the cell
87319f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param result Rect in which to write the result
87419f3792523fe2d55ea791a9286398a6120920690Adam Cohen     */
87519f3792523fe2d55ea791a9286398a6120920690Adam Cohen     void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) {
87619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int hStartPadding = getPaddingLeft();
87719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int vStartPadding = getPaddingTop();
87819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int left = hStartPadding + cellX * (mCellWidth + mWidthGap);
87919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int top = vStartPadding + cellY * (mCellHeight + mHeightGap);
88019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        result.set(left, top, left + (spanX * mCellWidth + (spanX - 1) * mWidthGap),
88119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                top + (spanY * mCellHeight + (spanY - 1) * mHeightGap));
88219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
88319f3792523fe2d55ea791a9286398a6120920690Adam Cohen
884482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public float getDistanceFromCell(float x, float y, int[] cell) {
885482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        cellToCenterPoint(cell[0], cell[1], mTmpPoint);
886482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        float distance = (float) Math.sqrt( Math.pow(x - mTmpPoint[0], 2) +
887482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                Math.pow(y - mTmpPoint[1], 2));
888482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return distance;
889482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
890482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
89184f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellWidth() {
89284f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellWidth;
89384f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
89484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
89584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellHeight() {
89684f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellHeight;
89784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
89884f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
899d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    int getWidthGap() {
900d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        return mWidthGap;
901d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    }
902d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
903d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    int getHeightGap() {
904d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        return mHeightGap;
905d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    }
906d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
9077f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen    Rect getContentRect(Rect r) {
9087f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        if (r == null) {
9097f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            r = new Rect();
9107f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
9117f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        int left = getPaddingLeft();
9127f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        int top = getPaddingTop();
9138b805b17158886035b38261eb611d8641701ae43Michael Jurka        int right = left + getWidth() - getPaddingLeft() - getPaddingRight();
9148b805b17158886035b38261eb611d8641701ae43Michael Jurka        int bottom = top + getHeight() - getPaddingTop() - getPaddingBottom();
9157f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        r.set(left, top, right, bottom);
9167f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        return r;
9177f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen    }
9187f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
919a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static void getMetrics(Rect metrics, Resources res, int measureWidth, int measureHeight,
920a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int countX, int countY, int orientation) {
921a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int numWidthGaps = countX - 1;
922a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int numHeightGaps = countY - 1;
923f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
924f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int widthGap;
925f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int heightGap;
926f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int cellWidth;
927f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int cellHeight;
928f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingLeft;
929f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingRight;
930f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingTop;
931f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingBottom;
932f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
933a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int maxGap = res.getDimensionPixelSize(R.dimen.workspace_max_gap);
934f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        if (orientation == LANDSCAPE) {
935f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellWidth = res.getDimensionPixelSize(R.dimen.workspace_cell_width_land);
936f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellHeight = res.getDimensionPixelSize(R.dimen.workspace_cell_height_land);
937f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            widthGap = res.getDimensionPixelSize(R.dimen.workspace_width_gap_land);
938f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            heightGap = res.getDimensionPixelSize(R.dimen.workspace_height_gap_land);
939f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingLeft = res.getDimensionPixelSize(R.dimen.cell_layout_left_padding_land);
940f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingRight = res.getDimensionPixelSize(R.dimen.cell_layout_right_padding_land);
941f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingTop = res.getDimensionPixelSize(R.dimen.cell_layout_top_padding_land);
942f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingBottom = res.getDimensionPixelSize(R.dimen.cell_layout_bottom_padding_land);
943f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        } else {
944f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            // PORTRAIT
945f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellWidth = res.getDimensionPixelSize(R.dimen.workspace_cell_width_port);
946f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellHeight = res.getDimensionPixelSize(R.dimen.workspace_cell_height_port);
947f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            widthGap = res.getDimensionPixelSize(R.dimen.workspace_width_gap_port);
948f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            heightGap = res.getDimensionPixelSize(R.dimen.workspace_height_gap_port);
949f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingLeft = res.getDimensionPixelSize(R.dimen.cell_layout_left_padding_port);
950f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingRight = res.getDimensionPixelSize(R.dimen.cell_layout_right_padding_port);
951f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingTop = res.getDimensionPixelSize(R.dimen.cell_layout_top_padding_port);
952f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingBottom = res.getDimensionPixelSize(R.dimen.cell_layout_bottom_padding_port);
953f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        }
954f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
955f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        if (widthGap < 0 || heightGap < 0) {
956f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int hSpace = measureWidth - paddingLeft - paddingRight;
957f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int vSpace = measureHeight - paddingTop - paddingBottom;
958a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int hFreeSpace = hSpace - (countX * cellWidth);
959a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int vFreeSpace = vSpace - (countY * cellHeight);
960a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            widthGap = Math.min(maxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
961a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            heightGap = Math.min(maxGap, numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
962f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        }
963f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        metrics.set(cellWidth, cellHeight, widthGap, heightGap);
964f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen    }
965f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
96631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
96731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
96831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
969aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
970aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
97131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
97231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
973aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
97431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
97531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
97631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
97731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
978d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numWidthGaps = mCountX - 1;
979d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numHeightGaps = mCountY - 1;
980d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
981234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
982dd13e3d0f9925b7bb80c37e21d039aab4fa7e7a1Michael Jurka            int hSpace = widthSpecSize - getPaddingLeft() - getPaddingRight();
983dd13e3d0f9925b7bb80c37e21d039aab4fa7e7a1Michael Jurka            int vSpace = heightSpecSize - getPaddingTop() - getPaddingBottom();
984f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int hFreeSpace = hSpace - (mCountX * mCellWidth);
985f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int vFreeSpace = vSpace - (mCountY * mCellHeight);
9864b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung            mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
9874b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung            mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
988a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
989234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        } else {
990234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen            mWidthGap = mOriginalWidthGap;
991234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen            mHeightGap = mOriginalHeightGap;
992ece7f5b3b55cab646941123e03589241a61678e2Winson Chung        }
9935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
9948c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY
9958c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int newWidth = widthSpecSize;
9968c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int newHeight = heightSpecSize;
9978c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        if (widthSpecMode == MeasureSpec.AT_MOST) {
9988b805b17158886035b38261eb611d8641701ae43Michael Jurka            newWidth = getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
9998c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka                ((mCountX - 1) * mWidthGap);
10008b805b17158886035b38261eb611d8641701ae43Michael Jurka            newHeight = getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
10018c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka                ((mCountY - 1) * mHeightGap);
10028c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            setMeasuredDimension(newWidth, newHeight);
10038c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        }
100431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10058c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int count = getChildCount();
100631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
100731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
10088b805b17158886035b38261eb611d8641701ae43Michael Jurka            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth - getPaddingLeft() -
10098b805b17158886035b38261eb611d8641701ae43Michael Jurka                    getPaddingRight(), MeasureSpec.EXACTLY);
10108b805b17158886035b38261eb611d8641701ae43Michael Jurka            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight - getPaddingTop() -
10118b805b17158886035b38261eb611d8641701ae43Michael Jurka                    getPaddingBottom(), MeasureSpec.EXACTLY);
101231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
101331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
10148c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        setMeasuredDimension(newWidth, newHeight);
101531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
101631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
101731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
101828750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int l, int t, int r, int b) {
101931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
102031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
10218c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            View child = getChildAt(i);
10228b805b17158886035b38261eb611d8641701ae43Michael Jurka            child.layout(getPaddingLeft(), getPaddingTop(),
10238b805b17158886035b38261eb611d8641701ae43Michael Jurka                    r - l - getPaddingRight(), b - t - getPaddingBottom());
102431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
102531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
102631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
102731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
1028dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1029dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super.onSizeChanged(w, h, oldw, oldh);
103018014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mBackgroundRect.set(0, 0, w, h);
1031b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundRect.set(mForegroundPadding, mForegroundPadding,
1032215b416c1f7cf0234b777155b823637a0902739fAdam Cohen                w - mForegroundPadding, h - mForegroundPadding);
1033dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1034dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1035dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @Override
103631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
1037a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setChildrenDrawingCacheEnabled(enabled);
103831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
103931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
104031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
104131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
1042a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setChildrenDrawnWithCacheEnabled(enabled);
104331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
104431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10455f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public float getBackgroundAlpha() {
10465f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        return mBackgroundAlpha;
1047dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1048dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
10491b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    public void setBackgroundAlphaMultiplier(float multiplier) {
1050a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka        if (mBackgroundAlphaMultiplier != multiplier) {
1051a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka            mBackgroundAlphaMultiplier = multiplier;
1052a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka            invalidate();
1053a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka        }
10541b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    }
10551b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen
1056ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen    public float getBackgroundAlphaMultiplier() {
1057ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen        return mBackgroundAlphaMultiplier;
1058ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen    }
1059ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen
10605f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public void setBackgroundAlpha(float alpha) {
1061afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka        if (mBackgroundAlpha != alpha) {
1062afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka            mBackgroundAlpha = alpha;
1063afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka            invalidate();
1064afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka        }
1065dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1066dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1067a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    public void setShortcutAndWidgetAlpha(float alpha) {
10680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int childCount = getChildCount();
10690142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        for (int i = 0; i < childCount; i++) {
1070dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            getChildAt(i).setAlpha(alpha);
1071dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
1072dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1073dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1074a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
1075a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (getChildCount() > 0) {
1076a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            return (ShortcutAndWidgetContainer) getChildAt(0);
1077a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        }
1078a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        return null;
1079a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    }
1080a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka
1081440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    public View getChildAt(int x, int y) {
1082a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        return mShortcutsAndWidgets.getChildAt(x, y);
1083440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
1084440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
108576fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen    public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
1086482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int delay, boolean permanent, boolean adjustOccupied) {
1087a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
1088482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean[][] occupied = mOccupied;
1089482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!permanent) {
1090482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            occupied = mTmpOccupied;
1091482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1092482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
109319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (clc.indexOfChild(child) != -1) {
1094bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1095bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            final ItemInfo info = (ItemInfo) child.getTag();
1096bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1097bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            // We cancel any existing animations
1098bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            if (mReorderAnimators.containsKey(lp)) {
1099bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                mReorderAnimators.get(lp).cancel();
1100bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                mReorderAnimators.remove(lp);
1101bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            }
1102bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1103482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int oldX = lp.x;
1104482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int oldY = lp.y;
1105482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (adjustOccupied) {
1106482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[lp.cellX][lp.cellY] = false;
1107482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[cellX][cellY] = true;
1108482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1109bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            lp.isLockedToGrid = true;
1110482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (permanent) {
1111482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.cellX = info.cellX = cellX;
1112482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.cellY = info.cellY = cellY;
1113482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else {
1114482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.tmpCellX = cellX;
1115482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.tmpCellY = cellY;
1116482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1117bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            clc.setupLp(lp);
1118bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            lp.isLockedToGrid = false;
1119482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int newX = lp.x;
1120482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int newY = lp.y;
1121bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
112276fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            lp.x = oldX;
112376fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            lp.y = oldY;
112476fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen
1125482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // Exit early if we're not actually moving the view
1126482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (oldX == newX && oldY == newY) {
1127482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.isLockedToGrid = true;
1128482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return true;
1129482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1130482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1131f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
1132482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.setDuration(duration);
1133482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mReorderAnimators.put(lp, va);
1134482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1135482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.addUpdateListener(new AnimatorUpdateListener() {
1136482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                @Override
1137bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationUpdate(ValueAnimator animation) {
1138482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    float r = ((Float) animation.getAnimatedValue()).floatValue();
113919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    lp.x = (int) ((1 - r) * oldX + r * newX);
114019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    lp.y = (int) ((1 - r) * oldY + r * newY);
11416b8a02d63a5d9cab8209381993e37db6a6afb753Adam Cohen                    child.requestLayout();
1142bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1143bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            });
1144482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.addListener(new AnimatorListenerAdapter() {
1145bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                boolean cancelled = false;
1146bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationEnd(Animator animation) {
1147bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // If the animation was cancelled, it means that another animation
1148bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // has interrupted this one, and we don't want to lock the item into
1149bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // place just yet.
1150bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    if (!cancelled) {
1151bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                        lp.isLockedToGrid = true;
1152482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        child.requestLayout();
1153bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    }
1154bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    if (mReorderAnimators.containsKey(lp)) {
1155bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                        mReorderAnimators.remove(lp);
1156bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    }
1157bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1158bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationCancel(Animator animation) {
1159bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    cancelled = true;
1160bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1161bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            });
1162482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.setStartDelay(delay);
1163482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.start();
1164bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            return true;
1165bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen        }
1166bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen        return false;
1167bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen    }
1168bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
11696569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
11706569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Estimate where the top left cell of the dragged item will land if it is dropped.
11716569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     *
11726569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originX The X value of the top left corner of the item
11736569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originY The Y value of the top left corner of the item
11746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanX The number of horizontal cells that the item spans
11756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanY The number of vertical cells that the item spans
11766569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param result The estimated drop cell X and Y.
11776569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
11786569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
1179d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countX = mCountX;
1180d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countY = mCountY;
11816569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1182a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // pointToCellRounded takes the top left of a cell but will pad that with
1183a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // cellWidth/2 and cellHeight/2 when finding the matching cell
1184a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        pointToCellRounded(originX, originY, result);
11856569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
11866569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // If the item isn't fully on this screen, snap to the edges
11876569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int rightOverhang = result[0] + spanX - countX;
11886569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (rightOverhang > 0) {
11896569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[0] -= rightOverhang; // Snap to right
11906569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
11916569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[0] = Math.max(0, result[0]); // Snap to left
11926569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int bottomOverhang = result[1] + spanY - countY;
11936569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (bottomOverhang > 0) {
11946569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[1] -= bottomOverhang; // Snap to bottom
11956569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
11966569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[1] = Math.max(0, result[1]); // Snap to top
11976569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
11986569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1199482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY, int cellX,
1200482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int cellY, int spanX, int spanY, boolean resize, Point dragOffset, Rect dragRegion) {
120108ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        final int oldDragCellX = mDragCell[0];
120208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        final int oldDragCellY = mDragCell[1];
1203482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1204b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung        if (v != null && dragOffset == null) {
1205a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2));
1206a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        } else {
1207a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mDragCenter.set(originX, originY);
1208a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        }
12096569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
12102801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        if (dragOutline == null && v == null) {
12112801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen            return;
12122801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        }
12132801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
1214482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (cellX != oldDragCellX || cellY != oldDragCellY) {
1215482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mDragCell[0] = cellX;
1216482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mDragCell[1] = cellY;
12176569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Find the top left corner of the rect the object will occupy
1218de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int[] topLeft = mTmpPoint;
1219482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            cellToPoint(cellX, cellY, topLeft);
1220de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
12214be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int left = topLeft[0];
12224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int top = topLeft[1];
12236569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1224b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung            if (v != null && dragOffset == null) {
122599e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // When drawing the drag outline, it did not account for margin offsets
122699e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // added by the view's parent.
122799e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
122899e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                left += lp.leftMargin;
122999e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                top += lp.topMargin;
123099e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen
123199e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // Offsets due to the size difference between the View and the dragOutline.
123299e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // There is a size difference to account for the outer blur, which may lie
123399e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // outside the bounds of the view.
1234a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                top += (v.getHeight() - dragOutline.getHeight()) / 2;
1235ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                // We center about the x axis
1236ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1237ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                        - dragOutline.getWidth()) / 2;
12386639687cd67bab1aeef2a75e5c6bc458b20dc082Adam Cohen            } else {
1239b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                if (dragOffset != null && dragRegion != null) {
1240b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // Center the drag region *horizontally* in the cell and apply a drag
1241b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // outline offset
1242b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1243b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                             - dragRegion.width()) / 2;
1244b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    top += dragOffset.y;
1245b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                } else {
1246b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // Center the drag outline in the cell
1247b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1248b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                            - dragOutline.getWidth()) / 2;
1249b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
1250b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                            - dragOutline.getHeight()) / 2;
1251b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                }
1252a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            }
12534be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int oldIndex = mDragOutlineCurrent;
125408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[oldIndex].animateOut();
125508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
1256d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            Rect r = mDragOutlines[mDragOutlineCurrent];
1257d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
1258d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            if (resize) {
1259482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                cellToRect(cellX, cellY, spanX, spanY, r);
1260d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            }
1261150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
126208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
126308ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[mDragOutlineCurrent].animateIn();
12646569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
12656569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
12666569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1267e0310965022e7a1adb7ad489505d404186608689Adam Cohen    public void clearDragOutlines() {
1268e0310965022e7a1adb7ad489505d404186608689Adam Cohen        final int oldIndex = mDragOutlineCurrent;
1269e0310965022e7a1adb7ad489505d404186608689Adam Cohen        mDragOutlineAnims[oldIndex].animateOut();
1270d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        mDragCell[0] = mDragCell[1] = -1;
1271e0310965022e7a1adb7ad489505d404186608689Adam Cohen    }
1272e0310965022e7a1adb7ad489505d404186608689Adam Cohen
127331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
127470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Find a vacant area that will fit the given bounds nearest the requested
127570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * cell location. Uses Euclidean distance to score multiple vacant areas.
1276aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
127751afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelX The X location at which you want to search for a vacant area.
127851afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelY The Y location at which you want to search for a vacant area.
127970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanX Horizontal span of the object.
128070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanY Vertical span of the object.
1281de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * @param result Array in which to place the result, or null (in which case a new array will
1282de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     *        be allocated)
128370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @return The X, Y cell of a vacant area that can contain this object,
128470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *         nearest the requested location.
128531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
1286d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
1287d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int[] result) {
1288de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
12896a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    }
1290aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
12916a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    /**
12926a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * Find a vacant area that will fit the given bounds nearest the requested
12936a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * cell location. Uses Euclidean distance to score multiple vacant areas.
12946a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *
12956a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelX The X location at which you want to search for a vacant area.
12966a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelY The Y location at which you want to search for a vacant area.
1297d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
1298d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
1299d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
1300d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
1301d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1302d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *        be allocated)
1303d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1304d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
1305d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
1306d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
1307d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int spanY, int[] result, int[] resultSpan) {
1308d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        return findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null,
1309d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                result, resultSpan);
1310d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1311d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1312d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
1313d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1314d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
1315d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
1316d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
1317d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
13186a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanX Horizontal span of the object.
13196a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanY Vertical span of the object.
1320df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreOccupied If true, the result can be an occupied cell
1321df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1322df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *        be allocated)
13236a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @return The X, Y cell of a vacant area that can contain this object,
13246a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *         nearest the requested location.
13256a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     */
1326df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView,
1327df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            boolean ignoreOccupied, int[] result) {
1328d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY,
1329482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                spanX, spanY, ignoreView, ignoreOccupied, result, null, mOccupied);
1330d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1331d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1332d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private final Stack<Rect> mTempRectStack = new Stack<Rect>();
1333d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private void lazyInitTempRectStack() {
1334d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        if (mTempRectStack.isEmpty()) {
1335d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            for (int i = 0; i < mCountX * mCountY; i++) {
1336d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                mTempRectStack.push(new Rect());
1337d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            }
1338d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1339d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1340482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1341d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private void recycleTempRects(Stack<Rect> used) {
1342d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        while (!used.isEmpty()) {
1343d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            mTempRectStack.push(used.pop());
1344d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1345d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1346d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1347d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
1348d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1349d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
1350d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
1351d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
1352d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
1353d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
1354d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
1355d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
1356d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
1357d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param ignoreOccupied If true, the result can be an occupied cell
1358d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1359d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *        be allocated)
1360d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1361d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
1362d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
1363d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
1364482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            View ignoreView, boolean ignoreOccupied, int[] result, int[] resultSpan,
1365482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            boolean[][] occupied) {
1366d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        lazyInitTempRectStack();
1367c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
1368482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(ignoreView, occupied);
1369c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka
1370e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
1371e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // to the center of the item, but we are searching based on the top-left cell, so
1372e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // we translate the point over to correspond to the top-left.
1373e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
1374e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
1375e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen
137670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Keep track of best-scoring drop area
1377de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int[] bestXY = result != null ? result : new int[2];
137870864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        double bestDistance = Double.MAX_VALUE;
1379d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        final Rect bestRect = new Rect(-1, -1, -1, -1);
1380d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        final Stack<Rect> validRegions = new Stack<Rect>();
1381aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1382de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countX = mCountX;
1383de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countY = mCountY;
1384de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
1385d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        if (minSpanX <= 0 || minSpanY <= 0 || spanX <= 0 || spanY <= 0 ||
1386d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                spanX < minSpanX || spanY < minSpanY) {
1387d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            return bestXY;
1388d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1389d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1390d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        for (int y = 0; y < countY - (minSpanY - 1); y++) {
1391c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            inner:
1392d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            for (int x = 0; x < countX - (minSpanX - 1); x++) {
1393d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                int ySize = -1;
1394d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                int xSize = -1;
1395df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                if (ignoreOccupied) {
1396d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // First, let's see if this thing fits anywhere
1397d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    for (int i = 0; i < minSpanX; i++) {
1398d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        for (int j = 0; j < minSpanY; j++) {
1399df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                            if (occupied[x + i][y + j]) {
1400df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                                continue inner;
1401df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                            }
1402c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        }
1403c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
1404d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    xSize = minSpanX;
1405d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    ySize = minSpanY;
1406d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1407d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // We know that the item will fit at _some_ acceptable size, now let's see
1408d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // how big we can make it. We'll alternate between incrementing x and y spans
1409d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // until we hit a limit.
1410d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean incX = true;
1411d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean hitMaxX = xSize >= spanX;
1412d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean hitMaxY = ySize >= spanY;
1413d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    while (!(hitMaxX && hitMaxY)) {
1414d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        if (incX && !hitMaxX) {
1415d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            for (int j = 0; j < ySize; j++) {
1416d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                if (x + xSize > countX -1 || occupied[x + xSize][y + j]) {
1417d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    // We can't move out horizontally
1418d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    hitMaxX = true;
1419d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                }
1420d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1421d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            if (!hitMaxX) {
1422d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                xSize++;
1423d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1424d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        } else if (!hitMaxY) {
1425d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            for (int i = 0; i < xSize; i++) {
1426d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                if (y + ySize > countY - 1 || occupied[x + i][y + ySize]) {
1427d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    // We can't move out vertically
1428d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    hitMaxY = true;
1429d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                }
1430d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1431d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            if (!hitMaxY) {
1432d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                ySize++;
1433d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1434d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        }
1435d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        hitMaxX |= xSize >= spanX;
1436d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        hitMaxY |= ySize >= spanY;
1437d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        incX = !incX;
1438d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1439d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    incX = true;
1440d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    hitMaxX = xSize >= spanX;
1441d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    hitMaxY = ySize >= spanY;
1442c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
14430be025d64c1f84138fe430a58875886e66aae767Winson Chung                final int[] cellXY = mTmpXY;
1444e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen                cellToCenterPoint(x, y, cellXY);
1445c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka
1446d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // We verify that the current rect is not a sub-rect of any of our previous
1447d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // candidates. In this case, the current rect is disqualified in favour of the
1448d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // containing rect.
1449d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                Rect currentRect = mTempRectStack.pop();
1450d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                currentRect.set(x, y, x + xSize, y + ySize);
1451d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                boolean contained = false;
1452d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                for (Rect r : validRegions) {
1453d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    if (r.contains(currentRect)) {
1454d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        contained = true;
1455d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        break;
1456d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1457d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                }
1458d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                validRegions.push(currentRect);
1459c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
1460c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        + Math.pow(cellXY[1] - pixelY, 2));
1461482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1462d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                if ((distance <= bestDistance && !contained) ||
1463d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        currentRect.contains(bestRect)) {
1464c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestDistance = distance;
1465c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[0] = x;
1466c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[1] = y;
1467d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    if (resultSpan != null) {
1468d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        resultSpan[0] = xSize;
1469d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        resultSpan[1] = ySize;
1470d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1471d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    bestRect.set(currentRect);
1472c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
147331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
147431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1475c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
1476482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(ignoreView, occupied);
147731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1478c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        // Return -1, -1 if no suitable location found
1479c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        if (bestDistance == Double.MAX_VALUE) {
1480c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen            bestXY[0] = -1;
1481c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen            bestXY[1] = -1;
148270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        }
1483d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        recycleTempRects(validRegions);
1484c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        return bestXY;
148531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1486aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1487482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     /**
1488482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1489482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * cell location, and will also weigh in a suggested direction vector of the
1490482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * desired location. This method computers distance based on unit grid distances,
1491482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * not pixel distances.
1492482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *
149347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellX The X cell nearest to which you want to search for a vacant area.
149447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellY The Y cell nearest which you want to search for a vacant area.
1495482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param spanX Horizontal span of the object.
1496482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param spanY Vertical span of the object.
149747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param direction The favored direction in which the views should move from x, y
149847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param exactDirectionOnly If this parameter is true, then only solutions where the direction
149947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *        matches exactly. Otherwise we find the best matching direction.
150047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param occoupied The array which represents which cells in the CellLayout are occupied
150147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param blockOccupied The array which represents which cells in the specified block (cellX,
150247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *        cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
1503482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1504482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *        be allocated)
1505482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1506482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *         nearest the requested location.
1507482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     */
1508482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
150947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            boolean[][] occupied, boolean blockOccupied[][], int[] result) {
1510482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Keep track of best-scoring drop area
1511482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int[] bestXY = result != null ? result : new int[2];
1512482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        float bestDistance = Float.MAX_VALUE;
1513482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int bestDirectionScore = Integer.MIN_VALUE;
1514482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1515482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int countX = mCountX;
1516482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int countY = mCountY;
1517482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1518482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int y = 0; y < countY - (spanY - 1); y++) {
1519482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            inner:
1520482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int x = 0; x < countX - (spanX - 1); x++) {
1521482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                // First, let's see if this thing fits anywhere
1522482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                for (int i = 0; i < spanX; i++) {
1523482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    for (int j = 0; j < spanY; j++) {
152447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
1525482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                            continue inner;
1526482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        }
1527482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    }
1528482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1529482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1530482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                float distance = (float)
1531482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
1532482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                int[] curDirection = mTmpPoint;
153347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                computeDirectionVector(x - cellX, y - cellY, curDirection);
153447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                // The direction score is just the dot product of the two candidate direction
153547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                // and that passed in.
1536482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                int curDirectionScore = direction[0] * curDirection[0] +
1537482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        direction[1] * curDirection[1];
153847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean exactDirectionOnly = false;
153947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean directionMatches = direction[0] == curDirection[0] &&
154047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        direction[0] == curDirection[0];
154147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                if ((directionMatches || !exactDirectionOnly) &&
154247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        Float.compare(distance,  bestDistance) < 0 || (Float.compare(distance,
1543482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
1544482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestDistance = distance;
1545482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestDirectionScore = curDirectionScore;
1546482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestXY[0] = x;
1547482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestXY[1] = y;
1548482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1549482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1550482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1551482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1552482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Return -1, -1 if no suitable location found
1553482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (bestDistance == Float.MAX_VALUE) {
1554482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            bestXY[0] = -1;
1555482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            bestXY[1] = -1;
1556482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1557482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return bestXY;
1558482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1559482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1560482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
15618baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int[] direction, ItemConfiguration currentState) {
15628baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        CellAndSpan c = currentState.map.get(v);
1563482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean success = false;
15648baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1565482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
1566482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
15678baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        findNearestArea(c.x, c.y, c.spanX, c.spanY, direction, mTmpOccupied, null, mTempLocation);
1568482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1569482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
15708baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            c.x = mTempLocation[0];
15718baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            c.y = mTempLocation[1];
1572482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            success = true;
1573482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
15748baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1575482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return success;
1576482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1577482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1578f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen    /**
1579f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * This helper class defines a cluster of views. It helps with defining complex edges
1580f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * of the cluster and determining how those edges interact with other views. The edges
1581f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * essentially define a fine-grained boundary around the cluster of views -- like a more
1582f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     * precise version of a bounding box.
1583f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen     */
1584f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen    private class ViewCluster {
1585f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int LEFT = 0;
1586f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int TOP = 1;
1587f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int RIGHT = 2;
1588f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        final static int BOTTOM = 3;
1589f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1590f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ArrayList<View> views;
1591f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ItemConfiguration config;
1592f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        Rect boundingRect = new Rect();
1593f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1594f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] leftEdge = new int[mCountY];
1595f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] rightEdge = new int[mCountY];
1596f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] topEdge = new int[mCountX];
1597f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int[] bottomEdge = new int[mCountX];
1598f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean leftEdgeDirty, rightEdgeDirty, topEdgeDirty, bottomEdgeDirty, boundingRectDirty;
159947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1600f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        @SuppressWarnings("unchecked")
1601f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
1602f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            this.views = (ArrayList<View>) views.clone();
1603f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            this.config = config;
1604f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            resetEdges();
1605f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
160647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1607f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void resetEdges() {
1608f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (int i = 0; i < mCountX; i++) {
1609f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                topEdge[i] = -1;
1610f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                bottomEdge[i] = -1;
1611f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1612f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (int i = 0; i < mCountY; i++) {
1613f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                leftEdge[i] = -1;
1614f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                rightEdge[i] = -1;
1615f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1616f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            leftEdgeDirty = true;
1617f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            rightEdgeDirty = true;
1618f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            bottomEdgeDirty = true;
1619f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            topEdgeDirty = true;
1620f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            boundingRectDirty = true;
1621f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1622f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1623f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void computeEdge(int which, int[] edge) {
1624f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int count = views.size();
1625f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (int i = 0; i < count; i++) {
1626f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan cs = config.map.get(views.get(i));
1627f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                switch (which) {
1628f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case LEFT:
1629f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int left = cs.x;
1630f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
1631f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (left < edge[j] || edge[j] < 0) {
1632f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = left;
1633f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1634f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1635f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1636f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case RIGHT:
1637f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int right = cs.x + cs.spanX;
1638f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
1639f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (right > edge[j]) {
1640f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = right;
1641f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1642f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1643f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1644f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case TOP:
1645f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int top = cs.y;
1646f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
1647f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (top < edge[j] || edge[j] < 0) {
1648f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = top;
1649f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1650f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1651f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1652f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case BOTTOM:
1653f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        int bottom = cs.y + cs.spanY;
1654f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
1655f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            if (bottom > edge[j]) {
1656f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                                edge[j] = bottom;
1657f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            }
1658f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1659f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1660f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1661f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
166247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
166347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1664f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean isViewTouchingEdge(View v, int whichEdge) {
1665f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            CellAndSpan cs = config.map.get(v);
166647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1667f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int[] edge = getEdge(whichEdge);
1668f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1669f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            switch (whichEdge) {
1670f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case LEFT:
1671f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
1672f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.x + cs.spanX) {
1673f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
167447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        }
167547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    }
1676f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1677f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case RIGHT:
1678f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
1679f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.x) {
1680f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
1681f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1682f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1683f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1684f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case TOP:
1685f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
1686f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.y + cs.spanY) {
1687f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
1688f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1689f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1690f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1691f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case BOTTOM:
1692f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
1693f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (edge[i] == cs.y) {
1694f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            return true;
1695f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1696f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1697f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    break;
1698f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1699f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return false;
1700f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1701f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1702f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void shift(int whichEdge, int delta) {
1703f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: views) {
1704f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan c = config.map.get(v);
1705f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                switch (whichEdge) {
1706f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case LEFT:
1707f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.x -= delta;
1708f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1709f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case RIGHT:
1710f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.x += delta;
1711f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1712f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case TOP:
1713f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.y -= delta;
1714f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1715f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case BOTTOM:
1716f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    default:
1717f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        c.y += delta;
1718f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        break;
1719f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1720f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1721f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            resetEdges();
1722f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1723f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1724f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public void addView(View v) {
1725f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            views.add(v);
1726f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            resetEdges();
1727f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1728f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1729f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public Rect getBoundingRect() {
1730f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (boundingRectDirty) {
1731f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                boolean first = true;
1732f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                for (View v: views) {
1733f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    CellAndSpan c = config.map.get(v);
1734f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    if (first) {
1735f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        boundingRect.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
1736f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        first = false;
1737f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    } else {
1738a56dc1077e09faca984a59b861c5dc6364ae2f21Adam Cohen                        boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
1739a56dc1077e09faca984a59b861c5dc6364ae2f21Adam Cohen                    }
174047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
174147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
1742f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return boundingRect;
1743f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1744f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1745f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getEdge(int which) {
1746f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            switch (which) {
1747f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case LEFT:
1748f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getLeftEdge();
1749f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case RIGHT:
1750f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getRightEdge();
1751f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case TOP:
1752f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getTopEdge();
1753f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                case BOTTOM:
1754f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                default:
1755f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    return getBottomEdge();
1756f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1757f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1758f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1759f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getLeftEdge() {
1760f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (leftEdgeDirty) {
1761f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(LEFT, leftEdge);
1762f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1763f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return leftEdge;
1764f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1765f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1766f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getRightEdge() {
1767f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (rightEdgeDirty) {
1768f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(RIGHT, rightEdge);
1769f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1770f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return rightEdge;
1771f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1772f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1773f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getTopEdge() {
1774f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (topEdgeDirty) {
1775f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(TOP, topEdge);
1776f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1777f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return topEdge;
1778f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1779f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1780f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public int[] getBottomEdge() {
1781f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (bottomEdgeDirty) {
1782f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                computeEdge(BOTTOM, bottomEdge);
1783f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1784f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return bottomEdge;
1785f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1786f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1787f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        PositionComparator comparator = new PositionComparator();
1788f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        class PositionComparator implements Comparator<View> {
1789f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int whichEdge = 0;
1790f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            public int compare(View left, View right) {
1791f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan l = config.map.get(left);
1792f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                CellAndSpan r = config.map.get(right);
1793f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                switch (whichEdge) {
1794f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case LEFT:
1795f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return (r.x + r.spanX) - (l.x + l.spanX);
1796f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case RIGHT:
1797f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return l.x - r.x;
1798f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case TOP:
1799f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return (r.y + r.spanY) - (l.y + l.spanY);
1800f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    case BOTTOM:
1801f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    default:
1802f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        return l.y - r.y;
1803f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1804f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1805f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1806f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1807f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public void sortConfigurationForEdgePush(int edge) {
1808f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            comparator.whichEdge = edge;
1809f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            Collections.sort(config.sortedViews, comparator);
181047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
181147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
181247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1813f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen    private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
1814f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int[] direction, View dragView, ItemConfiguration currentState) {
1815f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1816f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ViewCluster cluster = new ViewCluster(views, currentState);
1817f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        Rect clusterRect = cluster.getBoundingRect();
1818f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int whichEdge;
1819f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        int pushDistance;
1820f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean fail = false;
1821e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
1822f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Determine the edge of the cluster that will be leading the push and how far
1823f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // the cluster must be shifted.
1824f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (direction[0] < 0) {
1825f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.LEFT;
1826f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
1827e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen        } else if (direction[0] > 0) {
1828f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.RIGHT;
1829f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
1830f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        } else if (direction[1] < 0) {
1831f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.TOP;
1832f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
1833f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        } else {
1834f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            whichEdge = ViewCluster.BOTTOM;
1835f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
1836f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1837f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1838f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Break early for invalid push distance.
1839f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (pushDistance <= 0) {
1840f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return false;
1841f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1842f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1843f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Mark the occupied state as false for the group of views we want to move.
1844f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
1845f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            CellAndSpan c = currentState.map.get(v);
1846f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1847f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1848f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1849f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // We save the current configuration -- if we fail to find a solution we will revert
1850f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // to the initial state. The process of finding a solution modifies the configuration
1851f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // in place, hence the need for revert in the failure case.
1852f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        currentState.save();
1853f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1854f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // The pushing algorithm is simplified by considering the views in the order in which
1855f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // they would be pushed by the cluster. For example, if the cluster is leading with its
1856f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // left edge, we consider sort the views by their right edge, from right to left.
1857f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        cluster.sortConfigurationForEdgePush(whichEdge);
1858f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1859f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        while (pushDistance > 0 && !fail) {
1860f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: currentState.sortedViews) {
1861f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                // For each view that isn't in the cluster, we see if the leading edge of the
1862f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                // cluster is contacting the edge of that view. If so, we add that view to the
1863f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                // cluster.
1864f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                if (!cluster.views.contains(v) && v != dragView) {
1865f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    if (cluster.isViewTouchingEdge(v, whichEdge)) {
1866f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        LayoutParams lp = (LayoutParams) v.getLayoutParams();
1867f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        if (!lp.canReorder) {
1868f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            // The push solution includes the all apps button, this is not viable.
1869f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            fail = true;
1870f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                            break;
1871f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        }
1872f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        cluster.addView(v);
1873f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        CellAndSpan c = currentState.map.get(v);
1874f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1875f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        // Adding view to cluster, mark it as not occupied.
1876f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1877f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                    }
1878f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                }
1879f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
1880f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            pushDistance--;
1881f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1882f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // The cluster has been completed, now we move the whole thing over in the appropriate
1883f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // direction.
1884f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            cluster.shift(whichEdge, 1);
1885e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen        }
1886e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
1887f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        boolean foundSolution = false;
1888f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        clusterRect = cluster.getBoundingRect();
1889f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1890f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // Due to the nature of the algorithm, the only check required to verify a valid solution
1891f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // is to ensure that completed shifted cluster lies completely within the cell layout.
1892f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
1893f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                clusterRect.bottom <= mCountY) {
1894f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            foundSolution = true;
1895f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        } else {
1896f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            currentState.restore();
1897f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
1898e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
1899f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        // In either case, we set the occupied array as marked for the location of the views
1900f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: cluster.views) {
1901f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            CellAndSpan c = currentState.map.get(v);
1902f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1903e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen        }
1904f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1905f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        return foundSolution;
1906e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen    }
1907e0489500d2a2cf410cb3cb687cbc59e966d7084eAdam Cohen
19088baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
1909f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            int[] direction, View dragView, ItemConfiguration currentState) {
191047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (views.size() == 0) return true;
191147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
191247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean success = false;
191347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        Rect boundingRect = null;
19148baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We construct a rect which represents the entire group of views passed in
191547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        for (View v: views) {
19168baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
191747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            if (boundingRect == null) {
19188baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                boundingRect = new Rect(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
191947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            } else {
19208baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
192147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
192247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
192347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
19248baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Mark the occupied state as false for the group of views we want to move.
1925f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
19268baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
19278baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
192847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
192947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
193047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
193147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int top = boundingRect.top;
193247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int left = boundingRect.left;
19338baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We mark more precisely which parts of the bounding rect are truly occupied, allowing
1934a56dc1077e09faca984a59b861c5dc6364ae2f21Adam Cohen        // for interlocking.
1935f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
19368baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
19378baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x - left, c.y - top, c.spanX, c.spanY, blockOccupied, true);
193847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
193947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
194047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
194147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1942f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
1943f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
194447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
19458baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // If we successfuly found a location by pushing the block of views, we commit it
194647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
19478baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int deltaX = mTempLocation[0] - boundingRect.left;
19488baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int deltaY = mTempLocation[1] - boundingRect.top;
1949f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: views) {
19508baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                CellAndSpan c = currentState.map.get(v);
19518baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c.x += deltaX;
19528baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c.y += deltaY;
195347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
195447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            success = true;
195547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
1956482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
19578baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // In either case, we set the occupied array as marked for the location of the views
1958f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        for (View v: views) {
19598baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
19608baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1961482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1962482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return success;
1963482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1964482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1965482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void markCellsForRect(Rect r, boolean[][] occupied, boolean value) {
1966482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(r.left, r.top, r.width(), r.height(), occupied, value);
1967482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1968482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
19694abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // This method tries to find a reordering solution which satisfies the push mechanic by trying
19704abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // to push items in each of the cardinal directions, in an order based on the direction vector
19714abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // passed.
19724abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
19734abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int[] direction, View ignoreView, ItemConfiguration solution) {
19744abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
19754abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If the direction vector has two non-zero components, we try pushing
19764abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // separately in each of the components.
19774abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int temp = direction[1];
19784abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = 0;
1979f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1980f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
19814abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
19824abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
19834abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
19844abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = temp;
19854abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[0];
19864abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = 0;
1987f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
1988f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
19894abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
19904abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
19914abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
19924abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Revert the direction
19934abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
19944abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
19954abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Now we try pushing in each component of the opposite direction
19964abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
19974abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
19984abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[1];
19994abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = 0;
2000f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20014abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20024abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20034abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20044abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20054abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = temp;
20064abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[0];
20074abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = 0;
2008f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20094abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20104abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20114abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20124abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // revert the direction
20134abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
20144abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20154abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20164abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20174abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        } else {
20184abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If the direction vector has a single non-zero component, we push first in the
20194abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // direction of the vector
2020f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20214abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20224abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20234abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20244abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Then we try the opposite direction
20254abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20264abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
2027f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20284abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20294abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20304abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20314abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Switch the direction back
20324abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20334abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20344abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20354abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If we have failed to find a push solution with the above, then we try
20364abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // to find a solution by pushing along the perpendicular axis.
20374abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20384abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Swap the components
20394abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int temp = direction[1];
20404abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = direction[0];
20414abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
2042f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20434abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20444abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20454abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20464abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20474abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Then we try the opposite direction
20484abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20494abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
2050f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
20514abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
20524abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
20534abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
20544abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Switch the direction back
20554abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
20564abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
20574abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
20584abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Swap the components back
20594abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[1];
20604abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = direction[0];
20614abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
20624abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        }
20634abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        return false;
20644abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    }
20654abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
2066482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
20678baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            View ignoreView, ItemConfiguration solution) {
2068e3e03bcd313ba8060f2832b6a16dea6fd6d532eaWinson Chung        // Return early if get invalid cell positions
2069e3e03bcd313ba8060f2832b6a16dea6fd6d532eaWinson Chung        if (cellX < 0 || cellY < 0) return false;
2070482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
20718baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        mIntersectingViews.clear();
2072482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
2073482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
20748baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Mark the desired location of the view currently being dragged.
2075482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (ignoreView != null) {
20768baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(ignoreView);
207719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (c != null) {
207819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                c.x = cellX;
207919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                c.y = cellY;
208019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2081482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2082482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
2083482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        Rect r1 = new Rect();
20848baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (View child: solution.map.keySet()) {
2085482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == ignoreView) continue;
20868baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
2087482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
20888baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
2089482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (Rect.intersects(r0, r1)) {
2090482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                if (!lp.canReorder) {
2091482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    return false;
2092482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2093482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mIntersectingViews.add(child);
2094482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2095482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
209647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
20974abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // First we try to find a solution which respects the push mechanic. That is,
20984abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // we try to find a solution such that no displaced item travels through another item
20994abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // without also displacing that item.
21004abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
210119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                solution)) {
210247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            return true;
210347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
210447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
21054abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // Next we try moving the views as a block, but without requiring the push mechanic.
2106f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
210719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                solution)) {
2108482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            return true;
2109482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
211047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
2111482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Ok, they couldn't move as a block, let's move them individually
2112482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (View v : mIntersectingViews) {
21138baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
2114482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return false;
2115482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2116482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2117482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return true;
2118482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2119482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2120482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    /*
2121482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
2122482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * the provided point and the provided cell
2123482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     */
212447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
2125482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        double angle = Math.atan(((float) deltaY) / deltaX);
2126482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2127482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result[0] = 0;
2128482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result[1] = 0;
2129482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (Math.abs(Math.cos(angle)) > 0.5f) {
2130482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = (int) Math.signum(deltaX);
2131482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2132482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (Math.abs(Math.sin(angle)) > 0.5f) {
2133482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[1] = (int) Math.signum(deltaY);
2134482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2135482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2136482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
21378baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private void copyOccupiedArray(boolean[][] occupied) {
21388baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (int i = 0; i < mCountX; i++) {
21398baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            for (int j = 0; j < mCountY; j++) {
21408baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                occupied[i][j] = mOccupied[i][j];
21418baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            }
21428baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        }
21438baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    }
21448baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
2145482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    ItemConfiguration simpleSwap(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
2146482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) {
21478baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Copy the current state into the solution. This solution will be manipulated as necessary.
21488baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        copyCurrentStateToSolution(solution, false);
21498baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Copy the current occupied array into the temporary occupied array. This array will be
21508baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // manipulated as necessary to find a solution.
21518baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        copyOccupiedArray(mTmpOccupied);
2152482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2153482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // We find the nearest cell into which we would place the dragged item, assuming there's
2154482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // nothing in its way.
2155482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int result[] = new int[2];
2156482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
2157482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2158482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean success = false;
2159482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // First we try the exact nearest position of the item being dragged,
2160482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // we will then want to try to move this around to other neighbouring positions
21618baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
21628baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                solution);
2163482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2164482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!success) {
2165482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
2166482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // x, then 1 in y etc.
2167482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (spanX > minSpanX && (minSpanY == spanY || decX)) {
2168482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY, direction,
2169482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        dragView, false, solution);
2170482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else if (spanY > minSpanY) {
2171482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1, direction,
2172482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        dragView, true, solution);
2173482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2174482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = false;
2175482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2176482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = true;
2177482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewX = result[0];
2178482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewY = result[1];
2179482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanX = spanX;
2180482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanY = spanY;
2181482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2182482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return solution;
2183482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2184482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2185482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
2186a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2187482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2188a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
2189482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
21908baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c;
2191482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (temp) {
21928baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
2193482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else {
21948baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
2195482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2196f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            solution.add(child, c);
2197482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2198482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2199482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2200482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
2201482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2202482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2203482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mTmpOccupied[i][j] = false;
2204482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2205482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2206482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2207a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2208482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2209a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
2210482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == dragView) continue;
2211482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
22128baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
22138baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (c != null) {
22148baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.tmpCellX = c.x;
22158baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.tmpCellY = c.y;
22168baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.cellHSpan = c.spanX;
22178baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.cellVSpan = c.spanY;
22188baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
2219482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2220482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2221482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
2222482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                solution.dragViewSpanY, mTmpOccupied, true);
2223482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2224482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2225482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
2226482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            commitDragView) {
2227482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2228482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean[][] occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
2229482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2230482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2231482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[i][j] = false;
2232482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2233482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2234482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2235a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2236482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2237a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
2238482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == dragView) continue;
22398baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
22408baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (c != null) {
224119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                animateChildToPosition(child, c.x, c.y, REORDER_ANIMATION_DURATION, 0,
224219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        DESTRUCTIVE_REORDER, false);
22438baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                markCellsForView(c.x, c.y, c.spanX, c.spanY, occupied, true);
2244482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2245482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2246482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (commitDragView) {
2247482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
2248482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    solution.dragViewSpanY, occupied, true);
2249482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2250482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2251482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
225219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // This method starts or changes the reorder hint animations
225319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void beginOrAdjustHintAnimations(ItemConfiguration solution, View dragView, int delay) {
225419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int childCount = mShortcutsAndWidgets.getChildCount();
225519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < childCount; i++) {
225619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
225719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (child == dragView) continue;
225819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            CellAndSpan c = solution.map.get(child);
225919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
226019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (c != null) {
226119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                ReorderHintAnimation rha = new ReorderHintAnimation(child, lp.cellX, lp.cellY,
226219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        c.x, c.y, c.spanX, c.spanY);
2263d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                rha.animate();
226419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
226519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
226619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
226719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
226819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // Class which represents the reorder hint animations. These animations show that an item is
226919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // in a temporary state, and hint at where the item will return to.
227019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    class ReorderHintAnimation {
227119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        View child;
2272d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalDeltaX;
2273d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalDeltaY;
2274d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initDeltaX;
2275d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initDeltaY;
2276d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalScale;
2277d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initScale;
227850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        private static final int DURATION = 300;
2279e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen        Animator a;
228019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
228119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        public ReorderHintAnimation(View child, int cellX0, int cellY0, int cellX1, int cellY1,
228219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                int spanX, int spanY) {
228319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint);
228419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int x0 = mTmpPoint[0];
228519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int y0 = mTmpPoint[1];
228619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint);
228719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int x1 = mTmpPoint[0];
228819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int y1 = mTmpPoint[1];
228919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int dX = x1 - x0;
229019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int dY = y1 - y0;
2291d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalDeltaX = 0;
2292d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalDeltaY = 0;
229319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (dX == dY && dX == 0) {
229419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            } else {
229519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (dY == 0) {
2296d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaX = - Math.signum(dX) * mReorderHintAnimationMagnitude;
229719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else if (dX == 0) {
2298d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaY = - Math.signum(dY) * mReorderHintAnimationMagnitude;
229919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else {
230019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    double angle = Math.atan( (float) (dY) / dX);
2301d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaX = (int) (- Math.signum(dX) *
2302fe41ac641bdef7ea96dcbac59b4f3abdbdff6cfeAdam Cohen                            Math.abs(Math.cos(angle) * mReorderHintAnimationMagnitude));
2303d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaY = (int) (- Math.signum(dY) *
2304fe41ac641bdef7ea96dcbac59b4f3abdbdff6cfeAdam Cohen                            Math.abs(Math.sin(angle) * mReorderHintAnimationMagnitude));
230519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
230619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2307d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initDeltaX = child.getTranslationX();
2308d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initDeltaY = child.getTranslationY();
2309307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen            finalScale = getChildrenScale() - 4.0f / child.getWidth();
2310d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initScale = child.getScaleX();
231119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            this.child = child;
231219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
231319f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2314d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        void animate() {
231519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (mShakeAnimators.containsKey(child)) {
231619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                ReorderHintAnimation oldAnimation = mShakeAnimators.get(child);
2317d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                oldAnimation.cancel();
231819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mShakeAnimators.remove(child);
2319e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                if (finalDeltaX == 0 && finalDeltaY == 0) {
2320e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                    completeAnimationImmediately();
2321e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                    return;
2322e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                }
232319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2324d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            if (finalDeltaX == 0 && finalDeltaY == 0) {
232519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                return;
232619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2327f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
2328e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            a = va;
232919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.setRepeatMode(ValueAnimator.REVERSE);
233019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.setRepeatCount(ValueAnimator.INFINITE);
23317bdfc9700b1cad043c04c757f134db1bf3df00daAdam Cohen            va.setDuration(DURATION);
2332d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            va.setStartDelay((int) (Math.random() * 60));
233319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.addUpdateListener(new AnimatorUpdateListener() {
233419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                @Override
233519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                public void onAnimationUpdate(ValueAnimator animation) {
233619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    float r = ((Float) animation.getAnimatedValue()).floatValue();
2337d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float x = r * finalDeltaX + (1 - r) * initDeltaX;
2338d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float y = r * finalDeltaY + (1 - r) * initDeltaY;
233919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    child.setTranslationX(x);
234019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    child.setTranslationY(y);
2341d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float s = r * finalScale + (1 - r) * initScale;
234250e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely                    child.setScaleX(s);
234350e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely                    child.setScaleY(s);
234419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
234519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            });
234619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.addListener(new AnimatorListenerAdapter() {
234719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                public void onAnimationRepeat(Animator animation) {
234819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    // We make sure to end only after a full period
2349d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initDeltaX = 0;
2350d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initDeltaY = 0;
2351307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                    initScale = getChildrenScale();
235219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
235319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            });
235419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mShakeAnimators.put(child, this);
235519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.start();
235619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
235719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2358d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        private void cancel() {
2359e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            if (a != null) {
2360e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                a.cancel();
2361e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            }
236219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
2363e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen
236450e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        private void completeAnimationImmediately() {
2365e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            if (a != null) {
2366e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                a.cancel();
2367e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            }
236850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely
23692ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka            AnimatorSet s = LauncherAnimUtils.createAnimatorSet();
2370e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            a = s;
237150e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.playTogether(
2372307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                LauncherAnimUtils.ofFloat(child, "scaleX", getChildrenScale()),
2373307fe23f125cbbd5512ad8d4660025f2ab68f30bAdam Cohen                LauncherAnimUtils.ofFloat(child, "scaleY", getChildrenScale()),
23742ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "translationX", 0f),
23752ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "translationY", 0f)
237650e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            );
237750e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.setDuration(REORDER_ANIMATION_DURATION);
237850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
237950e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.start();
238050e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        }
238119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
238219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
238319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void completeAndClearReorderHintAnimations() {
238419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (ReorderHintAnimation a: mShakeAnimators.values()) {
238550e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            a.completeAnimationImmediately();
238619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
238719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mShakeAnimators.clear();
238819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
238919f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2390482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void commitTempPlacement() {
2391482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2392482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2393482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mOccupied[i][j] = mTmpOccupied[i][j];
2394482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2395482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2396a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2397482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2398ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
2399ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
2400ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            ItemInfo info = (ItemInfo) child.getTag();
24012acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            // We do a null check here because the item info can be null in the case of the
24022acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            // AllApps button in the hotseat.
24032acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            if (info != null) {
2404487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                if (info.cellX != lp.tmpCellX || info.cellY != lp.tmpCellY ||
2405487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                        info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan) {
2406487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                    info.requiresDbUpdate = true;
2407487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                }
24082acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen                info.cellX = lp.cellX = lp.tmpCellX;
24092acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen                info.cellY = lp.cellY = lp.tmpCellY;
2410bebf042666cffe52039b875a549a582abd78a431Adam Cohen                info.spanX = lp.cellHSpan;
2411bebf042666cffe52039b875a549a582abd78a431Adam Cohen                info.spanY = lp.cellVSpan;
24122acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            }
2413482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
24142acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen        mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
2415482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2416482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2417482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void setUseTempCoords(boolean useTempCoords) {
2418a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2419482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2420a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
2421482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            lp.useTmpCoords = useTempCoords;
2422482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2423482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2424482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2425482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY,
2426482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int spanX, int spanY, View dragView, ItemConfiguration solution) {
2427482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int[] result = new int[2];
2428482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int[] resultSpan = new int[2];
2429482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null, result,
2430482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                resultSpan);
2431482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (result[0] >= 0 && result[1] >= 0) {
2432482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            copyCurrentStateToSolution(solution, false);
2433482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewX = result[0];
2434482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewY = result[1];
2435482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanX = resultSpan[0];
2436482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanY = resultSpan[1];
2437482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = true;
2438482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2439482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = false;
2440482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2441482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return solution;
2442482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2443482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2444482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void prepareChildForDrag(View child) {
2445482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(child);
2446482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2447482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
244819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    /* This seems like it should be obvious and straight-forward, but when the direction vector
244919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    needs to match with the notion of the dragView pushing other views, we have to employ
245019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    a slightly more subtle notion of the direction vector. The question is what two points is
245119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    the vector between? The center of the dragView and its desired destination? Not quite, as
245219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    this doesn't necessarily coincide with the interaction of the dragView and items occupying
245319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    those cells. Instead we use some heuristics to often lock the vector to up, down, left
245419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    or right, which helps make pushing feel right.
245519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    */
245619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
245719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            int spanY, View dragView, int[] resultDirection) {
245819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int[] targetDestination = new int[2];
245919f3792523fe2d55ea791a9286398a6120920690Adam Cohen
246019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
246119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect dragRect = new Rect();
246219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        regionToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
246319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
246419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
246519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect dropRegionRect = new Rect();
246619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
246719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                dragView, dropRegionRect, mIntersectingViews);
246819f3792523fe2d55ea791a9286398a6120920690Adam Cohen
246919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int dropRegionSpanX = dropRegionRect.width();
247019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int dropRegionSpanY = dropRegionRect.height();
247119f3792523fe2d55ea791a9286398a6120920690Adam Cohen
247219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        regionToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
247319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                dropRegionRect.height(), dropRegionRect);
247419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
247519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
247619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
247719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
247819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (dropRegionSpanX == mCountX || spanX == mCountX) {
247919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            deltaX = 0;
248019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
248119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (dropRegionSpanY == mCountY || spanY == mCountY) {
248219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            deltaY = 0;
248319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
248419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
248519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (deltaX == 0 && deltaY == 0) {
248619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            // No idea what to do, give a random direction.
248719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            resultDirection[0] = 1;
248819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            resultDirection[1] = 0;
248919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        } else {
249019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            computeDirectionVector(deltaX, deltaY, resultDirection);
249119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
249219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
249319f3792523fe2d55ea791a9286398a6120920690Adam Cohen
249419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // For a given cell and span, fetch the set of views intersecting the region.
249519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
249619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
249719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (boundingRect != null) {
249819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
249919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
250019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        intersectingViews.clear();
250119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
250219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect r1 = new Rect();
250319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int count = mShortcutsAndWidgets.getChildCount();
250419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < count; i++) {
250519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
250619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (child == dragView) continue;
250719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
250819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
250919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (Rect.intersects(r0, r1)) {
251019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mIntersectingViews.add(child);
251119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (boundingRect != null) {
251219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    boundingRect.union(r1);
251319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
251419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
251519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
251619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
251719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
251819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
251919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View dragView, int[] result) {
252019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
252119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
252219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mIntersectingViews);
252319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        return !mIntersectingViews.isEmpty();
252419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
252519f3792523fe2d55ea791a9286398a6120920690Adam Cohen
252619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    void revertTempState() {
252719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (!isItemPlacementDirty() || DESTRUCTIVE_REORDER) return;
252819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int count = mShortcutsAndWidgets.getChildCount();
252919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < count; i++) {
253019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
253119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
253219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
253319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                lp.tmpCellX = lp.cellX;
253419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                lp.tmpCellY = lp.cellY;
253519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION,
253619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        0, false, false);
253719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
253819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
253919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        completeAndClearReorderHintAnimations();
254019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        setItemPlacementDirty(false);
254119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
254219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2543bebf042666cffe52039b875a549a582abd78a431Adam Cohen    boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
2544bebf042666cffe52039b875a549a582abd78a431Adam Cohen            View dragView, int[] direction, boolean commit) {
2545bebf042666cffe52039b875a549a582abd78a431Adam Cohen        int[] pixelXY = new int[2];
2546bebf042666cffe52039b875a549a582abd78a431Adam Cohen        regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
2547bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2548bebf042666cffe52039b875a549a582abd78a431Adam Cohen        // First we determine if things have moved enough to cause a different layout
2549bebf042666cffe52039b875a549a582abd78a431Adam Cohen        ItemConfiguration swapSolution = simpleSwap(pixelXY[0], pixelXY[1], spanX, spanY,
2550bebf042666cffe52039b875a549a582abd78a431Adam Cohen                 spanX,  spanY, direction, dragView,  true,  new ItemConfiguration());
2551bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2552bebf042666cffe52039b875a549a582abd78a431Adam Cohen        setUseTempCoords(true);
2553bebf042666cffe52039b875a549a582abd78a431Adam Cohen        if (swapSolution != null && swapSolution.isSolution) {
2554bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
2555bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // committing anything or animating anything as we just want to determine if a solution
2556bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // exists
2557bebf042666cffe52039b875a549a582abd78a431Adam Cohen            copySolutionToTempState(swapSolution, dragView);
2558bebf042666cffe52039b875a549a582abd78a431Adam Cohen            setItemPlacementDirty(true);
2559bebf042666cffe52039b875a549a582abd78a431Adam Cohen            animateItemsToSolution(swapSolution, dragView, commit);
2560bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2561bebf042666cffe52039b875a549a582abd78a431Adam Cohen            if (commit) {
2562bebf042666cffe52039b875a549a582abd78a431Adam Cohen                commitTempPlacement();
2563bebf042666cffe52039b875a549a582abd78a431Adam Cohen                completeAndClearReorderHintAnimations();
2564bebf042666cffe52039b875a549a582abd78a431Adam Cohen                setItemPlacementDirty(false);
2565bebf042666cffe52039b875a549a582abd78a431Adam Cohen            } else {
2566bebf042666cffe52039b875a549a582abd78a431Adam Cohen                beginOrAdjustHintAnimations(swapSolution, dragView,
2567bebf042666cffe52039b875a549a582abd78a431Adam Cohen                        REORDER_ANIMATION_DURATION);
2568bebf042666cffe52039b875a549a582abd78a431Adam Cohen            }
2569bebf042666cffe52039b875a549a582abd78a431Adam Cohen            mShortcutsAndWidgets.requestLayout();
2570bebf042666cffe52039b875a549a582abd78a431Adam Cohen        }
2571bebf042666cffe52039b875a549a582abd78a431Adam Cohen        return swapSolution.isSolution;
2572bebf042666cffe52039b875a549a582abd78a431Adam Cohen    }
2573bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2574482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    int[] createArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
2575482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            View dragView, int[] result, int resultSpan[], int mode) {
2576482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // First we determine if things have moved enough to cause a different layout
257747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
2578482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2579482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (resultSpan == null) {
2580482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan = new int[2];
2581482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2582482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
258319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // When we are checking drop validity or actually dropping, we don't recompute the
258419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // direction vector, since we want the solution to match the preview, and it's possible
258519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // that the exact position of the item has changed to result in a new reordering outcome.
2586b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen        if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
2587b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen               && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
258819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mDirectionVector[0] = mPreviousReorderDirection[0];
258919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mDirectionVector[1] = mPreviousReorderDirection[1];
259019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            // We reset this vector after drop
2591b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen            if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
2592b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen                mPreviousReorderDirection[0] = INVALID_DIRECTION;
2593b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen                mPreviousReorderDirection[1] = INVALID_DIRECTION;
259419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
259519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        } else {
259619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
259719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mPreviousReorderDirection[0] = mDirectionVector[0];
259819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mPreviousReorderDirection[1] = mDirectionVector[1];
259919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
260019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2601482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration swapSolution = simpleSwap(pixelX, pixelY, minSpanX, minSpanY,
2602482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                 spanX,  spanY, mDirectionVector, dragView,  true,  new ItemConfiguration());
2603482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2604482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // We attempt the approach which doesn't shuffle views at all
2605482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
2606482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                minSpanY, spanX, spanY, dragView, new ItemConfiguration());
2607482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2608482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration finalSolution = null;
2609482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
2610482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            finalSolution = swapSolution;
2611482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else if (noShuffleSolution.isSolution) {
2612482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            finalSolution = noShuffleSolution;
2613482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2614482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2615482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean foundSolution = true;
2616482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!DESTRUCTIVE_REORDER) {
2617482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            setUseTempCoords(true);
2618482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2619482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2620482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (finalSolution != null) {
2621482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = finalSolution.dragViewX;
2622482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[1] = finalSolution.dragViewY;
2623482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan[0] = finalSolution.dragViewSpanX;
2624482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan[1] = finalSolution.dragViewSpanY;
2625482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2626482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
2627482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // committing anything or animating anything as we just want to determine if a solution
2628482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // exists
2629482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
2630482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                if (!DESTRUCTIVE_REORDER) {
2631482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    copySolutionToTempState(finalSolution, dragView);
2632482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2633482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                setItemPlacementDirty(true);
2634482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP);
2635482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
263619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (!DESTRUCTIVE_REORDER &&
263719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
2638482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    commitTempPlacement();
263919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    completeAndClearReorderHintAnimations();
264019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    setItemPlacementDirty(false);
264119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else {
264219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    beginOrAdjustHintAnimations(finalSolution, dragView,
264319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                            REORDER_ANIMATION_DURATION);
2644482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2645482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2646482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2647482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            foundSolution = false;
2648482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
2649482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2650482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2651482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if ((mode == MODE_ON_DROP || !foundSolution) && !DESTRUCTIVE_REORDER) {
2652482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            setUseTempCoords(false);
2653482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2654482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2655a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.requestLayout();
2656482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return result;
2657482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2658482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
265919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    void setItemPlacementDirty(boolean dirty) {
266019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mItemPlacementDirty = dirty;
2661482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
266219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    boolean isItemPlacementDirty() {
266319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        return mItemPlacementDirty;
2664482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2665482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2666482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private class ItemConfiguration {
26678baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
2668f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
2669f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        ArrayList<View> sortedViews = new ArrayList<View>();
2670482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean isSolution = false;
2671482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
2672482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2673f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void save() {
2674f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // Copy current state into savedMap
2675f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: map.keySet()) {
2676f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                map.get(v).copy(savedMap.get(v));
2677f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
2678f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2679f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2680f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void restore() {
2681f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            // Restore current state from savedMap
2682f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            for (View v: savedMap.keySet()) {
2683f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen                savedMap.get(v).copy(map.get(v));
2684f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            }
2685f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2686f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2687f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        void add(View v, CellAndSpan cs) {
2688f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            map.put(v, cs);
2689f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            savedMap.put(v, new CellAndSpan());
2690f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            sortedViews.add(v);
2691f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2692f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2693482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int area() {
2694482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            return dragViewSpanX * dragViewSpanY;
2695482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
26968baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    }
26978baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
26988baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private class CellAndSpan {
26998baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        int x, y;
27008baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        int spanX, spanY;
27018baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
2702f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public CellAndSpan() {
2703f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2704f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2705f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public void copy(CellAndSpan copy) {
2706f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.x = x;
2707f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.y = y;
2708f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.spanX = spanX;
2709f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            copy.spanY = spanY;
2710f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2711f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
27128baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        public CellAndSpan(int x, int y, int spanX, int spanY) {
27138baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.x = x;
27148baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.y = y;
27158baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.spanX = spanX;
27168baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.spanY = spanY;
2717482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2718f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2719f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        public String toString() {
2720f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen            return "(" + x + ", " + y + ": " + spanX + ", " + spanY + ")";
2721f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen        }
2722f3900c287cd92f61863cbecab87d5513e48b7b09Adam Cohen
2723482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2724482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2725df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    /**
2726df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
2727df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2728df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *
2729df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2730df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2731df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanX Horizontal span of the object.
2732df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanY Vertical span of the object.
2733df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2734df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Previously returned value to possibly recycle.
2735df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2736df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *         nearest the requested location.
2737df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     */
2738df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestVacantArea(
2739df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
2740df0353815c629fc678824b07a234b89a1ff94208Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY, ignoreView, true, result);
2741df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    }
2742df0353815c629fc678824b07a234b89a1ff94208Adam Cohen
2743df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    /**
2744d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
2745d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2746d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
2747d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2748d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2749d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
2750d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
2751d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
2752d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
2753d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2754d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Previously returned value to possibly recycle.
2755d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2756d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
2757d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
2758d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY,
2759d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int spanX, int spanY, View ignoreView, int[] result, int[] resultSpan) {
2760482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, ignoreView, true,
2761482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                result, resultSpan, mOccupied);
2762d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
2763d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
2764d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
2765df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * Find a starting cell position that will fit the given bounds nearest the requested
2766df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2767df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *
2768df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2769df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2770df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanX Horizontal span of the object.
2771df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanY Vertical span of the object.
2772df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2773df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Previously returned value to possibly recycle.
2774df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2775df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *         nearest the requested location.
2776df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     */
2777df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestArea(
2778df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            int pixelX, int pixelY, int spanX, int spanY, int[] result) {
2779df0353815c629fc678824b07a234b89a1ff94208Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY, null, false, result);
2780df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    }
2781df0353815c629fc678824b07a234b89a1ff94208Adam Cohen
27820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean existsEmptyCell() {
27830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpan(null, 1, 1);
27840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
27850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
27860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
27870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Finds the upper-left coordinate of the first rectangle in the grid that can
27880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
27890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * then this method will only return coordinates for rectangles that contain the cell
27900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * (intersectX, intersectY)
27910280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
27920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
27930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
27940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
27950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
27960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
27970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
27980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
27990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
2800482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied);
28010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
28030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
28040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but ignores any cells occupied by the item "ignoreView"
28050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
28070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
28080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
28090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
28100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
28110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return
28120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
2814482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1,
2815482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                ignoreView, mOccupied);
28160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
28180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
28190280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but if intersectX and intersectY are not -1, then this method will try to
28200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * return coordinates for rectangles that contain the cell [intersectX, intersectY]
28210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28220280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
28230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
28240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
28250280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The X coordinate of the cell that we should try to overlap
28260280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The Y coordinate of the cell that we should try to overlap
28270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
28280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
28290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
28310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int intersectX, int intersectY) {
28320280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpanThatIntersectsIgnoring(
2833482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                cellXY, spanX, spanY, intersectX, intersectY, null, mOccupied);
28340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28350280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
28360280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
28370280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * The superset of the above two methods
28380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
28390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
2840482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int intersectX, int intersectY, View ignoreView, boolean occupied[][]) {
2841c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
2842482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(ignoreView, occupied);
28430280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
284428750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        boolean foundCell = false;
28450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        while (true) {
28460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startX = 0;
28470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
28480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startX = Math.max(startX, intersectX - (spanX - 1));
28490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28500280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endX = mCountX - (spanX - 1);
28510280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
28520280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
28530280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28540280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startY = 0;
28550280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
28560280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startY = Math.max(startY, intersectY - (spanY - 1));
28570280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28580280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endY = mCountY - (spanY - 1);
28590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
28600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
28610280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2863bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung            for (int y = startY; y < endY && !foundCell; y++) {
28640280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                inner:
2865bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                for (int x = startX; x < endX; x++) {
28660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    for (int i = 0; i < spanX; i++) {
28670280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        for (int j = 0; j < spanY; j++) {
2868482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                            if (occupied[x + i][y + j]) {
2869bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                                // small optimization: we can skip to after the column we just found
28700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                // an occupied cell
2871bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                                x += i;
28720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                continue inner;
28730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                            }
28740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        }
28750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
28760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    if (cellXY != null) {
28770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[0] = x;
28780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[1] = y;
28790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
288028750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    foundCell = true;
288128750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    break;
28820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                }
28830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX == -1 && intersectY == -1) {
28850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                break;
28860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            } else {
28870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // if we failed to find anything, try again but without any requirements of
28880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // intersecting
28890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectX = -1;
28900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectY = -1;
28910280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                continue;
28920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
28930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
28940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2895c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
2896482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(ignoreView, occupied);
289728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        return foundCell;
28980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
290031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2901c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * A drag event has begun over this layout.
2902c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * It may have begun over this layout (in which case onDragChild is called first),
2903c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * or it may have begun on another layout.
2904c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     */
2905c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    void onDragEnter() {
2906c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen        mDragEnforcer.onDragEnter();
2907c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung        mDragging = true;
2908c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    }
2909c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung
2910c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    /**
29110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Called when drag has left this CellLayout or has been completed (successfully or not)
29126569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
29130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    void onDragExit() {
2914c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen        mDragEnforcer.onDragExit();
29154be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // This can actually be called when we aren't in a drag, e.g. when adding a new
29164be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // item to this layout via the customize drawer.
29174be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Guard against that case.
29184be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mDragging) {
29194be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragging = false;
29204be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
292108ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy
292208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        // Invalidate the drag data
2923d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        mDragCell[0] = mDragCell[1] = -1;
292408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragOutlineAnims[mDragOutlineCurrent].animateOut();
292508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragOutlineCurrent = (mDragOutlineCurrent + 1) % mDragOutlineAnims.length;
292619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        revertTempState();
292733945b21544bc98381df17726a3537c292d8c985Michael Jurka        setIsDragOverlapping(false);
29286569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
29296569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
29306569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
2931aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Mark a child as having been dropped.
2932de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * At the beginning of the drag operation, the child may have been on another
2933ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy     * screen, but it is re-parented before this method is called.
293431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
293531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
293631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
2937716b51e030f9c6ed34af2b947760e46a280c65a6Adam Cohen    void onDropChild(View child) {
2938d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        if (child != null) {
2939d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
294084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy            lp.dropped = true;
2941d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            child.requestLayout();
2942d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        }
294331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
294431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
294531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
294631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes a bounding rectangle for a range of cells
2947aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
294831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of upper left corner expressed as a cell position
294931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of upper left corner expressed as a cell position
2950aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellHSpan Width in cells
295131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellVSpan Height in cells
29526569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param resultRect Rect into which to put the results
295331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
2954d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, Rect resultRect) {
295531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
295631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
295731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int widthGap = mWidthGap;
295831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int heightGap = mHeightGap;
2959aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
29604b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
29614b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
2962aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
296331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
296431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
296531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
296631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x = hStartPadding + cellX * (cellWidth + widthGap);
296731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = vStartPadding + cellY * (cellHeight + heightGap);
2968aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
29696569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        resultRect.set(x, y, x + width, y + height);
297031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
2971aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
297231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2973aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Computes the required horizontal and vertical cell spans to always
297431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * fit the given rectangle.
2975aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
297631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param width Width in pixels
297731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param height Height in pixels
29788f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * @param result An array of length 2 in which to store the result (may be null).
297931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
29808f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    public int[] rectToCell(int width, int height, int[] result) {
29819987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka        return rectToCell(getResources(), width, height, result);
29829987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    }
29839987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka
29849987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    public static int[] rectToCell(Resources resources, int width, int height, int[] result) {
298531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always assume we're working with the smallest span to make sure we
298631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // reserve enough space in both orientations.
298779e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
298879e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
298931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int smallerSize = Math.min(actualWidth, actualHeight);
299079e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
299131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always round up to next largest cell
299254c725cc294cd43ed0650179bfae64a622547660Winson Chung        int spanX = (int) Math.ceil(width / (float) smallerSize);
299354c725cc294cd43ed0650179bfae64a622547660Winson Chung        int spanY = (int) Math.ceil(height / (float) smallerSize);
299479e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
29958f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        if (result == null) {
29968f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            return new int[] { spanX, spanY };
29978f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        }
29988f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = spanX;
29998f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = spanY;
30008f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        return result;
300131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
300231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3003f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka    public int[] cellSpansToSize(int hSpans, int vSpans) {
3004f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        int[] size = new int[2];
3005f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        size[0] = hSpans * mCellWidth + (hSpans - 1) * mWidthGap;
3006f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        size[1] = vSpans * mCellHeight + (vSpans - 1) * mHeightGap;
3007f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        return size;
3008f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka    }
3009f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka
301031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
3011047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy     * Calculate the grid spans needed to fit given item
3012047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy     */
3013047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    public void calculateSpans(ItemInfo info) {
3014047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        final int minWidth;
3015047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        final int minHeight;
3016047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy
3017047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        if (info instanceof LauncherAppWidgetInfo) {
3018047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minWidth = ((LauncherAppWidgetInfo) info).minWidth;
3019047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minHeight = ((LauncherAppWidgetInfo) info).minHeight;
3020047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        } else if (info instanceof PendingAddWidgetInfo) {
3021047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minWidth = ((PendingAddWidgetInfo) info).minWidth;
3022047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minHeight = ((PendingAddWidgetInfo) info).minHeight;
3023047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        } else {
3024047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            // It's not a widget, so it must be 1x1
3025047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            info.spanX = info.spanY = 1;
3026047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            return;
3027047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        }
3028047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        int[] spans = rectToCell(minWidth, minHeight, null);
3029047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        info.spanX = spans[0];
3030047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        info.spanY = spans[1];
3031047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    }
3032047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy
3033047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    /**
303431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Find the first vacant cell, if there is one.
303531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
303631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param vacant Holds the x and y coordinate of the vacant cell
303731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanX Horizontal cell span.
303831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanY Vertical cell span.
3039aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
304031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @return True if a vacant cell was found
304131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
304231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
304331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30440280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
304531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
304631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
304731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static boolean findVacantCell(int[] vacant, int spanX, int spanY,
304831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
304931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30502801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        for (int y = 0; y < yCount; y++) {
30512801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen            for (int x = 0; x < xCount; x++) {
305231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean available = !occupied[x][y];
305331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectout:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
305431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
305531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        available = available && !occupied[i][j];
305631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        if (!available) break out;
305731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
305831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
305931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
306031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (available) {
306131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[0] = x;
306231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[1] = y;
306331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return true;
306431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
306531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
306631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
306731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
306831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
306931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
307031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void clearOccupiedCells() {
30720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = 0; x < mCountX; x++) {
30730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = 0; y < mCountY; y++) {
30740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                mOccupied[x][y] = false;
307531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
307631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
30770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
307831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3079d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) {
30800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
3081482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(newCellX, newCellY, newSpanX, newSpanY, mOccupied, true);
30820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
308331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3084d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    public void markCellsAsOccupiedForView(View view) {
3085482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(view, mOccupied);
3086482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
3087482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void markCellsAsOccupiedForView(View view, boolean[][] occupied) {
3088a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
30890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
3090482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, true);
30910280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
30920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
3093d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    public void markCellsAsUnoccupiedForView(View view) {
3094482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(view, mOccupied);
3095482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
3096482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void markCellsAsUnoccupiedForView(View view, boolean occupied[][]) {
3097a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
30980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
3099482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, false);
31000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
31010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
3102482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied,
3103482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            boolean value) {
3104482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (cellX < 0 || cellY < 0) return;
31050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
31060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
3107482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[x][y] = value;
310831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
310931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
311031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
311131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
31122801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public int getDesiredWidth() {
31138b805b17158886035b38261eb611d8641701ae43Michael Jurka        return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
31142801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen                (Math.max((mCountX - 1), 0) * mWidthGap);
31152801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
31162801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
31172801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public int getDesiredHeight()  {
31188b805b17158886035b38261eb611d8641701ae43Michael Jurka        return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
31192801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen                (Math.max((mCountY - 1), 0) * mHeightGap);
31202801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
31212801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
312266d72178af91d455700875635473be942bc90e54Michael Jurka    public boolean isOccupied(int x, int y) {
312366d72178af91d455700875635473be942bc90e54Michael Jurka        if (x < mCountX && y < mCountY) {
312466d72178af91d455700875635473be942bc90e54Michael Jurka            return mOccupied[x][y];
312566d72178af91d455700875635473be942bc90e54Michael Jurka        } else {
312666d72178af91d455700875635473be942bc90e54Michael Jurka            throw new RuntimeException("Position exceeds the bound of this CellLayout");
312766d72178af91d455700875635473be942bc90e54Michael Jurka        }
312866d72178af91d455700875635473be942bc90e54Michael Jurka    }
312966d72178af91d455700875635473be942bc90e54Michael Jurka
313031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
313131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
313231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(getContext(), attrs);
313331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
313431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
313531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
313631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
313731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return p instanceof CellLayout.LayoutParams;
313831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
313931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
314031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
314131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
314231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(p);
314331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
314431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3145aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public static class CellLayoutAnimationController extends LayoutAnimationController {
3146aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public CellLayoutAnimationController(Animation animation, float delay) {
3147aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(animation, delay);
3148aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
3149aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
3150aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        @Override
3151aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        protected long getDelayForView(View view) {
3152aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return (int) (Math.random() * 150);
3153aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
3154aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
3155aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
315631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
315731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
315831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Horizontal location of the item in the grid.
315931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
316031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
316131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellX;
316231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
316331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
316431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Vertical location of the item in the grid.
316531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
316631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
316731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellY;
316831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
316931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
3170482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Temporary horizontal location of the item in the grid during reorder
3171482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3172482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public int tmpCellX;
3173482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
3174482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
3175482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Temporary vertical location of the item in the grid during reorder
3176482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3177482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public int tmpCellY;
3178482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
3179482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
3180482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Indicates that the temporary coordinates should be used to layout the items
3181482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3182482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public boolean useTmpCoords;
3183482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
3184482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
318531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned horizontally by the item.
318631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
318731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
318831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellHSpan;
318931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
319031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
319131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned vertically by the item.
319231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
319331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
319431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellVSpan;
3195aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
31961b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen        /**
31971b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         * Indicates whether the item will set its x, y, width and height parameters freely,
31981b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
31991b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         */
3200d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        public boolean isLockedToGrid = true;
3201d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
3202482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
3203482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Indicates whether this item can be reordered. Always true except in the case of the
3204482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * the AllApps button.
3205482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
3206482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public boolean canReorder = true;
3207482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
320831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // X coordinate of the view in the layout.
320931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
321031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x;
321131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Y coordinate of the view in the layout.
321231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
321331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y;
321431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
321584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        boolean dropped;
3216fcb9e7144e58614f5ae0e9b272fb7ce040848c67Romain Guy
321731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(Context c, AttributeSet attrs) {
321831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(c, attrs);
321931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
322031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
322131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
322231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
322331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(ViewGroup.LayoutParams source) {
322431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(source);
322531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
322631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
322731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
3228aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
3229aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public LayoutParams(LayoutParams source) {
3230aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(source);
3231aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellX = source.cellX;
3232aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellY = source.cellY;
3233aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellHSpan = source.cellHSpan;
3234aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellVSpan = source.cellVSpan;
3235aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
3236aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
323731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
32388f19cdd62f6e2be05e3890916eabd11317ae1bc2Romain Guy            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
323931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellX = cellX;
324031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellY = cellY;
324131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellHSpan = cellHSpan;
324231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellVSpan = cellVSpan;
324331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
324431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
32457f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap) {
3246d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            if (isLockedToGrid) {
3247d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                final int myCellHSpan = cellHSpan;
3248d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                final int myCellVSpan = cellVSpan;
3249482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                final int myCellX = useTmpCoords ? tmpCellX : cellX;
3250482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                final int myCellY = useTmpCoords ? tmpCellY : cellY;
32511b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen
3252d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
3253d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                        leftMargin - rightMargin;
3254d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
3255d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                        topMargin - bottomMargin;
3256eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                x = (int) (myCellX * (cellWidth + widthGap) + leftMargin);
3257eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                y = (int) (myCellY * (cellHeight + heightGap) + topMargin);
3258d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            }
3259d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        }
3260d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
3261aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public String toString() {
3262aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "(" + this.cellX + ", " + this.cellY + ")";
3263aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
32647f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32657f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setWidth(int width) {
32667f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.width = width;
32677f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32687f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32697f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getWidth() {
32707f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return width;
32717f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32727f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32737f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setHeight(int height) {
32747f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.height = height;
32757f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32767f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32777f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getHeight() {
32787f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return height;
32797f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32807f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32817f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setX(int x) {
32827f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.x = x;
32837f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32847f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32857f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getX() {
32867f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return x;
32877f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32887f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32897f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setY(int y) {
32907f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.y = y;
32917f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
32927f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
32937f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getY() {
32947f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return y;
32957f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
329631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
329731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
32980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // This class stores info for two purposes:
32990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
33000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    its spanX, spanY, and the screen it is on
33010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 2. When long clicking on an empty cell in a CellLayout, we save information about the
33020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
33030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    the CellLayout that was long clicked
3304e5fb0f27bca7afb996258a7163c76ca7390d7bffMichael Jurka    static final class CellInfo {
330531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        View cell;
3306a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellX = -1;
3307a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellY = -1;
330831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX;
330931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY;
331031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int screen;
33113d503fbd9468fb2b9fa645f4f7b91e11229edbfaWinson Chung        long container;
331231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
331331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
331431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public String toString() {
3315aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "Cell[view=" + (cell == null ? "null" : cell.getClass())
3316aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    + ", x=" + cellX + ", y=" + cellY + "]";
331731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
331831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
3319d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka
3320d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    public boolean lastDownOnOccupiedCell() {
3321d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka        return mLastDownOnOccupiedCell;
3322d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    }
332331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
3324