Workspace.java revision 265b9a66542148e0ff13971001cb9461065e1e0e
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
1968846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport android.animation.Animator;
2068846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport android.animation.Animator.AnimatorListener;
2168846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport android.animation.AnimatorListenerAdapter;
22495f2892288504f4ab87e62957b3f71144dd73c2Adam Powellimport android.animation.AnimatorSet;
23580e27748137ff08599aa719d106b31215a28353Winson Chungimport android.animation.ObjectAnimator;
240280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurkaimport android.animation.TimeInterpolator;
2568846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport android.animation.ValueAnimator;
26b1254a6bdb11024042fb6c27e179210c1bbd6e1cChet Haaseimport android.animation.ValueAnimator.AnimatorUpdateListener;
270280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurkaimport android.app.AlertDialog;
280280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurkaimport android.app.WallpaperManager;
29cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroyimport android.appwidget.AppWidgetHostView;
30cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroyimport android.appwidget.AppWidgetManager;
31cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroyimport android.appwidget.AppWidgetProviderInfo;
3268846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport android.content.ClipData;
338f573952b8729b4319043ae0908997ecd2d68951Dianne Hackbornimport android.content.ClipDescription;
34629de3ef739883c0962423cc0c3a26299f162d3dRomain Guyimport android.content.ComponentName;
35629de3ef739883c0962423cc0c3a26299f162d3dRomain Guyimport android.content.Context;
36a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chungimport android.content.Intent;
37a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chungimport android.content.res.Resources;
38495f2892288504f4ab87e62957b3f71144dd73c2Adam Powellimport android.content.res.TypedArray;
3931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Bitmap;
4031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Camera;
41495f2892288504f4ab87e62957b3f71144dd73c2Adam Powellimport android.graphics.Canvas;
425c16f3ecd6b47bff3abbe40deb3d39c66a3b0012Romain Guyimport android.graphics.Matrix;
437247f6315baf16eacb3286f21bd80321385c1defPatrick Dubroyimport android.graphics.Paint;
4431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.Point;
454be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.PorterDuff;
466b879f0a5885274a85333531e091283405d490ccAdam Lesinskiimport android.graphics.Rect;
4731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.graphics.RectF;
48a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurkaimport android.graphics.Region.Op;
49a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chungimport android.graphics.drawable.Drawable;
5031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.os.IBinder;
51a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chungimport android.os.Parcelable;
524be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.util.AttributeSet;
53f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Projectimport android.util.DisplayMetrics;
545c16f3ecd6b47bff3abbe40deb3d39c66a3b0012Romain Guyimport android.util.Log;
55956091ba7863bd72086275e61084864994dd6fa7Joe Onoratoimport android.util.Pair;
56495f2892288504f4ab87e62957b3f71144dd73c2Adam Powellimport android.view.Display;
5731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.DragEvent;
58291ad12232c98e383d44d76ffe09e97e204c61bcDaniel Sandlerimport android.view.MotionEvent;
59580e27748137ff08599aa719d106b31215a28353Winson Chungimport android.view.View;
60a34abf8c781485b788fddacb352d586bffca886cWinson Chungimport android.view.ViewConfiguration;
61a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chungimport android.view.ViewGroup;
6231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.view.animation.DecelerateInterpolator;
6331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.widget.ImageView;
64cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroyimport android.widget.TextView;
65f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Projectimport android.widget.Toast;
66a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
6731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport com.android.launcher.R;
6868846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport com.android.launcher2.FolderIcon.FolderRingAnimator;
6968846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chungimport com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
70edcce099c98a6c40d10109ac092ab50f9d2668f3Romain Guy
7131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport java.util.ArrayList;
720142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkaimport java.util.HashSet;
730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkaimport java.util.List;
74aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
7531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project/**
760142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka * The workspace is a wide area with a wallpaper and a finite number of pages.
77dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka * Each page contains a number of icons, folders or widgets the user can
78e47f55c30b9c24f01f2be861247c92f17fbe4a61Romain Guy * interact with. A workspace is meant to be used with a fixed width only.
793a8820bdbad90642cf5cda4b00a8c92ecb699159Joe Onorato */
800142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurkapublic class Workspace extends SmoothPagedView
81dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
82dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        DragController.DragListener {
83dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    @SuppressWarnings({"UnusedDeclaration"})
84a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    private static final String TAG = "Launcher.Workspace";
85f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
86f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    // Y rotation to apply to the workspace screens
876b879f0a5885274a85333531e091283405d490ccAdam Lesinski    private static final float WORKSPACE_ROTATION = 12.5f;
88a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
89a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    private static float CAMERA_DISTANCE = 6500;
90a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen
911b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
92a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
931b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
94a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen
959171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private static final int BACKGROUND_FADE_OUT_DURATION = 350;
969171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
979171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private static final int FLING_THRESHOLD_VELOCITY = 500;
98f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
999171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    // These animators are used to fade the children's outlines
1009171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private ObjectAnimator mChildrenOutlineFadeInAnimation;
1019171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private ObjectAnimator mChildrenOutlineFadeOutAnimation;
1029171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private float mChildrenOutlineAlpha = 0;
1039171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
1049171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    // These properties refer to the background protection gradient used for AllApps and Customize
1059171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private ValueAnimator mBackgroundFadeInAnimation;
1069171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private ValueAnimator mBackgroundFadeOutAnimation;
1079171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private Drawable mBackground;
108472b281d5cb4f5660df981a6c912266b9f5703feChet Haase    boolean mDrawBackground = true;
109472b281d5cb4f5660df981a6c912266b9f5703feChet Haase    private float mBackgroundAlpha = 0;
1109171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    private float mOverScrollMaxBackgroundAlpha = 0.0f;
111f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    private int mOverScrollPageIndex = -1;
112f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
1138f573952b8729b4319043ae0908997ecd2d68951Dianne Hackborn    private float mWallpaperScrollRatio = 1.0f;
114aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
1150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private final WallpaperManager mWallpaperManager;
11631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private IBinder mWindowToken;
11721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen    private static final float WALLPAPER_SCREENS_SPAN = 2f;
11821f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen
11931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mDefaultPage;
12031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
12131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
12231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * CellInfo for the cell that is currently being dragged
123aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung     */
12470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    private CellLayout.CellInfo mDragInfo;
12570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
12670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    /**
12770864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey     * Target drop area calculated during last acceptDrop call.
12831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
1296569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    private int[] mTargetCell = new int[2];
1306569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
1316569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    /**
1326569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     * The CellLayout that is currently being dragged over
1336569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy     */
13431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private CellLayout mDragTargetLayout = null;
1350589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato
13600acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato    private Launcher mLauncher;
137aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private IconCache mIconCache;
1384516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    private DragController mDragController;
1394516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
14031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    // These are temporary variables to prevent having to allocate a new object just to
14170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
1420280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private int[] mTempCell = new int[2];
143a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    private int[] mTempEstimate = new int[2];
1444516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    private float[] mDragViewVisualCenter = new float[2];
1454516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    private float[] mTempDragCoordinates = new float[2];
146a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    private float[] mTempCellLayoutCenterCoordinates = new float[2];
1470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    private float[] mTempDragBottomRightCoordinates = new float[2];
14831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private Matrix mTempInverseMatrix = new Matrix();
149d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen
150d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private SpringLoadedDragController mSpringLoadedDragController;
15131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float mSpringLoadedShrinkFactor;
1528a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy
1538a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy    private static final int DEFAULT_CELL_COUNT_X = 4;
154aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private static final int DEFAULT_CELL_COUNT_Y = 4;
1551262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
156dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    // State variable that indicates whether the pages are small (ie when you're
157c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka    // in all apps or customize mode)
158c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka
159b1254a6bdb11024042fb6c27e179210c1bbd6e1cChet Haase    enum State { NORMAL, SPRING_LOADED, SMALL };
1603e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private State mState = State.NORMAL;
1613e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private boolean mIsSwitchingState = false;
1623e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private boolean mSwitchStateAfterFirstLayout = false;
163883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka    private State mStateAfterFirstLayout;
164883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka
1653c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka    private AnimatorSet mAnimator;
166dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    private AnimatorListener mChangeStateAnimationListener;
16754fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy
1681262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    boolean mAnimatingViewIntoPlace = false;
1691262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    boolean mIsDragOccuring = false;
17054fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    boolean mChildrenLayersEnabled = true;
17154fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy
17254fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    /** Is the user is dragging an item near the edge of a page? */
17354fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    private boolean mInScrollArea = false;
17454fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy
1758e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy    private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
1764be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private Bitmap mDragOutline = null;
1778e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy    private final Rect mTempRect = new Rect();
1788e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy    private final int[] mTempXY = new int[2];
1794be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private int mDragViewMultiplyColor;
180cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    private float mOverscrollFade = 0;
181cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
182cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    // Paint used to draw external drop outline
183cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    private final Paint mExternalDragOutlinePaint = new Paint();
184cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
185a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    // Camera and Matrix used to determine the final position of a neighboring CellLayout
186a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    private final Matrix mMatrix = new Matrix();
187a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    private final Camera mCamera = new Camera();
1888ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    private final float mTempFloat2[] = new float[2];
1898ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy
1908ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM };
1916b879f0a5885274a85333531e091283405d490ccAdam Lesinski    int mWallpaperWidth;
1926b879f0a5885274a85333531e091283405d490ccAdam Lesinski    int mWallpaperHeight;
1936b879f0a5885274a85333531e091283405d490ccAdam Lesinski    WallpaperOffsetInterpolator mWallpaperOffset;
1946b879f0a5885274a85333531e091283405d490ccAdam Lesinski    boolean mUpdateWallpaperOffsetImmediately = false;
1956b879f0a5885274a85333531e091283405d490ccAdam Lesinski    private Runnable mDelayedResizeRunnable;
19631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mDisplayWidth;
19731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mDisplayHeight;
19831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int mWallpaperTravelWidth;
19931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
2000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
20131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private static final int FOLDER_CREATION_TIMEOUT = 250;
20231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private final Alarm mFolderCreationAlarm = new Alarm();
20331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private FolderRingAnimator mDragFolderRingAnimator = null;
20431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private View mLastDragOverView = null;
20531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private boolean mCreateUserFolderOnDrop = false;
20631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
20731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
20831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float mXDown;
20931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float mYDown;
2100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
21131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
21231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
21331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
21431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    // These variables are used for storing the initial and final values during workspace animations
2150142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private int mSavedScrollX;
2165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mSavedRotationY;
2175f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mSavedTranslationX;
2185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mCurrentScaleX;
2195f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private float mCurrentScaleY;
22031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float mCurrentRotationY;
2218f573952b8729b4319043ae0908997ecd2d68951Dianne Hackborn    private float mCurrentTranslationX;
222aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private float mCurrentTranslationY;
223aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private float[] mOldTranslationXs;
224aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    private float[] mOldTranslationYs;
225d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private float[] mOldScaleXs;
226d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private float[] mOldScaleYs;
2270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private float[] mOldBackgroundAlphas;
22831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mOldBackgroundAlphaMultipliers;
22931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mOldAlphas;
230d22015cd37ea6ef53762eca5be57daca123ff607Adam Cohen    private float[] mOldRotationYs;
2310d44e9482b95d8f163b28bf20131c4349185b589Joe Onorato    private float[] mNewTranslationXs;
2320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private float[] mNewTranslationYs;
23331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mNewScaleXs;
23431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mNewScaleYs;
23531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mNewBackgroundAlphas;
23631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mNewBackgroundAlphaMultipliers;
23731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mNewAlphas;
23831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float[] mNewRotationYs;
2390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private float mTransitionProgress;
2400589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato
2410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
2420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * Used to inflate the Workspace from XML.
2430589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato     *
2440589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato     * @param context The application's context.
245a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung     * @param attrs The attributes set containing the Workspace's customization values.
246cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     */
24731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public Workspace(Context context, AttributeSet attrs) {
2489171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        this(context, attrs, 0);
2499171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    }
2509171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
2513c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka    /**
2523c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka     * Used to inflate the Workspace from XML.
253c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka     *
254c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka     * @param context The application's context.
255c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka     * @param attrs The attributes set containing the Workspace's customization values.
2563c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka     * @param defStyle Unused.
2573c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka     */
258c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka    public Workspace(Context context, AttributeSet attrs, int defStyle) {
259dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        super(context, attrs, defStyle);
260dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mContentIsRefreshable = false;
2610142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
26231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // With workspace, data is available straight from the get-go
26331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setDataIsReady();
26431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
265f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        mFadeInAdjacentScreens =
266f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            getResources().getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
267f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        mWallpaperManager = WallpaperManager.getInstance(context);
268f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
269f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int cellCountX = DEFAULT_CELL_COUNT_X;
270f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        int cellCountY = DEFAULT_CELL_COUNT_Y;
271f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
272f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
273f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                R.styleable.Workspace, defStyle, 0);
27431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final Resources res = context.getResources();
27631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (LauncherApplication.isScreenLarge()) {
27731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Determine number of rows/columns dynamically
27821f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
27931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // Around that ratio we should make cells the same size in portrait and
28031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // landscape
28131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            TypedArray actionBarSizeTypedArray =
28231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
28331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
28431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final float systemBarHeight = res.getDimension(R.dimen.status_bar_height);
28531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp;
28631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
28721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            cellCountX = 1;
28831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            while (CellLayout.widthInPortrait(res, cellCountX + 1) <= smallestScreenDim) {
28931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellCountX++;
29031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
29131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
29231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            cellCountY = 1;
29331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
29431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                <= smallestScreenDim - systemBarHeight) {
29531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cellCountY++;
29621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            }
29731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
29831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
29931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mSpringLoadedShrinkFactor =
30031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
30131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDragViewMultiplyColor = res.getColor(R.color.drag_view_multiply_color);
30231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // if the value is manually specified, use that instead
30431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
30521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
30631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
30731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        a.recycle();
30831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
30931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
31031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setHapticFeedbackEnabled(false);
31131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
31231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLauncher = (Launcher) context;
31331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        initWorkspace();
31421f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen
31531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Disable multitouch across the workspace/all apps/customize tray
31631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setMotionEventSplittingEnabled(true);
31731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
31831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
31931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
32031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    // dimension if unsuccessful
32131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public int[] estimateItemSize(int hSpan, int vSpan,
3220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            PendingAddItemInfo pendingItemInfo, boolean springLoaded) {
3230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        int[] size = new int[2];
32431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (getChildCount() > 0) {
3250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            CellLayout cl = (CellLayout) mLauncher.getWorkspace().getChildAt(0);
326aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            RectF r = estimateItemPosition(cl, pendingItemInfo, 0, 0, hSpan, vSpan);
327aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            size[0] = (int) r.width();
328aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            size[1] = (int) r.height();
329aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            if (springLoaded) {
33031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                size[0] *= mSpringLoadedShrinkFactor;
33131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                size[1] *= mSpringLoadedShrinkFactor;
33231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
33331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return size;
33431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else {
33531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            size[0] = Integer.MAX_VALUE;
336dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            size[1] = Integer.MAX_VALUE;
337dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            return size;
33831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
339dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
3400142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public RectF estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
3410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            int hCell, int vCell, int hSpan, int vSpan) {
34231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        RectF r = new RectF();
3430142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
344aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        if (pendingInfo instanceof PendingAddWidgetInfo) {
345aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            PendingAddWidgetInfo widgetInfo = (PendingAddWidgetInfo) pendingInfo;
346aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            Rect p = AppWidgetHostView.getDefaultPaddingForWidget(mContext,
347aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    widgetInfo.componentName, null);
34831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            r.top += p.top;
34931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            r.left += p.left;
35031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            r.right -= p.right;
35131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            r.bottom -= p.bottom;
35231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
35331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return r;
35431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
3550142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3560142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void buildPageHardwareLayers() {
35731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (getWindowToken() != null) {
35831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int childCount = getChildCount();
35931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int i = 0; i < childCount; i++) {
36031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                CellLayout cl = (CellLayout) getChildAt(i);
36131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                cl.buildChildrenLayer();
3620142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
36331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
3640142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
3650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
3660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void onDragStart(DragSource source, Object info, int dragAction) {
3670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        mIsDragOccuring = true;
36831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        updateChildrenLayersEnabled();
36931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLauncher.lockScreenOrientationOnLargeUI();
37031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
37131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
37231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public void onDragEnd() {
37331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mIsDragOccuring = false;
37431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        updateChildrenLayersEnabled();
37531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLauncher.unlockScreenOrientationOnLargeUI();
37631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
37731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
37831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    /**
37931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     * Initializes various states for this workspace.
38031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project     */
38131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void initWorkspace() {
38231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        Context context = getContext();
38331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mCurrentPage = mDefaultPage;
38431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        Launcher.setScreen(mCurrentPage);
385aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
386aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        mIconCache = app.getIconCache();
387aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        mExternalDragOutlinePaint.setAntiAlias(true);
388aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        setWillNotDraw(false);
38931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        setChildrenDrawnWithCacheEnabled(true);
39031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        try {
39231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final Resources res = getResources();
39331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            mBackground = res.getDrawable(R.drawable.apps_customize_bg);
39431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } catch (Resources.NotFoundException e) {
39531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // In this case, we will skip drawing background protection
39631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
39731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
39831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mChangeStateAnimationListener = new AnimatorListenerAdapter() {
39931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            @Override
40031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            public void onAnimationStart(Animator animation) {
40131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mIsSwitchingState = true;
40231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
403291ad12232c98e383d44d76ffe09e97e204c61bcDaniel Sandler
404291ad12232c98e383d44d76ffe09e97e204c61bcDaniel Sandler            @Override
405291ad12232c98e383d44d76ffe09e97e204c61bcDaniel Sandler            public void onAnimationEnd(Animator animation) {
40631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mIsSwitchingState = false;
40731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
40831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mAnimator = null;
40931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                updateChildrenLayersEnabled();
41031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
41131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        };
41231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
41331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mWallpaperOffset = new WallpaperOffsetInterpolator();
41431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        Display display = mLauncher.getWindowManager().getDefaultDisplay();
41531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDisplayWidth = display.getWidth();
41631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mDisplayHeight = display.getHeight();
41731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mWallpaperTravelWidth = (int) (mDisplayWidth *
418aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                wallpaperTravelToScreenWidthRatio(mDisplayWidth, mDisplayHeight));
4196569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
420845ba3b17b83a2b11d79c6fb076cf96ab4a737dfMichael Jurka        mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
421f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka
422f3ca3ab6958b104cbf2c2fa04add97d372a94d1cMichael Jurka    }
423aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
424aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    @Override
4256569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy    protected int getScrollMode() {
426aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return SmoothPagedView.X_LARGE_MODE;
427aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
428aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
42931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
4300d44e9482b95d8f163b28bf20131c4349185b589Joe Onorato    protected void onViewAdded(View child) {
43131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onViewAdded(child);
43231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (!(child instanceof CellLayout)) {
43300acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
434aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
43500acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato        CellLayout cl = ((CellLayout) child);
43631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cl.setOnInterceptTouchListener(this);
43731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        cl.setClickable(true);
438dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        cl.enableHardwareLayers();
439dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
440c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka
4416b879f0a5885274a85333531e091283405d490ccAdam Lesinski    /**
4426b879f0a5885274a85333531e091283405d490ccAdam Lesinski     * @return The open folder on the current screen, or null if there is none
4436b879f0a5885274a85333531e091283405d490ccAdam Lesinski     */
4446b879f0a5885274a85333531e091283405d490ccAdam Lesinski    Folder getOpenFolder() {
44596226223d9849842bb2a67af051acbae9e0677d5Michael Jurka        DragLayer dragLayer = mLauncher.getDragLayer();
44621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        int count = dragLayer.getChildCount();
44721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        for (int i = 0; i < count; i++) {
44821f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            View child = dragLayer.getChildAt(i);
44921f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            if (child instanceof Folder) {
45021f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                Folder folder = (Folder) child;
45121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                if (folder.getInfo().opened)
45221f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                    return folder;
45321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            }
454dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
455dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        return null;
4560e26059548e429e5d1c973bebe4c561bead2926fMichael Jurka    }
4570e26059548e429e5d1c973bebe4c561bead2926fMichael Jurka
458ded9f8d8658d0b6601006c0a954cd3bf530e55c1Adam Cohen    boolean isTouchActive() {
459ded9f8d8658d0b6601006c0a954cd3bf530e55c1Adam Cohen        return mTouchState != TOUCH_STATE_REST;
460ded9f8d8658d0b6601006c0a954cd3bf530e55c1Adam Cohen    }
461ded9f8d8658d0b6601006c0a954cd3bf530e55c1Adam Cohen
4625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    /**
4635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * Adds the specified child in the specified screen. The position and dimension of
464c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka     * the child are defined by x, y, spanX and spanY.
4655f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     *
4665f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param child The child to add in one of the workspace's screens.
4675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param screen The screen in which to add the child.
4685f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param x The X position of the child in the screen's grid.
4695f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param y The Y position of the child in the screen's grid.
4705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param spanX The number of cells spanned horizontally by the child.
4715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param spanY The number of cells spanned vertically by the child.
4725f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     */
473c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka    void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY) {
4746b879f0a5885274a85333531e091283405d490ccAdam Lesinski        addInScreen(child, container, screen, x, y, spanX, spanY, false);
4756b879f0a5885274a85333531e091283405d490ccAdam Lesinski    }
4766b879f0a5885274a85333531e091283405d490ccAdam Lesinski
4776b879f0a5885274a85333531e091283405d490ccAdam Lesinski    /**
4786b879f0a5885274a85333531e091283405d490ccAdam Lesinski     * Adds the specified child in the specified screen. The position and dimension of
4796b879f0a5885274a85333531e091283405d490ccAdam Lesinski     * the child are defined by x, y, spanX and spanY.
4805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     *
4815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param child The child to add in one of the workspace's screens.
4825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param screen The screen in which to add the child.
4835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param x The X position of the child in the screen's grid.
4845f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * @param y The Y position of the child in the screen's grid.
4851adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka     * @param spanX The number of cells spanned horizontally by the child.
4861adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka     * @param spanY The number of cells spanned vertically by the child.
4871adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka     * @param insert When true, the child is inserted at the beginning of the children list.
4881adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka     */
4891adf5391a3a3d215b226adf3702019c22a99e3b1Michael Jurka    void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
4901262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            boolean insert) {
4910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
4920142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (screen < 0 || screen >= getChildCount()) {
49316fed41e5e680c547b23e108788eb85f1b04d36dMichael Jurka                Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
4940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    + " (was " + screen + "); skipping child");
4950142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                return;
4960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
49716fed41e5e680c547b23e108788eb85f1b04d36dMichael Jurka        }
49831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
499f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        final CellLayout layout;
50021f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
50131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            layout = mLauncher.getHotseat().getLayout();
50231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            child.setOnKeyListener(null);
5031262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
50416fed41e5e680c547b23e108788eb85f1b04d36dMichael Jurka            // Hide folder title in the hotseat
5051262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            if (child instanceof FolderIcon) {
5061262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy                ((FolderIcon) child).setTextVisible(false);
5071262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            }
5081262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
5098ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy            if (screen < 0) {
5108ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy                screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
5118ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy            } else {
5128ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy                // Note: We do this to ensure that the hotseat is always laid out in the orientation
5138ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy                // of the hotseat in order regardless of which orientation they were added
5148ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy                x = mLauncher.getHotseat().getCellXFromOrder(screen);
51521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen                y = mLauncher.getHotseat().getCellYFromOrder(screen);
5160142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
5170142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        } else {
5180142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // Show folder title if not in the hotseat
5190142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            if (child instanceof FolderIcon) {
5200142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                ((FolderIcon) child).setTextVisible(true);
5210142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
5220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
5230142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            layout = (CellLayout) getChildAt(screen);
5240142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            child.setOnKeyListener(new IconKeyEventListener());
5250142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
5260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
5270142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
5280142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (lp == null) {
5290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
5300142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        } else {
5310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            lp.cellX = x;
5320142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            lp.cellY = y;
5330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            lp.cellHSpan = spanX;
5340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            lp.cellVSpan = spanY;
5358f573952b8729b4319043ae0908997ecd2d68951Dianne Hackborn        }
536798300c4c05b12228f2e4f31c49c3cb728a37889Romain Guy
537798300c4c05b12228f2e4f31c49c3cb728a37889Romain Guy        if (spanX < 0 && spanY < 0) {
538798300c4c05b12228f2e4f31c49c3cb728a37889Romain Guy            lp.isLockedToGrid = false;
539798300c4c05b12228f2e4f31c49c3cb728a37889Romain Guy        }
54086f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung
54186f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        // Get the canonical child id to uniquely represent this view in this screen
54286f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
54386f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        boolean markCellsAsOccupied = !(child instanceof Folder);
54486f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung        if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
54586f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            // TODO: This branch occurs when the workspace is adding views
54686f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            // outside of the defined grid
54786f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            // maybe we should be deleting these items from the LauncherModel?
54886f7753bbd6d87ce85195b0715e5548edbfdb7d1Winson Chung            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
549956091ba7863bd72086275e61084864994dd6fa7Joe Onorato        }
5508f573952b8729b4319043ae0908997ecd2d68951Dianne Hackborn
551aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        if (!(child instanceof Folder)) {
552f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            child.setHapticFeedbackEnabled(false);
5533e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            child.setOnLongClickListener(mLongClickListener);
5549171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        }
5559171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        if (child instanceof DropTarget) {
5569171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            mDragController.addDropTarget((DropTarget) child);
5579171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        }
5589171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    }
5593e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
560f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    /**
561f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen     * Check if the point (x, y) hits a given page.
562f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen     */
5633e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private boolean hitsPage(int index, float x, float y) {
5649171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        final View page = getChildAt(index);
5659171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        if (page != null) {
5669171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            float[] localXY = { x, y };
5679171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            mapPointFromSelfToChild(page, localXY);
5689171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            return (localXY[0] >= 0 && localXY[0] < page.getWidth()
5699171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                    && localXY[1] >= 0 && localXY[1] < page.getHeight());
5703e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
571f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        return false;
572f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
5739171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
5749171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    @Override
575f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    protected boolean hitsPreviousPage(float x, float y) {
576f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        // mNextPage is set to INVALID_PAGE whenever we are stationary.
577f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        // Calculating "next page" this way ensures that you scroll to whatever page you tap on
578f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
579f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
580f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        // Only allow tap to next page on large devices, where there's significant margin outside
5819171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        // the active workspace
5829171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        return LauncherApplication.isScreenLarge() && hitsPage(current - 1, x, y);
5839171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    }
5849171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
5859171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    @Override
5869171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    protected boolean hitsNextPage(float x, float y) {
5879171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        // mNextPage is set to INVALID_PAGE whenever we are stationary.
5889171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        // Calculating "next page" this way ensures that you scroll to whatever page you tap on
5899171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
5909171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
5919171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        // Only allow tap to next page on large devices, where there's significant margin outside
5929171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        // the active workspace
5939171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        return LauncherApplication.isScreenLarge() && hitsPage(current + 1, x, y);
5949171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    }
5959171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
5969171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    /**
5979171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung     * Called directly from a CellLayout (not by the framework), after we've been added as a
5989171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung     * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
5999171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung     * that it should intercept touch events, which is not something that is normally supported.
6009171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung     */
6019171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    @Override
6029171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    public boolean onTouch(View v, MotionEvent event) {
6039171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        return (isSmall() || mIsSwitchingState);
6049171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    }
6059171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung
606f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    public boolean isSwitchingState() {
607f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        return mIsSwitchingState;
608f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen    }
609f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
6106b879f0a5885274a85333531e091283405d490ccAdam Lesinski    protected void onWindowVisibilityChanged (int visibility) {
6116b879f0a5885274a85333531e091283405d490ccAdam Lesinski        mLauncher.onWindowVisibilityChanged(visibility);
6126b879f0a5885274a85333531e091283405d490ccAdam Lesinski    }
6136b879f0a5885274a85333531e091283405d490ccAdam Lesinski
6146b879f0a5885274a85333531e091283405d490ccAdam Lesinski    @Override
6156b879f0a5885274a85333531e091283405d490ccAdam Lesinski    public boolean dispatchUnhandledMove(View focused, int direction) {
6166b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (isSmall() || mIsSwitchingState) {
6176b879f0a5885274a85333531e091283405d490ccAdam Lesinski            // when the home screens are shrunken, shouldn't allow side-scrolling
6186b879f0a5885274a85333531e091283405d490ccAdam Lesinski            return false;
6196b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
6206b879f0a5885274a85333531e091283405d490ccAdam Lesinski        return super.dispatchUnhandledMove(focused, direction);
6216b879f0a5885274a85333531e091283405d490ccAdam Lesinski    }
6226b879f0a5885274a85333531e091283405d490ccAdam Lesinski
6236b879f0a5885274a85333531e091283405d490ccAdam Lesinski    @Override
6246b879f0a5885274a85333531e091283405d490ccAdam Lesinski    public boolean onInterceptTouchEvent(MotionEvent ev) {
6256b879f0a5885274a85333531e091283405d490ccAdam Lesinski        switch (ev.getAction() & MotionEvent.ACTION_MASK) {
6266b879f0a5885274a85333531e091283405d490ccAdam Lesinski        case MotionEvent.ACTION_DOWN:
6276b879f0a5885274a85333531e091283405d490ccAdam Lesinski            mXDown = ev.getX();
6286b879f0a5885274a85333531e091283405d490ccAdam Lesinski            mYDown = ev.getY();
6296b879f0a5885274a85333531e091283405d490ccAdam Lesinski            break;
6306b879f0a5885274a85333531e091283405d490ccAdam Lesinski        case MotionEvent.ACTION_POINTER_UP:
6316b879f0a5885274a85333531e091283405d490ccAdam Lesinski        case MotionEvent.ACTION_UP:
6326b879f0a5885274a85333531e091283405d490ccAdam Lesinski            if (mTouchState == TOUCH_STATE_REST) {
6336b879f0a5885274a85333531e091283405d490ccAdam Lesinski                final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
6346b879f0a5885274a85333531e091283405d490ccAdam Lesinski                if (!currentPage.lastDownOnOccupiedCell()) {
635f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    onWallpaperTap(ev);
636f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                }
63721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            }
63821f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        }
6391b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen        return super.onInterceptTouchEvent(ev);
6401b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen    }
6411b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen
64221f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen    @Override
64321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen    protected void determineScrollingStart(MotionEvent ev) {
644f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen        if (!isSmall() && !mIsSwitchingState) {
645ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen            float deltaX = Math.abs(ev.getX() - mXDown);
64621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            float deltaY = Math.abs(ev.getY() - mYDown);
64721f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen
648f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            if (Float.compare(deltaX, 0f) == 0) return;
649ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen
6501b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen            float slope = deltaY / deltaX;
65121f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            float theta = (float) Math.atan(slope);
6526b879f0a5885274a85333531e091283405d490ccAdam Lesinski
6536b879f0a5885274a85333531e091283405d490ccAdam Lesinski            if (deltaX > mTouchSlop || deltaY > mTouchSlop) {
6541b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen                cancelCurrentPageLongPress();
655f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            }
656f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen
657f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            if (theta > MAX_SWIPE_ANGLE) {
658f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
65900acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato                return;
66000acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato            } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
661956091ba7863bd72086275e61084864994dd6fa7Joe Onorato                // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
66200acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato                // increase the touch slop to make it harder to begin scrolling the workspace. This
66300acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato                // results in vertically scrolling widgets to more easily. The higher the angle, the
66400acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato                // more we increase touch slop.
66531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
66628750fba6a2d141eb9a1e566718c17236030b815Michael Jurka                float extraRatio = (float)
6670142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
668aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
66979212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka            } else {
67079212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka                // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
671883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka                super.determineScrollingStart(ev);
672883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka            }
673883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        }
67479212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka    }
67579212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka
676aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    @Override
677aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    protected boolean isScrollingIndicatorEnabled() {
678aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        return mState != State.SPRING_LOADED;
6790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
680aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
68131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    protected void onPageBeginMoving() {
68231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        super.onPageBeginMoving();
68331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6849171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        if (isHardwareAccelerated()) {
6859171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            updateChildrenLayersEnabled();
6869171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        } else {
6879171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            if (mNextPage != INVALID_PAGE) {
6889171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                // we're snapping to a particular screen
6899171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                enableChildrenCache(mCurrentPage, mNextPage);
6909171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            } else {
6919171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                // this is when user is actively dragging a particular screen, they might
6929171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                // swipe it either left or right (but we won't advance by more than one screen)
6939171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
6949171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung            }
6959171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        }
6960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
697c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka        // Only show page outlines as we pan if we are on large screen
6980142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (LauncherApplication.isScreenLarge()) {
6990142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            showOutlines();
7000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
7010142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
7020142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
7030142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void onPageEndMoving() {
7040142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        super.onPageEndMoving();
7050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
7060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        if (isHardwareAccelerated()) {
7070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            updateChildrenLayersEnabled();
708cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        } else {
7090207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy            clearChildrenCache();
7100207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        }
7110207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy
7120207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        // Hide the outlines, as long as we're not dragging
7130207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        if (!mDragController.dragging()) {
7140207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy            // Only hide page outlines as we pan if we are on large screen
7150207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy            if (LauncherApplication.isScreenLarge()) {
7160207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy                hideOutlines();
7170207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy            }
7180207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        }
7190207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        mOverScrollMaxBackgroundAlpha = 0.0f;
7200207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        mOverScrollPageIndex = -1;
7210207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy
7220207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        if (mDelayedResizeRunnable != null) {
7230207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy            mDelayedResizeRunnable.run();
7240207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy            mDelayedResizeRunnable = null;
7250207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        }
7260207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy    }
7270207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy
7280207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy    @Override
7290207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy    protected void notifyPageSwitchListener() {
7300207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        super.notifyPageSwitchListener();
7310207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy        Launcher.setScreen(mCurrentPage);
7320207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy    };
733cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
7348ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    // As a ratio of screen height, the total distance we want the parallax effect to span
7358ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    // horizontally
736cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    private float wallpaperTravelToScreenWidthRatio(int width, int height) {
737cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        float aspectRatio = width / (float) height;
738cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
739cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
740cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
741cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        // We will use these two data points to extrapolate how much the wallpaper parallax effect
742cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        // to span (ie travel) at any aspect ratio:
74331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
74431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
74531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final float ASPECT_RATIO_PORTRAIT = 10/16f;
74631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
74731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
748678862186af4ea70d9b4012c9bc127e7fea5e607Joe Onorato
74931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // To find out the desired width at different aspect ratios, we use the following two
75031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // formulas, where the coefficient on x is the aspect ratio (width/height):
75131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        //   (16/10)x + y = 1.5
75231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        //   (10/16)x + y = 1.2
7530142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        // We solve for x and y and end up with a final formula:
75431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final float x =
75531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
75631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
75731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
75831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return x * aspectRatio + y;
75931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
760c2e24c0a37ddeab930f731c062705d435e477f75Romain Guy
761678862186af4ea70d9b4012c9bc127e7fea5e607Joe Onorato    // The range of scroll values for Workspace
76231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private int getScrollRange() {
7630142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        return getChildOffset(getChildCount() - 1) - getChildOffset(0);
76431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
7650142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
7660142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    protected void setWallpaperDimension() {
76731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        DisplayMetrics displayMetrics = new DisplayMetrics();
76831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mLauncher.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
76931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int maxDim = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
77031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int minDim = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
77131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
7727bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato        // We need to ensure that there is enough extra space in the wallpaper for the intended
7737bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato        // parallax effects
77454dd75463d0eb47c2f468e19063bdc4141dfdf74Michael Jurka        if (LauncherApplication.isScreenLarge()) {
77554dd75463d0eb47c2f468e19063bdc4141dfdf74Michael Jurka            mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
7766b4adbc0558fcebbd44998bd00dcd334ddbee32dJoe Onorato            mWallpaperHeight = maxDim;
7777bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato        } else {
7787bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato            mWallpaperWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
7797bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato            mWallpaperHeight = maxDim;
7806b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
7817bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato        new Thread("setWallpaperDimension") {
7827bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato            public void run() {
7837bb1749c69384faf00b238f0684d3b2e23406451Joe Onorato                mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight);
7840142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            }
7850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }.start();
7860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
7870142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
7880142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setVerticalWallpaperOffset(float offset) {
7893a2b3f2be58843d26549fb0ec6c6533627c7cd19Mike Cleron        mWallpaperOffset.setFinalY(offset);
790aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
791dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    public float getVerticalWallpaperOffset() {
792fea5d0250767ab938f623a404e6292a32dd2fdf5Adam Powell        return mWallpaperOffset.getCurrY();
7930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
7940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    public void setHorizontalWallpaperOffset(float offset) {
795fea5d0250767ab938f623a404e6292a32dd2fdf5Adam Powell        mWallpaperOffset.setFinalX(offset);
7960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
79731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public float getHorizontalWallpaperOffset() {
79831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return mWallpaperOffset.getCurrX();
79931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
80031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
80131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    private float wallpaperOffsetForCurrentScroll() {
80231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // The wallpaper travel width is how far, from left to right, the wallpaper will move
80331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // at this orientation. On tablets in portrait mode we don't move all the way to the
804dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        // edges of the wallpaper, or otherwise the parallax effect would be too strong.
805dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        int wallpaperTravelWidth = mWallpaperWidth;
80631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (LauncherApplication.isScreenLarge()) {
80731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            wallpaperTravelWidth = mWallpaperTravelWidth;
80831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
80931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
81031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        // Set wallpaper offset steps (1 / (number of screens - 1))
81131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
81231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
8132bc6b7c2b8b651dde0a65c64c127f5b85c1290d2Joe Onorato        // For the purposes of computing the scrollRange and overScrollOffset, we assume
8142bc6b7c2b8b651dde0a65c64c127f5b85c1290d2Joe Onorato        // that mLayoutScale is 1. This means that when we're in spring-loaded mode,
8152bc6b7c2b8b651dde0a65c64c127f5b85c1290d2Joe Onorato        // there's no discrepancy between the wallpaper offset for a given page.
8162bc6b7c2b8b651dde0a65c64c127f5b85c1290d2Joe Onorato        float layoutScale = mLayoutScale;
8172bc6b7c2b8b651dde0a65c64c127f5b85c1290d2Joe Onorato        mLayoutScale = 1f;
8183e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        int scrollRange = getScrollRange();
8196b879f0a5885274a85333531e091283405d490ccAdam Lesinski
8206b879f0a5885274a85333531e091283405d490ccAdam Lesinski        // Again, we adjust the wallpaper offset to be consistent between values of mLayoutScale
8216b879f0a5885274a85333531e091283405d490ccAdam Lesinski        float adjustedScrollX = Math.max(0, Math.min(mScrollX, mMaxScrollX));
8226b879f0a5885274a85333531e091283405d490ccAdam Lesinski        adjustedScrollX *= mWallpaperScrollRatio;
8236b879f0a5885274a85333531e091283405d490ccAdam Lesinski        mLayoutScale = layoutScale;
8246b879f0a5885274a85333531e091283405d490ccAdam Lesinski
8256b879f0a5885274a85333531e091283405d490ccAdam Lesinski        float scrollProgress =
8266b879f0a5885274a85333531e091283405d490ccAdam Lesinski            adjustedScrollX / (float) scrollRange;
82731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        float offsetInDips = wallpaperTravelWidth * scrollProgress +
82831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            (mWallpaperWidth - wallpaperTravelWidth) / 2; // center it
8290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        float offset = offsetInDips / (float) mWallpaperWidth;
83031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return offset;
831aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
83201f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka    private void syncWallpaperOffsetWithScroll() {
83301f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka        final boolean enableWallpaperEffects = isHardwareAccelerated();
83401f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka        if (enableWallpaperEffects) {
83501f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka            mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
8366b509c1fd62a39916bb957eac07917161f549206Patrick Dubroy        }
8370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
83801f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka
83901f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka    public void updateWallpaperOffsetImmediately() {
84001f0ed4126be412c8c4352026dad3b2a49832267Michael Jurka        mUpdateWallpaperOffsetImmediately = true;
8410142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    }
842213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka
843213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka    private void updateWallpaperOffsets() {
8446b879f0a5885274a85333531e091283405d490ccAdam Lesinski        boolean updateNow = false;
8456b879f0a5885274a85333531e091283405d490ccAdam Lesinski        boolean keepUpdating = true;
8466b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (mUpdateWallpaperOffsetImmediately) {
8476b879f0a5885274a85333531e091283405d490ccAdam Lesinski            updateNow = true;
8486b879f0a5885274a85333531e091283405d490ccAdam Lesinski            keepUpdating = false;
8496b879f0a5885274a85333531e091283405d490ccAdam Lesinski            mWallpaperOffset.jumpToFinal();
85079212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka            mUpdateWallpaperOffsetImmediately = false;
85179212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka        } else {
8526b879f0a5885274a85333531e091283405d490ccAdam Lesinski            updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset();
853883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        }
854213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka        if (updateNow) {
855213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka            if (mWindowToken != null) {
8566b879f0a5885274a85333531e091283405d490ccAdam Lesinski                mWallpaperManager.setWallpaperOffsets(mWindowToken,
8576b879f0a5885274a85333531e091283405d490ccAdam Lesinski                        mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY());
8586b879f0a5885274a85333531e091283405d490ccAdam Lesinski            }
8596b879f0a5885274a85333531e091283405d490ccAdam Lesinski        }
860a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen        if (keepUpdating) {
861a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            fastInvalidate();
862a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen        }
863a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    }
864a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen
865a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    @Override
866a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    protected void updateCurrentPageScroll() {
867a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen        super.updateCurrentPageScroll();
868a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen        computeWallpaperScrollRatio(mCurrentPage);
869a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    }
870a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen
871a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen    @Override
872dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    protected void snapToPage(int whichPage) {
8730142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        super.snapToPage(whichPage);
8749171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung        computeWallpaperScrollRatio(whichPage);
8759171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung    }
876883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka
877883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka    private void computeWallpaperScrollRatio(int page) {
878883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        // Here, we determine what the desired scroll would be with and without a layout scale,
879883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        // and compute a ratio between the two. This allows us to adjust the wallpaper offset
880883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        // as though there is no layout scale.
881883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        float layoutScale = mLayoutScale;
882883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        int scaled = getChildOffset(page) - getRelativeChildOffset(page);
883883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        mLayoutScale = 1.0f;
884883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        float unscaled = getChildOffset(page) - getRelativeChildOffset(page);
885dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        mLayoutScale = layoutScale;
8863e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (scaled > 0) {
8873e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mWallpaperScrollRatio = (1.0f * unscaled) / scaled;
8883e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        } else {
889a997ac26664befbd0d5b172e36ef9e8b2c63b435Michael Jurka            mWallpaperScrollRatio = 1f;
8903e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
8913e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
8925f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
8935f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    class WallpaperOffsetInterpolator {
8945f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        float mFinalHorizontalWallpaperOffset = 0.0f;
8955f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        float mFinalVerticalWallpaperOffset = 0.5f;
8961b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen        float mHorizontalWallpaperOffset = 0.0f;
897ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen        float mVerticalWallpaperOffset = 0.5f;
898ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen        long mLastWallpaperOffsetUpdateTime;
899ddb821981e0919bbd3b4c9a2b6aa1811d6c86bb6Adam Cohen        boolean mIsMovingFast;
9001b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen        boolean mOverrideHorizontalCatchupConstant;
9011b0aaac0b3abd777ed319341f95a8dfff23c79f4Adam Cohen        float mHorizontalCatchupConstant = 0.35f;
9027247f6315baf16eacb3286f21bd80321385c1defPatrick Dubroy        float mVerticalCatchupConstant = 0.35f;
903dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
904213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka        public WallpaperOffsetInterpolator() {
9050142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
9060142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
9070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        public void setOverrideHorizontalCatchupConstant(boolean override) {
9080142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            mOverrideHorizontalCatchupConstant = override;
9090142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
9100142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
9110142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        public void setHorizontalCatchupConstant(float f) {
912a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            mHorizontalCatchupConstant = f;
913dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
914dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
915a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen        public void setVerticalCatchupConstant(float f) {
916213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka            mVerticalCatchupConstant = f;
917df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung        }
918df4b83dd9d6380ab963c62d1f9d1312efc87cb0fWinson Chung
919ef0066b52d2754ca0553ec79613c650b5649afaaWinson Chung        public boolean computeScrollOffset() {
920ef0066b52d2754ca0553ec79613c650b5649afaaWinson Chung            if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 &&
9213e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                    Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) {
9223e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mIsMovingFast = false;
9233e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                return false;
9243e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            }
9253e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            boolean isLandscape = mDisplayWidth > mDisplayHeight;
9263e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
9273e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            long currentTime = System.currentTimeMillis();
9283e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime;
9290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate);
9306b879f0a5885274a85333531e091283405d490ccAdam Lesinski            timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate);
9310142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
932b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung            float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset);
933b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung            if (!mIsMovingFast && xdiff > 0.07) {
934b0b2e6f588367cf40a4270cca81af7d78f8e382eWinson Chung                mIsMovingFast = true;
935ef0066b52d2754ca0553ec79613c650b5649afaaWinson Chung            }
936ef0066b52d2754ca0553ec79613c650b5649afaaWinson Chung
937ef0066b52d2754ca0553ec79613c650b5649afaaWinson Chung            float fractionToCatchUpIn1MsHorizontal;
938213d96354e7407dba5c9715eb10d04bebd82c395Michael Jurka            if (mOverrideHorizontalCatchupConstant) {
939dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant;
940dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            } else if (mIsMovingFast) {
941dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f;
942dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            } else {
943dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                // slow
9440142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f;
945a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            }
946a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant;
947a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen
948a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            fractionToCatchUpIn1MsHorizontal /= 33f;
949a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            fractionToCatchUpIn1MsVertical /= 33f;
9503c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka
9513c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka            final float UPDATE_THRESHOLD = 0.00001f;
9523c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka            float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset;
9533c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka            float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset;
9543c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka            boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD &&
955dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
956dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
957a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            // Don't have any lag between workspace and wallpaper on non-large devices
958f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            if (!LauncherApplication.isScreenLarge() || jumpToFinalValue) {
959a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen                mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
960a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen                mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
961a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen            } else {
96279212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka                float percentToCatchUpVertical =
9637247f6315baf16eacb3286f21bd80321385c1defPatrick Dubroy                    Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical);
964cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen                float percentToCatchUpHorizontal =
965cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen                    Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal);
966472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta;
967472b281d5cb4f5660df981a6c912266b9f5703feChet Haase                mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta;
968472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            }
9693e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
970472b281d5cb4f5660df981a6c912266b9f5703feChet Haase            mLastWallpaperOffsetUpdateTime = System.currentTimeMillis();
97161033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            return true;
97261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        }
97361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
97461033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        public float getCurrX() {
97561033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            return mHorizontalWallpaperOffset;
976cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen        }
97761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
97861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        public float getFinalX() {
97979212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka            return mFinalHorizontalWallpaperOffset;
98079212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka        }
98179212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka
982883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        public float getCurrY() {
983883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka            return mVerticalWallpaperOffset;
984883f55b1d261862b0de1b43af0b17d351761a9c6Michael Jurka        }
9853e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
986a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen        public float getFinalY() {
98779212d81361d1ad8c941c48f8323eb526643ca68Michael Jurka            return mFinalVerticalWallpaperOffset;
988dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
989a985e598f6071f4caca15ba3cb6b2cd3e38b217dAdam Cohen
990dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        public void setFinalX(float x) {
9913c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka            mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f));
9923c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka        }
9933c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka
994dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        public void setFinalY(float y) {
995dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka            mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f));
996dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
99761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
99861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        public void jumpToFinal() {
99961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
100061033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
100161033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        }
100261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
100361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
100461033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    @Override
100561033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    public void computeScroll() {
100661033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        super.computeScroll();
100761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        syncWallpaperOffsetWithScroll();
100861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
100961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
1010eed565d5126cb40b2a7ecdf5f6e8ac7a7464007bAdam Cohen    void showOutlines() {
1011eed565d5126cb40b2a7ecdf5f6e8ac7a7464007bAdam Cohen        if (!isSmall() && !mIsSwitchingState) {
101261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1013cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1014cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f);
1015cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
101661033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            mChildrenOutlineFadeInAnimation.start();
101761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        }
101861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
101961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
102061033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    void hideOutlines() {
102161033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        if (!isSmall() && !mIsSwitchingState) {
102261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
102361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1024cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f);
102561033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
1026cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
1027cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            mChildrenOutlineFadeOutAnimation.start();
1028cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen        }
102961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
103061033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
103161033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    public void showOutlinesTemporarily() {
103261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        if (!mIsPageMoving && !isTouchActive()) {
103361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            snapToPage(mCurrentPage);
103461033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        }
103561033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
103661033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
103761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    public void setChildrenOutlineAlpha(float alpha) {
103861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        mChildrenOutlineAlpha = alpha;
103961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        for (int i = 0; i < getChildCount(); i++) {
104061033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            CellLayout cl = (CellLayout) getChildAt(i);
104161033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            cl.setBackgroundAlpha(alpha);
104261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        }
104361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
104461033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
104561033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    public float getChildrenOutlineAlpha() {
104661033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        return mChildrenOutlineAlpha;
104761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
104861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
104961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    void disableBackground() {
105061033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        mDrawBackground = false;
105161033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
105261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    void enableBackground() {
105361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        mDrawBackground = true;
105461033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
10553e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
10563e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private void animateBackgroundGradient(float finalAlpha, boolean animated) {
10573e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (mBackground == null) return;
10583e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (mBackgroundFadeInAnimation != null) {
10593e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundFadeInAnimation.cancel();
10603e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundFadeInAnimation = null;
10613e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
10623e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (mBackgroundFadeOutAnimation != null) {
10633e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundFadeOutAnimation.cancel();
10643e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundFadeOutAnimation = null;
10653e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
10663e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        float startAlpha = getBackgroundAlpha();
10673e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (finalAlpha != startAlpha) {
10683e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            if (animated) {
10693e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
10703e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
10713e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                    public void onAnimationUpdate(ValueAnimator animation) {
10723e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                        setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
10733e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                    }
10743e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                });
10753e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
10763e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
10773e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                mBackgroundFadeOutAnimation.start();
10783e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            } else {
10793e7c7634531302271270c8cf418abc959d621cbcMichael Jurka                setBackgroundAlpha(finalAlpha);
10803e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            }
10813e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
10823e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
10833e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
10843e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    public void setBackgroundAlpha(float alpha) {
10853e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        if (alpha != mBackgroundAlpha) {
10863e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            mBackgroundAlpha = alpha;
10873e7c7634531302271270c8cf418abc959d621cbcMichael Jurka            invalidate();
10883e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        }
10893e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
10903e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
10913e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    public float getBackgroundAlpha() {
10923e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        return mBackgroundAlpha;
10933e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
10943e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
10953e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    /**
10963e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * Due to 3D transformations, if two CellLayouts are theoretically touching each other,
10973e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * on the xy plane, when one is rotated along the y-axis, the gap between them is perceived
10983e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * as being larger. This method computes what offset the rotated view should be translated
10993e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * in order to minimize this perceived gap.
11003e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * @param degrees Angle of the view
11013e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * @param width Width of the view
11026b879f0a5885274a85333531e091283405d490ccAdam Lesinski     * @param height Height of the view
11033e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     * @return Offset to be used in a View.setTranslationX() call
11043e7c7634531302271270c8cf418abc959d621cbcMichael Jurka     */
11053e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    private float getOffsetXForRotation(float degrees, int width, int height) {
11063e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mMatrix.reset();
11073e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mCamera.save();
11083e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mCamera.rotateY(Math.abs(degrees));
11093e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mCamera.getMatrix(mMatrix);
11103e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mCamera.restore();
11113e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
11123e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mMatrix.preTranslate(-width * 0.5f, -height * 0.5f);
11133e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mMatrix.postTranslate(width * 0.5f, height * 0.5f);
11143e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mTempFloat2[0] = width;
11153e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mTempFloat2[1] = height;
11163e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        mMatrix.mapPoints(mTempFloat2);
11173e7c7634531302271270c8cf418abc959d621cbcMichael Jurka        return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f);
11183e7c7634531302271270c8cf418abc959d621cbcMichael Jurka    }
11193e7c7634531302271270c8cf418abc959d621cbcMichael Jurka
1120dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    float backgroundAlphaInterpolator(float r) {
11212b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        float pivotA = 0.1f;
11220142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        float pivotB = 0.4f;
11234cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka        if (r < pivotA) {
11244cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka            return 0;
11254cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka        } else if (r > pivotB) {
11260142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            return 1.0f;
11274cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka        } else {
11284cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka            return (r - pivotA)/(pivotB - pivotA);
11290142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
11304cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka    }
11314cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka
113221f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen    float overScrollBackgroundAlphaInterpolator(float r) {
113321f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        float threshold = 0.08f;
113421f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen
113521f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen        if (r > mOverScrollMaxBackgroundAlpha) {
113621f12b52a54d2658ed14fcf7bb1ca17a198f62beAdam Cohen            mOverScrollMaxBackgroundAlpha = r;
11370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        } else if (r < mOverScrollMaxBackgroundAlpha) {
1138b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka            r = mOverScrollMaxBackgroundAlpha;
11395f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        }
11405f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
11414cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka        return Math.min(r / threshold, 1.0f);
1142b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka    }
1143b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka
1144b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka    private void screenScrolledLargeUI(int screenCenter) {
1145b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka        if (isSwitchingState()) return;
1146b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka        boolean isInOverscroll = false;
11470142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        for (int i = 0; i < getChildCount(); i++) {
1148c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka            CellLayout cl = (CellLayout) getChildAt(i);
1149b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka            if (cl != null) {
1150dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                float scrollProgress = getScrollProgress(screenCenter, cl, i);
1151dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                float rotation = WORKSPACE_ROTATION * scrollProgress;
11524cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka                float translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
11534cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka
11544cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka                // If the current page (i) is being over scrolled, we use a different
11554cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka                // set of rules for setting the background alpha multiplier.
11564cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka                if (!isSmall()) {
11579171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                    if ((mOverScrollX < 0 && i == 0) || (mOverScrollX > mMaxScrollX &&
11589171e6d8a2b7b5aa136617b9779a8bbadc5259f7Winson Chung                            i == getChildCount() -1)) {
1159b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka                        isInOverscroll = true;
1160c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka                        rotation *= -1;
11613c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                        cl.setBackgroundAlphaMultiplier(
11623c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                                overScrollBackgroundAlphaInterpolator(Math.abs(scrollProgress)));
11633c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                        mOverScrollPageIndex = i;
11646b879f0a5885274a85333531e091283405d490ccAdam Lesinski                        cl.setOverScrollAmount(Math.abs(scrollProgress), i == 0);
11653c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                        cl.setPivotX(cl.getMeasuredWidth() * (i == 0 ? 0.75f : 0.25f));
1166b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka                        cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
11674cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka                        cl.setOverscrollTransformsDirty(true);
11684cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka                    } else if (mOverScrollPageIndex != i) {
1169b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka                        cl.setBackgroundAlphaMultiplier(
11707247f6315baf16eacb3286f21bd80321385c1defPatrick Dubroy                                backgroundAlphaInterpolator(Math.abs(scrollProgress)));
11715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                    }
1172f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                }
1173f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                cl.setFastTranslationX(translationX);
1174f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                cl.setFastRotationY(rotation);
1175f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                if (mFadeInAdjacentScreens && !isSmall()) {
1176f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    float alpha = 1 - Math.abs(scrollProgress);
1177f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                    cl.setFastAlpha(alpha);
1178f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                }
1179f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen                cl.fastInvalidate();
11806b879f0a5885274a85333531e091283405d490ccAdam Lesinski            }
1181cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen        }
11826b879f0a5885274a85333531e091283405d490ccAdam Lesinski        if (!isSwitchingState() && !isInOverscroll) {
1183cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
11846b879f0a5885274a85333531e091283405d490ccAdam Lesinski            ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
1185cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen        }
1186cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen        invalidate();
118761033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    }
118861033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen
118961033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen    private void screenScrolledStandardUI(int screenCenter) {
119061033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen        if (mOverScrollX < 0 || mOverScrollX > mMaxScrollX) {
1191cbbaf9862aa160c5e397cb49b554de20981a35feAdam Cohen            int index = mOverScrollX < 0 ? 0 : getChildCount() - 1;
119261033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            CellLayout cl = (CellLayout) getChildAt(index);
119361033d3d86fcae1f654f3fbfc9979d131e265d76Adam Cohen            float scrollProgress = getScrollProgress(screenCenter, cl, index);
11944cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka            cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
11956b879f0a5885274a85333531e091283405d490ccAdam Lesinski            float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
11964cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka            cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
11974cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka            cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
11984cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka            cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
11995f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            cl.setRotationY(rotation);
1200f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            cl.setOverscrollTransformsDirty(true);
1201f34bab59fc0260f926aec44d044883dce1b4191fAdam Cohen            setFadeForOverScroll(Math.abs(scrollProgress));
12024cb3724a2c1e5f278e1531d643accc40fcd8e219Michael Jurka        } else {
1203b0f28bd9a52fa4e343c8299d1c3e225d8e01c1e9Michael Jurka            if (mOverscrollFade != 0) {
12046b879f0a5885274a85333531e091283405d490ccAdam Lesinski                setFadeForOverScroll(0);
1205c86756c20196e5c907df01fe0fc9193ffb934082Michael Jurka            }
1206c86756c20196e5c907df01fe0fc9193ffb934082Michael Jurka            // We don't want to mess with the translations during transitions
1207c86756c20196e5c907df01fe0fc9193ffb934082Michael Jurka            if (!isSwitchingState()) {
12083c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
12093c4c20fbe682cb4b3ef94f09afe0af09171583f3Michael Jurka                ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
1210c86756c20196e5c907df01fe0fc9193ffb934082Michael Jurka            }
1211dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        }
1212dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka    }
1213dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka
12144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    @Override
12154be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    protected void screenScrolled(int screenCenter) {
12164be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (LauncherApplication.isScreenLarge()) {
12174be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // We don't call super.screenScrolled() here because we handle the adjacent pages alpha
12184be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // ourselves (for efficiency), and there are no scrolling indicators to update.
12194be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            screenScrolledLargeUI(screenCenter);
12204be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        } else {
12214be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            super.screenScrolled(screenCenter);
12224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            screenScrolledStandardUI(screenCenter);
12234be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
12244be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
12254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12264be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    @Override
1227bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung    protected void overScroll(float amount) {
1228bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung        if (LauncherApplication.isScreenLarge()) {
12294be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            dampedOverScroll(amount);
12304be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        } else {
12314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            acceleratedOverScroll(amount);
12324be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
12334be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
12344be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12354be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    protected void onAttachedToWindow() {
12364be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        super.onAttachedToWindow();
12374be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mWindowToken = getWindowToken();
12384be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        computeScroll();
12394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mDragController.setWindowToken(mWindowToken);
12404be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
12414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12424be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    protected void onDetachedFromWindow() {
12434be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mWindowToken = null;
12444be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
12454be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12464be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    @Override
12474be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
12484be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
12494be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mUpdateWallpaperOffsetImmediately = true;
12504be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
12514be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        super.onLayout(changed, left, top, right, bottom);
12524be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12534be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // if shrinkToBottom() is called on initialization, it has to be deferred
12544be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // until after the first call to onLayout so that it has the correct width
12554be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mSwitchStateAfterFirstLayout) {
12564be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            mSwitchStateAfterFirstLayout = false;
12574be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // shrink can trigger a synchronous onLayout call, so we
12584be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // post this to avoid a stack overflow / tangled onLayout calls
1259a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            post(new Runnable() {
1260a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                public void run() {
1261a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    changeState(mStateAfterFirstLayout, false);
1262a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                }
1263a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            });
1264a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        }
1265a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    }
1266a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
1267a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    @Override
1268a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    protected void onDraw(Canvas canvas) {
1269a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        updateWallpaperOffsets();
1270a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
1271a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        // Draw the background gradient if necessary
1272a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
1273a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            int alpha = (int) (mBackgroundAlpha * 255);
1274a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mBackground.setAlpha(alpha);
1275a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mBackground.setBounds(mScrollX, 0, mScrollX + getMeasuredWidth(),
1276a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    getMeasuredHeight());
1277a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mBackground.draw(canvas);
1278a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        }
1279a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
1280a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        super.onDraw(canvas);
1281a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung    }
12824be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12834be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    boolean isDrawingBackgroundGradient() {
12844be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
12854be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
12864be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12874be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    public void scrollTo (int x, int y) {
12884be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        super.scrollTo(x, y);
12894be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        syncChildrenLayersEnabledOnVisiblePages();
12904be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
12914be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
12924be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    // This method just applies the value mChildrenLayersEnabled to all the pages that
12938e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy    // will be rendered on the next frame.
12944be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    // We do this because calling setChildrenLayersEnabled on a view that's not
12954be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    // visible/rendered causes slowdowns on some graphics cards
12964be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private void syncChildrenLayersEnabledOnVisiblePages() {
12974be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mChildrenLayersEnabled) {
129831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            getVisiblePages(mTempVisiblePagesRange);
129931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int leftScreen = mTempVisiblePagesRange[0];
1300aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            final int rightScreen = mTempVisiblePagesRange[1];
130131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (leftScreen != -1 && rightScreen != -1) {
13027984c9496bcaeca23ee7fdecc4d4f1bb2467e0f2Bjorn Bringert                for (int i = leftScreen; i <= rightScreen; i++) {
130331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    ViewGroup page = (ViewGroup) getPageAt(i);
130431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    if (page.getVisibility() == VISIBLE &&
1305aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                            page.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD) {
130631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        ((ViewGroup)getPageAt(i)).setChildrenLayersEnabled(true);
13070142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                    }
1308aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                }
130908ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy            }
131031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
131131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1312cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
13134be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    @Override
13144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    protected void dispatchDraw(Canvas canvas) {
13154be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        super.dispatchDraw(canvas);
13164be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
13174be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (mInScrollArea && !LauncherApplication.isScreenLarge()) {
13184be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int width = getWidth();
13198e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy            final int height = getHeight();
13208e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy            final int pageHeight = getChildAt(0).getHeight();
13218e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy
13224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // Set the height of the outline to be the height of the page
13238e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy            final int offset = (height - pageHeight - mPaddingTop - mPaddingBottom) / 2;
13244be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int paddingTop = mPaddingTop + offset;
13254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final int paddingBottom = mPaddingBottom + offset;
13268e58e916061cbe2623697efac0924f2aa3753a92Patrick Dubroy
13274be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final CellLayout leftPage = (CellLayout) getChildAt(mCurrentPage - 1);
13284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            final CellLayout rightPage = (CellLayout) getChildAt(mCurrentPage + 1);
13294be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
13304be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            if (leftPage != null && leftPage.getIsDragOverlapping()) {
13314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                final Drawable d = getResources().getDrawable(R.drawable.page_hover_left_holo);
13324be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                d.setBounds(mScrollX, paddingTop, mScrollX + d.getIntrinsicWidth(),
13334be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                        height - paddingBottom);
13344be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato                d.draw(canvas);
13354be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            } else if (rightPage != null && rightPage.getIsDragOverlapping()) {
133631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                final Drawable d = getResources().getDrawable(R.drawable.page_hover_right_holo);
133731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                d.setBounds(mScrollX + width - d.getIntrinsicWidth(), paddingTop, mScrollX + width,
13380280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                        height - paddingBottom);
13390280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                d.draw(canvas);
13400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
13410280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
134231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
13430280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
13440280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    @Override
13450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
13460280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        if (!mLauncher.isAllAppsVisible()) {
13470280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            final Folder openFolder = getOpenFolder();
13480280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (openFolder != null) {
134931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                return openFolder.requestFocus(direction, previouslyFocusedRect);
135031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
1351ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
1352ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy            }
1353ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        }
1354ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        return false;
1355ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    }
1356ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy
1357ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    @Override
1358ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    public int getDescendantFocusability() {
1359ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        if (isSmall()) {
1360ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy            return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1361cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        }
1362cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        return super.getDescendantFocusability();
1363ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    }
1364ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy
1365ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    @Override
13660280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1367cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        if (!mLauncher.isAllAppsVisible()) {
1368cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            final Folder openFolder = getOpenFolder();
1369cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            if (openFolder != null) {
1370cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                openFolder.addFocusables(views, direction);
1371cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            } else {
1372cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                super.addFocusables(views, direction, focusableMode);
1373cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            }
1374cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        }
1375cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    }
1376cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1377cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    public boolean isSmall() {
1378cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        return mState == State.SMALL || mState == State.SPRING_LOADED;
1379cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    }
1380cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1381cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    void enableChildrenCache(int fromPage, int toPage) {
13828ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy        if (fromPage > toPage) {
13838ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy            final int temp = fromPage;
13848ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy            fromPage = toPage;
13858ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy            toPage = temp;
1386cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        }
1387f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy
1388f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        final int screenCount = getChildCount();
1389f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy
1390f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        fromPage = Math.max(fromPage, 0);
1391f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        toPage = Math.min(toPage, screenCount - 1);
1392f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy
1393f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        for (int i = fromPage; i <= toPage; i++) {
1394f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            final CellLayout layout = (CellLayout) getChildAt(i);
1395cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            layout.setChildrenDrawnWithCacheEnabled(true);
1396f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            layout.setChildrenDrawingCacheEnabled(true);
1397f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        }
1398f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy    }
1399f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy
1400f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy    void clearChildrenCache() {
1401f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        final int screenCount = getChildCount();
1402f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        for (int i = 0; i < screenCount; i++) {
1403f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            final CellLayout layout = (CellLayout) getChildAt(i);
1404f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            layout.setChildrenDrawnWithCacheEnabled(false);
1405f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            // In software mode, we don't want the items to continue to be drawn into bitmaps
1406f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            if (!isHardwareAccelerated()) {
1407cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                layout.setChildrenDrawingCacheEnabled(false);
1408f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy            }
1409f24312876b097e25cec922edbb58334ed234dcd7Patrick Dubroy        }
14108ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    }
1411cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1412cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    private void updateChildrenLayersEnabled() {
1413cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        boolean small = isSmall() || mIsSwitchingState;
1414cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        boolean dragging = mAnimatingViewIntoPlace || mIsDragOccuring;
1415cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        boolean enableChildrenLayers = small || dragging || isPageMoving();
1416cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1417cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        if (enableChildrenLayers != mChildrenLayersEnabled) {
1418cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            mChildrenLayersEnabled = enableChildrenLayers;
1419cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            // calling setChildrenLayersEnabled on a view that's not visible/rendered
1420cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            // causes slowdowns on some graphics cards, so we only disable it here and leave
1421cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            // the enabling to dispatchDraw
1422cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            if (!enableChildrenLayers) {
1423cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                for (int i = 0; i < getPageCount(); i++) {
1424cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                    ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(false);
1425cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                }
1426cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            }
14278ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy        }
14288ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    }
14298ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy
14308ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy    protected void onWallpaperTap(MotionEvent ev) {
14318ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy        final int[] position = mTempCell;
14328ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy        getLocationOnScreen(position);
14338ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy
14348ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy        int pointerIndex = ev.getActionIndex();
14358ae3a62e2e48dfb8f860c8b6e2c7e72b9595d7aaPatrick Dubroy        position[0] += (int) ev.getX(pointerIndex);
1436cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        position[1] += (int) ev.getY(pointerIndex);
1437cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1438cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1439cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                ev.getAction() == MotionEvent.ACTION_UP
1440cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                        ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1441cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                position[0], position[1], 0, null);
1442cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    }
1443cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1444cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    /*
1445cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     * This interpolator emulates the rate at which the perceived scale of an object changes
1446cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     * as its distance from a camera increases. When this interpolator is applied to a scale
1447cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     * animation on a view, it evokes the sense that the object is shrinking due to moving away
1448cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     * from the camera.
1449cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     */
1450cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    static class ZInterpolator implements TimeInterpolator {
1451cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        private float focalLength;
1452cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1453cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        public ZInterpolator(float foc) {
1454cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            focalLength = foc;
1455cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        }
1456cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1457cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        public float getInterpolation(float input) {
1458cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            return (1.0f - focalLength / (focalLength + input)) /
1459cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy                (1.0f - focalLength / (focalLength + 1.0f));
1460cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        }
1461cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    }
1462cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1463cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    /*
1464cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     * The exact reverse of ZInterpolator.
1465cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy     */
146600acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato    static class InverseZInterpolator implements TimeInterpolator {
146700acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato        private ZInterpolator zInterpolator;
1468ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        public InverseZInterpolator(float foc) {
1469ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy            zInterpolator = new ZInterpolator(foc);
1470ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        }
1471ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        public float getInterpolation(float input) {
1472c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka            return 1 - zInterpolator.getInterpolation(1 - input);
14730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
14740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
14750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
1476d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    /*
14770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * ZInterpolator compounded with an ease-out.
14780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
1479a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    static class ZoomOutInterpolator implements TimeInterpolator {
1480c6ee42e25f203e408826e7eab4ad8faf67ed2ff9Michael Jurka        private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
148131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
1482d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1483ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        public float getInterpolation(float input) {
1484ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy            return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
148554fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        }
148654fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    }
148754fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy
148854fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    /*
148954fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy     * InvereZInterpolator compounded with an ease-out.
149054fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy     */
149154fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    static class ZoomInInterpolator implements TimeInterpolator {
149254fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
149354fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
149454fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy
149554fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        public float getInterpolation(float input) {
149654fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy            return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
149754fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        }
1498cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    }
1499cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
150054fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
1501cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1502cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    /*
150354fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    *
150454fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
150554fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
150654fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    *
150754fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    * These methods mark the appropriate pages as accepting drops (which alters their visual
150854fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    * appearance).
150954fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    *
151054fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    */
15119438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy    public void onDragStartedWithItem(View v) {
15129438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        final Canvas canvas = new Canvas();
15139438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy
15149438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        // We need to add extra padding to the bitmap to make room for the glow effect
15159438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
15169438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy
1517cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        // The outline is used to visualize where the item will land if dropped
15189438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        mDragOutline = createDragOutline(v, canvas, bitmapPadding);
15199438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy    }
15209438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy
152154fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy    public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, Paint alphaClipPaint) {
15229438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        final Canvas canvas = new Canvas();
15239438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy
15249438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        // We need to add extra padding to the bitmap to make room for the glow effect
15259438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
15269438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy
15279438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
15289438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy
15299438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        // The outline is used to visualize where the item will land if dropped
15309438336724b4f8b54c7cdc49c5f01dd9568926ebPatrick Dubroy        mDragOutline = createDragOutline(b, canvas, bitmapPadding, size[0], size[1], alphaClipPaint);
153131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1532cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
1533cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy    // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
1534ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    // never dragged over
1535ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy    public void onDragStopped(boolean success) {
1536ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        // In the success case, DragController has already called onDragExit()
1537cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy        if (!success) {
1538cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy            doDragExit(null);
153931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
154031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
154131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1542aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public void exitWidgetResizeMode() {
1543aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        DragLayer dragLayer = mLauncher.getDragLayer();
154408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        dragLayer.clearAllResizeFrames();
154508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy    }
1546d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
154708ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy    private void initAnimationArrays() {
1548a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        final int childCount = getChildCount();
1549d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        if (mOldTranslationXs != null) return;
1550d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        mOldTranslationXs = new float[childCount];
155131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mOldTranslationYs = new float[childCount];
155231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        mOldScaleXs = new float[childCount];
155318014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mOldScaleYs = new float[childCount];
155418014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka        mOldBackgroundAlphas = new float[childCount];
15556569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mOldBackgroundAlphaMultipliers = new float[childCount];
1556c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka        mOldAlphas = new float[childCount];
1557d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        mOldRotationYs = new float[childCount];
15580280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        mNewTranslationXs = new float[childCount];
15590280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        mNewTranslationYs = new float[childCount];
1560440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        mNewScaleXs = new float[childCount];
1561440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        mNewScaleYs = new float[childCount];
1562440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        mNewBackgroundAlphas = new float[childCount];
1563440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        mNewBackgroundAlphaMultipliers = new float[childCount];
15646569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mNewAlphas = new float[childCount];
1565440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        mNewRotationYs = new float[childCount];
1566440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
1567440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
1568440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    public void changeState(State shrinkState) {
1569440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        changeState(shrinkState, true);
1570440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
1571440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
1572440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    void changeState(final State state, boolean animated) {
1573440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        changeState(state, animated, 0);
1574440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    }
1575440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
1576440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy    void changeState(final State state, boolean animated, int delay) {
15770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        if (mState == state) {
15780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            return;
1579440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        }
1580440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        if (mFirstLayout) {
1581440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            // (mFirstLayout == "first layout has not happened yet")
1582440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            // cancel any pending shrinks that were set earlier
1583440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            mSwitchStateAfterFirstLayout = false;
1584440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            mStateAfterFirstLayout = state;
1585440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy            return;
1586440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        }
1587440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
1588440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        // Initialize animation arrays for the first time if necessary
1589440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        initAnimationArrays();
1590440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy
1591440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        // Cancel any running transition animations
1592440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy        if (mAnimator != null) mAnimator.cancel();
15936569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        mAnimator = new AnimatorSet();
1594a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
1595580e27748137ff08599aa719d106b31215a28353Winson Chung        // Stop any scrolling, move to the current page right away
1596580e27748137ff08599aa719d106b31215a28353Winson Chung        setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage);
1597580e27748137ff08599aa719d106b31215a28353Winson Chung
1598580e27748137ff08599aa719d106b31215a28353Winson Chung        final State oldState = mState;
1599580e27748137ff08599aa719d106b31215a28353Winson Chung        final boolean oldStateIsNormal = (oldState == State.NORMAL);
1600580e27748137ff08599aa719d106b31215a28353Winson Chung        final boolean oldStateIsSmall = (oldState == State.SMALL);
1601580e27748137ff08599aa719d106b31215a28353Winson Chung        mState = state;
1602580e27748137ff08599aa719d106b31215a28353Winson Chung        final boolean stateIsNormal = (state == State.NORMAL);
1603580e27748137ff08599aa719d106b31215a28353Winson Chung        final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
1604580e27748137ff08599aa719d106b31215a28353Winson Chung        final boolean stateIsSmall = (state == State.SMALL);
1605580e27748137ff08599aa719d106b31215a28353Winson Chung        float finalScaleFactor = 1.0f;
1606580e27748137ff08599aa719d106b31215a28353Winson Chung        float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
1607580e27748137ff08599aa719d106b31215a28353Winson Chung        float translationX = 0;
1608580e27748137ff08599aa719d106b31215a28353Winson Chung        float translationY = 0;
1609580e27748137ff08599aa719d106b31215a28353Winson Chung        boolean zoomIn = true;
1610580e27748137ff08599aa719d106b31215a28353Winson Chung
1611580e27748137ff08599aa719d106b31215a28353Winson Chung        if (state != State.NORMAL) {
1612580e27748137ff08599aa719d106b31215a28353Winson Chung            finalScaleFactor = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
1613580e27748137ff08599aa719d106b31215a28353Winson Chung            if (oldStateIsNormal && stateIsSmall) {
1614580e27748137ff08599aa719d106b31215a28353Winson Chung                zoomIn = false;
1615580e27748137ff08599aa719d106b31215a28353Winson Chung                setLayoutScale(finalScaleFactor);
1616580e27748137ff08599aa719d106b31215a28353Winson Chung                updateChildrenLayersEnabled();
1617580e27748137ff08599aa719d106b31215a28353Winson Chung            } else {
1618580e27748137ff08599aa719d106b31215a28353Winson Chung                finalBackgroundAlpha = 1.0f;
1619a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                setLayoutScale(finalScaleFactor);
1620a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            }
1621a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        } else {
1622a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            setLayoutScale(1.0f);
162368846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung        }
1624a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
1625a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        final int duration = zoomIn ?
1626a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                getResources().getInteger(R.integer.config_workspaceUnshrinkTime) :
1627a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
1628a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        for (int i = 0; i < getChildCount(); i++) {
1629a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            final CellLayout cl = (CellLayout) getChildAt(i);
1630a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            float rotation = 0f;
1631a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            float initialAlpha = cl.getAlpha();
1632580e27748137ff08599aa719d106b31215a28353Winson Chung            float finalAlphaMultiplierValue = 1f;
1633580e27748137ff08599aa719d106b31215a28353Winson Chung            float finalAlpha = (!mFadeInAdjacentScreens || stateIsSpringLoaded ||
1634580e27748137ff08599aa719d106b31215a28353Winson Chung                    (i == mCurrentPage)) ? 1f : 0f;
1635580e27748137ff08599aa719d106b31215a28353Winson Chung
1636580e27748137ff08599aa719d106b31215a28353Winson Chung            // Determine the pages alpha during the state transition
1637580e27748137ff08599aa719d106b31215a28353Winson Chung            if ((oldStateIsSmall && stateIsNormal) ||
1638580e27748137ff08599aa719d106b31215a28353Winson Chung                (oldStateIsNormal && stateIsSmall)) {
1639580e27748137ff08599aa719d106b31215a28353Winson Chung                // To/from workspace - only show the current page unless the transition is not
1640580e27748137ff08599aa719d106b31215a28353Winson Chung                //                     animated and the animation end callback below doesn't run
1641580e27748137ff08599aa719d106b31215a28353Winson Chung                if (i == mCurrentPage || !animated) {
1642580e27748137ff08599aa719d106b31215a28353Winson Chung                    finalAlpha = 1f;
1643580e27748137ff08599aa719d106b31215a28353Winson Chung                    finalAlphaMultiplierValue = 0f;
1644580e27748137ff08599aa719d106b31215a28353Winson Chung                } else {
1645580e27748137ff08599aa719d106b31215a28353Winson Chung                    initialAlpha = 0f;
1646580e27748137ff08599aa719d106b31215a28353Winson Chung                    finalAlpha = 0f;
1647580e27748137ff08599aa719d106b31215a28353Winson Chung                }
1648a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            }
1649a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung
1650a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            // Update the rotation of the screen (don't apply rotation on Phone UI)
1651a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            if (LauncherApplication.isScreenLarge()) {
165268846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                if (i < mCurrentPage) {
165368846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    rotation = WORKSPACE_ROTATION;
165468846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                } else if (i > mCurrentPage) {
165568846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    rotation = -WORKSPACE_ROTATION;
165668846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                }
1657a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            }
165868846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung
165968846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            // If the screen is not xlarge, then don't rotate the CellLayouts
166068846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            // NOTE: If we don't update the side pages alpha, then we should not hide the side
166168846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            //       pages. see unshrink().
166268846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            if (LauncherApplication.isScreenLarge()) {
1663a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
166468846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            }
1665580e27748137ff08599aa719d106b31215a28353Winson Chung
1666a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mOldAlphas[i] = initialAlpha;
1667a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            mNewAlphas[i] = finalAlpha;
1668a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            if (animated) {
1669a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                mOldTranslationXs[i] = cl.getTranslationX();
1670580e27748137ff08599aa719d106b31215a28353Winson Chung                mOldTranslationYs[i] = cl.getTranslationY();
1671a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                mOldScaleXs[i] = cl.getScaleX();
1672a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                mOldScaleYs[i] = cl.getScaleY();
1673a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
1674a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                mOldBackgroundAlphaMultipliers[i] = cl.getBackgroundAlphaMultiplier();
167555cef262f97749300c2f6e764da0b00cbcb78879Winson Chung                mOldRotationYs[i] = cl.getRotationY();
167668846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung
167768846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                mNewTranslationXs[i] = translationX;
167868846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                mNewTranslationYs[i] = translationY;
167968846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                mNewScaleXs[i] = finalScaleFactor;
168068846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                mNewScaleYs[i] = finalScaleFactor;
168168846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                mNewBackgroundAlphas[i] = finalBackgroundAlpha;
1682580e27748137ff08599aa719d106b31215a28353Winson Chung                mNewBackgroundAlphaMultipliers[i] = finalAlphaMultiplierValue;
1683580e27748137ff08599aa719d106b31215a28353Winson Chung                mNewRotationYs[i] = rotation;
1684580e27748137ff08599aa719d106b31215a28353Winson Chung            } else {
1685580e27748137ff08599aa719d106b31215a28353Winson Chung                cl.setTranslationX(translationX);
1686580e27748137ff08599aa719d106b31215a28353Winson Chung                cl.setTranslationY(translationY);
1687580e27748137ff08599aa719d106b31215a28353Winson Chung                cl.setScaleX(finalScaleFactor);
1688580e27748137ff08599aa719d106b31215a28353Winson Chung                cl.setScaleY(finalScaleFactor);
1689580e27748137ff08599aa719d106b31215a28353Winson Chung                cl.setBackgroundAlpha(finalBackgroundAlpha);
169068846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                cl.setBackgroundAlphaMultiplier(finalAlphaMultiplierValue);
169168846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                cl.setAlpha(finalAlpha);
169268846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                cl.setRotationY(rotation);
1693580e27748137ff08599aa719d106b31215a28353Winson Chung                mChangeStateAnimationListener.onAnimationEnd(null);
169468846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            }
169568846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung        }
169668846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung
169768846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung        if (animated) {
169868846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            ValueAnimator animWithInterpolator =
169968846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1700580e27748137ff08599aa719d106b31215a28353Winson Chung
170168846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            if (zoomIn) {
170268846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                animWithInterpolator.setInterpolator(mZoomInInterpolator);
170368846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            }
170468846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung
170568846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung            animWithInterpolator.addListener(new AnimatorListenerAdapter() {
170668846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                @Override
170768846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                public void onAnimationEnd(android.animation.Animator animation) {
170868846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    // The above code to determine initialAlpha and finalAlpha will ensure that only
170968846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    // the current page is visible during (and subsequently, after) the transition
171068846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    // animation.  If fade adjacent pages is disabled, then re-enable the page
171168846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    // visibility after the transition animation.
171268846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                    if (!mFadeInAdjacentScreens && stateIsNormal && oldStateIsSmall) {
171368846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung                        for (int i = 0; i < getChildCount(); i++) {
171455cef262f97749300c2f6e764da0b00cbcb78879Winson Chung                            final CellLayout cl = (CellLayout) getChildAt(i);
1715a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                            cl.setAlpha(1f);
1716a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                        }
1717a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    }
1718580e27748137ff08599aa719d106b31215a28353Winson Chung                }
1719a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            });
1720a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung            animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
1721a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                public void onAnimationUpdate(float a, float b) {
1722a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    mTransitionProgress = b;
1723a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    if (b == 0f) {
1724a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                        // an optimization, but not required
1725a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                        return;
1726a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    }
1727a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    invalidate();
1728a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung                    for (int i = 0; i < getChildCount(); i++) {
17294516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        final CellLayout cl = (CellLayout) getChildAt(i);
17304516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.fastInvalidate();
17314516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastTranslationX(a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
17324516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastTranslationY(a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
17334516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
17344516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
17354516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastBackgroundAlpha(
17364516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                                a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
17374516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setBackgroundAlphaMultiplier(a * mOldBackgroundAlphaMultipliers[i] +
17384516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                                b * mNewBackgroundAlphaMultipliers[i]);
17394516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
17404516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    }
17414516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    syncChildrenLayersEnabledOnVisiblePages();
17424516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                }
17434516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            });
17444516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
1745d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka            ValueAnimator rotationAnim =
17464516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
17474516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            rotationAnim.setInterpolator(new DecelerateInterpolator(2.0f));
17484516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            rotationAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
17494516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                public void onAnimationUpdate(float a, float b) {
17504516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    if (b == 0f) {
17514516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        // an optimization, but not required
17524516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        return;
17534516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    }
17544516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    for (int i = 0; i < getChildCount(); i++) {
17554516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        final CellLayout cl = (CellLayout) getChildAt(i);
17564516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
17574516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    }
17584516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                }
17594516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            });
17604516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
17614516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            mAnimator.playTogether(animWithInterpolator, rotationAnim);
17624516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            mAnimator.setStartDelay(delay);
17634516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            // If we call this when we're not animated, onAnimationEnd is never called on
17644516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            // the listener; make sure we only use the listener when we're actually animating
17654516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            mAnimator.addListener(mChangeStateAnimationListener);
17664516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            mAnimator.start();
17674516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        }
17684516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
17694516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        if (stateIsSpringLoaded) {
17704516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            // Right now we're covered by Apps Customize
17714516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            // Show the background gradient immediately, so the gradient will
17724516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            // be showing once AppsCustomize disappears
17734516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            animateBackgroundGradient(getResources().getInteger(
17744516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
17750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        } else {
17760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            // Fade the background gradient away
17774516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            animateBackgroundGradient(0f, true);
17784516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        }
17794516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        syncChildrenLayersEnabledOnVisiblePages();
17804516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    }
17814516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
17824516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    /**
17834516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * Draw the View v into the given Canvas.
17844516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     *
17854516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * @param v the view to draw
17864516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * @param destCanvas the canvas to draw on
17874516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * @param padding the horizontal and vertical padding to use when drawing
17884516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     */
17894516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
17904516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        final Rect clipRect = mTempRect;
17914516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        v.getDrawingRect(clipRect);
17924516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
17934516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        boolean textVisible = false;
17944516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
17954516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        destCanvas.save();
17964516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        if (v instanceof TextView && pruneToDrawable) {
17974516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            Drawable d = ((TextView) v).getCompoundDrawables()[1];
17984516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
17994516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            destCanvas.translate(padding / 2, padding / 2);
18004516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            d.draw(destCanvas);
18014516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        } else {
18024516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            if (v instanceof FolderIcon) {
18034516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                // For FolderIcons the text can bleed into the icon area, and so we need to
18044516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                // hide the text completely (which can't be achieved by clipping).
18054516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                if (((FolderIcon) v).getTextVisible()) {
18064516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    ((FolderIcon) v).setTextVisible(false);
18074516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    textVisible = true;
18084516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                }
18094516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            } else if (v instanceof BubbleTextView) {
18104516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                final BubbleTextView tv = (BubbleTextView) v;
18114516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
18124516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        tv.getLayout().getLineTop(0);
18134516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            } else if (v instanceof TextView) {
18144516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                final TextView tv = (TextView) v;
1815a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka                clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
18164516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                        tv.getLayout().getLineTop(0);
18174516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            }
18184516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
18194516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            destCanvas.clipRect(clipRect, Op.REPLACE);
18204516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            v.draw(destCanvas);
18214516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
18224516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            // Restore text visibility of FolderIcon if necessary
18234516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            if (textVisible) {
18244516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                ((FolderIcon) v).setTextVisible(true);
18254516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            }
18264516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        }
18274516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        destCanvas.restore();
18284516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    }
1829a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka
1830a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    /**
18310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * Returns a new bitmap to show when the given View is being dragged around.
18324516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * Responsibility for the bitmap is transferred to the caller.
1833a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka     */
1834a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
1835a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
18364516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        Bitmap b;
18374516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
18384516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        if (v instanceof TextView) {
1839a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka            Drawable d = ((TextView) v).getCompoundDrawables()[1];
18404516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
18414516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
18420280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        } else {
18434516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka            b = Bitmap.createBitmap(
18444516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                    v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
18454516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        }
18464516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
18474516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        canvas.setBitmap(b);
18484516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        drawDragView(v, canvas, padding, true);
18494516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
18504516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        canvas.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY);
18514516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        canvas.setBitmap(null);
18524516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
18534516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        return b;
18544516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    }
18554516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
18564516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    /**
18574516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
18584516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     * Responsibility for the bitmap is transferred to the caller.
18594516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka     */
18604516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka    private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
18614516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
18624516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        final Bitmap b = Bitmap.createBitmap(
18634516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka                v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
18644516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka
18650280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        canvas.setBitmap(b);
1866a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        drawDragView(v, canvas, padding, true);
1867a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
18684516c11039d77066281f69f9b5d30fdaf1bc05aeMichael Jurka        canvas.setBitmap(null);
1869a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        return b;
1870a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    }
1871a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka
1872a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    /**
1873440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
1874440c360bc395c43683fa9ca226e59f9e35f9e926Patrick Dubroy     * Responsibility for the bitmap is transferred to the caller.
1875d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka     */
1876d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
1877d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka            Paint alphaClipPaint) {
1878d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
1879d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
1880d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        canvas.setBitmap(b);
1881d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1882d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
1883d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
1884d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka                (h - padding) / (float) orig.getHeight());
1885d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        int scaledWidth = (int) (scaleFactor * orig.getWidth());
1886d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        int scaledHeight = (int) (scaleFactor * orig.getHeight());
1887d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
1888d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1889d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        // center the image
1890d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
1891d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1892d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        canvas.drawBitmap(orig, src, dst, null);
1893d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
1894d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka                alphaClipPaint);
1895d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        canvas.setBitmap(null);
1896d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1897d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        return b;
1898d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    }
1899d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1900d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    /**
1901d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka     * Creates a drag outline to represent a drop (that we don't have the actual information for
1902d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka     * yet).  May be changed in the future to alter the drop outline slightly depending on the
1903d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka     * clip description mime data.
1904d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka     */
1905d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    private Bitmap createExternalDragOutline(Canvas canvas, int padding) {
1906d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        Resources r = getResources();
1907d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final int outlineColor = r.getColor(android.R.color.holo_blue_light);
1908a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka        final int iconWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);
1909d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final int iconHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height);
1910d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final int rectRadius = r.getDimensionPixelSize(R.dimen.external_drop_icon_rect_radius);
1911d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final int inset = (int) (Math.min(iconWidth, iconHeight) * 0.2f);
1912d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final Bitmap b = Bitmap.createBitmap(
1913d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka                iconWidth + padding, iconHeight + padding, Bitmap.Config.ARGB_8888);
1914d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1915d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        canvas.setBitmap(b);
1916d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset),
1917d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka                rectRadius, rectRadius, mExternalDragOutlinePaint);
1918d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
1919d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        canvas.setBitmap(null);
1920d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        return b;
1921d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    }
1922d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1923a63c452f5bd491ba9b28c332ccedc6c6c7e2f3ccMichael Jurka    void startDrag(CellLayout.CellInfo cellInfo) {
19241262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        View child = cellInfo.cell;
1925d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1926d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        // Make sure the drag was started by a long press as opposed to a long click.
1927d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        if (!child.isInTouchMode()) {
1928d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka            return;
1929a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung        }
1930d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
19311262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mDragInfo = cellInfo;
19326569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        child.setVisibility(GONE);
1933d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1934d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        child.clearFocus();
1935d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        child.setPressed(false);
1936d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1937d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final Canvas canvas = new Canvas();
1938d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1939d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        // We need to add extra padding to the bitmap to make room for the glow effect
1940d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
1941d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
1942d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka        // The outline is used to visualize where the item will land if dropped
19431262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        mDragOutline = createDragOutline(child, canvas, bitmapPadding);
1944976ebec9e31643d3513f9f0de2b433d9aa186ce9Patrick Dubroy        beginDragShared(child, this);
194531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
194631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1947aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    public void beginDragShared(View child, DragSource source) {
1948aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        Resources r = getResources();
19496569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy
19500280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        // We need to add extra padding to the bitmap to make room for the glow effect
19516569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
19521262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
19531262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        // The drag bitmap follows the touch point around on the screen
19541262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        final Bitmap b = createDragBitmap(child, new Canvas(), bitmapPadding);
195508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy
195631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final int bmpWidth = b.getWidth();
195731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1958aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
1959aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        final int dragLayerX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
196031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int dragLayerY = mTempXY[1] - bitmapPadding / 2;
196131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
1962aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        Point dragVisualizeOffset = null;
1963a34abf8c781485b788fddacb352d586bffca886cWinson Chung        Rect dragRect = null;
1964a34abf8c781485b788fddacb352d586bffca886cWinson Chung        if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
1965a34abf8c781485b788fddacb352d586bffca886cWinson Chung            int iconSize = r.getDimensionPixelSize(R.dimen.app_icon_size);
1966a34abf8c781485b788fddacb352d586bffca886cWinson Chung            int iconPaddingTop = r.getDimensionPixelSize(R.dimen.app_icon_padding_top);
1967a34abf8c781485b788fddacb352d586bffca886cWinson Chung            int top = child.getPaddingTop();
1968a34abf8c781485b788fddacb352d586bffca886cWinson Chung            int left = (bmpWidth - iconSize) / 2;
1969a34abf8c781485b788fddacb352d586bffca886cWinson Chung            int right = left + iconSize;
1970a34abf8c781485b788fddacb352d586bffca886cWinson Chung            int bottom = top + iconSize;
19712b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            dragLayerY += top;
19722b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            // Note: The drag region is used to calculate drag layer offsets, but the
19732b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
19742b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            dragVisualizeOffset = new Point(-bitmapPadding / 2, iconPaddingTop - bitmapPadding / 2);
19752b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            dragRect = new Rect(left, top, right, bottom);
19762b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        } else if (child instanceof FolderIcon) {
19772b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            int previewSize = r.getDimensionPixelSize(R.dimen.folder_preview_size);
19782b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            dragRect = new Rect(0, 0, child.getWidth(), previewSize);
19792b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        }
19802b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy
19810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        // Clear the pressed state if necessary
198255cef262f97749300c2f6e764da0b00cbcb78879Winson Chung        if (child instanceof BubbleTextView) {
19832b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            BubbleTextView icon = (BubbleTextView) child;
19842b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy            icon.clearPressedOrFocusedBackground();
19850280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
19862b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy
19872b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy        mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
19882b9ff37edb3f5965559b3ff7d37e418b4a2917a1Patrick Dubroy                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect);
19890280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        b.recycle();
19904be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
19914be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
1992aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, int screen,
1993aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
19940280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
19950280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
19960280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        final int[] cellXY = new int[2];
19970280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
19980280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
19990280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen, cellXY[0],
20000280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                cellXY[1]);
20010280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
20020280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
20030280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public boolean transitionStateShouldAllowDrop() {
200455cef262f97749300c2f6e764da0b00cbcb78879Winson Chung        return (!isSwitchingState() || mTransitionProgress > 0.5f);
20050280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    }
20060280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
20070280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    /**
20080280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     * {@inheritDoc}
20090280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka     */
20100280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka    public boolean acceptDrop(DragObject d) {
20110280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        // If it's an external drop (e.g. from All Apps), check if it should be accepted
20120280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        if (d.dragSource != this) {
20130280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            // Don't accept the drop if we're not over a screen at time of drop
20140280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (mDragTargetLayout == null) {
20150280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                return false;
201618014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka            }
20170280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (!transitionStateShouldAllowDrop()) return false;
20180280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
20190280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
20200280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    d.dragView, mDragViewVisualCenter);
202131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
202231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // We want the point to be mapped to the dragTarget.
2023af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
202431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
202531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
202631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
202731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
2028e48e7c1a62e6a367803dca62c8fce9de57121b0fJoe Onorato
202931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int spanX = 1;
2030aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            int spanY = 1;
203131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            View ignoreView = null;
2032aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            if (mDragInfo != null) {
2033aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                final CellLayout.CellInfo dragCellInfo = mDragInfo;
203431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                spanX = dragCellInfo.spanX;
203531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                spanY = dragCellInfo.spanY;
203631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                ignoreView = dragCellInfo.cell;
2037c9a961952d1a057029874f8426b90181f6876034Michael Jurka            } else {
2038af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
203931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                spanX = dragInfo.spanX;
20400280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                spanY = dragInfo.spanY;
204131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
204231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
20436569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
20446569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                    (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell);
20450280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell, true)) {
20466569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                return true;
2047bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung            }
2048bbc60d8e79416e37cbede55c159bf6aaa6c171d5Winson Chung            if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
2049aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    mTargetCell)) {
2050aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                return true;
2051aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            }
205218014791be2e3f41080f0bf621c618e3f096c5c7Michael Jurka
20536569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy            // Don't accept the drop if there's no room for the item
205431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (!mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
2055af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                // Don't show the message if we are dropping on the AllApps button and the hotseat
20560280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                // is full
2057aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                if (mTargetCell != null && mLauncher.isHotseatLayout(mDragTargetLayout)) {
2058af44209bfa60da3c7ab49b7f508f9effd316ee41Michael Jurka                    Hotseat hotseat = mLauncher.getHotseat();
205931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    if (Hotseat.isAllAppsButtonRank(
2060aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                            hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
206170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                        return false;
206270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                    }
206370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                }
206470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
206570864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey                mLauncher.showOutOfSpaceMessage();
2066d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka                return false;
2067d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka            }
20680142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        }
206970864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        return true;
207070864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    }
207131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
207270864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey    boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
20730280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            boolean considerTimeout) {
20740280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
20750280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
20760280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        boolean hasntMoved = false;
20770280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        if (mDragInfo != null) {
20780280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
20790280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            hasntMoved = (mDragInfo.cellX == targetCell[0] &&
20800280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka                    mDragInfo.cellY == targetCell[1]) && (cellParent == target);
20810280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        }
20820280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
208370864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey        if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
208470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey            return false;
20856a1435d78d5133b1f37274c4d358bf6d22e10229Michael Jurka        }
208670864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
20876569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy        boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
20885f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        boolean willBecomeShortcut =
20895f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka                (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
20906569f2c80e179c2f8ed73dae6b01d971ec20f005Patrick Dubroy                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
209170864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
20920280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka        return (aboveShortcut && willBecomeShortcut);
2093fc9f07d42511231a26fa182c32e2efaa0f41d478Michael Jurka    }
209470864289fba6daf07b8de98524cdfb765a62552dJeff Sharkey
2095aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell) {
20968f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
20978f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        if (dropOverView instanceof FolderIcon) {
20988f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            FolderIcon fi = (FolderIcon) dropOverView;
20998f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            if (fi.acceptDrop(dragInfo)) {
21000142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                return true;
21018f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy            }
21028f86ddcb90063a56c25c9c782316574bc4e5dd93Patrick Dubroy        }
210331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return false;
210431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
210531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
210631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
210700acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato            int[] targetCell, boolean external, DragView dragView, Runnable postAnimationRunnable) {
210800acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato        View v = target.getChildAt(targetCell[0], targetCell[1]);
210931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean hasntMoved = false;
211031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (mDragInfo != null) {
211131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2112aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            hasntMoved = (mDragInfo.cellX == targetCell[0] &&
211331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    mDragInfo.cellY == targetCell[1]) && (cellParent == target);
211431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
211531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
211600acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato        if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
211700acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato        mCreateUserFolderOnDrop = false;
211800acb123c5100f06b8e89e8ec8978ebafc6f6d26Joe Onorato        final int screen = (targetCell == null) ? mDragInfo.screen : indexOfChild(target);
2119aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
212031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2121ce34a9768b01115def95f000a6a8f35870f10d3aPatrick Dubroy        boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2122cd68ff5b88de9b5136ff5a9ef698e4db2fc5db66Patrick Dubroy
212331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (aboveShortcut && willBecomeShortcut) {
212431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
21254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
212631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            // if the drag started here, we need to remove it from the workspace
212731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (!external) {
212831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
21290280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            }
21300280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka
21310280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            Rect folderLocation = new Rect();
21320280c3be4d9f8fc6fdf015b7ecd276eb26f76f2dMichael Jurka            float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
21330142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            target.removeView(v);
21340142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
21350142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            FolderIcon fi =
21360142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);
21370142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            destInfo.cellX = -1;
21380142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            destInfo.cellY = -1;
21390142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            sourceInfo.cellX = -1;
214031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            sourceInfo.cellY = -1;
2141c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka
21420142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            // If the dragView is null, we can't animate
214331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            boolean animate = dragView != null;
214431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (animate) {
214531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
21460142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                        postAnimationRunnable);
214731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
2148c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka                fi.addItem(destInfo);
21490142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka                fi.addItem(sourceInfo);
215031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
215131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return true;
2152138a04170d964da9cdec228e95b976875ae52481Karl Rosaen        }
21531262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        return false;
21541262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
2155d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka
2156d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka    boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
215754fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy            DragObject d, boolean external) {
215854fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
215954fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy        if (dropOverView instanceof FolderIcon) {
216054fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy            FolderIcon fi = (FolderIcon) dropOverView;
216154fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy            if (fi.acceptDrop(d.dragInfo)) {
216254fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy                fi.onDrop(d);
216354fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy
21641262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy                // if the drag started here, we need to remove it from the workspace
21650207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy                if (!external) {
21660207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy                    getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
21670207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy                }
21680207c522472d7252f146f4d39efa456ca5248c6bPatrick Dubroy                return true;
2169d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka            }
21701262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        }
21711262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        return false;
21721262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy    }
217308ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy
217408ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy    public void onDrop(DragObject d) {
217508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
217608ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy                mDragViewVisualCenter);
217708ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy
217808ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        // We want the point to be mapped to the dragTarget.
217908ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        if (mDragTargetLayout != null) {
21801262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
21811262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy                mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2182d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka            } else {
2183d718d6a7ba424b600052a147e5633a201a04ccb7Michael Jurka                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
218454fa3b95557c283976e8c1aa8a157b460b0b4513Patrick Dubroy            }
218508ae2ec4847a971ad1b19c163e3a0d6307a8ed72Patrick Dubroy        }
21861262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
21871262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy        CellLayout dropTargetLayout = mDragTargetLayout;
21881262e369484ce7f2565655ed80e6299232c70bd7Patrick Dubroy
218931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int snapScreen = -1;
2190dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        if (d.dragSource != this) {
219131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
219231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    (int) mDragViewVisualCenter[1] };
219331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
219431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else if (mDragInfo != null) {
219531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            final View cell = mDragInfo.cell;
219631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
219731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (dropTargetLayout != null) {
219831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // Move internally
2199aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
220031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
220131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                long container = hasMovedIntoHotseat ?
220231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        LauncherSettings.Favorites.CONTAINER_HOTSEAT :
220331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        LauncherSettings.Favorites.CONTAINER_DESKTOP;
220431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int screen = (mTargetCell[0] < 0) ?
220531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        mDragInfo.screen : indexOfChild(dropTargetLayout);
220631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
220731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
220831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // First we find the cell nearest to point at which the item is
220931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // dropped, without any consideration to whether there is an item there.
221031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
221131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
221231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // If the item being dropped is a shortcut and the nearest drop
221331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // cell also contains a shortcut, then create a folder with the two shortcuts.
221431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
221531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        dropTargetLayout, mTargetCell, false, d.dragView, null)) {
221631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return;
221731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
221831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
221931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, d, false)) {
222031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                    return;
222131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                }
222231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
222331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                // Aside from the special case where we're dropping a shortcut onto a shortcut,
222464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                // we need to find the nearest cell location that is vacant
2225dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
22265c16f3ecd6b47bff3abbe40deb3d39c66a3b0012Romain Guy                        (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
2227629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                        dropTargetLayout, mTargetCell);
2228574d20ec84551370987dde530c27ec493bdef564Romain Guy
222964e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                if (mCurrentPage != screen && !hasMovedIntoHotseat) {
223064e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                    snapScreen = screen;
223164e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                    snapToPage(screen);
223264e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                }
223364e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato
223464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                if (mTargetCell[0] >= 0 && mTargetCell[1] >= 0) {
2235dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka                    if (hasMovedLayouts) {
223631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                        // Reparent the view
2237629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                        getParentCellLayoutForView(cell).removeView(cell);
2238629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                        addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
2239629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                mDragInfo.spanX, mDragInfo.spanY);
2240629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    }
2241629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy
2242629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    // update the item's position after drop
2243aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    final ItemInfo info = (ItemInfo) cell.getTag();
2244629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
2245629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
2246629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    lp.cellX = mTargetCell[0];
2247629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    lp.cellY = mTargetCell[1];
2248aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                    cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
22490589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato                            mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
22500589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato
2251629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2252629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                            cell instanceof LauncherAppWidgetHostView) {
2253aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                        final CellLayout cellLayout = dropTargetLayout;
225464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        // We post this call so that the widget has a chance to be placed
225564e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        // in its final location
225664e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato
225764e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
225864e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
225964e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        if (pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
226064e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                            final Runnable resizeRunnable = new Runnable() {
2261629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                public void run() {
2262629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                    DragLayer dragLayer = mLauncher.getDragLayer();
2263629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                    dragLayer.addResizeFrame(info, hostView, cellLayout);
22640589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato                                }
22650589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato                            };
2266629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                            post(new Runnable() {
2267629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                public void run() {
2268aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                                    if (!isPageMoving()) {
2269629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                        resizeRunnable.run();
22700589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato                                    } else {
2271629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                        mDelayedResizeRunnable = resizeRunnable;
2272629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                                    }
2273aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                                }
227464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                            });
227564e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        }
227664e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                    }
227764e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato
227873013bf94f49ffbacba2b8300f6a2dd4eeebbd13Brad Fitzpatrick                    LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, lp.cellX,
227964e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                            lp.cellY);
228064e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                }
228164e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            }
2282629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy
2283629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            final CellLayout parent = (CellLayout) cell.getParent().getParent();
2284aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung
2285629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            // Prepare it to be animated into its new position
2286629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            // This must be called after the view has been re-parented
2287629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            final Runnable disableHardwareLayersRunnable = new Runnable() {
2288aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                @Override
2289aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung                public void run() {
2290629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    mAnimatingViewIntoPlace = false;
2291629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                    updateChildrenLayersEnabled();
2292629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                }
2293629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            };
2294629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            mAnimatingViewIntoPlace = true;
2295629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            if (d.dragView.hasDrawn()) {
2296629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy                int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
2297f11079b11e98912ed56546eaa300efd06c6eb917Joe Onorato                setFinalScrollForPageChange(snapScreen);
229864e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
229964e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                        disableHardwareLayersRunnable);
230064e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                resetFinalScrollForPageChange(snapScreen);
2301aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            } else {
230264e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato                cell.setVisibility(VISIBLE);
230364e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            }
2304629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            parent.onDropChild(cell);
2305629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy        }
2306629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy    }
2307629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy
2308629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy    public void setFinalScrollForPageChange(int screen) {
2309e1cc6c3745c9ba721b6ab888d74c9d9e1d13ea6aDaniel Sandler        if (screen >= 0) {
231064e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            mSavedScrollX = getScrollX();
231164e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            CellLayout cl = (CellLayout) getChildAt(screen);
231264e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            mSavedTranslationX = cl.getTranslationX();
2313aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung            mSavedRotationY = cl.getRotationY();
231464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            final int newX = getChildOffset(screen) - getRelativeChildOffset(screen);
231564e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            setScrollX(newX);
2316629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            cl.setTranslationX(0f);
2317574d20ec84551370987dde530c27ec493bdef564Romain Guy            cl.setRotationY(0f);
2318574d20ec84551370987dde530c27ec493bdef564Romain Guy        }
2319aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
2320629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy
2321629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy    public void resetFinalScrollForPageChange(int screen) {
2322629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy        if (screen >= 0) {
2323629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            CellLayout cl = (CellLayout) getChildAt(screen);
2324629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            setScrollX(mSavedScrollX);
2325629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            cl.setTranslationX(mSavedTranslationX);
2326629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy            cl.setRotationY(mSavedRotationY);
2327574d20ec84551370987dde530c27ec493bdef564Romain Guy        }
2328aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung    }
2329629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy
2330629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy    public void getViewLocationRelativeToSelf(View v, int[] location) {
2331629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy        getLocationInWindow(location);
23325c16f3ecd6b47bff3abbe40deb3d39c66a3b0012Romain Guy        int x = location[0];
233331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int y = location[1];
2334629de3ef739883c0962423cc0c3a26299f162d3dRomain Guy
233531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        v.getLocationInWindow(location);
233631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int vX = location[0];
2337f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        int vY = location[1];
233864e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato
2339dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        location[0] = vX - x;
2340dee0589388ba0f6373912e18bf86243282fb3b9bMichael Jurka        location[1] = vY - y;
2341f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project    }
2342f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project
2343f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project    public void onDragEnter(DragObject d) {
2344f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        if (mDragTargetLayout != null) {
2345f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project            mDragTargetLayout.setIsDragOverlapping(false);
23460589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato            mDragTargetLayout.onDragExit();
23470589f0f66ce498512c6ee47482c649d88294c9d0Joe Onorato        }
2348f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        mDragTargetLayout = getCurrentDropLayout();
2349f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        mDragTargetLayout.setIsDragOverlapping(true);
2350f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        mDragTargetLayout.onDragEnter();
2351f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project
2352f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
2353f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        // don't need to show the outlines
235464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato        if (LauncherApplication.isScreenLarge()) {
235564e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato            showOutlines();
2356aafa03cbb925c74be1c13f8bb99d928be429e62fWinson Chung        }
235764e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato    }
235864e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato
235964e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato    private void doDragExit(DragObject d) {
236064e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato        // Clean up folders
236164e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato        cleanupFolderCreation(d);
236264e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato
236364e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato        // Reset the scroll area and previous drag target
236464e6be78dc72e1a89fe8fb95c502586f9260df28Joe Onorato        onResetScrollArea();
2365f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project
2366f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        if (mDragTargetLayout != null) {
2367f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project            mDragTargetLayout.setIsDragOverlapping(false);
2368f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project            mDragTargetLayout.onDragExit();
2369f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        }
2370f96811cdf564469a7a654a0c876288c9fd14f35eThe Android Open Source Project        mLastDragOverView = null;
237114f122bf847e50a3e7730ccbe57abc25d086a01bJoe Onorato        mSpringLoadedDragController.cancel();
2372c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka
2373c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka        if (!mIsPageMoving) {
2374c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka            hideOutlines();
2375c0e8fcae492730210b2b8b6d5bb3d65636bad245Michael Jurka        }
2376c45b16862918a0c6c253bba12032165b030565afJoe Onorato    }
23770142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
2378c45b16862918a0c6c253bba12032165b030565afJoe Onorato    public void onDragExit(DragObject d) {
23790142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        doDragExit(d);
238031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
238131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
23828a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy    public DropTarget getDropTargetDelegate(DragObject d) {
23838a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy        return null;
23848a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy    }
23850142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka
23860142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    /**
23878a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy     * Tests to see if the drop will be accepted by Launcher, and if so, includes additional data
23888a73c51ee87b6d9b12daba188034889caf7a905bRomain Guy     * in the returned structure related to the widgets that match the drop (or a null list if it is
23890142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     * a shortcut drop).  If the drop is not accepted then a null structure is returned.
23900142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka     */
23910142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka    private Pair<Integer, List<WidgetMimeTypeHandlerData>> validateDrag(DragEvent event) {
239231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        final LauncherModel model = mLauncher.getModel();
23930142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final ClipDescription desc = event.getClipDescription();
23940142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka        final int mimeTypeCount = desc.getMimeTypeCount();
239531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        for (int i = 0; i < mimeTypeCount; ++i) {
23960142d49e1378a7155bcca1fb59965d9e73016dbcMichael Jurka            final String mimeType = desc.getMimeType(i);
239731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (mimeType.equals(InstallShortcutReceiver.SHORTCUT_MIMETYPE)) {
2398                return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, null);
2399            } else {
2400                final List<WidgetMimeTypeHandlerData> widgets =
2401                    model.resolveWidgetsForMimeType(mContext, mimeType);
2402                if (widgets.size() > 0) {
2403                    return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, widgets);
2404                }
2405            }
2406        }
2407        return null;
2408    }
2409
2410    /**
2411     * Global drag and drop handler
2412     */
2413    @Override
2414    public boolean onDragEvent(DragEvent event) {
2415        final ClipDescription desc = event.getClipDescription();
2416        final CellLayout layout = (CellLayout) getChildAt(mCurrentPage);
2417        final int[] pos = new int[2];
2418        layout.getLocationOnScreen(pos);
2419        // We need to offset the drag coordinates to layout coordinate space
2420        final int x = (int) event.getX() - pos[0];
2421        final int y = (int) event.getY() - pos[1];
2422
2423        switch (event.getAction()) {
2424        case DragEvent.ACTION_DRAG_STARTED: {
2425            // Validate this drag
2426            Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event);
2427            if (test != null) {
2428                boolean isShortcut = (test.second == null);
2429                if (isShortcut) {
2430                    // Check if we have enough space on this screen to add a new shortcut
2431                    if (!layout.findCellForSpan(pos, 1, 1)) {
2432                        mLauncher.showOutOfSpaceMessage();
2433                        return false;
2434                    }
2435                }
2436            } else {
2437                // Show error message if we couldn't accept any of the items
2438                Toast.makeText(mContext, mContext.getString(R.string.external_drop_widget_error),
2439                        Toast.LENGTH_SHORT).show();
2440                return false;
2441            }
2442
2443            // Create the drag outline
2444            // We need to add extra padding to the bitmap to make room for the glow effect
2445            final Canvas canvas = new Canvas();
2446            final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
2447            mDragOutline = createExternalDragOutline(canvas, bitmapPadding);
2448
2449            // Show the current page outlines to indicate that we can accept this drop
2450            showOutlines();
2451            layout.onDragEnter();
2452            layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1, null, null);
2453
2454            return true;
2455        }
2456        case DragEvent.ACTION_DRAG_LOCATION:
2457            // Visualize the drop location
2458            layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1, null, null);
2459            return true;
2460        case DragEvent.ACTION_DROP: {
2461            // Try and add any shortcuts
2462            final LauncherModel model = mLauncher.getModel();
2463            final ClipData data = event.getClipData();
2464
2465            // We assume that the mime types are ordered in descending importance of
2466            // representation. So we enumerate the list of mime types and alert the
2467            // user if any widgets can handle the drop.  Only the most preferred
2468            // representation will be handled.
2469            pos[0] = x;
2470            pos[1] = y;
2471            Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event);
2472            if (test != null) {
2473                final int index = test.first;
2474                final List<WidgetMimeTypeHandlerData> widgets = test.second;
2475                final boolean isShortcut = (widgets == null);
2476                final String mimeType = desc.getMimeType(index);
2477                if (isShortcut) {
2478                    final Intent intent = data.getItemAt(index).getIntent();
2479                    Object info = model.infoFromShortcutIntent(mContext, intent, data.getIcon());
2480                    if (info != null) {
2481                        onDropExternal(new int[] { x, y }, info, layout, false);
2482                    }
2483                } else {
2484                    if (widgets.size() == 1) {
2485                        // If there is only one item, then go ahead and add and configure
2486                        // that widget
2487                        final AppWidgetProviderInfo widgetInfo = widgets.get(0).widgetInfo;
2488                        final PendingAddWidgetInfo createInfo =
2489                                new PendingAddWidgetInfo(widgetInfo, mimeType, data);
2490                        mLauncher.addAppWidgetFromDrop(createInfo,
2491                            LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, null, pos);
2492                    } else {
2493                        // Show the widget picker dialog if there is more than one widget
2494                        // that can handle this data type
2495                        final InstallWidgetReceiver.WidgetListAdapter adapter =
2496                            new InstallWidgetReceiver.WidgetListAdapter(mLauncher, mimeType,
2497                                    data, widgets, layout, mCurrentPage, pos);
2498                        final AlertDialog.Builder builder =
2499                            new AlertDialog.Builder(mContext);
2500                        builder.setAdapter(adapter, adapter);
2501                        builder.setCancelable(true);
2502                        builder.setTitle(mContext.getString(
2503                                R.string.external_drop_widget_pick_title));
2504                        builder.setIcon(R.drawable.ic_no_applications);
2505                        builder.show();
2506                    }
2507                }
2508            }
2509            return true;
2510        }
2511        case DragEvent.ACTION_DRAG_ENDED:
2512            // Hide the page outlines after the drop
2513            layout.onDragExit();
2514            hideOutlines();
2515            return true;
2516        }
2517        return super.onDragEvent(event);
2518    }
2519
2520    /*
2521    *
2522    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2523    * coordinate space. The argument xy is modified with the return result.
2524    *
2525    */
2526   void mapPointFromSelfToChild(View v, float[] xy) {
2527       mapPointFromSelfToChild(v, xy, null);
2528   }
2529
2530   /*
2531    *
2532    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2533    * coordinate space. The argument xy is modified with the return result.
2534    *
2535    * if cachedInverseMatrix is not null, this method will just use that matrix instead of
2536    * computing it itself; we use this to avoid redundant matrix inversions in
2537    * findMatchingPageForDragOver
2538    *
2539    */
2540   void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
2541       if (cachedInverseMatrix == null) {
2542           v.getMatrix().invert(mTempInverseMatrix);
2543           cachedInverseMatrix = mTempInverseMatrix;
2544       }
2545       xy[0] = xy[0] + mScrollX - v.getLeft();
2546       xy[1] = xy[1] + mScrollY - v.getTop();
2547       cachedInverseMatrix.mapPoints(xy);
2548   }
2549
2550   /*
2551    * Maps a point from the Workspace's coordinate system to another sibling view's. (Workspace
2552    * covers the full screen)
2553    */
2554   void mapPointFromSelfToSibling(View v, float[] xy) {
2555       xy[0] = xy[0] - v.getLeft();
2556       xy[1] = xy[1] - v.getTop();
2557   }
2558
2559   /*
2560    *
2561    * Convert the 2D coordinate xy from this CellLayout's coordinate space to
2562    * the parent View's coordinate space. The argument xy is modified with the return result.
2563    *
2564    */
2565   void mapPointFromChildToSelf(View v, float[] xy) {
2566       v.getMatrix().mapPoints(xy);
2567       xy[0] -= (mScrollX - v.getLeft());
2568       xy[1] -= (mScrollY - v.getTop());
2569   }
2570
2571   static private float squaredDistance(float[] point1, float[] point2) {
2572        float distanceX = point1[0] - point2[0];
2573        float distanceY = point2[1] - point2[1];
2574        return distanceX * distanceX + distanceY * distanceY;
2575   }
2576
2577    /*
2578     *
2579     * Returns true if the passed CellLayout cl overlaps with dragView
2580     *
2581     */
2582    boolean overlaps(CellLayout cl, DragView dragView,
2583            int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
2584        // Transform the coordinates of the item being dragged to the CellLayout's coordinates
2585        final float[] draggedItemTopLeft = mTempDragCoordinates;
2586        draggedItemTopLeft[0] = dragViewX;
2587        draggedItemTopLeft[1] = dragViewY;
2588        final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
2589        draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth();
2590        draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight();
2591
2592        // Transform the dragged item's top left coordinates
2593        // to the CellLayout's local coordinates
2594        mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
2595        float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
2596        float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
2597
2598        if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
2599            // Transform the dragged item's bottom right coordinates
2600            // to the CellLayout's local coordinates
2601            mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
2602            float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
2603            float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
2604
2605            if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
2606                float overlap = (overlapRegionRight - overlapRegionLeft) *
2607                         (overlapRegionBottom - overlapRegionTop);
2608                if (overlap > 0) {
2609                    return true;
2610                }
2611             }
2612        }
2613        return false;
2614    }
2615
2616    /*
2617     *
2618     * This method returns the CellLayout that is currently being dragged to. In order to drag
2619     * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
2620     * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
2621     *
2622     * Return null if no CellLayout is currently being dragged over
2623     *
2624     */
2625    private CellLayout findMatchingPageForDragOver(
2626            DragView dragView, float originX, float originY, boolean exact) {
2627        // We loop through all the screens (ie CellLayouts) and see which ones overlap
2628        // with the item being dragged and then choose the one that's closest to the touch point
2629        final int screenCount = getChildCount();
2630        CellLayout bestMatchingScreen = null;
2631        float smallestDistSoFar = Float.MAX_VALUE;
2632
2633        for (int i = 0; i < screenCount; i++) {
2634            CellLayout cl = (CellLayout) getChildAt(i);
2635
2636            final float[] touchXy = {originX, originY};
2637            // Transform the touch coordinates to the CellLayout's local coordinates
2638            // If the touch point is within the bounds of the cell layout, we can return immediately
2639            cl.getMatrix().invert(mTempInverseMatrix);
2640            mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
2641
2642            if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
2643                    touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
2644                return cl;
2645            }
2646
2647            if (!exact) {
2648                // Get the center of the cell layout in screen coordinates
2649                final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
2650                cellLayoutCenter[0] = cl.getWidth()/2;
2651                cellLayoutCenter[1] = cl.getHeight()/2;
2652                mapPointFromChildToSelf(cl, cellLayoutCenter);
2653
2654                touchXy[0] = originX;
2655                touchXy[1] = originY;
2656
2657                // Calculate the distance between the center of the CellLayout
2658                // and the touch point
2659                float dist = squaredDistance(touchXy, cellLayoutCenter);
2660
2661                if (dist < smallestDistSoFar) {
2662                    smallestDistSoFar = dist;
2663                    bestMatchingScreen = cl;
2664                }
2665            }
2666        }
2667        return bestMatchingScreen;
2668    }
2669
2670    // This is used to compute the visual center of the dragView. This point is then
2671    // used to visualize drop locations and determine where to drop an item. The idea is that
2672    // the visual center represents the user's interpretation of where the item is, and hence
2673    // is the appropriate point to use when determining drop location.
2674    private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
2675            DragView dragView, float[] recycle) {
2676        float res[];
2677        if (recycle == null) {
2678            res = new float[2];
2679        } else {
2680            res = recycle;
2681        }
2682
2683        // First off, the drag view has been shifted in a way that is not represented in the
2684        // x and y values or the x/yOffsets. Here we account for that shift.
2685        x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
2686        y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
2687
2688        // These represent the visual top and left of drag view if a dragRect was provided.
2689        // If a dragRect was not provided, then they correspond to the actual view left and
2690        // top, as the dragRect is in that case taken to be the entire dragView.
2691        // R.dimen.dragViewOffsetY.
2692        int left = x - xOffset;
2693        int top = y - yOffset;
2694
2695        // In order to find the visual center, we shift by half the dragRect
2696        res[0] = left + dragView.getDragRegion().width() / 2;
2697        res[1] = top + dragView.getDragRegion().height() / 2;
2698
2699        return res;
2700    }
2701
2702    private boolean isDragWidget(DragObject d) {
2703        return (d.dragInfo instanceof LauncherAppWidgetInfo ||
2704                d.dragInfo instanceof PendingAddWidgetInfo);
2705    }
2706    private boolean isExternalDragWidget(DragObject d) {
2707        return d.dragSource != this && isDragWidget(d);
2708    }
2709
2710    public void onDragOver(DragObject d) {
2711        // Skip drag over events while we are dragging over side pages
2712        if (mInScrollArea) return;
2713        if (mIsSwitchingState) return;
2714
2715        Rect r = new Rect();
2716        CellLayout layout = null;
2717        ItemInfo item = (ItemInfo) d.dragInfo;
2718
2719        // Ensure that we have proper spans for the item that we are dropping
2720        if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
2721        mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
2722            d.dragView, mDragViewVisualCenter);
2723
2724        // Identify whether we have dragged over a side page
2725        if (isSmall()) {
2726            if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
2727                mLauncher.getHotseat().getHitRect(r);
2728                if (r.contains(d.x, d.y)) {
2729                    layout = mLauncher.getHotseat().getLayout();
2730                }
2731            }
2732            if (layout == null) {
2733                layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
2734            }
2735            if (layout != mDragTargetLayout) {
2736                // Cancel all intermediate folder states
2737                cleanupFolderCreation(d);
2738
2739                if (mDragTargetLayout != null) {
2740                    mDragTargetLayout.setIsDragOverlapping(false);
2741                    mDragTargetLayout.onDragExit();
2742                }
2743                mDragTargetLayout = layout;
2744                if (mDragTargetLayout != null) {
2745                    mDragTargetLayout.setIsDragOverlapping(true);
2746                    mDragTargetLayout.onDragEnter();
2747                } else {
2748                    mLastDragOverView = null;
2749                }
2750
2751                boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
2752                if (isInSpringLoadedMode) {
2753                    if (mLauncher.isHotseatLayout(layout)) {
2754                        mSpringLoadedDragController.cancel();
2755                    } else {
2756                        mSpringLoadedDragController.setAlarm(mDragTargetLayout);
2757                    }
2758                }
2759            }
2760        } else {
2761            // Test to see if we are over the hotseat otherwise just use the current page
2762            if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
2763                mLauncher.getHotseat().getHitRect(r);
2764                if (r.contains(d.x, d.y)) {
2765                    layout = mLauncher.getHotseat().getLayout();
2766                }
2767            }
2768            if (layout == null) {
2769                layout = getCurrentDropLayout();
2770            }
2771            if (layout != mDragTargetLayout) {
2772                if (mDragTargetLayout != null) {
2773                    mDragTargetLayout.setIsDragOverlapping(false);
2774                    mDragTargetLayout.onDragExit();
2775                }
2776                mDragTargetLayout = layout;
2777                mDragTargetLayout.setIsDragOverlapping(true);
2778                mDragTargetLayout.onDragEnter();
2779            }
2780        }
2781
2782        // Handle the drag over
2783        if (mDragTargetLayout != null) {
2784            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
2785
2786            // We want the point to be mapped to the dragTarget.
2787            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2788                mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2789            } else {
2790                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
2791            }
2792            ItemInfo info = (ItemInfo) d.dragInfo;
2793
2794            mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
2795                    (int) mDragViewVisualCenter[1], 1, 1, mDragTargetLayout, mTargetCell);
2796            final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
2797                    mTargetCell[1]);
2798
2799            boolean userFolderPending = willCreateUserFolder(info, mDragTargetLayout,
2800                    mTargetCell, false);
2801            boolean isOverFolder = dragOverView instanceof FolderIcon;
2802            if (dragOverView != mLastDragOverView) {
2803                cancelFolderCreation();
2804                if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
2805                    ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
2806                }
2807            }
2808
2809            if (userFolderPending && dragOverView != mLastDragOverView) {
2810                mFolderCreationAlarm.setOnAlarmListener(new
2811                        FolderCreationAlarmListener(mDragTargetLayout, mTargetCell[0], mTargetCell[1]));
2812                mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
2813            }
2814
2815            if (dragOverView != mLastDragOverView && isOverFolder) {
2816                ((FolderIcon) dragOverView).onDragEnter(d.dragInfo);
2817                if (mDragTargetLayout != null) {
2818                    mDragTargetLayout.clearDragOutlines();
2819                }
2820            }
2821            mLastDragOverView = dragOverView;
2822
2823            if (!mCreateUserFolderOnDrop && !isOverFolder) {
2824                mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
2825                        (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
2826                        item.spanX, item.spanY, d.dragView.getDragVisualizeOffset(),
2827                        d.dragView.getDragRegion());
2828            }
2829        }
2830    }
2831
2832    private void cleanupFolderCreation(DragObject d) {
2833        if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
2834            mDragFolderRingAnimator.animateToNaturalState();
2835        }
2836        if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
2837            if (d != null) {
2838                ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
2839            }
2840        }
2841        mFolderCreationAlarm.cancelAlarm();
2842    }
2843
2844    private void cancelFolderCreation() {
2845        if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
2846            mDragFolderRingAnimator.animateToNaturalState();
2847        }
2848        mCreateUserFolderOnDrop = false;
2849        mFolderCreationAlarm.cancelAlarm();
2850    }
2851
2852    class FolderCreationAlarmListener implements OnAlarmListener {
2853        CellLayout layout;
2854        int cellX;
2855        int cellY;
2856
2857        public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
2858            this.layout = layout;
2859            this.cellX = cellX;
2860            this.cellY = cellY;
2861        }
2862
2863        public void onAlarm(Alarm alarm) {
2864            if (mDragFolderRingAnimator == null) {
2865                mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
2866            }
2867            mDragFolderRingAnimator.setCell(cellX, cellY);
2868            mDragFolderRingAnimator.setCellLayout(layout);
2869            mDragFolderRingAnimator.animateToAcceptState();
2870            layout.showFolderAccept(mDragFolderRingAnimator);
2871            layout.clearDragOutlines();
2872            mCreateUserFolderOnDrop = true;
2873        }
2874    }
2875
2876    @Override
2877    public void getHitRect(Rect outRect) {
2878        // We want the workspace to have the whole area of the display (it will find the correct
2879        // cell layout to drop to in the existing drag/drop logic.
2880        outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
2881    }
2882
2883    /**
2884     * Add the item specified by dragInfo to the given layout.
2885     * @return true if successful
2886     */
2887    public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
2888        if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
2889            onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
2890            return true;
2891        }
2892        mLauncher.showOutOfSpaceMessage();
2893        return false;
2894    }
2895
2896    private void onDropExternal(int[] touchXY, Object dragInfo,
2897            CellLayout cellLayout, boolean insertAtFirst) {
2898        onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
2899    }
2900
2901    /**
2902     * Drop an item that didn't originate on one of the workspace screens.
2903     * It may have come from Launcher (e.g. from all apps or customize), or it may have
2904     * come from another app altogether.
2905     *
2906     * NOTE: This can also be called when we are outside of a drag event, when we want
2907     * to add an item to one of the workspace screens.
2908     */
2909    private void onDropExternal(final int[] touchXY, final Object dragInfo,
2910            final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
2911        final Runnable exitSpringLoadedRunnable = new Runnable() {
2912            @Override
2913            public void run() {
2914                mLauncher.exitSpringLoadedDragModeDelayed(true, false);
2915            }
2916        };
2917
2918        ItemInfo info = (ItemInfo) dragInfo;
2919        int spanX = info.spanX;
2920        int spanY = info.spanY;
2921        if (mDragInfo != null) {
2922            spanX = mDragInfo.spanX;
2923            spanY = mDragInfo.spanY;
2924        }
2925
2926        final long container = mLauncher.isHotseatLayout(cellLayout) ?
2927                LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2928                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
2929        final int screen = indexOfChild(cellLayout);
2930        if (!mLauncher.isHotseatLayout(cellLayout) && screen != mCurrentPage
2931                && mState != State.SPRING_LOADED) {
2932            snapToPage(screen);
2933        }
2934
2935        if (info instanceof PendingAddItemInfo) {
2936            final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
2937
2938            boolean findNearestVacantCell = true;
2939            if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
2940                mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
2941                        cellLayout, mTargetCell);
2942                if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell,
2943                        true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
2944                                mDragTargetLayout, mTargetCell)) {
2945                    findNearestVacantCell = false;
2946                }
2947            }
2948            if (findNearestVacantCell) {
2949                    mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], spanX, spanY, null,
2950                        cellLayout, mTargetCell);
2951            }
2952
2953            Runnable onAnimationCompleteRunnable = new Runnable() {
2954                @Override
2955                public void run() {
2956                    // When dragging and dropping from customization tray, we deal with creating
2957                    // widgets/shortcuts/folders in a slightly different way
2958                    switch (pendingInfo.itemType) {
2959                    case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2960                        mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
2961                                container, screen, mTargetCell, null);
2962                        break;
2963                    case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2964                        mLauncher.processShortcutFromDrop(pendingInfo.componentName,
2965                                container, screen, mTargetCell, null);
2966                        break;
2967                    default:
2968                        throw new IllegalStateException("Unknown item type: " +
2969                                pendingInfo.itemType);
2970                    }
2971                    cellLayout.onDragExit();
2972                }
2973            };
2974
2975            // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
2976            // location and size on the home screen.
2977            RectF r = estimateItemPosition(cellLayout, pendingInfo,
2978                    mTargetCell[0], mTargetCell[1], spanX, spanY);
2979            int loc[] = new int[2];
2980            loc[0] = (int) r.left;
2981            loc[1] = (int) r.top;
2982            setFinalTransitionTransform(cellLayout);
2983            float cellLayoutScale =
2984                    mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc);
2985            resetTransitionTransform(cellLayout);
2986
2987            float dragViewScale =  Math.min(r.width() / d.dragView.getMeasuredWidth(),
2988                    r.height() / d.dragView.getMeasuredHeight());
2989            // The animation will scale the dragView about its center, so we need to center about
2990            // the final location.
2991            loc[0] -= (d.dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
2992            loc[1] -= (d.dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
2993
2994            mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, loc,
2995                    dragViewScale * cellLayoutScale, onAnimationCompleteRunnable);
2996        } else {
2997            // This is for other drag/drop cases, like dragging from All Apps
2998            View view = null;
2999
3000            switch (info.itemType) {
3001            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3002            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
3003                if (info.container == NO_ID && info instanceof ApplicationInfo) {
3004                    // Came from all apps -- make a copy
3005                    info = new ShortcutInfo((ApplicationInfo) info);
3006                }
3007                view = mLauncher.createShortcut(R.layout.application, cellLayout,
3008                        (ShortcutInfo) info);
3009                break;
3010            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
3011                view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3012                        (FolderInfo) info, mIconCache);
3013                break;
3014            default:
3015                throw new IllegalStateException("Unknown item type: " + info.itemType);
3016            }
3017
3018            // First we find the cell nearest to point at which the item is
3019            // dropped, without any consideration to whether there is an item there.
3020            if (touchXY != null) {
3021                mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3022                        cellLayout, mTargetCell);
3023                d.postAnimationRunnable = exitSpringLoadedRunnable;
3024                if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, true,
3025                        d.dragView, d.postAnimationRunnable)) {
3026                    return;
3027                }
3028                if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, d, true)) {
3029                    return;
3030                }
3031            }
3032
3033            if (touchXY != null) {
3034                // when dragging and dropping, just find the closest free spot
3035                mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null,
3036                        cellLayout, mTargetCell);
3037            } else {
3038                cellLayout.findCellForSpan(mTargetCell, 1, 1);
3039            }
3040            addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
3041                    info.spanY, insertAtFirst);
3042            cellLayout.onDropChild(view);
3043            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
3044            cellLayout.getChildrenLayout().measureChild(view);
3045
3046            LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen,
3047                    lp.cellX, lp.cellY);
3048
3049            if (d.dragView != null) {
3050                // We wrap the animation call in the temporary set and reset of the current
3051                // cellLayout to its final transform -- this means we animate the drag view to
3052                // the correct final location.
3053                setFinalTransitionTransform(cellLayout);
3054                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
3055                        exitSpringLoadedRunnable);
3056                resetTransitionTransform(cellLayout);
3057            }
3058        }
3059    }
3060
3061    public void setFinalTransitionTransform(CellLayout layout) {
3062        if (isSwitchingState()) {
3063            int index = indexOfChild(layout);
3064            mCurrentScaleX = layout.getScaleX();
3065            mCurrentScaleY = layout.getScaleY();
3066            mCurrentTranslationX = layout.getTranslationX();
3067            mCurrentTranslationY = layout.getTranslationY();
3068            mCurrentRotationY = layout.getRotationY();
3069            layout.setScaleX(mNewScaleXs[index]);
3070            layout.setScaleY(mNewScaleYs[index]);
3071            layout.setTranslationX(mNewTranslationXs[index]);
3072            layout.setTranslationY(mNewTranslationYs[index]);
3073            layout.setRotationY(mNewRotationYs[index]);
3074        }
3075    }
3076    public void resetTransitionTransform(CellLayout layout) {
3077        if (isSwitchingState()) {
3078            mCurrentScaleX = layout.getScaleX();
3079            mCurrentScaleY = layout.getScaleY();
3080            mCurrentTranslationX = layout.getTranslationX();
3081            mCurrentTranslationY = layout.getTranslationY();
3082            mCurrentRotationY = layout.getRotationY();
3083            layout.setScaleX(mCurrentScaleX);
3084            layout.setScaleY(mCurrentScaleY);
3085            layout.setTranslationX(mCurrentTranslationX);
3086            layout.setTranslationY(mCurrentTranslationY);
3087            layout.setRotationY(mCurrentRotationY);
3088        }
3089    }
3090
3091    /**
3092     * Return the current {@link CellLayout}, correctly picking the destination
3093     * screen while a scroll is in progress.
3094     */
3095    public CellLayout getCurrentDropLayout() {
3096        return (CellLayout) getChildAt(mNextPage == INVALID_PAGE ? mCurrentPage : mNextPage);
3097    }
3098
3099    /**
3100     * Return the current CellInfo describing our current drag; this method exists
3101     * so that Launcher can sync this object with the correct info when the activity is created/
3102     * destroyed
3103     *
3104     */
3105    public CellLayout.CellInfo getDragInfo() {
3106        return mDragInfo;
3107    }
3108
3109    /**
3110     * Calculate the nearest cell where the given object would be dropped.
3111     *
3112     * pixelX and pixelY should be in the coordinate system of layout
3113     */
3114    private int[] findNearestVacantArea(int pixelX, int pixelY,
3115            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
3116        return layout.findNearestVacantArea(
3117                pixelX, pixelY, spanX, spanY, ignoreView, recycle);
3118    }
3119
3120    /**
3121     * Calculate the nearest cell where the given object would be dropped.
3122     *
3123     * pixelX and pixelY should be in the coordinate system of layout
3124     */
3125    private int[] findNearestArea(int pixelX, int pixelY,
3126            int spanX, int spanY, CellLayout layout, int[] recycle) {
3127        return layout.findNearestArea(
3128                pixelX, pixelY, spanX, spanY, recycle);
3129    }
3130
3131    void setup(DragController dragController) {
3132        mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
3133        mDragController = dragController;
3134
3135        // hardware layers on children are enabled on startup, but should be disabled until
3136        // needed
3137        updateChildrenLayersEnabled();
3138        setWallpaperDimension();
3139    }
3140
3141    /**
3142     * Called at the end of a drag which originated on the workspace.
3143     */
3144    public void onDropCompleted(View target, DragObject d, boolean success) {
3145        if (success) {
3146            if (target != this) {
3147                if (mDragInfo != null) {
3148                    getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
3149                    if (mDragInfo.cell instanceof DropTarget) {
3150                        mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
3151                    }
3152                }
3153            }
3154        } else if (mDragInfo != null) {
3155            // NOTE: When 'success' is true, onDragExit is called by the DragController before
3156            // calling onDropCompleted(). We call it ourselves here, but maybe this should be
3157            // moved into DragController.cancelDrag().
3158            doDragExit(null);
3159            CellLayout cellLayout;
3160            if (mLauncher.isHotseatLayout(target)) {
3161                cellLayout = mLauncher.getHotseat().getLayout();
3162            } else {
3163                cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
3164            }
3165            cellLayout.onDropChild(mDragInfo.cell);
3166        }
3167        if (d.cancelled &&  mDragInfo.cell != null) {
3168                mDragInfo.cell.setVisibility(VISIBLE);
3169        }
3170        mDragOutline = null;
3171        mDragInfo = null;
3172    }
3173
3174    public boolean isDropEnabled() {
3175        return true;
3176    }
3177
3178    @Override
3179    protected void onRestoreInstanceState(Parcelable state) {
3180        super.onRestoreInstanceState(state);
3181        Launcher.setScreen(mCurrentPage);
3182    }
3183
3184    @Override
3185    public void scrollLeft() {
3186        if (!isSmall() && !mIsSwitchingState) {
3187            super.scrollLeft();
3188        }
3189        Folder openFolder = getOpenFolder();
3190        if (openFolder != null) {
3191            openFolder.completeDragExit();
3192        }
3193    }
3194
3195    @Override
3196    public void scrollRight() {
3197        if (!isSmall() && !mIsSwitchingState) {
3198            super.scrollRight();
3199        }
3200        Folder openFolder = getOpenFolder();
3201        if (openFolder != null) {
3202            openFolder.completeDragExit();
3203        }
3204    }
3205
3206    @Override
3207    public boolean onEnterScrollArea(int x, int y, int direction) {
3208        // Ignore the scroll area if we are dragging over the hot seat
3209        if (mLauncher.getHotseat() != null) {
3210            Rect r = new Rect();
3211            mLauncher.getHotseat().getHitRect(r);
3212            if (r.contains(x, y)) {
3213                return false;
3214            }
3215        }
3216
3217        boolean result = false;
3218        if (!isSmall() && !mIsSwitchingState) {
3219            mInScrollArea = true;
3220
3221            final int page = mCurrentPage + (direction == DragController.SCROLL_LEFT ? -1 : 1);
3222            final CellLayout layout = (CellLayout) getChildAt(page);
3223            cancelFolderCreation();
3224
3225            if (layout != null) {
3226                // Exit the current layout and mark the overlapping layout
3227                if (mDragTargetLayout != null) {
3228                    mDragTargetLayout.setIsDragOverlapping(false);
3229                    mDragTargetLayout.onDragExit();
3230                }
3231                mDragTargetLayout = layout;
3232                mDragTargetLayout.setIsDragOverlapping(true);
3233
3234                // Workspace is responsible for drawing the edge glow on adjacent pages,
3235                // so we need to redraw the workspace when this may have changed.
3236                invalidate();
3237                result = true;
3238            }
3239        }
3240        return result;
3241    }
3242
3243    @Override
3244    public boolean onExitScrollArea() {
3245        boolean result = false;
3246        if (mInScrollArea) {
3247            if (mDragTargetLayout != null) {
3248                // Unmark the overlapping layout and re-enter the current layout
3249                mDragTargetLayout.setIsDragOverlapping(false);
3250                mDragTargetLayout = getCurrentDropLayout();
3251                mDragTargetLayout.onDragEnter();
3252
3253                // Workspace is responsible for drawing the edge glow on adjacent pages,
3254                // so we need to redraw the workspace when this may have changed.
3255                invalidate();
3256                result = true;
3257            }
3258            mInScrollArea = false;
3259        }
3260        return result;
3261    }
3262
3263    private void onResetScrollArea() {
3264        if (mDragTargetLayout != null) {
3265            // Unmark the overlapping layout
3266            mDragTargetLayout.setIsDragOverlapping(false);
3267
3268            // Workspace is responsible for drawing the edge glow on adjacent pages,
3269            // so we need to redraw the workspace when this may have changed.
3270            invalidate();
3271        }
3272        mInScrollArea = false;
3273    }
3274
3275    /**
3276     * Returns a specific CellLayout
3277     */
3278    CellLayout getParentCellLayoutForView(View v) {
3279        ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
3280        for (CellLayout layout : layouts) {
3281            if (layout.getChildrenLayout().indexOfChild(v) > -1) {
3282                return layout;
3283            }
3284        }
3285        return null;
3286    }
3287
3288    /**
3289     * Returns a list of all the CellLayouts in the workspace.
3290     */
3291    ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
3292        ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
3293        int screenCount = getChildCount();
3294        for (int screen = 0; screen < screenCount; screen++) {
3295            layouts.add(((CellLayout) getChildAt(screen)));
3296        }
3297        if (mLauncher.getHotseat() != null) {
3298            layouts.add(mLauncher.getHotseat().getLayout());
3299        }
3300        return layouts;
3301    }
3302
3303    /**
3304     * We should only use this to search for specific children.  Do not use this method to modify
3305     * CellLayoutChildren directly.
3306     */
3307    ArrayList<CellLayoutChildren> getWorkspaceAndHotseatCellLayoutChildren() {
3308        ArrayList<CellLayoutChildren> childrenLayouts = new ArrayList<CellLayoutChildren>();
3309        int screenCount = getChildCount();
3310        for (int screen = 0; screen < screenCount; screen++) {
3311            childrenLayouts.add(((CellLayout) getChildAt(screen)).getChildrenLayout());
3312        }
3313        if (mLauncher.getHotseat() != null) {
3314            childrenLayouts.add(mLauncher.getHotseat().getLayout().getChildrenLayout());
3315        }
3316        return childrenLayouts;
3317    }
3318
3319    public Folder getFolderForTag(Object tag) {
3320        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3321        for (CellLayoutChildren layout: childrenLayouts) {
3322            int count = layout.getChildCount();
3323            for (int i = 0; i < count; i++) {
3324                View child = layout.getChildAt(i);
3325                if (child instanceof Folder) {
3326                    Folder f = (Folder) child;
3327                    if (f.getInfo() == tag && f.getInfo().opened) {
3328                        return f;
3329                    }
3330                }
3331            }
3332        }
3333        return null;
3334    }
3335
3336    public View getViewForTag(Object tag) {
3337        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3338        for (CellLayoutChildren layout: childrenLayouts) {
3339            int count = layout.getChildCount();
3340            for (int i = 0; i < count; i++) {
3341                View child = layout.getChildAt(i);
3342                if (child.getTag() == tag) {
3343                    return child;
3344                }
3345            }
3346        }
3347        return null;
3348    }
3349
3350    void clearDropTargets() {
3351        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3352        for (CellLayoutChildren layout: childrenLayouts) {
3353            int childCount = layout.getChildCount();
3354            for (int j = 0; j < childCount; j++) {
3355                View v = layout.getChildAt(j);
3356                if (v instanceof DropTarget) {
3357                    mDragController.removeDropTarget((DropTarget) v);
3358                }
3359            }
3360        }
3361    }
3362
3363    void removeItems(final ArrayList<ApplicationInfo> apps) {
3364        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
3365
3366        final HashSet<String> packageNames = new HashSet<String>();
3367        final int appCount = apps.size();
3368        for (int i = 0; i < appCount; i++) {
3369            packageNames.add(apps.get(i).componentName.getPackageName());
3370        }
3371
3372        ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
3373        for (final CellLayout layoutParent: cellLayouts) {
3374            final ViewGroup layout = layoutParent.getChildrenLayout();
3375
3376            // Avoid ANRs by treating each screen separately
3377            post(new Runnable() {
3378                public void run() {
3379                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
3380                    childrenToRemove.clear();
3381
3382                    int childCount = layout.getChildCount();
3383                    for (int j = 0; j < childCount; j++) {
3384                        final View view = layout.getChildAt(j);
3385                        Object tag = view.getTag();
3386
3387                        if (tag instanceof ShortcutInfo) {
3388                            final ShortcutInfo info = (ShortcutInfo) tag;
3389                            final Intent intent = info.intent;
3390                            final ComponentName name = intent.getComponent();
3391
3392                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3393                                for (String packageName: packageNames) {
3394                                    if (packageName.equals(name.getPackageName())) {
3395                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
3396                                        childrenToRemove.add(view);
3397                                    }
3398                                }
3399                            }
3400                        } else if (tag instanceof FolderInfo) {
3401                            final FolderInfo info = (FolderInfo) tag;
3402                            final ArrayList<ShortcutInfo> contents = info.contents;
3403                            final int contentsCount = contents.size();
3404                            final ArrayList<ShortcutInfo> appsToRemoveFromFolder =
3405                                    new ArrayList<ShortcutInfo>();
3406
3407                            for (int k = 0; k < contentsCount; k++) {
3408                                final ShortcutInfo appInfo = contents.get(k);
3409                                final Intent intent = appInfo.intent;
3410                                final ComponentName name = intent.getComponent();
3411
3412                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3413                                    for (String packageName: packageNames) {
3414                                        if (packageName.equals(name.getPackageName())) {
3415                                            appsToRemoveFromFolder.add(appInfo);
3416                                        }
3417                                    }
3418                                }
3419                            }
3420                            for (ShortcutInfo item: appsToRemoveFromFolder) {
3421                                info.remove(item);
3422                                LauncherModel.deleteItemFromDatabase(mLauncher, item);
3423                            }
3424                        } else if (tag instanceof LauncherAppWidgetInfo) {
3425                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
3426                            final AppWidgetProviderInfo provider =
3427                                    widgets.getAppWidgetInfo(info.appWidgetId);
3428                            if (provider != null) {
3429                                for (String packageName: packageNames) {
3430                                    if (packageName.equals(provider.provider.getPackageName())) {
3431                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
3432                                        childrenToRemove.add(view);
3433                                    }
3434                                }
3435                            }
3436                        }
3437                    }
3438
3439                    childCount = childrenToRemove.size();
3440                    for (int j = 0; j < childCount; j++) {
3441                        View child = childrenToRemove.get(j);
3442                        // Note: We can not remove the view directly from CellLayoutChildren as this
3443                        // does not re-mark the spaces as unoccupied.
3444                        layoutParent.removeViewInLayout(child);
3445                        if (child instanceof DropTarget) {
3446                            mDragController.removeDropTarget((DropTarget)child);
3447                        }
3448                    }
3449
3450                    if (childCount > 0) {
3451                        layout.requestLayout();
3452                        layout.invalidate();
3453                    }
3454                }
3455            });
3456        }
3457    }
3458
3459    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
3460        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3461        for (CellLayoutChildren layout: childrenLayouts) {
3462            int childCount = layout.getChildCount();
3463            for (int j = 0; j < childCount; j++) {
3464                final View view = layout.getChildAt(j);
3465                Object tag = view.getTag();
3466                if (tag instanceof ShortcutInfo) {
3467                    ShortcutInfo info = (ShortcutInfo)tag;
3468                    // We need to check for ACTION_MAIN otherwise getComponent() might
3469                    // return null for some shortcuts (for instance, for shortcuts to
3470                    // web pages.)
3471                    final Intent intent = info.intent;
3472                    final ComponentName name = intent.getComponent();
3473                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
3474                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3475                        final int appCount = apps.size();
3476                        for (int k = 0; k < appCount; k++) {
3477                            ApplicationInfo app = apps.get(k);
3478                            if (app.componentName.equals(name)) {
3479                                info.setIcon(mIconCache.getIcon(info.intent));
3480                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
3481                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
3482                                        null, null);
3483                                }
3484                        }
3485                    }
3486                }
3487            }
3488        }
3489    }
3490
3491    void moveToDefaultScreen(boolean animate) {
3492        if (!isSmall()) {
3493            if (animate) {
3494                snapToPage(mDefaultPage);
3495            } else {
3496                setCurrentPage(mDefaultPage);
3497            }
3498        }
3499        getChildAt(mDefaultPage).requestFocus();
3500    }
3501
3502    @Override
3503    public void syncPages() {
3504    }
3505
3506    @Override
3507    public void syncPageItems(int page, boolean immediate) {
3508    }
3509
3510    @Override
3511    protected String getCurrentPageDescription() {
3512        int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
3513        return String.format(mContext.getString(R.string.workspace_scroll_format),
3514                page + 1, getChildCount());
3515    }
3516
3517    public void getLocationInDragLayer(int[] loc) {
3518        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
3519    }
3520
3521    void setFadeForOverScroll(float fade) {
3522        if (!isScrollingIndicatorEnabled()) return;
3523
3524        mOverscrollFade = fade;
3525        float reducedFade = 0.5f + 0.5f * (1 - fade);
3526        final ViewGroup parent = (ViewGroup) getParent();
3527        final ImageView qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
3528        final ImageView dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
3529        final ImageView scrollIndicator = getScrollingIndicator();
3530
3531        cancelScrollingIndicatorAnimations();
3532        if (qsbDivider != null) qsbDivider.setAlpha(reducedFade);
3533        if (dockDivider != null) dockDivider.setAlpha(reducedFade);
3534        scrollIndicator.setAlpha(1 - fade);
3535    }
3536}
3537