CellLayout.java revision 487f7dd3059621527eb439d7d51d34e00293f9b1
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;
3931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.util.AttributeSet;
404be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.util.Log;
4131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.MotionEvent;
4231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.View;
4331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewDebug;
4431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.ViewGroup;
45aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.Animation;
46150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chungimport android.view.animation.DecelerateInterpolator;
47aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chungimport android.view.animation.LayoutAnimationController;
4831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
496639687cd67bab1aeef2a75e5c6bc458b20dc082Adam Cohenimport com.android.launcher.R;
5069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohenimport com.android.launcher2.FolderIcon.FolderRingAnimator;
518e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy
5269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohenimport java.util.ArrayList;
53c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohenimport java.util.Arrays;
54bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohenimport java.util.HashMap;
55d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohenimport java.util.Stack;
56c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen
57bdb5c5342adc550559fd723af461e53248f2fba8Michael Jurkapublic class CellLayout extends ViewGroup {
58aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    static final String TAG = "CellLayout";
59aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
602acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen    private Launcher mLauncher;
6131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellWidth;
6231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mCellHeight;
63aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
64d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountX;
65d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private int mCountY;
6631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
67234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen    private int mOriginalWidthGap;
68234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen    private int mOriginalHeightGap;
6931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mWidthGap;
7031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mHeightGap;
714b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung    private int mMaxGap;
72ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    private boolean mScrollingTransformsDirty = false;
7331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final Rect mRect = new Rect();
7531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final CellInfo mCellInfo = new CellInfo();
76aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
77de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    // These are temporary variables to prevent having to allocate a new object just to
78de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
790be025d64c1f84138fe430a58875886e66aae767Winson Chung    private final int[] mTmpXY = new int[2];
80de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final int[] mTmpPoint = new int[2];
8169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    int[] mTempLocation = new int[2];
826569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
8331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean[][] mOccupied;
84482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    boolean[][] mTmpOccupied;
85d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    private boolean mLastDownOnOccupiedCell = false;
8631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
87dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private OnTouchListener mInterceptTouchListener;
88dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
8969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>();
90c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    private int[] mFolderLeaveBehindCell = {-1, -1};
9169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
92b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private int mForegroundAlpha = 0;
935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mBackgroundAlpha;
941b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    private float mBackgroundAlphaMultiplier = 1.0f;
95f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
9633945b21544bc98381df17726a3537c292d8c985Michael Jurka    private Drawable mNormalBackground;
9733945b21544bc98381df17726a3537c292d8c985Michael Jurka    private Drawable mActiveGlowBackground;
98b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Drawable mOverScrollForegroundDrawable;
99b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Drawable mOverScrollLeft;
100b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Drawable mOverScrollRight;
10118014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka    private Rect mBackgroundRect;
102b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private Rect mForegroundRect;
103b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    private int mForegroundPadding;
10433945b21544bc98381df17726a3537c292d8c985Michael Jurka
10533945b21544bc98381df17726a3537c292d8c985Michael Jurka    // If we're actively dragging something over this screen, mIsDragOverlapping is true
10633945b21544bc98381df17726a3537c292d8c985Michael Jurka    private boolean mIsDragOverlapping = false;
107de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy    private final Point mDragCenter = new Point();
1086569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
109150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung    // These arrays are used to implement the drag visualization on x-large screens.
1104be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    // They are used as circular arrays, indexed by mDragOutlineCurrent.
111d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private Rect[] mDragOutlines = new Rect[4];
112472b281d5cb4f5660df981a6c912266b9f5703feChet Haase    private float[] mDragOutlineAlphas = new float[mDragOutlines.length];
1134be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private InterruptibleInOutAnimator[] mDragOutlineAnims =
1144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            new InterruptibleInOutAnimator[mDragOutlines.length];
115150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
116150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung    // Used as an index into the above 3 arrays; indicates which is the most current value.
1174be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private int mDragOutlineCurrent = 0;
1188e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy    private final Paint mDragOutlinePaint = new Paint();
119150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
12096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    private BubbleTextView mPressedOrFocusedIcon;
12196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
122482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
123482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            HashMap<CellLayout.LayoutParams, Animator>();
12419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private HashMap<View, ReorderHintAnimation>
12519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mShakeAnimators = new HashMap<View, ReorderHintAnimation>();
12619f3792523fe2d55ea791a9286398a6120920690Adam Cohen
12719f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private boolean mItemPlacementDirty = false;
128bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1296569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    // When a drag operation is in progress, holds the nearest cell to the touch point
1306569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private final int[] mDragCell = new int[2];
13131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1324be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private boolean mDragging = false;
1334be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
134ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    private TimeInterpolator mEaseOutInterpolator;
135a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    private ShortcutAndWidgetContainer mShortcutsAndWidgets;
136ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy
1370dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    private boolean mIsHotseat = false;
1380dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
139482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_DRAG_OVER = 0;
140482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_ON_DROP = 1;
141482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_ON_DROP_EXTERNAL = 2;
142482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public static final int MODE_ACCEPT_DROP = 3;
14319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private static final boolean DESTRUCTIVE_REORDER = false;
144482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
145482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
146a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static final int LANDSCAPE = 0;
147a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static final int PORTRAIT = 1;
148a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen
1497bdfc9700b1cad043c04c757f134db1bf3df00daAdam Cohen    private static final float REORDER_HINT_MAGNITUDE = 0.12f;
15019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private static final int REORDER_ANIMATION_DURATION = 150;
15119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private float mReorderHintAnimationMagnitude;
15219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
153482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private ArrayList<View> mIntersectingViews = new ArrayList<View>();
154482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private Rect mOccupiedRect = new Rect();
155482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private int[] mDirectionVector = new int[2];
15619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    int[] mPreviousReorderDirection = new int[2];
157b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen    private static final int INVALID_DIRECTION = -100;
158c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen    private DropTarget.DragEnforcer mDragEnforcer;
159482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1608a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy    private final static PorterDuffXfermode sAddBlendMode =
1618a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy            new PorterDuffXfermode(PorterDuff.Mode.ADD);
1628a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy
16331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context) {
16431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, null);
16531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
16631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
16731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs) {
16831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        this(context, attrs, 0);
16931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
17031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
17131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellLayout(Context context, AttributeSet attrs, int defStyle) {
17231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super(context, attrs, defStyle);
1738b805b17158886035b38261eb611d8641701ae43Michael Jurka        mDragEnforcer = new DropTarget.DragEnforcer(context);
1746569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1756569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
1766569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // the user where a dragged item will land when dropped.
1776569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        setWillNotDraw(false);
1782acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen        mLauncher = (Launcher) context;
179a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka
18031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
18131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
182f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
183f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
184234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        mWidthGap = mOriginalWidthGap = a.getDimensionPixelSize(R.styleable.CellLayout_widthGap, 0);
185234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        mHeightGap = mOriginalHeightGap = a.getDimensionPixelSize(R.styleable.CellLayout_heightGap, 0);
1864b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        mMaxGap = a.getDimensionPixelSize(R.styleable.CellLayout_maxGap, 0);
187d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountX = LauncherModel.getCellCountX();
188d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        mCountY = LauncherModel.getCellCountY();
1890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        mOccupied = new boolean[mCountX][mCountY];
190482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mTmpOccupied = new boolean[mCountX][mCountY];
1915b53f29f809a0dad4a1e0717ba610ce737ee0f43Adam Cohen        mPreviousReorderDirection[0] = INVALID_DIRECTION;
1925b53f29f809a0dad4a1e0717ba610ce737ee0f43Adam Cohen        mPreviousReorderDirection[1] = INVALID_DIRECTION;
19331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        a.recycle();
19531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setAlwaysDrawnWithCacheEnabled(false);
19731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
198046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        final Resources res = getResources();
199de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
200967289b6d5fec77f5c381d11ffb2319f3bb5e737Winson Chung        mNormalBackground = res.getDrawable(R.drawable.homescreen_blue_normal_holo);
201dea74b7d12b0fcd50bfdb4274f9867ba76d75238Winson Chung        mActiveGlowBackground = res.getDrawable(R.drawable.homescreen_blue_strong_holo);
202b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung
203b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left);
204b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right);
205b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundPadding =
206b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen                res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding);
207b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung
20819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mReorderHintAnimationMagnitude = (REORDER_HINT_MAGNITUDE *
20919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                res.getDimensionPixelSize(R.dimen.app_icon_size));
21019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
211b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        mNormalBackground.setFilterBitmap(true);
212b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        mActiveGlowBackground.setFilterBitmap(true);
213de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
214046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Initialize the data structures used for the drag visualization.
215150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
216ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out
217de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
218046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy
219b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung        mDragCell[0] = mDragCell[1] = -1;
2204be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
221d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            mDragOutlines[i] = new Rect(-1, -1, -1, -1);
222046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        }
223046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy
224046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // When dragging things around the home screens, we show a green outline of
225046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // where the item will land. The outlines gradually fade out, leaving a trail
226046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // behind the drag path.
227046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        // Set up all the animations that are used to implement this fading.
228046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy        final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime);
229472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        final float fromAlphaValue = 0;
230472b281d5cb4f5660df981a6c912266b9f5703feChet Haase        final float toAlphaValue = (float)res.getInteger(R.integer.config_dragOutlineMaxAlpha);
2314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2328e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy        Arrays.fill(mDragOutlineAlphas, fromAlphaValue);
2334be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2344be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlineAnims.length; i++) {
235046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            final InterruptibleInOutAnimator anim =
236046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy                new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
237ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy            anim.getAnimator().setInterpolator(mEaseOutInterpolator);
238046e7eb472acac800128ab026f3bc6348b0f933fPatrick Dubroy            final int thisIndex = i;
239472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
240de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                public void onAnimationUpdate(ValueAnimator animation) {
2414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    final Bitmap outline = (Bitmap)anim.getTag();
2424be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
2434be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    // If an animation is started and then stopped very quickly, we can still
2444be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    // get spurious updates we've cleared the tag. Guard against this.
2454be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    if (outline == null) {
2463a9fcedbcd235372cd2ab64f825a0b5b3937f59eMichael Jurka                        @SuppressWarnings("all") // suppress dead code warning
2473a9fcedbcd235372cd2ab64f825a0b5b3937f59eMichael Jurka                        final boolean debug = false;
2483a9fcedbcd235372cd2ab64f825a0b5b3937f59eMichael Jurka                        if (debug) {
249fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                            Object val = animation.getAnimatedValue();
250fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                            Log.d(TAG, "anim " + thisIndex + " update: " + val +
251fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                                     ", isStopped " + anim.isStopped());
252fe6bd87881e47b9ff38f58bd083042ae0f6a39d7Patrick Dubroy                        }
2534be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        // Try to prevent it from continuing to run
2544be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        animation.cancel();
2554be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    } else {
256472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                        mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();
257d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        CellLayout.this.invalidate(mDragOutlines[thisIndex]);
2584be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    }
259de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy                }
260de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            });
2614be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // The animation holds a reference to the drag outline bitmap as long is it's
2624be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // running. This way the bitmap can be GCed when the animations are complete.
263472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            anim.getAnimator().addListener(new AnimatorListenerAdapter() {
2643c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                @Override
2654be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                public void onAnimationEnd(Animator animation) {
266472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                    if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {
2674be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        anim.setTag(null);
2684be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                    }
2694be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                }
2704be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            });
2714be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragOutlineAnims[i] = anim;
272de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        }
273ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy
27418014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mBackgroundRect = new Rect();
275b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundRect = new Rect();
276bea15195346bab3c52b0156e92f2b71f0811b210Michael Jurka
277a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context);
278a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
279a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        addView(mShortcutsAndWidgets);
28018014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka    }
28118014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka
282f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    static int widthInPortrait(Resources r, int numCells) {
283f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // We use this method from Workspace to figure out how many rows/columns Launcher should
284f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // have. We ignore the left/right padding on CellLayout because it turns out in our design
285f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // the padding extends outside the visible screen size, but it looked fine anyway.
286f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        int cellWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);
2874b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),
2884b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                r.getDimensionPixelSize(R.dimen.workspace_height_gap));
289f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
2904b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        return  minGap * (numCells - 1) + cellWidth * numCells;
291f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    }
292f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
293f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    static int heightInLandscape(Resources r, int numCells) {
294f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // We use this method from Workspace to figure out how many rows/columns Launcher should
295f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // have. We ignore the left/right padding on CellLayout because it turns out in our design
296f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        // the padding extends outside the visible screen size, but it looked fine anyway.
297f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka        int cellHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height);
2984b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),
2994b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                r.getDimensionPixelSize(R.dimen.workspace_height_gap));
300f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3014b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        return minGap * (numCells - 1) + cellHeight * numCells;
302f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka    }
303f6440da9d02f3ee1553db4bd431a202eb1d1a9ddMichael Jurka
3042801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public void enableHardwareLayers() {
305d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka        mShortcutsAndWidgets.setLayerType(LAYER_TYPE_HARDWARE, null);
306d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    }
307d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka
308d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    public void disableHardwareLayers() {
309d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka        mShortcutsAndWidgets.setLayerType(LAYER_TYPE_NONE, null);
310d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    }
311d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka
312d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka    public void buildHardwareLayer() {
313d51f33a6316c34fc69b8040946ed9a62519bb225Michael Jurka        mShortcutsAndWidgets.buildLayer();
3142801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
3152801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
3162801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public void setGridSize(int x, int y) {
3172801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mCountX = x;
3182801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mCountY = y;
3192801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        mOccupied = new boolean[mCountX][mCountY];
320482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mTmpOccupied = new boolean[mCountX][mCountY];
3217fbec10b36818f100b631f3d73fe1ad5360975aaAdam Cohen        mTempRectStack.clear();
32276fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen        requestLayout();
3232801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
3242801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
32596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    private void invalidateBubbleTextView(BubbleTextView icon) {
32696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        final int padding = icon.getPressedOrFocusedBackgroundPadding();
3274b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        invalidate(icon.getLeft() + getPaddingLeft() - padding,
3284b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getTop() + getPaddingTop() - padding,
3294b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getRight() + getPaddingLeft() + padding,
3304b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                icon.getBottom() + getPaddingTop() + padding);
33196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    }
33296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
333b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    void setOverScrollAmount(float r, boolean left) {
334b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (left && mOverScrollForegroundDrawable != mOverScrollLeft) {
335b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable = mOverScrollLeft;
336b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        } else if (!left && mOverScrollForegroundDrawable != mOverScrollRight) {
337b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable = mOverScrollRight;
338b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
339b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
340b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundAlpha = (int) Math.round((r * 255));
341b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mOverScrollForegroundDrawable.setAlpha(mForegroundAlpha);
342b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        invalidate();
343b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
344b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
34596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    void setPressedOrFocusedIcon(BubbleTextView icon) {
34696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
34796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // requires an expanded clip rect (due to the glow's blur radius)
34896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        BubbleTextView oldIcon = mPressedOrFocusedIcon;
34996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        mPressedOrFocusedIcon = icon;
35096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (oldIcon != null) {
35196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            invalidateBubbleTextView(oldIcon);
35296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
35396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (mPressedOrFocusedIcon != null) {
35496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            invalidateBubbleTextView(mPressedOrFocusedIcon);
35596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
35696864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy    }
35796864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
35833945b21544bc98381df17726a3537c292d8c985Michael Jurka    void setIsDragOverlapping(boolean isDragOverlapping) {
35933945b21544bc98381df17726a3537c292d8c985Michael Jurka        if (mIsDragOverlapping != isDragOverlapping) {
36033945b21544bc98381df17726a3537c292d8c985Michael Jurka            mIsDragOverlapping = isDragOverlapping;
36133945b21544bc98381df17726a3537c292d8c985Michael Jurka            invalidate();
36233945b21544bc98381df17726a3537c292d8c985Michael Jurka        }
36333945b21544bc98381df17726a3537c292d8c985Michael Jurka    }
36433945b21544bc98381df17726a3537c292d8c985Michael Jurka
36533945b21544bc98381df17726a3537c292d8c985Michael Jurka    boolean getIsDragOverlapping() {
36633945b21544bc98381df17726a3537c292d8c985Michael Jurka        return mIsDragOverlapping;
36733945b21544bc98381df17726a3537c292d8c985Michael Jurka    }
36833945b21544bc98381df17726a3537c292d8c985Michael Jurka
369ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected void setOverscrollTransformsDirty(boolean dirty) {
370ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        mScrollingTransformsDirty = dirty;
371ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    }
372ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
373ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    protected void resetOverscrollTransforms() {
374ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        if (mScrollingTransformsDirty) {
375ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setOverscrollTransformsDirty(false);
376ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setTranslationX(0);
377ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setRotationY(0);
378ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            // It doesn't matter if we pass true or false here, the important thing is that we
379ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            // pass 0, which results in the overscroll drawable not being drawn any more.
380ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setOverScrollAmount(0, false);
381ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setPivotX(getMeasuredWidth() / 2);
382ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen            setPivotY(getMeasuredHeight() / 2);
383ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen        }
384ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen    }
385ebea84d1c95f4c38ba8cee46cd586fd757b4fce2Adam Cohen
386a6abce8464b57ce91e8f083951ad263370fc2da8Romain Guy    @Override
3871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    protected void onDraw(Canvas canvas) {
3883e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
3893e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
3903e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // When we're small, we are either drawn normally or in the "accepts drops" state (during
3913e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // a drag). However, we also drag the mini hover background *over* one of those two
3923e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        // backgrounds
393b26f3d6a8c62e7c1a603b6c7979375d8dd4f20d4Winson Chung        if (mBackgroundAlpha > 0.0f) {
394f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            Drawable bg;
39533945b21544bc98381df17726a3537c292d8c985Michael Jurka
39633945b21544bc98381df17726a3537c292d8c985Michael Jurka            if (mIsDragOverlapping) {
39733945b21544bc98381df17726a3537c292d8c985Michael Jurka                // In the mini case, we draw the active_glow bg *over* the active background
398bdf78559f223ac11e01e3311edd5a48a80383e1eMichael Jurka                bg = mActiveGlowBackground;
399f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            } else {
400bdf78559f223ac11e01e3311edd5a48a80383e1eMichael Jurka                bg = mNormalBackground;
401f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            }
40233945b21544bc98381df17726a3537c292d8c985Michael Jurka
40333945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
40433945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.setBounds(mBackgroundRect);
40533945b21544bc98381df17726a3537c292d8c985Michael Jurka            bg.draw(canvas);
406a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        }
40731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4088e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy        final Paint paint = mDragOutlinePaint;
4094be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        for (int i = 0; i < mDragOutlines.length; i++) {
410472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            final float alpha = mDragOutlineAlphas[i];
4114be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (alpha > 0) {
412d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                final Rect r = mDragOutlines[i];
4134be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
414472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                paint.setAlpha((int)(alpha + .5f));
415d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                canvas.drawBitmap(b, null, r, paint);
416150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung            }
4176569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
41896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy
41996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
42096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        // requires an expanded clip rect (due to the glow's blur radius)
42196864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        if (mPressedOrFocusedIcon != null) {
42296864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            final int padding = mPressedOrFocusedIcon.getPressedOrFocusedBackgroundPadding();
42396864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            final Bitmap b = mPressedOrFocusedIcon.getPressedOrFocusedBackground();
42496864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            if (b != null) {
42596864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy                canvas.drawBitmap(b,
4264b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                        mPressedOrFocusedIcon.getLeft() + getPaddingLeft() - padding,
4274b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung                        mPressedOrFocusedIcon.getTop() + getPaddingTop() - padding,
42896864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy                        null);
42996864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy            }
43096864c3c27d03b98d5a25b74b7647be064071144Patrick Dubroy        }
43169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
432482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (DEBUG_VISUALIZE_OCCUPIED) {
433482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int[] pt = new int[2];
434482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            ColorDrawable cd = new ColorDrawable(Color.RED);
435e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            cd.setBounds(0, 0,  mCellWidth, mCellHeight);
436482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int i = 0; i < mCountX; i++) {
437482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                for (int j = 0; j < mCountY; j++) {
438482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    if (mOccupied[i][j]) {
439482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        cellToPoint(i, j, pt);
440482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.save();
441482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.translate(pt[0], pt[1]);
442482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        cd.draw(canvas);
443482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        canvas.restore();
444482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    }
445482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
446482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
447482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
448482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
449850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn        int previewOffset = FolderRingAnimator.sPreviewSize;
450850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn
45169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        // The folder outer / inner ring image(s)
45269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        for (int i = 0; i < mFolderOuterRings.size(); i++) {
45369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            FolderRingAnimator fra = mFolderOuterRings.get(i);
45469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
45569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            // Draw outer ring
45669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            Drawable d = FolderRingAnimator.sSharedOuterRingDrawable;
45769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int width = (int) fra.getOuterRingSize();
45869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int height = width;
45969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
46069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
46169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            int centerX = mTempLocation[0] + mCellWidth / 2;
462850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            int centerY = mTempLocation[1] + previewOffset / 2;
46369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
46469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.save();
46569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.translate(centerX - width / 2, centerY - height / 2);
46669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.setBounds(0, 0, width, height);
46769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.draw(canvas);
46869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.restore();
46969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
47069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            // Draw inner ring
47169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d = FolderRingAnimator.sSharedInnerRingDrawable;
47269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            width = (int) fra.getInnerRingSize();
47369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            height = width;
47469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
47569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
47669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            centerX = mTempLocation[0] + mCellWidth / 2;
477850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            centerY = mTempLocation[1] + previewOffset / 2;
47869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.save();
47969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.translate(centerX - width / 2, centerY - width / 2);
48069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.setBounds(0, 0, width, height);
48169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            d.draw(canvas);
48269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            canvas.restore();
48369ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        }
484c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
485c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        if (mFolderLeaveBehindCell[0] >= 0 && mFolderLeaveBehindCell[1] >= 0) {
486c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            Drawable d = FolderIcon.sSharedFolderLeaveBehind;
487c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int width = d.getIntrinsicWidth();
488c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int height = d.getIntrinsicHeight();
489c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
490c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            cellToPoint(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1], mTempLocation);
491c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            int centerX = mTempLocation[0] + mCellWidth / 2;
492850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            int centerY = mTempLocation[1] + previewOffset / 2;
493c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
494c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.save();
495c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.translate(centerX - width / 2, centerY - width / 2);
496c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            d.setBounds(0, 0, width, height);
497c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            d.draw(canvas);
498c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen            canvas.restore();
499c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        }
50069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    }
50169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
502b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    @Override
503b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    protected void dispatchDraw(Canvas canvas) {
504b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        super.dispatchDraw(canvas);
505b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        if (mForegroundAlpha > 0) {
506b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable.setBounds(mForegroundRect);
507b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            Paint p = ((NinePatchDrawable) mOverScrollForegroundDrawable).getPaint();
5088a0bff5e35f63321178bbf777ec24d42544866b3Romain Guy            p.setXfermode(sAddBlendMode);
509b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            mOverScrollForegroundDrawable.draw(canvas);
510b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen            p.setXfermode(null);
511b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        }
512b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen    }
513b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen
51469ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    public void showFolderAccept(FolderRingAnimator fra) {
51569ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        mFolderOuterRings.add(fra);
51669ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    }
51769ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen
51869ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen    public void hideFolderAccept(FolderRingAnimator fra) {
51969ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        if (mFolderOuterRings.contains(fra)) {
52069ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen            mFolderOuterRings.remove(fra);
52169ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        }
52269ce2e5beaa1a57c7b8fa14a2d6ff0b3abeb93c0Adam Cohen        invalidate();
5236569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
5246569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
525c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    public void setFolderLeaveBehindCell(int x, int y) {
526c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[0] = x;
527c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[1] = y;
528c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        invalidate();
529c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    }
530c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
531c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    public void clearFolderLeaveBehind() {
532c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[0] = -1;
533c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        mFolderLeaveBehindCell[1] = -1;
534c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen        invalidate();
535c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen    }
536c51934bfdfed6a5011c6d6c5b7b70f2d75613d41Adam Cohen
5376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    @Override
538e6235dd225404239b55c459245543f3302326112Michael Jurka    public boolean shouldDelayChildPressedState() {
539e6235dd225404239b55c459245543f3302326112Michael Jurka        return false;
540e6235dd225404239b55c459245543f3302326112Michael Jurka    }
541e6235dd225404239b55c459245543f3302326112Michael Jurka
542e6235dd225404239b55c459245543f3302326112Michael Jurka    @Override
54383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    public void cancelLongPress() {
54483f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        super.cancelLongPress();
54583f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
54683f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        // Cancel long press for all children
54783f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        final int count = getChildCount();
54883f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        for (int i = 0; i < count; i++) {
54983f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            final View child = getChildAt(i);
55083f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey            child.cancelLongPress();
55183f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey        }
55283f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey    }
55383f111d129fcb8c50b35da789f0d75604b9c0864Jeff Sharkey
554dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public void setOnInterceptTouchListener(View.OnTouchListener listener) {
555dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mInterceptTouchListener = listener;
556dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
557dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
55831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountX() {
559d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountX;
56031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
56131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
56231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    int getCountY() {
563d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        return mCountY;
56431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
56531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
5660dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    public void setIsHotseat(boolean isHotseat) {
5670dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        mIsHotseat = isHotseat;
5680dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    }
5690dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
5700dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
571850d2e718560cb12ae73292e9d39f21a93d3c2c1Andrew Flynn            boolean markCells) {
572aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final LayoutParams lp = params;
573aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
574de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn        // Hotseat icons - remove text
5750dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        if (child instanceof BubbleTextView) {
5760dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn            BubbleTextView bubbleChild = (BubbleTextView) child;
5770dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
578de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            Resources res = getResources();
579de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            if (mIsHotseat) {
580de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn                bubbleChild.setTextColor(res.getColor(android.R.color.transparent));
581de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn            } else {
582de38e42fe89fd7490221427908cd97c51da21b44Andrew Flynn                bubbleChild.setTextColor(res.getColor(R.color.workspace_icon_text_color));
5830dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn            }
5840dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn        }
5850dca1ec41479a74f8da080224fa0c7eacab674d6Andrew Flynn
58631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Generate an id for each view, this assumes we have at most 256x256 cells
58731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // per workspace screen
588d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
589aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // If the horizontal or vertical span is set to -1, it is taken to
590aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            // mean that it spans the extent of the CellLayout
591d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
592d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen            if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
593aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
594aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            child.setId(childId);
59531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
596a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.addView(child, index, lp);
597dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
598f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka            if (markCells) markCellsAsOccupiedForView(child);
5990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
600aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return true;
601aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
602aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return false;
60331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
6043e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
60531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
6060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViews() {
6070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        clearOccupiedCells();
608a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeAllViews();
6090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeAllViewsInLayout() {
613a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (mShortcutsAndWidgets.getChildCount() > 0) {
6147cfc2825c3a1029f962d2fc387ae2eaa85b51798Michael Jurka            clearOccupiedCells();
615a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.removeAllViewsInLayout();
6167cfc2825c3a1029f962d2fc387ae2eaa85b51798Michael Jurka        }
6170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
619f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    public void removeViewWithoutMarkingCells(View view) {
620a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeView(view);
621f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    }
622f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka
6230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeView(View view) {
6250280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
626a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeView(view);
6270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewAt(int index) {
631a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(index));
632a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewAt(index);
6330280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6350280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6360280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewInLayout(View view) {
6370280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
638a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewInLayout(view);
6390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6410280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6420280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViews(int start, int count) {
6430280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
644a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
6450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
646a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViews(start, count);
6470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
6480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
6490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
6500280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void removeViewsInLayout(int start, int count) {
6510280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int i = start; i < start + count; i++) {
652a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
6530280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
654a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.removeViewsInLayout(start, count);
655abded66084680bb31cc7ea403c88f44f79a3c884Michael Jurka    }
656abded66084680bb31cc7ea403c88f44f79a3c884Michael Jurka
65731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
65831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onAttachedToWindow() {
65931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onAttachedToWindow();
66031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
66131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
66231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
663af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public void setTagToCellInfoForPoint(int touchX, int touchY) {
66431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final CellInfo cellInfo = mCellInfo;
665eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung        Rect frame = mRect;
6668b805b17158886035b38261eb611d8641701ae43Michael Jurka        final int x = touchX + getScrollX();
6678b805b17158886035b38261eb611d8641701ae43Michael Jurka        final int y = touchY + getScrollY();
668a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        final int count = mShortcutsAndWidgets.getChildCount();
66931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
670af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        boolean found = false;
671af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        for (int i = count - 1; i >= 0; i--) {
672a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            final View child = mShortcutsAndWidgets.getChildAt(i);
673d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
674af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka
6751b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen            if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) &&
6761b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen                    lp.isLockedToGrid) {
677af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                child.getHitRect(frame);
6780be025d64c1f84138fe430a58875886e66aae767Winson Chung
679eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                float scale = child.getScaleX();
680eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                frame = new Rect(child.getLeft(), child.getTop(), child.getRight(),
681eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                        child.getBottom());
6820be025d64c1f84138fe430a58875886e66aae767Winson Chung                // The child hit rect is relative to the CellLayoutChildren parent, so we need to
6830be025d64c1f84138fe430a58875886e66aae767Winson Chung                // offset that by this CellLayout's padding to test an (x,y) point that is relative
6840be025d64c1f84138fe430a58875886e66aae767Winson Chung                // to this view.
6858b805b17158886035b38261eb611d8641701ae43Michael Jurka                frame.offset(getPaddingLeft(), getPaddingTop());
686eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                frame.inset((int) (frame.width() * (1f - scale) / 2),
687eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                        (int) (frame.height() * (1f - scale) / 2));
6880be025d64c1f84138fe430a58875886e66aae767Winson Chung
689af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                if (frame.contains(x, y)) {
690af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cell = child;
691af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellX = lp.cellX;
692af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.cellY = lp.cellY;
693af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanX = lp.cellHSpan;
694af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    cellInfo.spanY = lp.cellVSpan;
695af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    found = true;
696af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    break;
69731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
69831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
699af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
700aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
701d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka        mLastDownOnOccupiedCell = found;
702d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka
703af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (!found) {
7040be025d64c1f84138fe430a58875886e66aae767Winson Chung            final int cellXY[] = mTmpXY;
705af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            pointToCellExact(x, y, cellXY);
70631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
707af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cell = null;
708af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellX = cellXY[0];
709af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.cellY = cellXY[1];
710af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanX = 1;
711af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            cellInfo.spanY = 1;
712af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        }
713af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        setTag(cellInfo);
714af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    }
71531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
716af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    @Override
717af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka    public boolean onInterceptTouchEvent(MotionEvent ev) {
718c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // First we clear the tag to ensure that on every touch down we start with a fresh slate,
719c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // even in the case where we return early. Not clearing here was causing bugs whereby on
720c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        // long-press we'd end up picking up an item from a previous drag operation.
721c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        final int action = ev.getAction();
722c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
723c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        if (action == MotionEvent.ACTION_DOWN) {
724c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen            clearTagCellInfo();
725c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        }
726c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
727dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
728dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            return true;
729dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
73031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
731af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka        if (action == MotionEvent.ACTION_DOWN) {
732af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
73331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
734eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung
73531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
73631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
73731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
738c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen    private void clearTagCellInfo() {
739c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        final CellInfo cellInfo = mCellInfo;
740c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cell = null;
741c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cellX = -1;
742c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.cellY = -1;
743c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.spanX = 0;
744c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        cellInfo.spanY = 0;
745c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen        setTag(cellInfo);
746c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen    }
747c1997fd6debbc69b53be71b7d871657fd5843c7aAdam Cohen
74831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public CellInfo getTag() {
7490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return (CellInfo) super.getTag();
75031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
75131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7526569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
753aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Given a point, return the cell that strictly encloses that point
75431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
75531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
75631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
75731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
75831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellExact(int x, int y, int[] result) {
7594b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
7604b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
76131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
76231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
76331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
76431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
765d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int xAxis = mCountX;
766d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int yAxis = mCountY;
76731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
76831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] < 0) result[0] = 0;
76931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[0] >= xAxis) result[0] = xAxis - 1;
77031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] < 0) result[1] = 0;
77131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (result[1] >= yAxis) result[1] = yAxis - 1;
77231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
773aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
77431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
77531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a point, return the cell that most closely encloses that point
77631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param x X coordinate of the point
77731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param y Y coordinate of the point
77831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the cell
77931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
78031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void pointToCellRounded(int x, int y, int[] result) {
78131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
78231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
78331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
78431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
78531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Given a cell coordinate, return the point that represents the upper left corner of that cell
786aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
787aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellX X coordinate of the cell
78831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of the cell
789aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
79031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param result Array of 2 ints to hold the x and y coordinate of the point
79131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
79231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    void cellToPoint(int cellX, int cellY, int[] result) {
7934b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
7944b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
79531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
79631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
79731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
79831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
79931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
800e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    /**
801482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Given a cell coordinate, return the point that represents the center of the cell
802e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     *
803e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param cellX X coordinate of the cell
804e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param cellY Y coordinate of the cell
805e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     *
806e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     * @param result Array of 2 ints to hold the x and y coordinate of the point
807e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen     */
808e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    void cellToCenterPoint(int cellX, int cellY, int[] result) {
80947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        regionToCenterPoint(cellX, cellY, 1, 1, result);
81047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
81147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
81247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    /**
81347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * Given a cell coordinate and span return the point that represents the center of the regio
81447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *
81547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellX X coordinate of the cell
81647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellY Y coordinate of the cell
81747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *
81847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param result Array of 2 ints to hold the x and y coordinate of the point
81947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     */
82047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
8214b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
8224b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
82347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) +
82447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                (spanX * mCellWidth + (spanX - 1) * mWidthGap) / 2;
82547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) +
82647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                (spanY * mCellHeight + (spanY - 1) * mHeightGap) / 2;
827e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen    }
828e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen
82919f3792523fe2d55ea791a9286398a6120920690Adam Cohen     /**
83019f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * Given a cell coordinate and span fills out a corresponding pixel rect
83119f3792523fe2d55ea791a9286398a6120920690Adam Cohen     *
83219f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param cellX X coordinate of the cell
83319f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param cellY Y coordinate of the cell
83419f3792523fe2d55ea791a9286398a6120920690Adam Cohen     * @param result Rect in which to write the result
83519f3792523fe2d55ea791a9286398a6120920690Adam Cohen     */
83619f3792523fe2d55ea791a9286398a6120920690Adam Cohen     void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) {
83719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int hStartPadding = getPaddingLeft();
83819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int vStartPadding = getPaddingTop();
83919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int left = hStartPadding + cellX * (mCellWidth + mWidthGap);
84019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int top = vStartPadding + cellY * (mCellHeight + mHeightGap);
84119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        result.set(left, top, left + (spanX * mCellWidth + (spanX - 1) * mWidthGap),
84219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                top + (spanY * mCellHeight + (spanY - 1) * mHeightGap));
84319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
84419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
845482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public float getDistanceFromCell(float x, float y, int[] cell) {
846482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        cellToCenterPoint(cell[0], cell[1], mTmpPoint);
847482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        float distance = (float) Math.sqrt( Math.pow(x - mTmpPoint[0], 2) +
848482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                Math.pow(y - mTmpPoint[1], 2));
849482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return distance;
850482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
851482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
85284f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellWidth() {
85384f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellWidth;
85484f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
85584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
85684f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    int getCellHeight() {
85784f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        return mCellHeight;
85884f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy    }
85984f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy
860d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    int getWidthGap() {
861d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        return mWidthGap;
862d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    }
863d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
864d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    int getHeightGap() {
865d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        return mHeightGap;
866d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    }
867d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
8687f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen    Rect getContentRect(Rect r) {
8697f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        if (r == null) {
8707f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            r = new Rect();
8717f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
8727f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        int left = getPaddingLeft();
8737f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        int top = getPaddingTop();
8748b805b17158886035b38261eb611d8641701ae43Michael Jurka        int right = left + getWidth() - getPaddingLeft() - getPaddingRight();
8758b805b17158886035b38261eb611d8641701ae43Michael Jurka        int bottom = top + getHeight() - getPaddingTop() - getPaddingBottom();
8767f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        r.set(left, top, right, bottom);
8777f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        return r;
8787f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen    }
8797f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
880a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen    static void getMetrics(Rect metrics, Resources res, int measureWidth, int measureHeight,
881a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int countX, int countY, int orientation) {
882a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int numWidthGaps = countX - 1;
883a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int numHeightGaps = countY - 1;
884f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
885f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int widthGap;
886f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int heightGap;
887f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int cellWidth;
888f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int cellHeight;
889f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingLeft;
890f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingRight;
891f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingTop;
892f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        int paddingBottom;
893f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
894a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen        int maxGap = res.getDimensionPixelSize(R.dimen.workspace_max_gap);
895f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        if (orientation == LANDSCAPE) {
896f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellWidth = res.getDimensionPixelSize(R.dimen.workspace_cell_width_land);
897f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellHeight = res.getDimensionPixelSize(R.dimen.workspace_cell_height_land);
898f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            widthGap = res.getDimensionPixelSize(R.dimen.workspace_width_gap_land);
899f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            heightGap = res.getDimensionPixelSize(R.dimen.workspace_height_gap_land);
900f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingLeft = res.getDimensionPixelSize(R.dimen.cell_layout_left_padding_land);
901f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingRight = res.getDimensionPixelSize(R.dimen.cell_layout_right_padding_land);
902f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingTop = res.getDimensionPixelSize(R.dimen.cell_layout_top_padding_land);
903f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingBottom = res.getDimensionPixelSize(R.dimen.cell_layout_bottom_padding_land);
904f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        } else {
905f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            // PORTRAIT
906f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellWidth = res.getDimensionPixelSize(R.dimen.workspace_cell_width_port);
907f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            cellHeight = res.getDimensionPixelSize(R.dimen.workspace_cell_height_port);
908f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            widthGap = res.getDimensionPixelSize(R.dimen.workspace_width_gap_port);
909f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            heightGap = res.getDimensionPixelSize(R.dimen.workspace_height_gap_port);
910f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingLeft = res.getDimensionPixelSize(R.dimen.cell_layout_left_padding_port);
911f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingRight = res.getDimensionPixelSize(R.dimen.cell_layout_right_padding_port);
912f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingTop = res.getDimensionPixelSize(R.dimen.cell_layout_top_padding_port);
913f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            paddingBottom = res.getDimensionPixelSize(R.dimen.cell_layout_bottom_padding_port);
914f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        }
915f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
916f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        if (widthGap < 0 || heightGap < 0) {
917f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int hSpace = measureWidth - paddingLeft - paddingRight;
918f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int vSpace = measureHeight - paddingTop - paddingBottom;
919a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int hFreeSpace = hSpace - (countX * cellWidth);
920a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            int vFreeSpace = vSpace - (countY * cellHeight);
921a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            widthGap = Math.min(maxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
922a897f397e553826f327d853d5728d0e1d24513a6Adam Cohen            heightGap = Math.min(maxGap, numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
923f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        }
924f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen        metrics.set(cellWidth, cellHeight, widthGap, heightGap);
925f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen    }
926f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen
92731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
92831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
92931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
930aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
931aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
93231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
93331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
934aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
93531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
93631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
93731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
93831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
939d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numWidthGaps = mCountX - 1;
940d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        int numHeightGaps = mCountY - 1;
941d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
942234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
943dd13e3d0f9925b7bb80c37e21d039aab4fa7e7a1Michael Jurka            int hSpace = widthSpecSize - getPaddingLeft() - getPaddingRight();
944dd13e3d0f9925b7bb80c37e21d039aab4fa7e7a1Michael Jurka            int vSpace = heightSpecSize - getPaddingTop() - getPaddingBottom();
945f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int hFreeSpace = hSpace - (mCountX * mCellWidth);
946f4bd5792d505a83ef35b30e4fa4e786ac0df58a3Adam Cohen            int vFreeSpace = vSpace - (mCountY * mCellHeight);
9474b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung            mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
9484b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung            mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
949a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
950234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen        } else {
951234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen            mWidthGap = mOriginalWidthGap;
952234c4cd54406e363a2ebc213f6ae5be284414988Adam Cohen            mHeightGap = mOriginalHeightGap;
953ece7f5b3b55cab646941123e03589241a61678e2Winson Chung        }
9545f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
9558c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY
9568c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int newWidth = widthSpecSize;
9578c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int newHeight = heightSpecSize;
9588c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        if (widthSpecMode == MeasureSpec.AT_MOST) {
9598b805b17158886035b38261eb611d8641701ae43Michael Jurka            newWidth = getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
9608c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka                ((mCountX - 1) * mWidthGap);
9618b805b17158886035b38261eb611d8641701ae43Michael Jurka            newHeight = getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
9628c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka                ((mCountY - 1) * mHeightGap);
9638c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            setMeasuredDimension(newWidth, newHeight);
9648c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        }
96531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
9668c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        int count = getChildCount();
96731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
96831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View child = getChildAt(i);
9698b805b17158886035b38261eb611d8641701ae43Michael Jurka            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth - getPaddingLeft() -
9708b805b17158886035b38261eb611d8641701ae43Michael Jurka                    getPaddingRight(), MeasureSpec.EXACTLY);
9718b805b17158886035b38261eb611d8641701ae43Michael Jurka            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight - getPaddingTop() -
9728b805b17158886035b38261eb611d8641701ae43Michael Jurka                    getPaddingBottom(), MeasureSpec.EXACTLY);
97331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
97431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
9758c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka        setMeasuredDimension(newWidth, newHeight);
97631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
97731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
97831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
97928750fba6a2d141eb9a1e566718c17236030b815Michael Jurka    protected void onLayout(boolean changed, int l, int t, int r, int b) {
98031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = getChildCount();
98131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < count; i++) {
9828c920dd3683d752aa4c43e964831ce53f9b72887Michael Jurka            View child = getChildAt(i);
9838b805b17158886035b38261eb611d8641701ae43Michael Jurka            child.layout(getPaddingLeft(), getPaddingTop(),
9848b805b17158886035b38261eb611d8641701ae43Michael Jurka                    r - l - getPaddingRight(), b - t - getPaddingBottom());
98531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
98631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
98731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
98831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
989dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
990dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super.onSizeChanged(w, h, oldw, oldh);
99118014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mBackgroundRect.set(0, 0, w, h);
992b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen        mForegroundRect.set(mForegroundPadding, mForegroundPadding,
993b5ba097015c4794fa822f30b38a60a7070a00097Adam Cohen                w - 2 * mForegroundPadding, h - 2 * mForegroundPadding);
994dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
995dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
996dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @Override
99731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
998a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setChildrenDrawingCacheEnabled(enabled);
99931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
100031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
100131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
100231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
1003a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.setChildrenDrawnWithCacheEnabled(enabled);
100431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
100531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
10065f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public float getBackgroundAlpha() {
10075f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        return mBackgroundAlpha;
1008dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1009dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
10101b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    public void setBackgroundAlphaMultiplier(float multiplier) {
1011a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka        if (mBackgroundAlphaMultiplier != multiplier) {
1012a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka            mBackgroundAlphaMultiplier = multiplier;
1013a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka            invalidate();
1014a3d30adbfd4f013260f1f5ba3a56bc9bb4a11717Michael Jurka        }
10151b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    }
10161b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen
1017ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen    public float getBackgroundAlphaMultiplier() {
1018ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen        return mBackgroundAlphaMultiplier;
1019ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen    }
1020ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen
10215f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public void setBackgroundAlpha(float alpha) {
1022afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka        if (mBackgroundAlpha != alpha) {
1023afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka            mBackgroundAlpha = alpha;
1024afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka            invalidate();
1025afaa05014e0bf3ed1192f9ddec2af4283bc50248Michael Jurka        }
1026dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1027dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1028a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    public void setShortcutAndWidgetAlpha(float alpha) {
10290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int childCount = getChildCount();
10300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        for (int i = 0; i < childCount; i++) {
1031dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            getChildAt(i).setAlpha(alpha);
1032dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
1033dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1034dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
1035a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
1036a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (getChildCount() > 0) {
1037a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            return (ShortcutAndWidgetContainer) getChildAt(0);
1038a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        }
1039a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        return null;
1040a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka    }
1041a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka
1042440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    public View getChildAt(int x, int y) {
1043a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        return mShortcutsAndWidgets.getChildAt(x, y);
1044440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
1045440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
104676fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen    public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
1047482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int delay, boolean permanent, boolean adjustOccupied) {
1048a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
1049482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean[][] occupied = mOccupied;
1050482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!permanent) {
1051482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            occupied = mTmpOccupied;
1052482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1053482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
105419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (clc.indexOfChild(child) != -1) {
1055bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1056bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            final ItemInfo info = (ItemInfo) child.getTag();
1057bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1058bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            // We cancel any existing animations
1059bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            if (mReorderAnimators.containsKey(lp)) {
1060bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                mReorderAnimators.get(lp).cancel();
1061bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                mReorderAnimators.remove(lp);
1062bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            }
1063bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
1064482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int oldX = lp.x;
1065482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int oldY = lp.y;
1066482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (adjustOccupied) {
1067482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[lp.cellX][lp.cellY] = false;
1068482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[cellX][cellY] = true;
1069482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1070bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            lp.isLockedToGrid = true;
1071482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (permanent) {
1072482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.cellX = info.cellX = cellX;
1073482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.cellY = info.cellY = cellY;
1074482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else {
1075482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.tmpCellX = cellX;
1076482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.tmpCellY = cellY;
1077482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1078bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            clc.setupLp(lp);
1079bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            lp.isLockedToGrid = false;
1080482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int newX = lp.x;
1081482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            final int newY = lp.y;
1082bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
108376fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            lp.x = oldX;
108476fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen            lp.y = oldY;
108576fc085d28178a5d4fb3787ede956281a2cc3179Adam Cohen
1086482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // Exit early if we're not actually moving the view
1087482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (oldX == newX && oldY == newY) {
1088482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                lp.isLockedToGrid = true;
1089482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return true;
1090482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1091482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
10922ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka            ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
1093482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.setDuration(duration);
1094482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mReorderAnimators.put(lp, va);
1095482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1096482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.addUpdateListener(new AnimatorUpdateListener() {
1097482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                @Override
1098bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationUpdate(ValueAnimator animation) {
1099482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    float r = ((Float) animation.getAnimatedValue()).floatValue();
110019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    lp.x = (int) ((1 - r) * oldX + r * newX);
110119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    lp.y = (int) ((1 - r) * oldY + r * newY);
11026b8a02d63a5d9cab8209381993e37db6a6afb753Adam Cohen                    child.requestLayout();
1103bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1104bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            });
1105482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.addListener(new AnimatorListenerAdapter() {
1106bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                boolean cancelled = false;
1107bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationEnd(Animator animation) {
1108bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // If the animation was cancelled, it means that another animation
1109bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // has interrupted this one, and we don't want to lock the item into
1110bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    // place just yet.
1111bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    if (!cancelled) {
1112bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                        lp.isLockedToGrid = true;
1113482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        child.requestLayout();
1114bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    }
1115bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    if (mReorderAnimators.containsKey(lp)) {
1116bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                        mReorderAnimators.remove(lp);
1117bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    }
1118bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1119bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                public void onAnimationCancel(Animator animation) {
1120bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                    cancelled = true;
1121bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen                }
1122bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            });
1123482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.setStartDelay(delay);
1124482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            va.start();
1125bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen            return true;
1126bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen        }
1127bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen        return false;
1128bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen    }
1129bfbfd26c627a18f8e1e3e6d0e53e78feab360203Adam Cohen
11306569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
11316569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * Estimate where the top left cell of the dragged item will land if it is dropped.
11326569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     *
11336569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originX The X value of the top left corner of the item
11346569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param originY The Y value of the top left corner of the item
11356569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanX The number of horizontal cells that the item spans
11366569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param spanY The number of vertical cells that the item spans
11376569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param result The estimated drop cell X and Y.
11386569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
11396569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
1140d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countX = mCountX;
1141d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen        final int countY = mCountY;
11426569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1143a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // pointToCellRounded takes the top left of a cell but will pad that with
1144a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        // cellWidth/2 and cellHeight/2 when finding the matching cell
1145a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        pointToCellRounded(originX, originY, result);
11466569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
11476569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        // If the item isn't fully on this screen, snap to the edges
11486569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int rightOverhang = result[0] + spanX - countX;
11496569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (rightOverhang > 0) {
11506569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[0] -= rightOverhang; // Snap to right
11516569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
11526569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[0] = Math.max(0, result[0]); // Snap to left
11536569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        int bottomOverhang = result[1] + spanY - countY;
11546569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        if (bottomOverhang > 0) {
11556569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            result[1] -= bottomOverhang; // Snap to bottom
11566569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
11576569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        result[1] = Math.max(0, result[1]); // Snap to top
11586569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
11596569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1160482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY, int cellX,
1161482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int cellY, int spanX, int spanY, boolean resize, Point dragOffset, Rect dragRegion) {
116208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        final int oldDragCellX = mDragCell[0];
116308ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        final int oldDragCellY = mDragCell[1];
1164482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1165b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung        if (v != null && dragOffset == null) {
1166a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2));
1167a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        } else {
1168a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mDragCenter.set(originX, originY);
1169a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        }
11706569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
11712801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        if (dragOutline == null && v == null) {
11722801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen            return;
11732801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        }
11742801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
1175482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (cellX != oldDragCellX || cellY != oldDragCellY) {
1176482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mDragCell[0] = cellX;
1177482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            mDragCell[1] = cellY;
11786569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Find the top left corner of the rect the object will occupy
1179de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy            final int[] topLeft = mTmpPoint;
1180482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            cellToPoint(cellX, cellY, topLeft);
1181de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
11824be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int left = topLeft[0];
11834be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            int top = topLeft[1];
11846569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1185b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung            if (v != null && dragOffset == null) {
118699e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // When drawing the drag outline, it did not account for margin offsets
118799e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // added by the view's parent.
118899e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
118999e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                left += lp.leftMargin;
119099e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                top += lp.topMargin;
119199e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen
119299e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // Offsets due to the size difference between the View and the dragOutline.
119399e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // There is a size difference to account for the outer blur, which may lie
119499e8b40b374d49baabf0c4f4e4278ac25828899bAdam Cohen                // outside the bounds of the view.
1195a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                top += (v.getHeight() - dragOutline.getHeight()) / 2;
1196ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                // We center about the x axis
1197ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1198ae915cecd36af4973061a1cb0b58c5be1be699a0Adam Cohen                        - dragOutline.getWidth()) / 2;
11996639687cd67bab1aeef2a75e5c6bc458b20dc082Adam Cohen            } else {
1200b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                if (dragOffset != null && dragRegion != null) {
1201b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // Center the drag region *horizontally* in the cell and apply a drag
1202b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // outline offset
1203b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1204b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                             - dragRegion.width()) / 2;
1205b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    top += dragOffset.y;
1206b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                } else {
1207b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    // Center the drag outline in the cell
1208b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
1209b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                            - dragOutline.getWidth()) / 2;
1210b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                    top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
1211b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                            - dragOutline.getHeight()) / 2;
1212b8c69f3c17a40adc2d85e8e996f754c383c293dcWinson Chung                }
1213a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            }
12144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int oldIndex = mDragOutlineCurrent;
121508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[oldIndex].animateOut();
121608ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
1217d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            Rect r = mDragOutlines[mDragOutlineCurrent];
1218d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
1219d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            if (resize) {
1220482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                cellToRect(cellX, cellY, spanX, spanY, r);
1221d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            }
1222150fbab7de7df45ce0e2d08fb0f0be87ff091c2fWinson Chung
122308ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
122408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            mDragOutlineAnims[mDragOutlineCurrent].animateIn();
12256569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        }
12266569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
12276569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1228e0310965022e7a1adb7ad489505d404186608689Adam Cohen    public void clearDragOutlines() {
1229e0310965022e7a1adb7ad489505d404186608689Adam Cohen        final int oldIndex = mDragOutlineCurrent;
1230e0310965022e7a1adb7ad489505d404186608689Adam Cohen        mDragOutlineAnims[oldIndex].animateOut();
1231d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        mDragCell[0] = mDragCell[1] = -1;
1232e0310965022e7a1adb7ad489505d404186608689Adam Cohen    }
1233e0310965022e7a1adb7ad489505d404186608689Adam Cohen
123431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
123570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Find a vacant area that will fit the given bounds nearest the requested
123670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * cell location. Uses Euclidean distance to score multiple vacant areas.
1237aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
123851afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelX The X location at which you want to search for a vacant area.
123951afc022fa76c79f0d1ece470ddc126c08fea8a4Romain Guy     * @param pixelY The Y location at which you want to search for a vacant area.
124070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanX Horizontal span of the object.
124170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @param spanY Vertical span of the object.
1242de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * @param result Array in which to place the result, or null (in which case a new array will
1243de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     *        be allocated)
124470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * @return The X, Y cell of a vacant area that can contain this object,
124570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     *         nearest the requested location.
124631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
1247d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
1248d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int[] result) {
1249de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
12506a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    }
1251aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
12526a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka    /**
12536a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * Find a vacant area that will fit the given bounds nearest the requested
12546a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * cell location. Uses Euclidean distance to score multiple vacant areas.
12556a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *
12566a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelX The X location at which you want to search for a vacant area.
12576a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param pixelY The Y location at which you want to search for a vacant area.
1258d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
1259d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
1260d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
1261d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
1262d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1263d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *        be allocated)
1264d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1265d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
1266d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
1267d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
1268d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int spanY, int[] result, int[] resultSpan) {
1269d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        return findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null,
1270d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                result, resultSpan);
1271d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1272d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1273d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
1274d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1275d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
1276d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
1277d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
1278d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
12796a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanX Horizontal span of the object.
12806a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @param spanY Vertical span of the object.
1281df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreOccupied If true, the result can be an occupied cell
1282df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1283df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *        be allocated)
12846a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     * @return The X, Y cell of a vacant area that can contain this object,
12856a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     *         nearest the requested location.
12866a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka     */
1287df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView,
1288df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            boolean ignoreOccupied, int[] result) {
1289d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY,
1290482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                spanX, spanY, ignoreView, ignoreOccupied, result, null, mOccupied);
1291d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1292d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1293d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private final Stack<Rect> mTempRectStack = new Stack<Rect>();
1294d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private void lazyInitTempRectStack() {
1295d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        if (mTempRectStack.isEmpty()) {
1296d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            for (int i = 0; i < mCountX * mCountY; i++) {
1297d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                mTempRectStack.push(new Rect());
1298d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            }
1299d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1300d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1301482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1302d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    private void recycleTempRects(Stack<Rect> used) {
1303d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        while (!used.isEmpty()) {
1304d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            mTempRectStack.push(used.pop());
1305d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1306d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
1307d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1308d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
1309d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1310d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
1311d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
1312d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
1313d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
1314d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
1315d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
1316d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
1317d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
1318d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param ignoreOccupied If true, the result can be an occupied cell
1319d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1320d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *        be allocated)
1321d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1322d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
1323d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
1324d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
1325482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            View ignoreView, boolean ignoreOccupied, int[] result, int[] resultSpan,
1326482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            boolean[][] occupied) {
1327d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        lazyInitTempRectStack();
1328c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
1329482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(ignoreView, occupied);
1330c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka
1331e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
1332e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // to the center of the item, but we are searching based on the top-left cell, so
1333e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        // we translate the point over to correspond to the top-left.
1334e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
1335e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen        pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
1336e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen
133770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        // Keep track of best-scoring drop area
1338de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int[] bestXY = result != null ? result : new int[2];
133970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        double bestDistance = Double.MAX_VALUE;
1340d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        final Rect bestRect = new Rect(-1, -1, -1, -1);
1341d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        final Stack<Rect> validRegions = new Stack<Rect>();
1342aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1343de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countX = mCountX;
1344de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy        final int countY = mCountY;
1345de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy
1346d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        if (minSpanX <= 0 || minSpanY <= 0 || spanX <= 0 || spanY <= 0 ||
1347d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                spanX < minSpanX || spanY < minSpanY) {
1348d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            return bestXY;
1349d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        }
1350d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1351d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        for (int y = 0; y < countY - (minSpanY - 1); y++) {
1352c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka            inner:
1353d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            for (int x = 0; x < countX - (minSpanX - 1); x++) {
1354d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                int ySize = -1;
1355d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                int xSize = -1;
1356df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                if (ignoreOccupied) {
1357d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // First, let's see if this thing fits anywhere
1358d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    for (int i = 0; i < minSpanX; i++) {
1359d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        for (int j = 0; j < minSpanY; j++) {
1360df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                            if (occupied[x + i][y + j]) {
1361df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                                continue inner;
1362df0353815c629fc678824b07a234b89a1ff94208Adam Cohen                            }
1363c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        }
1364c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    }
1365d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    xSize = minSpanX;
1366d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    ySize = minSpanY;
1367d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
1368d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // We know that the item will fit at _some_ acceptable size, now let's see
1369d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // how big we can make it. We'll alternate between incrementing x and y spans
1370d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    // until we hit a limit.
1371d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean incX = true;
1372d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean hitMaxX = xSize >= spanX;
1373d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    boolean hitMaxY = ySize >= spanY;
1374d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    while (!(hitMaxX && hitMaxY)) {
1375d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        if (incX && !hitMaxX) {
1376d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            for (int j = 0; j < ySize; j++) {
1377d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                if (x + xSize > countX -1 || occupied[x + xSize][y + j]) {
1378d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    // We can't move out horizontally
1379d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    hitMaxX = true;
1380d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                }
1381d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1382d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            if (!hitMaxX) {
1383d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                xSize++;
1384d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1385d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        } else if (!hitMaxY) {
1386d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            for (int i = 0; i < xSize; i++) {
1387d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                if (y + ySize > countY - 1 || occupied[x + i][y + ySize]) {
1388d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    // We can't move out vertically
1389d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                    hitMaxY = true;
1390d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                }
1391d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1392d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            if (!hitMaxY) {
1393d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                                ySize++;
1394d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                            }
1395d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        }
1396d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        hitMaxX |= xSize >= spanX;
1397d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        hitMaxY |= ySize >= spanY;
1398d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        incX = !incX;
1399d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1400d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    incX = true;
1401d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    hitMaxX = xSize >= spanX;
1402d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    hitMaxY = ySize >= spanY;
1403c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
14040be025d64c1f84138fe430a58875886e66aae767Winson Chung                final int[] cellXY = mTmpXY;
1405e3e27a854f3eca363d3c5ce353d19de475272d87Adam Cohen                cellToCenterPoint(x, y, cellXY);
1406c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka
1407d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // We verify that the current rect is not a sub-rect of any of our previous
1408d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // candidates. In this case, the current rect is disqualified in favour of the
1409d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                // containing rect.
1410d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                Rect currentRect = mTempRectStack.pop();
1411d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                currentRect.set(x, y, x + xSize, y + ySize);
1412d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                boolean contained = false;
1413d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                for (Rect r : validRegions) {
1414d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    if (r.contains(currentRect)) {
1415d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        contained = true;
1416d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        break;
1417d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1418d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                }
1419d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                validRegions.push(currentRect);
1420c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
1421c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                        + Math.pow(cellXY[1] - pixelY, 2));
1422482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1423d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                if ((distance <= bestDistance && !contained) ||
1424d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        currentRect.contains(bestRect)) {
1425c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestDistance = distance;
1426c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[0] = x;
1427c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                    bestXY[1] = y;
1428d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    if (resultSpan != null) {
1429d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        resultSpan[0] = xSize;
1430d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                        resultSpan[1] = ySize;
1431d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    }
1432d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen                    bestRect.set(currentRect);
1433c28de51eedb26848abf9245ddd19e021d30be318Michael Jurka                }
143431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
143531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
1436c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
1437482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(ignoreView, occupied);
143831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1439c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        // Return -1, -1 if no suitable location found
1440c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        if (bestDistance == Double.MAX_VALUE) {
1441c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen            bestXY[0] = -1;
1442c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen            bestXY[1] = -1;
144370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        }
1444d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        recycleTempRects(validRegions);
1445c0dcf597084d00e4c23a7fea5fd0738f6c095a6bAdam Cohen        return bestXY;
144631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1447aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1448482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     /**
1449482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
1450482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * cell location, and will also weigh in a suggested direction vector of the
1451482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * desired location. This method computers distance based on unit grid distances,
1452482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * not pixel distances.
1453482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *
145447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellX The X cell nearest to which you want to search for a vacant area.
145547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param cellY The Y cell nearest which you want to search for a vacant area.
1456482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param spanX Horizontal span of the object.
1457482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param spanY Vertical span of the object.
145847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param direction The favored direction in which the views should move from x, y
145947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param exactDirectionOnly If this parameter is true, then only solutions where the direction
146047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *        matches exactly. Otherwise we find the best matching direction.
146147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param occoupied The array which represents which cells in the CellLayout are occupied
146247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     * @param blockOccupied The array which represents which cells in the specified block (cellX,
146347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen     *        cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
1464482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @param result Array in which to place the result, or null (in which case a new array will
1465482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *        be allocated)
1466482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
1467482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     *         nearest the requested location.
1468482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     */
1469482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
147047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            boolean[][] occupied, boolean blockOccupied[][], int[] result) {
1471482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Keep track of best-scoring drop area
1472482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int[] bestXY = result != null ? result : new int[2];
1473482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        float bestDistance = Float.MAX_VALUE;
1474482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int bestDirectionScore = Integer.MIN_VALUE;
1475482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1476482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int countX = mCountX;
1477482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        final int countY = mCountY;
1478482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1479482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int y = 0; y < countY - (spanY - 1); y++) {
1480482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            inner:
1481482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int x = 0; x < countX - (spanX - 1); x++) {
1482482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                // First, let's see if this thing fits anywhere
1483482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                for (int i = 0; i < spanX; i++) {
1484482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    for (int j = 0; j < spanY; j++) {
148547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
1486482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                            continue inner;
1487482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        }
1488482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    }
1489482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1490482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1491482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                float distance = (float)
1492482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
1493482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                int[] curDirection = mTmpPoint;
149447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                computeDirectionVector(x - cellX, y - cellY, curDirection);
149547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                // The direction score is just the dot product of the two candidate direction
149647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                // and that passed in.
1497482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                int curDirectionScore = direction[0] * curDirection[0] +
1498482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        direction[1] * curDirection[1];
149947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean exactDirectionOnly = false;
150047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean directionMatches = direction[0] == curDirection[0] &&
150147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        direction[0] == curDirection[0];
150247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                if ((directionMatches || !exactDirectionOnly) &&
150347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        Float.compare(distance,  bestDistance) < 0 || (Float.compare(distance,
1504482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
1505482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestDistance = distance;
1506482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestDirectionScore = curDirectionScore;
1507482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestXY[0] = x;
1508482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    bestXY[1] = y;
1509482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1510482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1511482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1512482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1513482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Return -1, -1 if no suitable location found
1514482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (bestDistance == Float.MAX_VALUE) {
1515482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            bestXY[0] = -1;
1516482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            bestXY[1] = -1;
1517482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1518482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return bestXY;
1519482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1520482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
152147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    private int[] findNearestAreaInDirection(int cellX, int cellY, int spanX, int spanY,
152247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            int[] direction,boolean[][] occupied,
152347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            boolean blockOccupied[][], int[] result) {
152447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        // Keep track of best-scoring drop area
152547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        final int[] bestXY = result != null ? result : new int[2];
152647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        bestXY[0] = -1;
152747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        bestXY[1] = -1;
152847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        float bestDistance = Float.MAX_VALUE;
152947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
153047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        // We use this to march in a single direction
15315b53f29f809a0dad4a1e0717ba610ce737ee0f43Adam Cohen        if ((direction[0] != 0 && direction[1] != 0) ||
15325b53f29f809a0dad4a1e0717ba610ce737ee0f43Adam Cohen                (direction[0] == 0 && direction[1] == 0)) {
153347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            return bestXY;
153447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
153547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
153647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        // This will only incrememnet one of x or y based on the assertion above
153747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int x = cellX + direction[0];
153847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int y = cellY + direction[1];
153947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        while (x >= 0 && x + spanX <= mCountX && y >= 0 && y + spanY <= mCountY) {
154047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
154147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            boolean fail = false;
154247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            for (int i = 0; i < spanX; i++) {
154347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                for (int j = 0; j < spanY; j++) {
154447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
154547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        fail = true;
154647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    }
154747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
154847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
154947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            if (!fail) {
155047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                float distance = (float)
155147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
155247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                if (Float.compare(distance,  bestDistance) < 0) {
155347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    bestDistance = distance;
155447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    bestXY[0] = x;
155547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    bestXY[1] = y;
155647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
155747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
155847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            x += direction[0];
155947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            y += direction[1];
156047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
156147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        return bestXY;
156247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
156347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1564482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
15658baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int[] direction, ItemConfiguration currentState) {
15668baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        CellAndSpan c = currentState.map.get(v);
1567482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean success = false;
15688baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
1569482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
1570482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
15718baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        findNearestArea(c.x, c.y, c.spanX, c.spanY, direction, mTmpOccupied, null, mTempLocation);
1572482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1573482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
15748baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            c.x = mTempLocation[0];
15758baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            c.y = mTempLocation[1];
1576482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            success = true;
1577482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1578482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
15798baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1580482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return success;
1581482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1582482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
158347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    // This method looks in the specified direction to see if there is an additional view
158447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    // immediately adjecent in that direction
158547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    private boolean addViewInDirection(ArrayList<View> views, Rect boundingRect, int[] direction,
158619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            boolean[][] occupied, View dragView, ItemConfiguration currentState) {
158747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean found = false;
158847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1589a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
159047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        Rect r0 = new Rect(boundingRect);
159147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        Rect r1 = new Rect();
159247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
159347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int deltaX = 0;
159447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int deltaY = 0;
159547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (direction[1] < 0) {
159647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            r0.set(r0.left, r0.top - 1, r0.right, r0.bottom);
159747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            deltaY = -1;
159847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        } else if (direction[1] > 0) {
159947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            r0.set(r0.left, r0.top, r0.right, r0.bottom + 1);
160047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            deltaY = 1;
160147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        } else if (direction[0] < 0) {
160247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            r0.set(r0.left - 1, r0.top, r0.right, r0.bottom);
160347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            deltaX = -1;
160447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        } else if (direction[0] > 0) {
160547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            r0.set(r0.left, r0.top, r0.right + 1, r0.bottom);
160647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            deltaX = 1;
160747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
160847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
160947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        for (int i = 0; i < childCount; i++) {
1610a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
161119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (views.contains(child) || child == dragView) continue;
16128baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(child);
161347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
16148baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
16158baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
161647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            if (Rect.intersects(r0, r1)) {
161747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                if (!lp.canReorder) {
161847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    return false;
161947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
162047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                boolean pushed = false;
16218baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                for (int x = c.x; x < c.x + c.spanX; x++) {
16228baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                    for (int y = c.y; y < c.y + c.spanY; y++) {
162347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        boolean inBounds = x - deltaX >= 0 && x -deltaX < mCountX
162447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                                && y - deltaY >= 0 && y - deltaY < mCountY;
162547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        if (inBounds && occupied[x - deltaX][y - deltaY]) {
162647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                            pushed = true;
162747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                        }
162847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    }
162947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
163047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                if (pushed) {
163147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    views.add(child);
16328baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                    boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
163347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                    found = true;
163447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen                }
163547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
163647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
163747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        return found;
163847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    }
163947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
16408baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
164119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            int[] direction, boolean push, View dragView, ItemConfiguration currentState) {
164247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (views.size() == 0) return true;
164347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
164447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean success = false;
164547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        Rect boundingRect = null;
16468baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We construct a rect which represents the entire group of views passed in
164747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        for (View v: views) {
16488baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
164947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            if (boundingRect == null) {
16508baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                boundingRect = new Rect(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
165147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            } else {
16528baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
165347a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
165447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
165547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
16568baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        @SuppressWarnings("unchecked")
165747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        ArrayList<View> dup = (ArrayList<View>) views.clone();
16588baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We try and expand the group of views in the direction vector passed, based on
16598baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // whether they are physically adjacent, ie. based on "push mechanics".
166019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        while (push && addViewInDirection(dup, boundingRect, direction, mTmpOccupied, dragView,
16618baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                currentState)) {
166247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
16638baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
16648baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Mark the occupied state as false for the group of views we want to move.
166547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        for (View v: dup) {
16668baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
16678baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
166847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
166947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
167047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
167147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int top = boundingRect.top;
167247a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        int left = boundingRect.left;
16738baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // We mark more precisely which parts of the bounding rect are truly occupied, allowing
16748baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // for tetris-style interlocking.
167547a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        for (View v: dup) {
16768baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
16778baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x - left, c.y - top, c.spanX, c.spanY, blockOccupied, true);
167847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
167947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
168047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
168147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
16828baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        if (push) {
16838baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            findNearestAreaInDirection(boundingRect.left, boundingRect.top, boundingRect.width(),
16848baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                    boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
16858baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        } else {
16868baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
16878baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                    boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
16888baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        }
168947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
16908baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // If we successfuly found a location by pushing the block of views, we commit it
169147a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
16928baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int deltaX = mTempLocation[0] - boundingRect.left;
16938baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            int deltaY = mTempLocation[1] - boundingRect.top;
169447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            for (View v: dup) {
16958baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                CellAndSpan c = currentState.map.get(v);
16968baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c.x += deltaX;
16978baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c.y += deltaY;
169847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            }
169947a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            success = true;
170047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
1701482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
17028baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // In either case, we set the occupied array as marked for the location of the views
17038baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (View v: dup) {
17048baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = currentState.map.get(v);
17058baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1706482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1707482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return success;
1708482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1709482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1710482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void markCellsForRect(Rect r, boolean[][] occupied, boolean value) {
1711482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(r.left, r.top, r.width(), r.height(), occupied, value);
1712482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1713482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
17144abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // This method tries to find a reordering solution which satisfies the push mechanic by trying
17154abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // to push items in each of the cardinal directions, in an order based on the direction vector
17164abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    // passed.
17174abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
17184abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int[] direction, View ignoreView, ItemConfiguration solution) {
17194abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
17204abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If the direction vector has two non-zero components, we try pushing
17214abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // separately in each of the components.
17224abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int temp = direction[1];
17234abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = 0;
17244abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17254abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17264abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17274abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17284abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = temp;
17294abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[0];
17304abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = 0;
17314abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17324abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17334abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17344abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17354abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Revert the direction
17364abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
17374abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17384abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Now we try pushing in each component of the opposite direction
17394abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
17404abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
17414abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[1];
17424abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = 0;
17434abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17444abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17454abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17464abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17474abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17484abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = temp;
17494abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[0];
17504abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = 0;
17514abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17524abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17534abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17544abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17554abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // revert the direction
17564abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
17574abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
17584abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
17594abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17604abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        } else {
17614abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If the direction vector has a single non-zero component, we push first in the
17624abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // direction of the vector
17634abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17644abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17654abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17664abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17674abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17684abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Then we try the opposite direction
17694abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
17704abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
17714abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17724abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17734abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17744abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17754abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Switch the direction back
17764abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
17774abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
17784abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17794abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // If we have failed to find a push solution with the above, then we try
17804abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // to find a solution by pushing along the perpendicular axis.
17814abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17824abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Swap the components
17834abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            int temp = direction[1];
17844abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = direction[0];
17854abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
17864abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17874abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17884abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17894abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17904abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
17914abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Then we try the opposite direction
17924abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
17934abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
17944abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
17954abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                    ignoreView, solution)) {
17964abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen                return true;
17974abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            }
17984abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Switch the direction back
17994abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] *= -1;
18004abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] *= -1;
18014abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
18024abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            // Swap the components back
18034abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            temp = direction[1];
18044abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[1] = direction[0];
18054abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen            direction[0] = temp;
18064abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        }
18074abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        return false;
18084abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen    }
18094abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen
1810482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
18118baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            View ignoreView, ItemConfiguration solution) {
1812e3e03bcd313ba8060f2832b6a16dea6fd6d532eaWinson Chung        // Return early if get invalid cell positions
1813e3e03bcd313ba8060f2832b6a16dea6fd6d532eaWinson Chung        if (cellX < 0 || cellY < 0) return false;
1814482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
18158baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        mIntersectingViews.clear();
1816482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
1817482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
18188baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Mark the desired location of the view currently being dragged.
1819482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (ignoreView != null) {
18208baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(ignoreView);
182119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (c != null) {
182219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                c.x = cellX;
182319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                c.y = cellY;
182419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
1825482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1826482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
1827482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        Rect r1 = new Rect();
18288baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (View child: solution.map.keySet()) {
1829482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == ignoreView) continue;
18308baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
1831482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
18328baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
1833482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (Rect.intersects(r0, r1)) {
1834482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                if (!lp.canReorder) {
1835482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    return false;
1836482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
1837482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mIntersectingViews.add(child);
1838482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1839482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
184047a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
18414abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // First we try to find a solution which respects the push mechanic. That is,
18424abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // we try to find a solution such that no displaced item travels through another item
18434abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // without also displacing that item.
18444abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
184519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                solution)) {
184647a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen            return true;
184747a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        }
184847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
18494abc5bdfdfc7021d8519c0b7e1bffe0728ff1d9dAdam Cohen        // Next we try moving the views as a block, but without requiring the push mechanic.
185019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, false, ignoreView,
185119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                solution)) {
1852482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            return true;
1853482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
185447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen
1855482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // Ok, they couldn't move as a block, let's move them individually
1856482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (View v : mIntersectingViews) {
18578baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
1858482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return false;
1859482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1860482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1861482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return true;
1862482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1863482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1864482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    /*
1865482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
1866482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     * the provided point and the provided cell
1867482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen     */
186847a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen    private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
1869482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        double angle = Math.atan(((float) deltaY) / deltaX);
1870482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1871482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result[0] = 0;
1872482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result[1] = 0;
1873482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (Math.abs(Math.cos(angle)) > 0.5f) {
1874482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = (int) Math.signum(deltaX);
1875482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1876482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (Math.abs(Math.sin(angle)) > 0.5f) {
1877482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[1] = (int) Math.signum(deltaY);
1878482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1879482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1880482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
18818baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private void copyOccupiedArray(boolean[][] occupied) {
18828baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        for (int i = 0; i < mCountX; i++) {
18838baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            for (int j = 0; j < mCountY; j++) {
18848baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                occupied[i][j] = mOccupied[i][j];
18858baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            }
18868baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        }
18878baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    }
18888baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
1889482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    ItemConfiguration simpleSwap(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
1890482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) {
18918baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Copy the current state into the solution. This solution will be manipulated as necessary.
18928baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        copyCurrentStateToSolution(solution, false);
18938baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // Copy the current occupied array into the temporary occupied array. This array will be
18948baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        // manipulated as necessary to find a solution.
18958baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        copyOccupiedArray(mTmpOccupied);
1896482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1897482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // We find the nearest cell into which we would place the dragged item, assuming there's
1898482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // nothing in its way.
1899482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int result[] = new int[2];
1900482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
1901482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1902482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean success = false;
1903482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // First we try the exact nearest position of the item being dragged,
1904482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // we will then want to try to move this around to other neighbouring positions
19058baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
19068baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                solution);
1907482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1908482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!success) {
1909482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
1910482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // x, then 1 in y etc.
1911482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (spanX > minSpanX && (minSpanY == spanY || decX)) {
1912482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY, direction,
1913482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        dragView, false, solution);
1914482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else if (spanY > minSpanY) {
1915482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1, direction,
1916482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                        dragView, true, solution);
1917482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1918482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = false;
1919482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
1920482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = true;
1921482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewX = result[0];
1922482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewY = result[1];
1923482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanX = spanX;
1924482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanY = spanY;
1925482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1926482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return solution;
1927482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1928482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1929482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
1930a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
1931482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
1932a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
1933482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
19348baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c;
1935482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (temp) {
19368baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
1937482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            } else {
19388baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
1939482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
19408baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            solution.map.put(child, c);
1941482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1942482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1943482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1944482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
1945482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
1946482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
1947482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mTmpOccupied[i][j] = false;
1948482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1949482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1950482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1951a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
1952482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
1953a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
1954482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == dragView) continue;
1955482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
19568baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
19578baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (c != null) {
19588baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.tmpCellX = c.x;
19598baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.tmpCellY = c.y;
19608baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.cellHSpan = c.spanX;
19618baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                lp.cellVSpan = c.spanY;
19628baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
1963482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1964482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1965482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
1966482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                solution.dragViewSpanY, mTmpOccupied, true);
1967482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1968482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1969482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
1970482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            commitDragView) {
1971482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1972482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean[][] occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
1973482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
1974482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
1975482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[i][j] = false;
1976482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1977482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1978482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
1979a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
1980482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
1981a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            View child = mShortcutsAndWidgets.getChildAt(i);
1982482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (child == dragView) continue;
19838baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            CellAndSpan c = solution.map.get(child);
19848baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            if (c != null) {
198519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                animateChildToPosition(child, c.x, c.y, REORDER_ANIMATION_DURATION, 0,
198619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        DESTRUCTIVE_REORDER, false);
19878baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen                markCellsForView(c.x, c.y, c.spanX, c.spanY, occupied, true);
1988482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
1989482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1990482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (commitDragView) {
1991482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
1992482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    solution.dragViewSpanY, occupied, true);
1993482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
1994482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
1995482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
199619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // This method starts or changes the reorder hint animations
199719f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void beginOrAdjustHintAnimations(ItemConfiguration solution, View dragView, int delay) {
199819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int childCount = mShortcutsAndWidgets.getChildCount();
199919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < childCount; i++) {
200019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
200119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (child == dragView) continue;
200219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            CellAndSpan c = solution.map.get(child);
200319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
200419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (c != null) {
200519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                ReorderHintAnimation rha = new ReorderHintAnimation(child, lp.cellX, lp.cellY,
200619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        c.x, c.y, c.spanX, c.spanY);
2007d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                rha.animate();
200819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
200919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
201019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
201119f3792523fe2d55ea791a9286398a6120920690Adam Cohen
201219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // Class which represents the reorder hint animations. These animations show that an item is
201319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // in a temporary state, and hint at where the item will return to.
201419f3792523fe2d55ea791a9286398a6120920690Adam Cohen    class ReorderHintAnimation {
201519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        View child;
2016d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalDeltaX;
2017d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalDeltaY;
2018d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initDeltaX;
2019d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initDeltaY;
2020d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float finalScale;
2021d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        float initScale;
202250e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        private static final int DURATION = 300;
2023e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen        Animator a;
202419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
202519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        public ReorderHintAnimation(View child, int cellX0, int cellY0, int cellX1, int cellY1,
202619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                int spanX, int spanY) {
202719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint);
202819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int x0 = mTmpPoint[0];
202919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int y0 = mTmpPoint[1];
203019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint);
203119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int x1 = mTmpPoint[0];
203219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int y1 = mTmpPoint[1];
203319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int dX = x1 - x0;
203419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            final int dY = y1 - y0;
2035d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalDeltaX = 0;
2036d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalDeltaY = 0;
203719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (dX == dY && dX == 0) {
203819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            } else {
203919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (dY == 0) {
2040d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaX = - Math.signum(dX) * mReorderHintAnimationMagnitude;
204119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else if (dX == 0) {
2042d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaY = - Math.signum(dY) * mReorderHintAnimationMagnitude;
204319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else {
204419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    double angle = Math.atan( (float) (dY) / dX);
2045d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaX = (int) (- Math.signum(dX) *
2046fe41ac641bdef7ea96dcbac59b4f3abdbdff6cfeAdam Cohen                            Math.abs(Math.cos(angle) * mReorderHintAnimationMagnitude));
2047d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    finalDeltaY = (int) (- Math.signum(dY) *
2048fe41ac641bdef7ea96dcbac59b4f3abdbdff6cfeAdam Cohen                            Math.abs(Math.sin(angle) * mReorderHintAnimationMagnitude));
204919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
205019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2051d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initDeltaX = child.getTranslationX();
2052d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initDeltaY = child.getTranslationY();
2053d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            finalScale = 1.0f - 4.0f / child.getWidth();
2054d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            initScale = child.getScaleX();
2055d024f9845a0974ab525baad085f316031cd5a742Adam Cohen
205650e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            child.setPivotY(child.getMeasuredHeight() * 0.5f);
205750e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            child.setPivotX(child.getMeasuredWidth() * 0.5f);
205819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            this.child = child;
205919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
206019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2061d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        void animate() {
206219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (mShakeAnimators.containsKey(child)) {
206319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                ReorderHintAnimation oldAnimation = mShakeAnimators.get(child);
2064d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                oldAnimation.cancel();
206519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mShakeAnimators.remove(child);
2066e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                if (finalDeltaX == 0 && finalDeltaY == 0) {
2067e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                    completeAnimationImmediately();
2068e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                    return;
2069e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                }
207019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
2071d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            if (finalDeltaX == 0 && finalDeltaY == 0) {
207219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                return;
207319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
20742ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka            ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
2075e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            a = va;
207619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.setRepeatMode(ValueAnimator.REVERSE);
207719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.setRepeatCount(ValueAnimator.INFINITE);
20787bdfc9700b1cad043c04c757f134db1bf3df00daAdam Cohen            va.setDuration(DURATION);
2079d024f9845a0974ab525baad085f316031cd5a742Adam Cohen            va.setStartDelay((int) (Math.random() * 60));
208019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.addUpdateListener(new AnimatorUpdateListener() {
208119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                @Override
208219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                public void onAnimationUpdate(ValueAnimator animation) {
208319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    float r = ((Float) animation.getAnimatedValue()).floatValue();
2084d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float x = r * finalDeltaX + (1 - r) * initDeltaX;
2085d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float y = r * finalDeltaY + (1 - r) * initDeltaY;
208619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    child.setTranslationX(x);
208719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    child.setTranslationY(y);
2088d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    float s = r * finalScale + (1 - r) * initScale;
208950e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely                    child.setScaleX(s);
209050e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely                    child.setScaleY(s);
209119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
209219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            });
209319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.addListener(new AnimatorListenerAdapter() {
209419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                public void onAnimationRepeat(Animator animation) {
209519f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    // We make sure to end only after a full period
2096d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initDeltaX = 0;
2097d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initDeltaY = 0;
2098d024f9845a0974ab525baad085f316031cd5a742Adam Cohen                    initScale = 1.0f;
209919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
210019f3792523fe2d55ea791a9286398a6120920690Adam Cohen            });
210119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mShakeAnimators.put(child, this);
210219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            va.start();
210319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
210419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2105d024f9845a0974ab525baad085f316031cd5a742Adam Cohen        private void cancel() {
2106e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            if (a != null) {
2107e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                a.cancel();
2108e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            }
210919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
2110e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen
211150e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        private void completeAnimationImmediately() {
2112e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            if (a != null) {
2113e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen                a.cancel();
2114e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            }
211550e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely
21162ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka            AnimatorSet s = LauncherAnimUtils.createAnimatorSet();
2117e7587d2f23f3ab9e133b40cd7fed4b030609f252Adam Cohen            a = s;
211850e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.playTogether(
21192ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "scaleX", 1f),
21202ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "scaleY", 1f),
21212ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "translationX", 0f),
21222ecf995e0d2d55eb71d03f7230ca87270872d1a3Michael Jurka                LauncherAnimUtils.ofFloat(child, "translationY", 0f)
212350e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            );
212450e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.setDuration(REORDER_ANIMATION_DURATION);
212550e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
212650e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            s.start();
212750e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely        }
212819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
212919f3792523fe2d55ea791a9286398a6120920690Adam Cohen
213019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void completeAndClearReorderHintAnimations() {
213119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (ReorderHintAnimation a: mShakeAnimators.values()) {
213250e6e565bcf1f51023c2a14d31b17c8a550aac84Brandon Keely            a.completeAnimationImmediately();
213319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
213419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mShakeAnimators.clear();
213519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
213619f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2137482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void commitTempPlacement() {
2138482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < mCountX; i++) {
2139482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            for (int j = 0; j < mCountY; j++) {
2140482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                mOccupied[i][j] = mTmpOccupied[i][j];
2141482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2142482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2143a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2144482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2145ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
2146ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
2147ea889a294be004f4b1c21e9b874f9e63abfb8bd6Adam Cohen            ItemInfo info = (ItemInfo) child.getTag();
21482acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            // We do a null check here because the item info can be null in the case of the
21492acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            // AllApps button in the hotseat.
21502acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            if (info != null) {
2151487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                if (info.cellX != lp.tmpCellX || info.cellY != lp.tmpCellY ||
2152487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                        info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan) {
2153487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                    info.requiresDbUpdate = true;
2154487f7dd3059621527eb439d7d51d34e00293f9b1Adam Cohen                }
21552acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen                info.cellX = lp.cellX = lp.tmpCellX;
21562acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen                info.cellY = lp.cellY = lp.tmpCellY;
2157bebf042666cffe52039b875a549a582abd78a431Adam Cohen                info.spanX = lp.cellHSpan;
2158bebf042666cffe52039b875a549a582abd78a431Adam Cohen                info.spanY = lp.cellVSpan;
21592acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen            }
2160482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
21612acce88b5fa316e7a314109f9957ad233a6c31a6Adam Cohen        mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
2162482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2163482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2164482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void setUseTempCoords(boolean useTempCoords) {
2165a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        int childCount = mShortcutsAndWidgets.getChildCount();
2166482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        for (int i = 0; i < childCount; i++) {
2167a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka            LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
2168482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            lp.useTmpCoords = useTempCoords;
2169482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2170482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2171482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2172482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY,
2173482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int spanX, int spanY, View dragView, ItemConfiguration solution) {
2174482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int[] result = new int[2];
2175482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int[] resultSpan = new int[2];
2176482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null, result,
2177482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                resultSpan);
2178482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (result[0] >= 0 && result[1] >= 0) {
2179482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            copyCurrentStateToSolution(solution, false);
2180482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewX = result[0];
2181482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewY = result[1];
2182482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanX = resultSpan[0];
2183482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.dragViewSpanY = resultSpan[1];
2184482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = true;
2185482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2186482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            solution.isSolution = false;
2187482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2188482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return solution;
2189482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2190482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2191482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void prepareChildForDrag(View child) {
2192482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(child);
2193482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2194482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
219519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    /* This seems like it should be obvious and straight-forward, but when the direction vector
219619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    needs to match with the notion of the dragView pushing other views, we have to employ
219719f3792523fe2d55ea791a9286398a6120920690Adam Cohen    a slightly more subtle notion of the direction vector. The question is what two points is
219819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    the vector between? The center of the dragView and its desired destination? Not quite, as
219919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    this doesn't necessarily coincide with the interaction of the dragView and items occupying
220019f3792523fe2d55ea791a9286398a6120920690Adam Cohen    those cells. Instead we use some heuristics to often lock the vector to up, down, left
220119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    or right, which helps make pushing feel right.
220219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    */
220319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
220419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            int spanY, View dragView, int[] resultDirection) {
220519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int[] targetDestination = new int[2];
220619f3792523fe2d55ea791a9286398a6120920690Adam Cohen
220719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
220819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect dragRect = new Rect();
220919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        regionToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
221019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
221119f3792523fe2d55ea791a9286398a6120920690Adam Cohen
221219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect dropRegionRect = new Rect();
221319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
221419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                dragView, dropRegionRect, mIntersectingViews);
221519f3792523fe2d55ea791a9286398a6120920690Adam Cohen
221619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int dropRegionSpanX = dropRegionRect.width();
221719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int dropRegionSpanY = dropRegionRect.height();
221819f3792523fe2d55ea791a9286398a6120920690Adam Cohen
221919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        regionToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
222019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                dropRegionRect.height(), dropRegionRect);
222119f3792523fe2d55ea791a9286398a6120920690Adam Cohen
222219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
222319f3792523fe2d55ea791a9286398a6120920690Adam Cohen        int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
222419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
222519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (dropRegionSpanX == mCountX || spanX == mCountX) {
222619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            deltaX = 0;
222719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
222819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (dropRegionSpanY == mCountY || spanY == mCountY) {
222919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            deltaY = 0;
223019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
223119f3792523fe2d55ea791a9286398a6120920690Adam Cohen
223219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (deltaX == 0 && deltaY == 0) {
223319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            // No idea what to do, give a random direction.
223419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            resultDirection[0] = 1;
223519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            resultDirection[1] = 0;
223619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        } else {
223719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            computeDirectionVector(deltaX, deltaY, resultDirection);
223819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
223919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
224019f3792523fe2d55ea791a9286398a6120920690Adam Cohen
224119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    // For a given cell and span, fetch the set of views intersecting the region.
224219f3792523fe2d55ea791a9286398a6120920690Adam Cohen    private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
224319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
224419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (boundingRect != null) {
224519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
224619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
224719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        intersectingViews.clear();
224819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
224919f3792523fe2d55ea791a9286398a6120920690Adam Cohen        Rect r1 = new Rect();
225019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int count = mShortcutsAndWidgets.getChildCount();
225119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < count; i++) {
225219f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
225319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (child == dragView) continue;
225419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
225519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
225619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (Rect.intersects(r0, r1)) {
225719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mIntersectingViews.add(child);
225819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (boundingRect != null) {
225919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    boundingRect.union(r1);
226019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                }
226119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
226219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
226319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
226419f3792523fe2d55ea791a9286398a6120920690Adam Cohen
226519f3792523fe2d55ea791a9286398a6120920690Adam Cohen    boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
226619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View dragView, int[] result) {
226719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
226819f3792523fe2d55ea791a9286398a6120920690Adam Cohen        getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
226919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                mIntersectingViews);
227019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        return !mIntersectingViews.isEmpty();
227119f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
227219f3792523fe2d55ea791a9286398a6120920690Adam Cohen
227319f3792523fe2d55ea791a9286398a6120920690Adam Cohen    void revertTempState() {
227419f3792523fe2d55ea791a9286398a6120920690Adam Cohen        if (!isItemPlacementDirty() || DESTRUCTIVE_REORDER) return;
227519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        final int count = mShortcutsAndWidgets.getChildCount();
227619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        for (int i = 0; i < count; i++) {
227719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            View child = mShortcutsAndWidgets.getChildAt(i);
227819f3792523fe2d55ea791a9286398a6120920690Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
227919f3792523fe2d55ea791a9286398a6120920690Adam Cohen            if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
228019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                lp.tmpCellX = lp.cellX;
228119f3792523fe2d55ea791a9286398a6120920690Adam Cohen                lp.tmpCellY = lp.cellY;
228219f3792523fe2d55ea791a9286398a6120920690Adam Cohen                animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION,
228319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        0, false, false);
228419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
228519f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
228619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        completeAndClearReorderHintAnimations();
228719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        setItemPlacementDirty(false);
228819f3792523fe2d55ea791a9286398a6120920690Adam Cohen    }
228919f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2290bebf042666cffe52039b875a549a582abd78a431Adam Cohen    boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
2291bebf042666cffe52039b875a549a582abd78a431Adam Cohen            View dragView, int[] direction, boolean commit) {
2292bebf042666cffe52039b875a549a582abd78a431Adam Cohen        int[] pixelXY = new int[2];
2293bebf042666cffe52039b875a549a582abd78a431Adam Cohen        regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
2294bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2295bebf042666cffe52039b875a549a582abd78a431Adam Cohen        // First we determine if things have moved enough to cause a different layout
2296bebf042666cffe52039b875a549a582abd78a431Adam Cohen        ItemConfiguration swapSolution = simpleSwap(pixelXY[0], pixelXY[1], spanX, spanY,
2297bebf042666cffe52039b875a549a582abd78a431Adam Cohen                 spanX,  spanY, direction, dragView,  true,  new ItemConfiguration());
2298bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2299bebf042666cffe52039b875a549a582abd78a431Adam Cohen        setUseTempCoords(true);
2300bebf042666cffe52039b875a549a582abd78a431Adam Cohen        if (swapSolution != null && swapSolution.isSolution) {
2301bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
2302bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // committing anything or animating anything as we just want to determine if a solution
2303bebf042666cffe52039b875a549a582abd78a431Adam Cohen            // exists
2304bebf042666cffe52039b875a549a582abd78a431Adam Cohen            copySolutionToTempState(swapSolution, dragView);
2305bebf042666cffe52039b875a549a582abd78a431Adam Cohen            setItemPlacementDirty(true);
2306bebf042666cffe52039b875a549a582abd78a431Adam Cohen            animateItemsToSolution(swapSolution, dragView, commit);
2307bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2308bebf042666cffe52039b875a549a582abd78a431Adam Cohen            if (commit) {
2309bebf042666cffe52039b875a549a582abd78a431Adam Cohen                commitTempPlacement();
2310bebf042666cffe52039b875a549a582abd78a431Adam Cohen                completeAndClearReorderHintAnimations();
2311bebf042666cffe52039b875a549a582abd78a431Adam Cohen                setItemPlacementDirty(false);
2312bebf042666cffe52039b875a549a582abd78a431Adam Cohen            } else {
2313bebf042666cffe52039b875a549a582abd78a431Adam Cohen                beginOrAdjustHintAnimations(swapSolution, dragView,
2314bebf042666cffe52039b875a549a582abd78a431Adam Cohen                        REORDER_ANIMATION_DURATION);
2315bebf042666cffe52039b875a549a582abd78a431Adam Cohen            }
2316bebf042666cffe52039b875a549a582abd78a431Adam Cohen            mShortcutsAndWidgets.requestLayout();
2317bebf042666cffe52039b875a549a582abd78a431Adam Cohen        }
2318bebf042666cffe52039b875a549a582abd78a431Adam Cohen        return swapSolution.isSolution;
2319bebf042666cffe52039b875a549a582abd78a431Adam Cohen    }
2320bebf042666cffe52039b875a549a582abd78a431Adam Cohen
2321482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    int[] createArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
2322482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            View dragView, int[] result, int resultSpan[], int mode) {
2323482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // First we determine if things have moved enough to cause a different layout
232447a876d443ddc65c8d9a0c95da58d892dbb1faeaAdam Cohen        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
2325482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2326482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (resultSpan == null) {
2327482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan = new int[2];
2328482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2329482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
233019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // When we are checking drop validity or actually dropping, we don't recompute the
233119f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // direction vector, since we want the solution to match the preview, and it's possible
233219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        // that the exact position of the item has changed to result in a new reordering outcome.
2333b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen        if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
2334b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen               && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
233519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mDirectionVector[0] = mPreviousReorderDirection[0];
233619f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mDirectionVector[1] = mPreviousReorderDirection[1];
233719f3792523fe2d55ea791a9286398a6120920690Adam Cohen            // We reset this vector after drop
2338b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen            if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
2339b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen                mPreviousReorderDirection[0] = INVALID_DIRECTION;
2340b209e634a29a0cb5514fafb4e5882ea49ba1cfa7Adam Cohen                mPreviousReorderDirection[1] = INVALID_DIRECTION;
234119f3792523fe2d55ea791a9286398a6120920690Adam Cohen            }
234219f3792523fe2d55ea791a9286398a6120920690Adam Cohen        } else {
234319f3792523fe2d55ea791a9286398a6120920690Adam Cohen            getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
234419f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mPreviousReorderDirection[0] = mDirectionVector[0];
234519f3792523fe2d55ea791a9286398a6120920690Adam Cohen            mPreviousReorderDirection[1] = mDirectionVector[1];
234619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        }
234719f3792523fe2d55ea791a9286398a6120920690Adam Cohen
2348482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration swapSolution = simpleSwap(pixelX, pixelY, minSpanX, minSpanY,
2349482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                 spanX,  spanY, mDirectionVector, dragView,  true,  new ItemConfiguration());
2350482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2351482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        // We attempt the approach which doesn't shuffle views at all
2352482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
2353482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                minSpanY, spanX, spanY, dragView, new ItemConfiguration());
2354482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2355482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        ItemConfiguration finalSolution = null;
2356482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
2357482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            finalSolution = swapSolution;
2358482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else if (noShuffleSolution.isSolution) {
2359482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            finalSolution = noShuffleSolution;
2360482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2361482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2362482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean foundSolution = true;
2363482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (!DESTRUCTIVE_REORDER) {
2364482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            setUseTempCoords(true);
2365482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2366482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2367482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (finalSolution != null) {
2368482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = finalSolution.dragViewX;
2369482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[1] = finalSolution.dragViewY;
2370482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan[0] = finalSolution.dragViewSpanX;
2371482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            resultSpan[1] = finalSolution.dragViewSpanY;
2372482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2373482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
2374482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // committing anything or animating anything as we just want to determine if a solution
2375482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            // exists
2376482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
2377482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                if (!DESTRUCTIVE_REORDER) {
2378482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    copySolutionToTempState(finalSolution, dragView);
2379482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2380482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                setItemPlacementDirty(true);
2381482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP);
2382482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
238319f3792523fe2d55ea791a9286398a6120920690Adam Cohen                if (!DESTRUCTIVE_REORDER &&
238419f3792523fe2d55ea791a9286398a6120920690Adam Cohen                        (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
2385482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                    commitTempPlacement();
238619f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    completeAndClearReorderHintAnimations();
238719f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    setItemPlacementDirty(false);
238819f3792523fe2d55ea791a9286398a6120920690Adam Cohen                } else {
238919f3792523fe2d55ea791a9286398a6120920690Adam Cohen                    beginOrAdjustHintAnimations(finalSolution, dragView,
239019f3792523fe2d55ea791a9286398a6120920690Adam Cohen                            REORDER_ANIMATION_DURATION);
2391482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                }
2392482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            }
2393482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        } else {
2394482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            foundSolution = false;
2395482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
2396482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2397482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2398482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if ((mode == MODE_ON_DROP || !foundSolution) && !DESTRUCTIVE_REORDER) {
2399482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            setUseTempCoords(false);
2400482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2401482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2402a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        mShortcutsAndWidgets.requestLayout();
2403482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return result;
2404482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2405482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
240619f3792523fe2d55ea791a9286398a6120920690Adam Cohen    void setItemPlacementDirty(boolean dirty) {
240719f3792523fe2d55ea791a9286398a6120920690Adam Cohen        mItemPlacementDirty = dirty;
2408482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
240919f3792523fe2d55ea791a9286398a6120920690Adam Cohen    boolean isItemPlacementDirty() {
241019f3792523fe2d55ea791a9286398a6120920690Adam Cohen        return mItemPlacementDirty;
2411482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2412482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2413482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private class ItemConfiguration {
24148baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
2415482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        boolean isSolution = false;
2416482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
2417482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2418482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        int area() {
2419482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            return dragViewSpanX * dragViewSpanY;
2420482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
24218baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    }
24228baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
24238baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen    private class CellAndSpan {
24248baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        int x, y;
24258baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        int spanX, spanY;
24268baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen
24278baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen        public CellAndSpan(int x, int y, int spanX, int spanY) {
24288baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.x = x;
24298baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.y = y;
24308baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.spanX = spanX;
24318baab35b000d2dec9b33ea6a67988b6f0185b27eAdam Cohen            this.spanY = spanY;
2432482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        }
2433482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2434482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2435df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    /**
2436df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
2437df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2438df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *
2439df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2440df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2441df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanX Horizontal span of the object.
2442df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanY Vertical span of the object.
2443df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2444df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Previously returned value to possibly recycle.
2445df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2446df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *         nearest the requested location.
2447df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     */
2448df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestVacantArea(
2449df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
2450df0353815c629fc678824b07a234b89a1ff94208Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY, ignoreView, true, result);
2451df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    }
2452df0353815c629fc678824b07a234b89a1ff94208Adam Cohen
2453df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    /**
2454d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * Find a vacant area that will fit the given bounds nearest the requested
2455d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2456d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *
2457d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2458d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2459d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanX The minimum horizontal span required
2460d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param minSpanY The minimum vertical span required
2461d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanX Horizontal span of the object.
2462d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param spanY Vertical span of the object.
2463d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2464d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @param result Previously returned value to possibly recycle.
2465d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2466d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     *         nearest the requested location.
2467d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen     */
2468d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY,
2469d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen            int spanX, int spanY, View ignoreView, int[] result, int[] resultSpan) {
2470482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, ignoreView, true,
2471482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                result, resultSpan, mOccupied);
2472d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    }
2473d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen
2474d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    /**
2475df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * Find a starting cell position that will fit the given bounds nearest the requested
2476df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * cell location. Uses Euclidean distance to score multiple vacant areas.
2477df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *
2478df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelX The X location at which you want to search for a vacant area.
2479df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param pixelY The Y location at which you want to search for a vacant area.
2480df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanX Horizontal span of the object.
2481df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param spanY Vertical span of the object.
2482df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param ignoreView Considers space occupied by this view as unoccupied
2483df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @param result Previously returned value to possibly recycle.
2484df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     * @return The X, Y cell of a vacant area that can contain this object,
2485df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     *         nearest the requested location.
2486df0353815c629fc678824b07a234b89a1ff94208Adam Cohen     */
2487df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    int[] findNearestArea(
2488df0353815c629fc678824b07a234b89a1ff94208Adam Cohen            int pixelX, int pixelY, int spanX, int spanY, int[] result) {
2489df0353815c629fc678824b07a234b89a1ff94208Adam Cohen        return findNearestArea(pixelX, pixelY, spanX, spanY, null, false, result);
2490df0353815c629fc678824b07a234b89a1ff94208Adam Cohen    }
2491df0353815c629fc678824b07a234b89a1ff94208Adam Cohen
24920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean existsEmptyCell() {
24930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpan(null, 1, 1);
24940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
24950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
24960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
24970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Finds the upper-left coordinate of the first rectangle in the grid that can
24980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
24990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * then this method will only return coordinates for rectangles that contain the cell
25000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * (intersectX, intersectY)
25010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
25020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
25030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
25040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
25050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
25060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
25070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
25080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
25090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
2510482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied);
25110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
25120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
25130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
25140280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but ignores any cells occupied by the item "ignoreView"
25150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
25160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param cellXY The array that will contain the position of a vacant cell if such a cell
25170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *               can be found.
25180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
25190280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
25200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
25210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return
25220280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
25230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
2524482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1,
2525482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                ignoreView, mOccupied);
25260280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
25270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
25280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
25290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Like above, but if intersectX and intersectY are not -1, then this method will try to
25300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * return coordinates for rectangles that contain the cell [intersectX, intersectY]
25310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
25320280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanX The horizontal span of the cell we want to find.
25330280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param spanY The vertical span of the cell we want to find.
25340280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param ignoreView The home screen item we should treat as not occupying any space
25350280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The X coordinate of the cell that we should try to overlap
25360280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @param intersectX The Y coordinate of the cell that we should try to overlap
25370280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     *
25380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * @return True if a vacant cell of the specified dimension was found, false otherwise.
25390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
25400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
25410280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int intersectX, int intersectY) {
25420280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findCellForSpanThatIntersectsIgnoring(
2543482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                cellXY, spanX, spanY, intersectX, intersectY, null, mOccupied);
25440280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
25450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
25460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
25470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * The superset of the above two methods
25480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
25490280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
2550482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            int intersectX, int intersectY, View ignoreView, boolean occupied[][]) {
2551c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // mark space take by ignoreView as available (method checks if ignoreView is null)
2552482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(ignoreView, occupied);
25530280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
255428750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        boolean foundCell = false;
25550280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        while (true) {
25560280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startX = 0;
25570280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
25580280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startX = Math.max(startX, intersectX - (spanX - 1));
25590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
25600280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endX = mCountX - (spanX - 1);
25610280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX >= 0) {
25620280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
25630280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
25640280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int startY = 0;
25650280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
25660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                startY = Math.max(startY, intersectY - (spanY - 1));
25670280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
25680280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            int endY = mCountY - (spanY - 1);
25690280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectY >= 0) {
25700280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
25710280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
25720280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2573bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung            for (int y = startY; y < endY && !foundCell; y++) {
25740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                inner:
2575bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                for (int x = startX; x < endX; x++) {
25760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    for (int i = 0; i < spanX; i++) {
25770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        for (int j = 0; j < spanY; j++) {
2578482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                            if (occupied[x + i][y + j]) {
2579bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                                // small optimization: we can skip to after the column we just found
25800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                // an occupied cell
2581bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung                                x += i;
25820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                                continue inner;
25830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                            }
25840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        }
25850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
25860280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    if (cellXY != null) {
25870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[0] = x;
25880280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        cellXY[1] = y;
25890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    }
259028750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    foundCell = true;
259128750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                    break;
25920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                }
25930280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
25940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (intersectX == -1 && intersectY == -1) {
25950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                break;
25960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            } else {
25970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // if we failed to find anything, try again but without any requirements of
25980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // intersecting
25990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectX = -1;
26000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                intersectY = -1;
26010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                continue;
26020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
26030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
26040280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2605c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        // re-mark space taken by ignoreView as occupied
2606482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(ignoreView, occupied);
260728750fba6a2d141eb9a1e566718c17236030b815Michael Jurka        return foundCell;
26080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
26090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
261031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2611c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * A drag event has begun over this layout.
2612c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * It may have begun over this layout (in which case onDragChild is called first),
2613c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     * or it may have begun on another layout.
2614c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung     */
2615c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    void onDragEnter() {
2616c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen        mDragEnforcer.onDragEnter();
2617c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung        mDragging = true;
2618c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    }
2619c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung
2620c07918d0053fc7d2a19d7b013565a5d2e7d4af51Winson Chung    /**
26210280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Called when drag has left this CellLayout or has been completed (successfully or not)
26226569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
26230280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    void onDragExit() {
2624c6cc61d45836e4081920883cc4d448ccb0bb8417Adam Cohen        mDragEnforcer.onDragExit();
26254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // This can actually be called when we aren't in a drag, e.g. when adding a new
26264be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // item to this layout via the customize drawer.
26274be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Guard against that case.
26284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mDragging) {
26294be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mDragging = false;
26304be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
263108ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy
263208ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        // Invalidate the drag data
2633d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen        mDragCell[0] = mDragCell[1] = -1;
263408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragOutlineAnims[mDragOutlineCurrent].animateOut();
263508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragOutlineCurrent = (mDragOutlineCurrent + 1) % mDragOutlineAnims.length;
263619f3792523fe2d55ea791a9286398a6120920690Adam Cohen        revertTempState();
263733945b21544bc98381df17726a3537c292d8c985Michael Jurka        setIsDragOverlapping(false);
26386569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    }
26396569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
26406569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
2641aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Mark a child as having been dropped.
2642de7658b5e02ae10010e44fcf8d9c5814f54d9eb0Patrick Dubroy     * At the beginning of the drag operation, the child may have been on another
2643ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy     * screen, but it is re-parented before this method is called.
264431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
264531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param child The child that is being dropped
264631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
2647716b51e030f9c6ed34af2b947760e46a280c65a6Adam Cohen    void onDropChild(View child) {
2648d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        if (child != null) {
2649d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            LayoutParams lp = (LayoutParams) child.getLayoutParams();
265084f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy            lp.dropped = true;
2651d94533d04a5f8f5485f106d10af60169857ea899Romain Guy            child.requestLayout();
2652d94533d04a5f8f5485f106d10af60169857ea899Romain Guy        }
265331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
265431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
265531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
265631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Computes a bounding rectangle for a range of cells
2657aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
265831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellX X coordinate of upper left corner expressed as a cell position
265931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellY Y coordinate of upper left corner expressed as a cell position
2660aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * @param cellHSpan Width in cells
266131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param cellVSpan Height in cells
26626569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * @param resultRect Rect into which to put the results
266331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
2664d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, Rect resultRect) {
266531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellWidth = mCellWidth;
266631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int cellHeight = mCellHeight;
266731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int widthGap = mWidthGap;
266831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int heightGap = mHeightGap;
2669aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
26704b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int hStartPadding = getPaddingLeft();
26714b825dcd5f64a5ebb60271844fbc5257374422bcWinson Chung        final int vStartPadding = getPaddingTop();
2672aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
267331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
267431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
267531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
267631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x = hStartPadding + cellX * (cellWidth + widthGap);
267731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = vStartPadding + cellY * (cellHeight + heightGap);
2678aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
26796569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        resultRect.set(x, y, x + width, y + height);
268031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
2681aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
268231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2683aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     * Computes the required horizontal and vertical cell spans to always
268431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * fit the given rectangle.
2685aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
268631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param width Width in pixels
268731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param height Height in pixels
26888f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy     * @param result An array of length 2 in which to store the result (may be null).
268931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
26908f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy    public int[] rectToCell(int width, int height, int[] result) {
26919987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka        return rectToCell(getResources(), width, height, result);
26929987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    }
26939987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka
26949987a5c45e7d01a780d73b269bdce8d8a5309219Michael Jurka    public static int[] rectToCell(Resources resources, int width, int height, int[] result) {
269531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always assume we're working with the smallest span to make sure we
269631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // reserve enough space in both orientations.
269779e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
269879e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato        int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
269931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int smallerSize = Math.min(actualWidth, actualHeight);
270079e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
270131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Always round up to next largest cell
270254c725cc294cd43ed0650179bfae64a622547660Winson Chung        int spanX = (int) Math.ceil(width / (float) smallerSize);
270354c725cc294cd43ed0650179bfae64a622547660Winson Chung        int spanY = (int) Math.ceil(height / (float) smallerSize);
270479e56263dbcbe85dc434df372bc6e6730aa13477Joe Onorato
27058f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        if (result == null) {
27068f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            return new int[] { spanX, spanY };
27078f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        }
27088f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[0] = spanX;
27098f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        result[1] = spanY;
27108f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        return result;
271131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
271231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
2713f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka    public int[] cellSpansToSize(int hSpans, int vSpans) {
2714f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        int[] size = new int[2];
2715f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        size[0] = hSpans * mCellWidth + (hSpans - 1) * mWidthGap;
2716f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        size[1] = vSpans * mCellHeight + (vSpans - 1) * mHeightGap;
2717f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka        return size;
2718f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka    }
2719f12c75cb48f87955600c56ccbe0aac84b0c11b28Michael Jurka
272031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
2721047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy     * Calculate the grid spans needed to fit given item
2722047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy     */
2723047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    public void calculateSpans(ItemInfo info) {
2724047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        final int minWidth;
2725047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        final int minHeight;
2726047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy
2727047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        if (info instanceof LauncherAppWidgetInfo) {
2728047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minWidth = ((LauncherAppWidgetInfo) info).minWidth;
2729047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minHeight = ((LauncherAppWidgetInfo) info).minHeight;
2730047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        } else if (info instanceof PendingAddWidgetInfo) {
2731047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minWidth = ((PendingAddWidgetInfo) info).minWidth;
2732047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            minHeight = ((PendingAddWidgetInfo) info).minHeight;
2733047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        } else {
2734047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            // It's not a widget, so it must be 1x1
2735047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            info.spanX = info.spanY = 1;
2736047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy            return;
2737047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        }
2738047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        int[] spans = rectToCell(minWidth, minHeight, null);
2739047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        info.spanX = spans[0];
2740047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy        info.spanY = spans[1];
2741047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    }
2742047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy
2743047379aa61b4719ab38ce595f23732e8f3b1b8e1Patrick Dubroy    /**
274431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Find the first vacant cell, if there is one.
274531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     *
274631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param vacant Holds the x and y coordinate of the vacant cell
274731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanX Horizontal cell span.
274831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @param spanY Vertical cell span.
2749aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     *
275031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * @return True if a vacant cell was found
275131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
275231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
275331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27540280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
275531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
275631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
275731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static boolean findVacantCell(int[] vacant, int spanX, int spanY,
275831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int xCount, int yCount, boolean[][] occupied) {
275931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27602801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen        for (int y = 0; y < yCount; y++) {
27612801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen            for (int x = 0; x < xCount; x++) {
276231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean available = !occupied[x][y];
276331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectout:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
276431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
276531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        available = available && !occupied[i][j];
276631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        if (!available) break out;
276731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    }
276831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
276931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
277031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (available) {
277131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[0] = x;
277231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    vacant[1] = y;
277331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return true;
277431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
277531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
277631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
277731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
277831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
277931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
278031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private void clearOccupiedCells() {
27820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = 0; x < mCountX; x++) {
27830280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = 0; y < mCountY; y++) {
27840280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                mOccupied[x][y] = false;
278531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
278631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
27870280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
278831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
2789d41fbf5680750e34335bba6b38298186c144a4b7Adam Cohen    public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) {
27900280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        markCellsAsUnoccupiedForView(view);
2791482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(newCellX, newCellY, newSpanX, newSpanY, mOccupied, true);
27920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
279331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
2794d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    public void markCellsAsOccupiedForView(View view) {
2795482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsOccupiedForView(view, mOccupied);
2796482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2797482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void markCellsAsOccupiedForView(View view, boolean[][] occupied) {
2798a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
27990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
2800482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, true);
28010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2803d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen    public void markCellsAsUnoccupiedForView(View view) {
2804482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsAsUnoccupiedForView(view, mOccupied);
2805482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    }
2806482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    public void markCellsAsUnoccupiedForView(View view, boolean occupied[][]) {
2807a52570f8f9ad65b85e33a2f2e87722f9edd6c6f4Michael Jurka        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
28080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LayoutParams lp = (LayoutParams) view.getLayoutParams();
2809482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, false);
28100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
28110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
2812482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied,
2813482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen            boolean value) {
2814482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        if (cellX < 0 || cellY < 0) return;
28150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
28160280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
2817482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                occupied[x][y] = value;
281831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
281931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
282031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
282131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
28222801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public int getDesiredWidth() {
28238b805b17158886035b38261eb611d8641701ae43Michael Jurka        return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
28242801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen                (Math.max((mCountX - 1), 0) * mWidthGap);
28252801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
28262801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
28272801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    public int getDesiredHeight()  {
28288b805b17158886035b38261eb611d8641701ae43Michael Jurka        return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
28292801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen                (Math.max((mCountY - 1), 0) * mHeightGap);
28302801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen    }
28312801cafe62653131fdc9da402e5c44e5ffd0bf47Adam Cohen
283266d72178af91d455700875635473be942bc90e54Michael Jurka    public boolean isOccupied(int x, int y) {
283366d72178af91d455700875635473be942bc90e54Michael Jurka        if (x < mCountX && y < mCountY) {
283466d72178af91d455700875635473be942bc90e54Michael Jurka            return mOccupied[x][y];
283566d72178af91d455700875635473be942bc90e54Michael Jurka        } else {
283666d72178af91d455700875635473be942bc90e54Michael Jurka            throw new RuntimeException("Position exceeds the bound of this CellLayout");
283766d72178af91d455700875635473be942bc90e54Michael Jurka        }
283866d72178af91d455700875635473be942bc90e54Michael Jurka    }
283966d72178af91d455700875635473be942bc90e54Michael Jurka
284031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
284131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
284231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(getContext(), attrs);
284331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
284431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
284531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
284631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
284731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return p instanceof CellLayout.LayoutParams;
284831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
284931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
285031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
285131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
285231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return new CellLayout.LayoutParams(p);
285331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
285431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
2855aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public static class CellLayoutAnimationController extends LayoutAnimationController {
2856aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public CellLayoutAnimationController(Animation animation, float delay) {
2857aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(animation, delay);
2858aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
2859aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
2860aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        @Override
2861aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        protected long getDelayForView(View view) {
2862aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return (int) (Math.random() * 150);
2863aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
2864aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
2865aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
286631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
286731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
286831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Horizontal location of the item in the grid.
286931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
287031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
287131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellX;
287231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
287331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
287431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Vertical location of the item in the grid.
287531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
287631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
287731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellY;
287831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
287931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
2880482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Temporary horizontal location of the item in the grid during reorder
2881482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
2882482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public int tmpCellX;
2883482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2884482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
2885482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Temporary vertical location of the item in the grid during reorder
2886482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
2887482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public int tmpCellY;
2888482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2889482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
2890482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Indicates that the temporary coordinates should be used to layout the items
2891482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
2892482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public boolean useTmpCoords;
2893482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
2894482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
289531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned horizontally by the item.
289631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
289731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
289831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellHSpan;
289931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
290031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        /**
290131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         * Number of cells spanned vertically by the item.
290231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project         */
290331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
290431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public int cellVSpan;
2905aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
29061b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen        /**
29071b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         * Indicates whether the item will set its x, y, width and height parameters freely,
29081b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
29091b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen         */
2910d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        public boolean isLockedToGrid = true;
2911d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
2912482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        /**
2913482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * Indicates whether this item can be reordered. Always true except in the case of the
2914482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         * the AllApps button.
2915482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen         */
2916482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen        public boolean canReorder = true;
2917482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen
291831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // X coordinate of the view in the layout.
291931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
292031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int x;
292131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Y coordinate of the view in the layout.
292231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @ViewDebug.ExportedProperty
292331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y;
292431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
292584f296c106cb1c7b6d3ae6c6d5508a17f1324e29Romain Guy        boolean dropped;
2926fcb9e7144e58614f5ae0e9b272fb7ce040848c67Romain Guy
292731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(Context c, AttributeSet attrs) {
292831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(c, attrs);
292931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
293031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
293131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
293231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
293331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(ViewGroup.LayoutParams source) {
293431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            super(source);
293531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellHSpan = 1;
293631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellVSpan = 1;
293731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
2938aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
2939aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public LayoutParams(LayoutParams source) {
2940aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            super(source);
2941aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellX = source.cellX;
2942aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellY = source.cellY;
2943aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellHSpan = source.cellHSpan;
2944aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            this.cellVSpan = source.cellVSpan;
2945aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
2946aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
294731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
29488f19cdd62f6e2be05e3890916eabd11317ae1bc2Romain Guy            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
294931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellX = cellX;
295031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellY = cellY;
295131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellHSpan = cellHSpan;
295231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            this.cellVSpan = cellVSpan;
295331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
295431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
29557f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap) {
2956d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            if (isLockedToGrid) {
2957d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                final int myCellHSpan = cellHSpan;
2958d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                final int myCellVSpan = cellVSpan;
2959482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                final int myCellX = useTmpCoords ? tmpCellX : cellX;
2960482ed823afb4c7452e037ce8add7ea425fc83da2Adam Cohen                final int myCellY = useTmpCoords ? tmpCellY : cellY;
29611b607ed454ed22c2fd855cb3e428376520fb2388Adam Cohen
2962d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
2963d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                        leftMargin - rightMargin;
2964d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
2965d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen                        topMargin - bottomMargin;
2966eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                x = (int) (myCellX * (cellWidth + widthGap) + leftMargin);
2967eecf02da58adef5486bf0c9ff7127ea891f457a4Winson Chung                y = (int) (myCellY * (cellHeight + heightGap) + topMargin);
2968d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen            }
2969d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen        }
2970d4844c3e731b00547a31f23a00f8bd4a271e2b62Adam Cohen
2971aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        public String toString() {
2972aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "(" + this.cellX + ", " + this.cellY + ")";
2973aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
29747f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29757f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setWidth(int width) {
29767f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.width = width;
29777f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
29787f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29797f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getWidth() {
29807f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return width;
29817f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
29827f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29837f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setHeight(int height) {
29847f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.height = height;
29857f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
29867f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29877f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getHeight() {
29887f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return height;
29897f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
29907f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29917f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setX(int x) {
29927f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.x = x;
29937f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
29947f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29957f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getX() {
29967f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return x;
29977f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
29987f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
29997f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public void setY(int y) {
30007f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            this.y = y;
30017f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
30027f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen
30037f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        public int getY() {
30047f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen            return y;
30057f4eabe3709a72b416569136e4a095431c493c8bAdam Cohen        }
300631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
300731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // This class stores info for two purposes:
30090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
30100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    its spanX, spanY, and the screen it is on
30110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    // 2. When long clicking on an empty cell in a CellLayout, we save information about the
30120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
30130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    //    the CellLayout that was long clicked
3014e5fb0f27bca7afb996258a7163c76ca7390d7bffMichael Jurka    static final class CellInfo {
301531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        View cell;
3016a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellX = -1;
3017a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        int cellY = -1;
301831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanX;
301931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int spanY;
302031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int screen;
30213d503fbd9468fb2b9fa645f4f7b91e11229edbfaWinson Chung        long container;
302231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
302331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
302431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public String toString() {
3025aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            return "Cell[view=" + (cell == null ? "null" : cell.getClass())
3026aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    + ", x=" + cellX + ", y=" + cellY + "]";
302731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
302831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
3029d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka
3030d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    public boolean lastDownOnOccupiedCell() {
3031d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka        return mLastDownOnOccupiedCell;
3032d771c96e5d156ffde5d35ee13ce053de60dc3163Michael Jurka    }
303331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project}
3034