Home | History | Annotate | Download | only in launcher2
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher2;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorSet;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.TimeInterpolator;
     23 import android.animation.ValueAnimator;
     24 import android.animation.ValueAnimator.AnimatorUpdateListener;
     25 import android.app.WallpaperManager;
     26 import android.appwidget.AppWidgetHostView;
     27 import android.appwidget.AppWidgetManager;
     28 import android.appwidget.AppWidgetProviderInfo;
     29 import android.content.ComponentName;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.SharedPreferences;
     33 import android.content.res.Resources;
     34 import android.content.res.TypedArray;
     35 import android.graphics.Bitmap;
     36 import android.graphics.Camera;
     37 import android.graphics.Canvas;
     38 import android.graphics.Matrix;
     39 import android.graphics.Paint;
     40 import android.graphics.Point;
     41 import android.graphics.PointF;
     42 import android.graphics.Rect;
     43 import android.graphics.Region.Op;
     44 import android.graphics.drawable.Drawable;
     45 import android.os.IBinder;
     46 import android.os.Parcelable;
     47 import android.util.AttributeSet;
     48 import android.util.DisplayMetrics;
     49 import android.util.Log;
     50 import android.view.Display;
     51 import android.view.MotionEvent;
     52 import android.view.View;
     53 import android.view.ViewGroup;
     54 import android.view.animation.DecelerateInterpolator;
     55 import android.widget.ImageView;
     56 import android.widget.TextView;
     57 
     58 import com.android.launcher.R;
     59 import com.android.launcher2.FolderIcon.FolderRingAnimator;
     60 import com.android.launcher2.LauncherSettings.Favorites;
     61 
     62 import java.net.URISyntaxException;
     63 import java.util.ArrayList;
     64 import java.util.HashSet;
     65 import java.util.Iterator;
     66 import java.util.Set;
     67 
     68 /**
     69  * The workspace is a wide area with a wallpaper and a finite number of pages.
     70  * Each page contains a number of icons, folders or widgets the user can
     71  * interact with. A workspace is meant to be used with a fixed width only.
     72  */
     73 public class Workspace extends SmoothPagedView
     74         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
     75         DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener {
     76     private static final String TAG = "Launcher.Workspace";
     77 
     78     // Y rotation to apply to the workspace screens
     79     private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
     80 
     81     private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
     82     private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
     83     private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
     84 
     85     private static final int BACKGROUND_FADE_OUT_DURATION = 350;
     86     private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
     87     private static final int FLING_THRESHOLD_VELOCITY = 500;
     88 
     89     // These animators are used to fade the children's outlines
     90     private ObjectAnimator mChildrenOutlineFadeInAnimation;
     91     private ObjectAnimator mChildrenOutlineFadeOutAnimation;
     92     private float mChildrenOutlineAlpha = 0;
     93 
     94     // These properties refer to the background protection gradient used for AllApps and Customize
     95     private ValueAnimator mBackgroundFadeInAnimation;
     96     private ValueAnimator mBackgroundFadeOutAnimation;
     97     private Drawable mBackground;
     98     boolean mDrawBackground = true;
     99     private float mBackgroundAlpha = 0;
    100     private float mOverScrollMaxBackgroundAlpha = 0.0f;
    101 
    102     private float mWallpaperScrollRatio = 1.0f;
    103 
    104     private final WallpaperManager mWallpaperManager;
    105     private IBinder mWindowToken;
    106     private static final float WALLPAPER_SCREENS_SPAN = 2f;
    107 
    108     private int mDefaultPage;
    109 
    110     /**
    111      * CellInfo for the cell that is currently being dragged
    112      */
    113     private CellLayout.CellInfo mDragInfo;
    114 
    115     /**
    116      * Target drop area calculated during last acceptDrop call.
    117      */
    118     private int[] mTargetCell = new int[2];
    119     private int mDragOverX = -1;
    120     private int mDragOverY = -1;
    121 
    122     static Rect mLandscapeCellLayoutMetrics = null;
    123     static Rect mPortraitCellLayoutMetrics = null;
    124 
    125     /**
    126      * The CellLayout that is currently being dragged over
    127      */
    128     private CellLayout mDragTargetLayout = null;
    129     /**
    130      * The CellLayout that we will show as glowing
    131      */
    132     private CellLayout mDragOverlappingLayout = null;
    133 
    134     /**
    135      * The CellLayout which will be dropped to
    136      */
    137     private CellLayout mDropToLayout = null;
    138 
    139     private Launcher mLauncher;
    140     private IconCache mIconCache;
    141     private DragController mDragController;
    142 
    143     // These are temporary variables to prevent having to allocate a new object just to
    144     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
    145     private int[] mTempCell = new int[2];
    146     private int[] mTempEstimate = new int[2];
    147     private float[] mDragViewVisualCenter = new float[2];
    148     private float[] mTempDragCoordinates = new float[2];
    149     private float[] mTempCellLayoutCenterCoordinates = new float[2];
    150     private float[] mTempDragBottomRightCoordinates = new float[2];
    151     private Matrix mTempInverseMatrix = new Matrix();
    152 
    153     private SpringLoadedDragController mSpringLoadedDragController;
    154     private float mSpringLoadedShrinkFactor;
    155 
    156     private static final int DEFAULT_CELL_COUNT_X = 4;
    157     private static final int DEFAULT_CELL_COUNT_Y = 4;
    158 
    159     // State variable that indicates whether the pages are small (ie when you're
    160     // in all apps or customize mode)
    161 
    162     enum State { NORMAL, SPRING_LOADED, SMALL };
    163     private State mState = State.NORMAL;
    164     private boolean mIsSwitchingState = false;
    165 
    166     boolean mAnimatingViewIntoPlace = false;
    167     boolean mIsDragOccuring = false;
    168     boolean mChildrenLayersEnabled = true;
    169 
    170     /** Is the user is dragging an item near the edge of a page? */
    171     private boolean mInScrollArea = false;
    172 
    173     private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
    174     private Bitmap mDragOutline = null;
    175     private final Rect mTempRect = new Rect();
    176     private final int[] mTempXY = new int[2];
    177     private float mOverscrollFade = 0;
    178     private boolean mOverscrollTransformsSet;
    179     public static final int DRAG_BITMAP_PADDING = 2;
    180     private boolean mWorkspaceFadeInAdjacentScreens;
    181 
    182     // Camera and Matrix used to determine the final position of a neighboring CellLayout
    183     private final Matrix mMatrix = new Matrix();
    184     private final Camera mCamera = new Camera();
    185     private final float mTempFloat2[] = new float[2];
    186 
    187     enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM };
    188     int mWallpaperWidth;
    189     int mWallpaperHeight;
    190     WallpaperOffsetInterpolator mWallpaperOffset;
    191     boolean mUpdateWallpaperOffsetImmediately = false;
    192     private Runnable mDelayedResizeRunnable;
    193     private Runnable mDelayedSnapToPageRunnable;
    194     private Point mDisplaySize = new Point();
    195     private boolean mIsStaticWallpaper;
    196     private int mWallpaperTravelWidth;
    197     private int mSpringLoadedPageSpacing;
    198     private int mCameraDistance;
    199 
    200     // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
    201     private static final int FOLDER_CREATION_TIMEOUT = 0;
    202     private static final int REORDER_TIMEOUT = 250;
    203     private final Alarm mFolderCreationAlarm = new Alarm();
    204     private final Alarm mReorderAlarm = new Alarm();
    205     private FolderRingAnimator mDragFolderRingAnimator = null;
    206     private FolderIcon mDragOverFolderIcon = null;
    207     private boolean mCreateUserFolderOnDrop = false;
    208     private boolean mAddToExistingFolderOnDrop = false;
    209     private DropTarget.DragEnforcer mDragEnforcer;
    210     private float mMaxDistanceForFolderCreation;
    211 
    212     // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
    213     private float mXDown;
    214     private float mYDown;
    215     final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
    216     final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
    217     final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
    218 
    219     // Relating to the animation of items being dropped externally
    220     public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
    221     public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
    222     public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
    223     public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
    224     public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
    225 
    226     // Related to dragging, folder creation and reordering
    227     private static final int DRAG_MODE_NONE = 0;
    228     private static final int DRAG_MODE_CREATE_FOLDER = 1;
    229     private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
    230     private static final int DRAG_MODE_REORDER = 3;
    231     private int mDragMode = DRAG_MODE_NONE;
    232     private int mLastReorderX = -1;
    233     private int mLastReorderY = -1;
    234 
    235     // These variables are used for storing the initial and final values during workspace animations
    236     private int mSavedScrollX;
    237     private float mSavedRotationY;
    238     private float mSavedTranslationX;
    239     private float mCurrentScaleX;
    240     private float mCurrentScaleY;
    241     private float mCurrentRotationY;
    242     private float mCurrentTranslationX;
    243     private float mCurrentTranslationY;
    244     private float[] mOldTranslationXs;
    245     private float[] mOldTranslationYs;
    246     private float[] mOldScaleXs;
    247     private float[] mOldScaleYs;
    248     private float[] mOldBackgroundAlphas;
    249     private float[] mOldAlphas;
    250     private float[] mNewTranslationXs;
    251     private float[] mNewTranslationYs;
    252     private float[] mNewScaleXs;
    253     private float[] mNewScaleYs;
    254     private float[] mNewBackgroundAlphas;
    255     private float[] mNewAlphas;
    256     private float[] mNewRotationYs;
    257     private float mTransitionProgress;
    258 
    259     /**
    260      * Used to inflate the Workspace from XML.
    261      *
    262      * @param context The application's context.
    263      * @param attrs The attributes set containing the Workspace's customization values.
    264      */
    265     public Workspace(Context context, AttributeSet attrs) {
    266         this(context, attrs, 0);
    267     }
    268 
    269     /**
    270      * Used to inflate the Workspace from XML.
    271      *
    272      * @param context The application's context.
    273      * @param attrs The attributes set containing the Workspace's customization values.
    274      * @param defStyle Unused.
    275      */
    276     public Workspace(Context context, AttributeSet attrs, int defStyle) {
    277         super(context, attrs, defStyle);
    278         mContentIsRefreshable = false;
    279 
    280         mDragEnforcer = new DropTarget.DragEnforcer(context);
    281         // With workspace, data is available straight from the get-go
    282         setDataIsReady();
    283 
    284         final Resources res = getResources();
    285         mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
    286         mFadeInAdjacentScreens = false;
    287         mWallpaperManager = WallpaperManager.getInstance(context);
    288 
    289         int cellCountX = DEFAULT_CELL_COUNT_X;
    290         int cellCountY = DEFAULT_CELL_COUNT_Y;
    291 
    292         TypedArray a = context.obtainStyledAttributes(attrs,
    293                 R.styleable.Workspace, defStyle, 0);
    294 
    295         if (LauncherApplication.isScreenLarge()) {
    296             // Determine number of rows/columns dynamically
    297             // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
    298             // Around that ratio we should make cells the same size in portrait and
    299             // landscape
    300             TypedArray actionBarSizeTypedArray =
    301                 context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
    302             DisplayMetrics displayMetrics = res.getDisplayMetrics();
    303             final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
    304             final float systemBarHeight = res.getDimension(R.dimen.status_bar_height);
    305             final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp *
    306                     displayMetrics.density;
    307 
    308             cellCountX = 1;
    309             while (CellLayout.widthInPortrait(res, cellCountX + 1) <= smallestScreenDim) {
    310                 cellCountX++;
    311             }
    312 
    313             cellCountY = 1;
    314             while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
    315                 <= smallestScreenDim - systemBarHeight) {
    316                 cellCountY++;
    317             }
    318         }
    319 
    320         mSpringLoadedShrinkFactor =
    321             res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
    322         mSpringLoadedPageSpacing =
    323                 res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing);
    324         mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
    325 
    326         // if the value is manually specified, use that instead
    327         cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
    328         cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
    329         mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
    330         a.recycle();
    331 
    332         setOnHierarchyChangeListener(this);
    333 
    334         LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
    335         setHapticFeedbackEnabled(false);
    336 
    337         mLauncher = (Launcher) context;
    338         initWorkspace();
    339 
    340         // Disable multitouch across the workspace/all apps/customize tray
    341         setMotionEventSplittingEnabled(true);
    342 
    343         // Unless otherwise specified this view is important for accessibility.
    344         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    345             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    346         }
    347     }
    348 
    349     // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
    350     // dimension if unsuccessful
    351     public int[] estimateItemSize(int hSpan, int vSpan,
    352             ItemInfo itemInfo, boolean springLoaded) {
    353         int[] size = new int[2];
    354         if (getChildCount() > 0) {
    355             CellLayout cl = (CellLayout) mLauncher.getWorkspace().getChildAt(0);
    356             Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
    357             size[0] = r.width();
    358             size[1] = r.height();
    359             if (springLoaded) {
    360                 size[0] *= mSpringLoadedShrinkFactor;
    361                 size[1] *= mSpringLoadedShrinkFactor;
    362             }
    363             return size;
    364         } else {
    365             size[0] = Integer.MAX_VALUE;
    366             size[1] = Integer.MAX_VALUE;
    367             return size;
    368         }
    369     }
    370     public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
    371             int hCell, int vCell, int hSpan, int vSpan) {
    372         Rect r = new Rect();
    373         cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
    374         return r;
    375     }
    376 
    377     public void buildPageHardwareLayers() {
    378         if (getWindowToken() != null) {
    379             final int childCount = getChildCount();
    380             for (int i = 0; i < childCount; i++) {
    381                 CellLayout cl = (CellLayout) getChildAt(i);
    382                 cl.getShortcutsAndWidgets().buildLayer();
    383             }
    384         }
    385     }
    386 
    387     public void onDragStart(DragSource source, Object info, int dragAction) {
    388         mIsDragOccuring = true;
    389         updateChildrenLayersEnabled();
    390         mLauncher.lockScreenOrientation();
    391         setChildrenBackgroundAlphaMultipliers(1f);
    392         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
    393         InstallShortcutReceiver.enableInstallQueue();
    394         UninstallShortcutReceiver.enableUninstallQueue();
    395     }
    396 
    397     public void onDragEnd() {
    398         mIsDragOccuring = false;
    399         updateChildrenLayersEnabled();
    400         mLauncher.unlockScreenOrientation(false);
    401 
    402         // Re-enable any Un/InstallShortcutReceiver and now process any queued items
    403         InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
    404         UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
    405     }
    406 
    407     /**
    408      * Initializes various states for this workspace.
    409      */
    410     protected void initWorkspace() {
    411         Context context = getContext();
    412         mCurrentPage = mDefaultPage;
    413         Launcher.setScreen(mCurrentPage);
    414         LauncherApplication app = (LauncherApplication)context.getApplicationContext();
    415         mIconCache = app.getIconCache();
    416         setWillNotDraw(false);
    417         setChildrenDrawnWithCacheEnabled(true);
    418 
    419         final Resources res = getResources();
    420         try {
    421             mBackground = res.getDrawable(R.drawable.apps_customize_bg);
    422         } catch (Resources.NotFoundException e) {
    423             // In this case, we will skip drawing background protection
    424         }
    425 
    426         mWallpaperOffset = new WallpaperOffsetInterpolator();
    427         Display display = mLauncher.getWindowManager().getDefaultDisplay();
    428         display.getSize(mDisplaySize);
    429         mWallpaperTravelWidth = (int) (mDisplaySize.x *
    430                 wallpaperTravelToScreenWidthRatio(mDisplaySize.x, mDisplaySize.y));
    431 
    432         mMaxDistanceForFolderCreation = (0.55f * res.getDimensionPixelSize(R.dimen.app_icon_size));
    433         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
    434     }
    435 
    436     @Override
    437     protected int getScrollMode() {
    438         return SmoothPagedView.X_LARGE_MODE;
    439     }
    440 
    441     @Override
    442     public void onChildViewAdded(View parent, View child) {
    443         if (!(child instanceof CellLayout)) {
    444             throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
    445         }
    446         CellLayout cl = ((CellLayout) child);
    447         cl.setOnInterceptTouchListener(this);
    448         cl.setClickable(true);
    449         cl.enableHardwareLayers();
    450         cl.setContentDescription(getContext().getString(
    451                 R.string.workspace_description_format, getChildCount()));
    452     }
    453 
    454     @Override
    455     public void onChildViewRemoved(View parent, View child) {
    456     }
    457 
    458     protected boolean shouldDrawChild(View child) {
    459         final CellLayout cl = (CellLayout) child;
    460         return super.shouldDrawChild(child) &&
    461             (cl.getShortcutsAndWidgets().getAlpha() > 0 ||
    462              cl.getBackgroundAlpha() > 0);
    463     }
    464 
    465     /**
    466      * @return The open folder on the current screen, or null if there is none
    467      */
    468     Folder getOpenFolder() {
    469         DragLayer dragLayer = mLauncher.getDragLayer();
    470         int count = dragLayer.getChildCount();
    471         for (int i = 0; i < count; i++) {
    472             View child = dragLayer.getChildAt(i);
    473             if (child instanceof Folder) {
    474                 Folder folder = (Folder) child;
    475                 if (folder.getInfo().opened)
    476                     return folder;
    477             }
    478         }
    479         return null;
    480     }
    481 
    482     boolean isTouchActive() {
    483         return mTouchState != TOUCH_STATE_REST;
    484     }
    485 
    486     /**
    487      * Adds the specified child in the specified screen. The position and dimension of
    488      * the child are defined by x, y, spanX and spanY.
    489      *
    490      * @param child The child to add in one of the workspace's screens.
    491      * @param screen The screen in which to add the child.
    492      * @param x The X position of the child in the screen's grid.
    493      * @param y The Y position of the child in the screen's grid.
    494      * @param spanX The number of cells spanned horizontally by the child.
    495      * @param spanY The number of cells spanned vertically by the child.
    496      */
    497     void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY) {
    498         addInScreen(child, container, screen, x, y, spanX, spanY, false);
    499     }
    500 
    501     /**
    502      * Adds the specified child in the specified screen. The position and dimension of
    503      * the child are defined by x, y, spanX and spanY.
    504      *
    505      * @param child The child to add in one of the workspace's screens.
    506      * @param screen The screen in which to add the child.
    507      * @param x The X position of the child in the screen's grid.
    508      * @param y The Y position of the child in the screen's grid.
    509      * @param spanX The number of cells spanned horizontally by the child.
    510      * @param spanY The number of cells spanned vertically by the child.
    511      * @param insert When true, the child is inserted at the beginning of the children list.
    512      */
    513     void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
    514             boolean insert) {
    515         if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
    516             if (screen < 0 || screen >= getChildCount()) {
    517                 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
    518                     + " (was " + screen + "); skipping child");
    519                 return;
    520             }
    521         }
    522 
    523         final CellLayout layout;
    524         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    525             layout = mLauncher.getHotseat().getLayout();
    526             child.setOnKeyListener(null);
    527 
    528             // Hide folder title in the hotseat
    529             if (child instanceof FolderIcon) {
    530                 ((FolderIcon) child).setTextVisible(false);
    531             }
    532 
    533             if (screen < 0) {
    534                 screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
    535             } else {
    536                 // Note: We do this to ensure that the hotseat is always laid out in the orientation
    537                 // of the hotseat in order regardless of which orientation they were added
    538                 x = mLauncher.getHotseat().getCellXFromOrder(screen);
    539                 y = mLauncher.getHotseat().getCellYFromOrder(screen);
    540             }
    541         } else {
    542             // Show folder title if not in the hotseat
    543             if (child instanceof FolderIcon) {
    544                 ((FolderIcon) child).setTextVisible(true);
    545             }
    546 
    547             layout = (CellLayout) getChildAt(screen);
    548             child.setOnKeyListener(new IconKeyEventListener());
    549         }
    550 
    551         LayoutParams genericLp = child.getLayoutParams();
    552         CellLayout.LayoutParams lp;
    553         if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
    554             lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
    555         } else {
    556             lp = (CellLayout.LayoutParams) genericLp;
    557             lp.cellX = x;
    558             lp.cellY = y;
    559             lp.cellHSpan = spanX;
    560             lp.cellVSpan = spanY;
    561         }
    562 
    563         if (spanX < 0 && spanY < 0) {
    564             lp.isLockedToGrid = false;
    565         }
    566 
    567         // Get the canonical child id to uniquely represent this view in this screen
    568         int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
    569         boolean markCellsAsOccupied = !(child instanceof Folder);
    570         if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
    571             // TODO: This branch occurs when the workspace is adding views
    572             // outside of the defined grid
    573             // maybe we should be deleting these items from the LauncherModel?
    574             Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
    575         }
    576 
    577         if (!(child instanceof Folder)) {
    578             child.setHapticFeedbackEnabled(false);
    579             child.setOnLongClickListener(mLongClickListener);
    580         }
    581         if (child instanceof DropTarget) {
    582             mDragController.addDropTarget((DropTarget) child);
    583         }
    584     }
    585 
    586     /**
    587      * Check if the point (x, y) hits a given page.
    588      */
    589     private boolean hitsPage(int index, float x, float y) {
    590         final View page = getChildAt(index);
    591         if (page != null) {
    592             float[] localXY = { x, y };
    593             mapPointFromSelfToChild(page, localXY);
    594             return (localXY[0] >= 0 && localXY[0] < page.getWidth()
    595                     && localXY[1] >= 0 && localXY[1] < page.getHeight());
    596         }
    597         return false;
    598     }
    599 
    600     @Override
    601     protected boolean hitsPreviousPage(float x, float y) {
    602         // mNextPage is set to INVALID_PAGE whenever we are stationary.
    603         // Calculating "next page" this way ensures that you scroll to whatever page you tap on
    604         final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
    605 
    606         // Only allow tap to next page on large devices, where there's significant margin outside
    607         // the active workspace
    608         return LauncherApplication.isScreenLarge() && hitsPage(current - 1, x, y);
    609     }
    610 
    611     @Override
    612     protected boolean hitsNextPage(float x, float y) {
    613         // mNextPage is set to INVALID_PAGE whenever we are stationary.
    614         // Calculating "next page" this way ensures that you scroll to whatever page you tap on
    615         final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
    616 
    617         // Only allow tap to next page on large devices, where there's significant margin outside
    618         // the active workspace
    619         return LauncherApplication.isScreenLarge() && hitsPage(current + 1, x, y);
    620     }
    621 
    622     /**
    623      * Called directly from a CellLayout (not by the framework), after we've been added as a
    624      * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
    625      * that it should intercept touch events, which is not something that is normally supported.
    626      */
    627     @Override
    628     public boolean onTouch(View v, MotionEvent event) {
    629         return (isSmall() || !isFinishedSwitchingState());
    630     }
    631 
    632     public boolean isSwitchingState() {
    633         return mIsSwitchingState;
    634     }
    635 
    636     /** This differs from isSwitchingState in that we take into account how far the transition
    637      *  has completed. */
    638     public boolean isFinishedSwitchingState() {
    639         return !mIsSwitchingState || (mTransitionProgress > 0.5f);
    640     }
    641 
    642     protected void onWindowVisibilityChanged (int visibility) {
    643         mLauncher.onWindowVisibilityChanged(visibility);
    644     }
    645 
    646     @Override
    647     public boolean dispatchUnhandledMove(View focused, int direction) {
    648         if (isSmall() || !isFinishedSwitchingState()) {
    649             // when the home screens are shrunken, shouldn't allow side-scrolling
    650             return false;
    651         }
    652         return super.dispatchUnhandledMove(focused, direction);
    653     }
    654 
    655     @Override
    656     public boolean onInterceptTouchEvent(MotionEvent ev) {
    657         switch (ev.getAction() & MotionEvent.ACTION_MASK) {
    658         case MotionEvent.ACTION_DOWN:
    659             mXDown = ev.getX();
    660             mYDown = ev.getY();
    661             break;
    662         case MotionEvent.ACTION_POINTER_UP:
    663         case MotionEvent.ACTION_UP:
    664             if (mTouchState == TOUCH_STATE_REST) {
    665                 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
    666                 if (!currentPage.lastDownOnOccupiedCell()) {
    667                     onWallpaperTap(ev);
    668                 }
    669             }
    670         }
    671         return super.onInterceptTouchEvent(ev);
    672     }
    673 
    674     protected void reinflateWidgetsIfNecessary() {
    675         final int clCount = getChildCount();
    676         for (int i = 0; i < clCount; i++) {
    677             CellLayout cl = (CellLayout) getChildAt(i);
    678             ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
    679             final int itemCount = swc.getChildCount();
    680             for (int j = 0; j < itemCount; j++) {
    681                 View v = swc.getChildAt(j);
    682 
    683                 if (v.getTag() instanceof LauncherAppWidgetInfo) {
    684                     LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
    685                     LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
    686                     if (lahv != null && lahv.orientationChangedSincedInflation()) {
    687                         mLauncher.removeAppWidget(info);
    688                         // Remove the current widget which is inflated with the wrong orientation
    689                         cl.removeView(lahv);
    690                         mLauncher.bindAppWidget(info);
    691                     }
    692                 }
    693             }
    694         }
    695     }
    696 
    697     @Override
    698     protected void determineScrollingStart(MotionEvent ev) {
    699         if (isSmall()) return;
    700         if (!isFinishedSwitchingState()) return;
    701 
    702         float deltaX = Math.abs(ev.getX() - mXDown);
    703         float deltaY = Math.abs(ev.getY() - mYDown);
    704 
    705         if (Float.compare(deltaX, 0f) == 0) return;
    706 
    707         float slope = deltaY / deltaX;
    708         float theta = (float) Math.atan(slope);
    709 
    710         if (deltaX > mTouchSlop || deltaY > mTouchSlop) {
    711             cancelCurrentPageLongPress();
    712         }
    713 
    714         if (theta > MAX_SWIPE_ANGLE) {
    715             // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
    716             return;
    717         } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
    718             // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
    719             // increase the touch slop to make it harder to begin scrolling the workspace. This
    720             // results in vertically scrolling widgets to more easily. The higher the angle, the
    721             // more we increase touch slop.
    722             theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
    723             float extraRatio = (float)
    724                     Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
    725             super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
    726         } else {
    727             // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
    728             super.determineScrollingStart(ev);
    729         }
    730     }
    731 
    732     @Override
    733     protected boolean isScrollingIndicatorEnabled() {
    734         return super.isScrollingIndicatorEnabled() && (mState != State.SPRING_LOADED);
    735     }
    736 
    737     protected void onPageBeginMoving() {
    738         super.onPageBeginMoving();
    739 
    740         if (isHardwareAccelerated()) {
    741             updateChildrenLayersEnabled();
    742         } else {
    743             if (mNextPage != INVALID_PAGE) {
    744                 // we're snapping to a particular screen
    745                 enableChildrenCache(mCurrentPage, mNextPage);
    746             } else {
    747                 // this is when user is actively dragging a particular screen, they might
    748                 // swipe it either left or right (but we won't advance by more than one screen)
    749                 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
    750             }
    751         }
    752 
    753         // Only show page outlines as we pan if we are on large screen
    754         if (LauncherApplication.isScreenLarge()) {
    755             showOutlines();
    756             mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null;
    757         }
    758 
    759         // If we are not fading in adjacent screens, we still need to restore the alpha in case the
    760         // user scrolls while we are transitioning (should not affect dispatchDraw optimizations)
    761         if (!mWorkspaceFadeInAdjacentScreens) {
    762             for (int i = 0; i < getChildCount(); ++i) {
    763                 ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f);
    764             }
    765         }
    766 
    767         // Show the scroll indicator as you pan the page
    768         showScrollingIndicator(false);
    769     }
    770 
    771     protected void onPageEndMoving() {
    772         super.onPageEndMoving();
    773 
    774         if (isHardwareAccelerated()) {
    775             updateChildrenLayersEnabled();
    776         } else {
    777             clearChildrenCache();
    778         }
    779 
    780 
    781         if (mDragController.isDragging()) {
    782             if (isSmall()) {
    783                 // If we are in springloaded mode, then force an event to check if the current touch
    784                 // is under a new page (to scroll to)
    785                 mDragController.forceMoveEvent();
    786             }
    787         } else {
    788             // If we are not mid-dragging, hide the page outlines if we are on a large screen
    789             if (LauncherApplication.isScreenLarge()) {
    790                 hideOutlines();
    791             }
    792 
    793             // Hide the scroll indicator as you pan the page
    794             if (!mDragController.isDragging()) {
    795                 hideScrollingIndicator(false);
    796             }
    797         }
    798         mOverScrollMaxBackgroundAlpha = 0.0f;
    799 
    800         if (mDelayedResizeRunnable != null) {
    801             mDelayedResizeRunnable.run();
    802             mDelayedResizeRunnable = null;
    803         }
    804 
    805         if (mDelayedSnapToPageRunnable != null) {
    806             mDelayedSnapToPageRunnable.run();
    807             mDelayedSnapToPageRunnable = null;
    808         }
    809     }
    810 
    811     @Override
    812     protected void notifyPageSwitchListener() {
    813         super.notifyPageSwitchListener();
    814         Launcher.setScreen(mCurrentPage);
    815     };
    816 
    817     // As a ratio of screen height, the total distance we want the parallax effect to span
    818     // horizontally
    819     private float wallpaperTravelToScreenWidthRatio(int width, int height) {
    820         float aspectRatio = width / (float) height;
    821 
    822         // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
    823         // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
    824         // We will use these two data points to extrapolate how much the wallpaper parallax effect
    825         // to span (ie travel) at any aspect ratio:
    826 
    827         final float ASPECT_RATIO_LANDSCAPE = 16/10f;
    828         final float ASPECT_RATIO_PORTRAIT = 10/16f;
    829         final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
    830         final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
    831 
    832         // To find out the desired width at different aspect ratios, we use the following two
    833         // formulas, where the coefficient on x is the aspect ratio (width/height):
    834         //   (16/10)x + y = 1.5
    835         //   (10/16)x + y = 1.2
    836         // We solve for x and y and end up with a final formula:
    837         final float x =
    838             (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
    839             (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
    840         final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
    841         return x * aspectRatio + y;
    842     }
    843 
    844     // The range of scroll values for Workspace
    845     private int getScrollRange() {
    846         return getChildOffset(getChildCount() - 1) - getChildOffset(0);
    847     }
    848 
    849     protected void setWallpaperDimension() {
    850         DisplayMetrics displayMetrics = new DisplayMetrics();
    851         mLauncher.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
    852         final int maxDim = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
    853         final int minDim = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
    854 
    855         // We need to ensure that there is enough extra space in the wallpaper for the intended
    856         // parallax effects
    857         if (LauncherApplication.isScreenLarge()) {
    858             mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
    859             mWallpaperHeight = maxDim;
    860         } else {
    861             mWallpaperWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
    862             mWallpaperHeight = maxDim;
    863         }
    864         new Thread("setWallpaperDimension") {
    865             public void run() {
    866                 mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight);
    867             }
    868         }.start();
    869     }
    870 
    871     private float wallpaperOffsetForCurrentScroll() {
    872         // Set wallpaper offset steps (1 / (number of screens - 1))
    873         mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
    874 
    875         // For the purposes of computing the scrollRange and overScrollOffset, we assume
    876         // that mLayoutScale is 1. This means that when we're in spring-loaded mode,
    877         // there's no discrepancy between the wallpaper offset for a given page.
    878         float layoutScale = mLayoutScale;
    879         mLayoutScale = 1f;
    880         int scrollRange = getScrollRange();
    881 
    882         // Again, we adjust the wallpaper offset to be consistent between values of mLayoutScale
    883         float adjustedScrollX = Math.max(0, Math.min(getScrollX(), mMaxScrollX));
    884         adjustedScrollX *= mWallpaperScrollRatio;
    885         mLayoutScale = layoutScale;
    886 
    887         float scrollProgress =
    888             adjustedScrollX / (float) scrollRange;
    889 
    890         if (LauncherApplication.isScreenLarge() && mIsStaticWallpaper) {
    891             // The wallpaper travel width is how far, from left to right, the wallpaper will move
    892             // at this orientation. On tablets in portrait mode we don't move all the way to the
    893             // edges of the wallpaper, or otherwise the parallax effect would be too strong.
    894             int wallpaperTravelWidth = Math.min(mWallpaperTravelWidth, mWallpaperWidth);
    895 
    896             float offsetInDips = wallpaperTravelWidth * scrollProgress +
    897                 (mWallpaperWidth - wallpaperTravelWidth) / 2; // center it
    898             float offset = offsetInDips / (float) mWallpaperWidth;
    899             return offset;
    900         } else {
    901             return scrollProgress;
    902         }
    903     }
    904 
    905     private void syncWallpaperOffsetWithScroll() {
    906         final boolean enableWallpaperEffects = isHardwareAccelerated();
    907         if (enableWallpaperEffects) {
    908             mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
    909         }
    910     }
    911 
    912     public void updateWallpaperOffsetImmediately() {
    913         mUpdateWallpaperOffsetImmediately = true;
    914     }
    915 
    916     private void updateWallpaperOffsets() {
    917         boolean updateNow = false;
    918         boolean keepUpdating = true;
    919         if (mUpdateWallpaperOffsetImmediately) {
    920             updateNow = true;
    921             keepUpdating = false;
    922             mWallpaperOffset.jumpToFinal();
    923             mUpdateWallpaperOffsetImmediately = false;
    924         } else {
    925             updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset();
    926         }
    927         if (updateNow) {
    928             if (mWindowToken != null) {
    929                 mWallpaperManager.setWallpaperOffsets(mWindowToken,
    930                         mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY());
    931             }
    932         }
    933         if (keepUpdating) {
    934             invalidate();
    935         }
    936     }
    937 
    938     @Override
    939     protected void updateCurrentPageScroll() {
    940         super.updateCurrentPageScroll();
    941         computeWallpaperScrollRatio(mCurrentPage);
    942     }
    943 
    944     @Override
    945     protected void snapToPage(int whichPage) {
    946         super.snapToPage(whichPage);
    947         computeWallpaperScrollRatio(whichPage);
    948     }
    949 
    950     @Override
    951     protected void snapToPage(int whichPage, int duration) {
    952         super.snapToPage(whichPage, duration);
    953         computeWallpaperScrollRatio(whichPage);
    954     }
    955 
    956     protected void snapToPage(int whichPage, Runnable r) {
    957         if (mDelayedSnapToPageRunnable != null) {
    958             mDelayedSnapToPageRunnable.run();
    959         }
    960         mDelayedSnapToPageRunnable = r;
    961         snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION);
    962     }
    963 
    964     private void computeWallpaperScrollRatio(int page) {
    965         // Here, we determine what the desired scroll would be with and without a layout scale,
    966         // and compute a ratio between the two. This allows us to adjust the wallpaper offset
    967         // as though there is no layout scale.
    968         float layoutScale = mLayoutScale;
    969         int scaled = getChildOffset(page) - getRelativeChildOffset(page);
    970         mLayoutScale = 1.0f;
    971         float unscaled = getChildOffset(page) - getRelativeChildOffset(page);
    972         mLayoutScale = layoutScale;
    973         if (scaled > 0) {
    974             mWallpaperScrollRatio = (1.0f * unscaled) / scaled;
    975         } else {
    976             mWallpaperScrollRatio = 1f;
    977         }
    978     }
    979 
    980     class WallpaperOffsetInterpolator {
    981         float mFinalHorizontalWallpaperOffset = 0.0f;
    982         float mFinalVerticalWallpaperOffset = 0.5f;
    983         float mHorizontalWallpaperOffset = 0.0f;
    984         float mVerticalWallpaperOffset = 0.5f;
    985         long mLastWallpaperOffsetUpdateTime;
    986         boolean mIsMovingFast;
    987         boolean mOverrideHorizontalCatchupConstant;
    988         float mHorizontalCatchupConstant = 0.35f;
    989         float mVerticalCatchupConstant = 0.35f;
    990 
    991         public WallpaperOffsetInterpolator() {
    992         }
    993 
    994         public void setOverrideHorizontalCatchupConstant(boolean override) {
    995             mOverrideHorizontalCatchupConstant = override;
    996         }
    997 
    998         public void setHorizontalCatchupConstant(float f) {
    999             mHorizontalCatchupConstant = f;
   1000         }
   1001 
   1002         public void setVerticalCatchupConstant(float f) {
   1003             mVerticalCatchupConstant = f;
   1004         }
   1005 
   1006         public boolean computeScrollOffset() {
   1007             if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 &&
   1008                     Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) {
   1009                 mIsMovingFast = false;
   1010                 return false;
   1011             }
   1012             boolean isLandscape = mDisplaySize.x > mDisplaySize.y;
   1013 
   1014             long currentTime = System.currentTimeMillis();
   1015             long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime;
   1016             timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate);
   1017             timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate);
   1018 
   1019             float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset);
   1020             if (!mIsMovingFast && xdiff > 0.07) {
   1021                 mIsMovingFast = true;
   1022             }
   1023 
   1024             float fractionToCatchUpIn1MsHorizontal;
   1025             if (mOverrideHorizontalCatchupConstant) {
   1026                 fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant;
   1027             } else if (mIsMovingFast) {
   1028                 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f;
   1029             } else {
   1030                 // slow
   1031                 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f;
   1032             }
   1033             float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant;
   1034 
   1035             fractionToCatchUpIn1MsHorizontal /= 33f;
   1036             fractionToCatchUpIn1MsVertical /= 33f;
   1037 
   1038             final float UPDATE_THRESHOLD = 0.00001f;
   1039             float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset;
   1040             float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset;
   1041             boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD &&
   1042                 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
   1043 
   1044             // Don't have any lag between workspace and wallpaper on non-large devices
   1045             if (!LauncherApplication.isScreenLarge() || jumpToFinalValue) {
   1046                 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
   1047                 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
   1048             } else {
   1049                 float percentToCatchUpVertical =
   1050                     Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical);
   1051                 float percentToCatchUpHorizontal =
   1052                     Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal);
   1053                 mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta;
   1054                 mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta;
   1055             }
   1056 
   1057             mLastWallpaperOffsetUpdateTime = System.currentTimeMillis();
   1058             return true;
   1059         }
   1060 
   1061         public float getCurrX() {
   1062             return mHorizontalWallpaperOffset;
   1063         }
   1064 
   1065         public float getFinalX() {
   1066             return mFinalHorizontalWallpaperOffset;
   1067         }
   1068 
   1069         public float getCurrY() {
   1070             return mVerticalWallpaperOffset;
   1071         }
   1072 
   1073         public float getFinalY() {
   1074             return mFinalVerticalWallpaperOffset;
   1075         }
   1076 
   1077         public void setFinalX(float x) {
   1078             mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f));
   1079         }
   1080 
   1081         public void setFinalY(float y) {
   1082             mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f));
   1083         }
   1084 
   1085         public void jumpToFinal() {
   1086             mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
   1087             mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
   1088         }
   1089     }
   1090 
   1091     @Override
   1092     public void computeScroll() {
   1093         super.computeScroll();
   1094         syncWallpaperOffsetWithScroll();
   1095     }
   1096 
   1097     void showOutlines() {
   1098         if (!isSmall() && !mIsSwitchingState) {
   1099             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
   1100             if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
   1101             mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f);
   1102             mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
   1103             mChildrenOutlineFadeInAnimation.start();
   1104         }
   1105     }
   1106 
   1107     void hideOutlines() {
   1108         if (!isSmall() && !mIsSwitchingState) {
   1109             if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
   1110             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
   1111             mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f);
   1112             mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
   1113             mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
   1114             mChildrenOutlineFadeOutAnimation.start();
   1115         }
   1116     }
   1117 
   1118     public void showOutlinesTemporarily() {
   1119         if (!mIsPageMoving && !isTouchActive()) {
   1120             snapToPage(mCurrentPage);
   1121         }
   1122     }
   1123 
   1124     public void setChildrenOutlineAlpha(float alpha) {
   1125         mChildrenOutlineAlpha = alpha;
   1126         for (int i = 0; i < getChildCount(); i++) {
   1127             CellLayout cl = (CellLayout) getChildAt(i);
   1128             cl.setBackgroundAlpha(alpha);
   1129         }
   1130     }
   1131 
   1132     public float getChildrenOutlineAlpha() {
   1133         return mChildrenOutlineAlpha;
   1134     }
   1135 
   1136     void disableBackground() {
   1137         mDrawBackground = false;
   1138     }
   1139     void enableBackground() {
   1140         mDrawBackground = true;
   1141     }
   1142 
   1143     private void animateBackgroundGradient(float finalAlpha, boolean animated) {
   1144         if (mBackground == null) return;
   1145         if (mBackgroundFadeInAnimation != null) {
   1146             mBackgroundFadeInAnimation.cancel();
   1147             mBackgroundFadeInAnimation = null;
   1148         }
   1149         if (mBackgroundFadeOutAnimation != null) {
   1150             mBackgroundFadeOutAnimation.cancel();
   1151             mBackgroundFadeOutAnimation = null;
   1152         }
   1153         float startAlpha = getBackgroundAlpha();
   1154         if (finalAlpha != startAlpha) {
   1155             if (animated) {
   1156                 mBackgroundFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
   1157                 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
   1158                     public void onAnimationUpdate(ValueAnimator animation) {
   1159                         setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
   1160                     }
   1161                 });
   1162                 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
   1163                 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
   1164                 mBackgroundFadeOutAnimation.start();
   1165             } else {
   1166                 setBackgroundAlpha(finalAlpha);
   1167             }
   1168         }
   1169     }
   1170 
   1171     public void setBackgroundAlpha(float alpha) {
   1172         if (alpha != mBackgroundAlpha) {
   1173             mBackgroundAlpha = alpha;
   1174             invalidate();
   1175         }
   1176     }
   1177 
   1178     public float getBackgroundAlpha() {
   1179         return mBackgroundAlpha;
   1180     }
   1181 
   1182     /**
   1183      * Due to 3D transformations, if two CellLayouts are theoretically touching each other,
   1184      * on the xy plane, when one is rotated along the y-axis, the gap between them is perceived
   1185      * as being larger. This method computes what offset the rotated view should be translated
   1186      * in order to minimize this perceived gap.
   1187      * @param degrees Angle of the view
   1188      * @param width Width of the view
   1189      * @param height Height of the view
   1190      * @return Offset to be used in a View.setTranslationX() call
   1191      */
   1192     private float getOffsetXForRotation(float degrees, int width, int height) {
   1193         mMatrix.reset();
   1194         mCamera.save();
   1195         mCamera.rotateY(Math.abs(degrees));
   1196         mCamera.getMatrix(mMatrix);
   1197         mCamera.restore();
   1198 
   1199         mMatrix.preTranslate(-width * 0.5f, -height * 0.5f);
   1200         mMatrix.postTranslate(width * 0.5f, height * 0.5f);
   1201         mTempFloat2[0] = width;
   1202         mTempFloat2[1] = height;
   1203         mMatrix.mapPoints(mTempFloat2);
   1204         return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f);
   1205     }
   1206 
   1207     float backgroundAlphaInterpolator(float r) {
   1208         float pivotA = 0.1f;
   1209         float pivotB = 0.4f;
   1210         if (r < pivotA) {
   1211             return 0;
   1212         } else if (r > pivotB) {
   1213             return 1.0f;
   1214         } else {
   1215             return (r - pivotA)/(pivotB - pivotA);
   1216         }
   1217     }
   1218 
   1219     float overScrollBackgroundAlphaInterpolator(float r) {
   1220         float threshold = 0.08f;
   1221 
   1222         if (r > mOverScrollMaxBackgroundAlpha) {
   1223             mOverScrollMaxBackgroundAlpha = r;
   1224         } else if (r < mOverScrollMaxBackgroundAlpha) {
   1225             r = mOverScrollMaxBackgroundAlpha;
   1226         }
   1227 
   1228         return Math.min(r / threshold, 1.0f);
   1229     }
   1230 
   1231     private void updatePageAlphaValues(int screenCenter) {
   1232         boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
   1233         if (mWorkspaceFadeInAdjacentScreens &&
   1234                 mState == State.NORMAL &&
   1235                 !mIsSwitchingState &&
   1236                 !isInOverscroll) {
   1237             for (int i = 0; i < getChildCount(); i++) {
   1238                 CellLayout child = (CellLayout) getChildAt(i);
   1239                 if (child != null) {
   1240                     float scrollProgress = getScrollProgress(screenCenter, child, i);
   1241                     float alpha = 1 - Math.abs(scrollProgress);
   1242                     child.getShortcutsAndWidgets().setAlpha(alpha);
   1243                     if (!mIsDragOccuring) {
   1244                         child.setBackgroundAlphaMultiplier(
   1245                                 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
   1246                     } else {
   1247                         child.setBackgroundAlphaMultiplier(1f);
   1248                     }
   1249                 }
   1250             }
   1251         }
   1252     }
   1253 
   1254     private void setChildrenBackgroundAlphaMultipliers(float a) {
   1255         for (int i = 0; i < getChildCount(); i++) {
   1256             CellLayout child = (CellLayout) getChildAt(i);
   1257             child.setBackgroundAlphaMultiplier(a);
   1258         }
   1259     }
   1260 
   1261     @Override
   1262     protected void screenScrolled(int screenCenter) {
   1263         super.screenScrolled(screenCenter);
   1264 
   1265         updatePageAlphaValues(screenCenter);
   1266 
   1267         if (mOverScrollX < 0 || mOverScrollX > mMaxScrollX) {
   1268             int index = mOverScrollX < 0 ? 0 : getChildCount() - 1;
   1269             CellLayout cl = (CellLayout) getChildAt(index);
   1270             float scrollProgress = getScrollProgress(screenCenter, cl, index);
   1271             cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
   1272             float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
   1273             cl.setRotationY(rotation);
   1274             setFadeForOverScroll(Math.abs(scrollProgress));
   1275             if (!mOverscrollTransformsSet) {
   1276                 mOverscrollTransformsSet = true;
   1277                 cl.setCameraDistance(mDensity * mCameraDistance);
   1278                 cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
   1279                 cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
   1280                 cl.setOverscrollTransformsDirty(true);
   1281             }
   1282         } else {
   1283             if (mOverscrollFade != 0) {
   1284                 setFadeForOverScroll(0);
   1285             }
   1286             if (mOverscrollTransformsSet) {
   1287                 mOverscrollTransformsSet = false;
   1288                 ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
   1289                 ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
   1290             }
   1291         }
   1292     }
   1293 
   1294     @Override
   1295     protected void overScroll(float amount) {
   1296         acceleratedOverScroll(amount);
   1297     }
   1298 
   1299     protected void onAttachedToWindow() {
   1300         super.onAttachedToWindow();
   1301         mWindowToken = getWindowToken();
   1302         computeScroll();
   1303         mDragController.setWindowToken(mWindowToken);
   1304     }
   1305 
   1306     protected void onDetachedFromWindow() {
   1307         mWindowToken = null;
   1308     }
   1309 
   1310     @Override
   1311     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
   1312         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
   1313             mUpdateWallpaperOffsetImmediately = true;
   1314         }
   1315         super.onLayout(changed, left, top, right, bottom);
   1316     }
   1317 
   1318     @Override
   1319     protected void onDraw(Canvas canvas) {
   1320         updateWallpaperOffsets();
   1321 
   1322         // Draw the background gradient if necessary
   1323         if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
   1324             int alpha = (int) (mBackgroundAlpha * 255);
   1325             mBackground.setAlpha(alpha);
   1326             mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(),
   1327                     getMeasuredHeight());
   1328             mBackground.draw(canvas);
   1329         }
   1330 
   1331         super.onDraw(canvas);
   1332     }
   1333 
   1334     boolean isDrawingBackgroundGradient() {
   1335         return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
   1336     }
   1337 
   1338     @Override
   1339     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
   1340         if (!mLauncher.isAllAppsVisible()) {
   1341             final Folder openFolder = getOpenFolder();
   1342             if (openFolder != null) {
   1343                 return openFolder.requestFocus(direction, previouslyFocusedRect);
   1344             } else {
   1345                 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
   1346             }
   1347         }
   1348         return false;
   1349     }
   1350 
   1351     @Override
   1352     public int getDescendantFocusability() {
   1353         if (isSmall()) {
   1354             return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
   1355         }
   1356         return super.getDescendantFocusability();
   1357     }
   1358 
   1359     @Override
   1360     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
   1361         if (!mLauncher.isAllAppsVisible()) {
   1362             final Folder openFolder = getOpenFolder();
   1363             if (openFolder != null) {
   1364                 openFolder.addFocusables(views, direction);
   1365             } else {
   1366                 super.addFocusables(views, direction, focusableMode);
   1367             }
   1368         }
   1369     }
   1370 
   1371     public boolean isSmall() {
   1372         return mState == State.SMALL || mState == State.SPRING_LOADED;
   1373     }
   1374 
   1375     void enableChildrenCache(int fromPage, int toPage) {
   1376         if (fromPage > toPage) {
   1377             final int temp = fromPage;
   1378             fromPage = toPage;
   1379             toPage = temp;
   1380         }
   1381 
   1382         final int screenCount = getChildCount();
   1383 
   1384         fromPage = Math.max(fromPage, 0);
   1385         toPage = Math.min(toPage, screenCount - 1);
   1386 
   1387         for (int i = fromPage; i <= toPage; i++) {
   1388             final CellLayout layout = (CellLayout) getChildAt(i);
   1389             layout.setChildrenDrawnWithCacheEnabled(true);
   1390             layout.setChildrenDrawingCacheEnabled(true);
   1391         }
   1392     }
   1393 
   1394     void clearChildrenCache() {
   1395         final int screenCount = getChildCount();
   1396         for (int i = 0; i < screenCount; i++) {
   1397             final CellLayout layout = (CellLayout) getChildAt(i);
   1398             layout.setChildrenDrawnWithCacheEnabled(false);
   1399             // In software mode, we don't want the items to continue to be drawn into bitmaps
   1400             if (!isHardwareAccelerated()) {
   1401                 layout.setChildrenDrawingCacheEnabled(false);
   1402             }
   1403         }
   1404     }
   1405 
   1406     private void updateChildrenLayersEnabled() {
   1407         boolean small = mState == State.SMALL || mIsSwitchingState;
   1408         boolean enableChildrenLayers = small || mAnimatingViewIntoPlace || isPageMoving();
   1409 
   1410         if (enableChildrenLayers != mChildrenLayersEnabled) {
   1411             mChildrenLayersEnabled = enableChildrenLayers;
   1412             for (int i = 0; i < getPageCount(); i++) {
   1413                 ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(mChildrenLayersEnabled);
   1414             }
   1415         }
   1416     }
   1417 
   1418     protected void onWallpaperTap(MotionEvent ev) {
   1419         final int[] position = mTempCell;
   1420         getLocationOnScreen(position);
   1421 
   1422         int pointerIndex = ev.getActionIndex();
   1423         position[0] += (int) ev.getX(pointerIndex);
   1424         position[1] += (int) ev.getY(pointerIndex);
   1425 
   1426         mWallpaperManager.sendWallpaperCommand(getWindowToken(),
   1427                 ev.getAction() == MotionEvent.ACTION_UP
   1428                         ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
   1429                 position[0], position[1], 0, null);
   1430     }
   1431 
   1432     /*
   1433      * This interpolator emulates the rate at which the perceived scale of an object changes
   1434      * as its distance from a camera increases. When this interpolator is applied to a scale
   1435      * animation on a view, it evokes the sense that the object is shrinking due to moving away
   1436      * from the camera.
   1437      */
   1438     static class ZInterpolator implements TimeInterpolator {
   1439         private float focalLength;
   1440 
   1441         public ZInterpolator(float foc) {
   1442             focalLength = foc;
   1443         }
   1444 
   1445         public float getInterpolation(float input) {
   1446             return (1.0f - focalLength / (focalLength + input)) /
   1447                 (1.0f - focalLength / (focalLength + 1.0f));
   1448         }
   1449     }
   1450 
   1451     /*
   1452      * The exact reverse of ZInterpolator.
   1453      */
   1454     static class InverseZInterpolator implements TimeInterpolator {
   1455         private ZInterpolator zInterpolator;
   1456         public InverseZInterpolator(float foc) {
   1457             zInterpolator = new ZInterpolator(foc);
   1458         }
   1459         public float getInterpolation(float input) {
   1460             return 1 - zInterpolator.getInterpolation(1 - input);
   1461         }
   1462     }
   1463 
   1464     /*
   1465      * ZInterpolator compounded with an ease-out.
   1466      */
   1467     static class ZoomOutInterpolator implements TimeInterpolator {
   1468         private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
   1469         private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
   1470 
   1471         public float getInterpolation(float input) {
   1472             return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
   1473         }
   1474     }
   1475 
   1476     /*
   1477      * InvereZInterpolator compounded with an ease-out.
   1478      */
   1479     static class ZoomInInterpolator implements TimeInterpolator {
   1480         private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
   1481         private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
   1482 
   1483         public float getInterpolation(float input) {
   1484             return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
   1485         }
   1486     }
   1487 
   1488     private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
   1489 
   1490     /*
   1491     *
   1492     * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
   1493     * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
   1494     *
   1495     * These methods mark the appropriate pages as accepting drops (which alters their visual
   1496     * appearance).
   1497     *
   1498     */
   1499     public void onDragStartedWithItem(View v) {
   1500         final Canvas canvas = new Canvas();
   1501 
   1502         // The outline is used to visualize where the item will land if dropped
   1503         mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
   1504     }
   1505 
   1506     public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, Paint alphaClipPaint) {
   1507         final Canvas canvas = new Canvas();
   1508 
   1509         int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
   1510 
   1511         // The outline is used to visualize where the item will land if dropped
   1512         mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
   1513                 size[1], alphaClipPaint);
   1514     }
   1515 
   1516     public void exitWidgetResizeMode() {
   1517         DragLayer dragLayer = mLauncher.getDragLayer();
   1518         dragLayer.clearAllResizeFrames();
   1519     }
   1520 
   1521     private void initAnimationArrays() {
   1522         final int childCount = getChildCount();
   1523         if (mOldTranslationXs != null) return;
   1524         mOldTranslationXs = new float[childCount];
   1525         mOldTranslationYs = new float[childCount];
   1526         mOldScaleXs = new float[childCount];
   1527         mOldScaleYs = new float[childCount];
   1528         mOldBackgroundAlphas = new float[childCount];
   1529         mOldAlphas = new float[childCount];
   1530         mNewTranslationXs = new float[childCount];
   1531         mNewTranslationYs = new float[childCount];
   1532         mNewScaleXs = new float[childCount];
   1533         mNewScaleYs = new float[childCount];
   1534         mNewBackgroundAlphas = new float[childCount];
   1535         mNewAlphas = new float[childCount];
   1536         mNewRotationYs = new float[childCount];
   1537     }
   1538 
   1539     Animator getChangeStateAnimation(final State state, boolean animated) {
   1540         return getChangeStateAnimation(state, animated, 0);
   1541     }
   1542 
   1543     Animator getChangeStateAnimation(final State state, boolean animated, int delay) {
   1544         if (mState == state) {
   1545             return null;
   1546         }
   1547 
   1548         // Initialize animation arrays for the first time if necessary
   1549         initAnimationArrays();
   1550 
   1551         AnimatorSet anim = animated ? new AnimatorSet() : null;
   1552 
   1553         // Stop any scrolling, move to the current page right away
   1554         setCurrentPage(getNextPage());
   1555 
   1556         final State oldState = mState;
   1557         final boolean oldStateIsNormal = (oldState == State.NORMAL);
   1558         final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
   1559         final boolean oldStateIsSmall = (oldState == State.SMALL);
   1560         mState = state;
   1561         final boolean stateIsNormal = (state == State.NORMAL);
   1562         final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
   1563         final boolean stateIsSmall = (state == State.SMALL);
   1564         float finalScaleFactor = 1.0f;
   1565         float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
   1566         float translationX = 0;
   1567         float translationY = 0;
   1568         boolean zoomIn = true;
   1569 
   1570         if (state != State.NORMAL) {
   1571             finalScaleFactor = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
   1572             setPageSpacing(mSpringLoadedPageSpacing);
   1573             if (oldStateIsNormal && stateIsSmall) {
   1574                 zoomIn = false;
   1575                 setLayoutScale(finalScaleFactor);
   1576                 updateChildrenLayersEnabled();
   1577             } else {
   1578                 finalBackgroundAlpha = 1.0f;
   1579                 setLayoutScale(finalScaleFactor);
   1580             }
   1581         } else {
   1582             setPageSpacing(PagedView.AUTOMATIC_PAGE_SPACING);
   1583             setLayoutScale(1.0f);
   1584         }
   1585 
   1586         final int duration = zoomIn ?
   1587                 getResources().getInteger(R.integer.config_workspaceUnshrinkTime) :
   1588                 getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
   1589         for (int i = 0; i < getChildCount(); i++) {
   1590             final CellLayout cl = (CellLayout) getChildAt(i);
   1591             float finalAlpha = (!mWorkspaceFadeInAdjacentScreens || stateIsSpringLoaded ||
   1592                     (i == mCurrentPage)) ? 1f : 0f;
   1593             float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
   1594             float initialAlpha = currentAlpha;
   1595 
   1596             // Determine the pages alpha during the state transition
   1597             if ((oldStateIsSmall && stateIsNormal) ||
   1598                 (oldStateIsNormal && stateIsSmall)) {
   1599                 // To/from workspace - only show the current page unless the transition is not
   1600                 //                     animated and the animation end callback below doesn't run;
   1601                 //                     or, if we're in spring-loaded mode
   1602                 if (i == mCurrentPage || !animated || oldStateIsSpringLoaded) {
   1603                     finalAlpha = 1f;
   1604                 } else {
   1605                     initialAlpha = 0f;
   1606                     finalAlpha = 0f;
   1607                 }
   1608             }
   1609 
   1610             mOldAlphas[i] = initialAlpha;
   1611             mNewAlphas[i] = finalAlpha;
   1612             if (animated) {
   1613                 mOldTranslationXs[i] = cl.getTranslationX();
   1614                 mOldTranslationYs[i] = cl.getTranslationY();
   1615                 mOldScaleXs[i] = cl.getScaleX();
   1616                 mOldScaleYs[i] = cl.getScaleY();
   1617                 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
   1618 
   1619                 mNewTranslationXs[i] = translationX;
   1620                 mNewTranslationYs[i] = translationY;
   1621                 mNewScaleXs[i] = finalScaleFactor;
   1622                 mNewScaleYs[i] = finalScaleFactor;
   1623                 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
   1624             } else {
   1625                 cl.setTranslationX(translationX);
   1626                 cl.setTranslationY(translationY);
   1627                 cl.setScaleX(finalScaleFactor);
   1628                 cl.setScaleY(finalScaleFactor);
   1629                 cl.setBackgroundAlpha(finalBackgroundAlpha);
   1630                 cl.setShortcutAndWidgetAlpha(finalAlpha);
   1631             }
   1632         }
   1633 
   1634         if (animated) {
   1635             for (int index = 0; index < getChildCount(); index++) {
   1636                 final int i = index;
   1637                 final CellLayout cl = (CellLayout) getChildAt(i);
   1638                 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
   1639                 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
   1640                     cl.setTranslationX(mNewTranslationXs[i]);
   1641                     cl.setTranslationY(mNewTranslationYs[i]);
   1642                     cl.setScaleX(mNewScaleXs[i]);
   1643                     cl.setScaleY(mNewScaleYs[i]);
   1644                     cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
   1645                     cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
   1646                     cl.setRotationY(mNewRotationYs[i]);
   1647                 } else {
   1648                     LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl);
   1649                     a.translationX(mNewTranslationXs[i])
   1650                         .translationY(mNewTranslationYs[i])
   1651                         .scaleX(mNewScaleXs[i])
   1652                         .scaleY(mNewScaleYs[i])
   1653                         .setDuration(duration)
   1654                         .setInterpolator(mZoomInInterpolator);
   1655                     anim.play(a);
   1656 
   1657                     if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
   1658                         LauncherViewPropertyAnimator alphaAnim =
   1659                             new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
   1660                         alphaAnim.alpha(mNewAlphas[i])
   1661                             .setDuration(duration)
   1662                             .setInterpolator(mZoomInInterpolator);
   1663                         anim.play(alphaAnim);
   1664                     }
   1665                     if (mOldBackgroundAlphas[i] != 0 ||
   1666                         mNewBackgroundAlphas[i] != 0) {
   1667                         ValueAnimator bgAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
   1668                         bgAnim.setInterpolator(mZoomInInterpolator);
   1669                         bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
   1670                                 public void onAnimationUpdate(float a, float b) {
   1671                                     cl.setBackgroundAlpha(
   1672                                             a * mOldBackgroundAlphas[i] +
   1673                                             b * mNewBackgroundAlphas[i]);
   1674                                 }
   1675                             });
   1676                         anim.play(bgAnim);
   1677                     }
   1678                 }
   1679             }
   1680             buildPageHardwareLayers();
   1681             anim.setStartDelay(delay);
   1682         }
   1683 
   1684         if (stateIsSpringLoaded) {
   1685             // Right now we're covered by Apps Customize
   1686             // Show the background gradient immediately, so the gradient will
   1687             // be showing once AppsCustomize disappears
   1688             animateBackgroundGradient(getResources().getInteger(
   1689                     R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
   1690         } else {
   1691             // Fade the background gradient away
   1692             animateBackgroundGradient(0f, true);
   1693         }
   1694         return anim;
   1695     }
   1696 
   1697     @Override
   1698     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
   1699         mIsSwitchingState = true;
   1700         cancelScrollingIndicatorAnimations();
   1701     }
   1702 
   1703     @Override
   1704     public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
   1705     }
   1706 
   1707     @Override
   1708     public void onLauncherTransitionStep(Launcher l, float t) {
   1709         mTransitionProgress = t;
   1710     }
   1711 
   1712     @Override
   1713     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
   1714         mIsSwitchingState = false;
   1715         mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
   1716         updateChildrenLayersEnabled();
   1717         // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
   1718         // ensure that only the current page is visible during (and subsequently, after) the
   1719         // transition animation.  If fade adjacent pages is disabled, then re-enable the page
   1720         // visibility after the transition animation.
   1721         if (!mWorkspaceFadeInAdjacentScreens) {
   1722             for (int i = 0; i < getChildCount(); i++) {
   1723                 final CellLayout cl = (CellLayout) getChildAt(i);
   1724                 cl.setShortcutAndWidgetAlpha(1f);
   1725             }
   1726         }
   1727     }
   1728 
   1729     @Override
   1730     public View getContent() {
   1731         return this;
   1732     }
   1733 
   1734     /**
   1735      * Draw the View v into the given Canvas.
   1736      *
   1737      * @param v the view to draw
   1738      * @param destCanvas the canvas to draw on
   1739      * @param padding the horizontal and vertical padding to use when drawing
   1740      */
   1741     private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
   1742         final Rect clipRect = mTempRect;
   1743         v.getDrawingRect(clipRect);
   1744 
   1745         boolean textVisible = false;
   1746 
   1747         destCanvas.save();
   1748         if (v instanceof TextView && pruneToDrawable) {
   1749             Drawable d = ((TextView) v).getCompoundDrawables()[1];
   1750             clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
   1751             destCanvas.translate(padding / 2, padding / 2);
   1752             d.draw(destCanvas);
   1753         } else {
   1754             if (v instanceof FolderIcon) {
   1755                 // For FolderIcons the text can bleed into the icon area, and so we need to
   1756                 // hide the text completely (which can't be achieved by clipping).
   1757                 if (((FolderIcon) v).getTextVisible()) {
   1758                     ((FolderIcon) v).setTextVisible(false);
   1759                     textVisible = true;
   1760                 }
   1761             } else if (v instanceof BubbleTextView) {
   1762                 final BubbleTextView tv = (BubbleTextView) v;
   1763                 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
   1764                         tv.getLayout().getLineTop(0);
   1765             } else if (v instanceof TextView) {
   1766                 final TextView tv = (TextView) v;
   1767                 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
   1768                         tv.getLayout().getLineTop(0);
   1769             }
   1770             destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
   1771             destCanvas.clipRect(clipRect, Op.REPLACE);
   1772             v.draw(destCanvas);
   1773 
   1774             // Restore text visibility of FolderIcon if necessary
   1775             if (textVisible) {
   1776                 ((FolderIcon) v).setTextVisible(true);
   1777             }
   1778         }
   1779         destCanvas.restore();
   1780     }
   1781 
   1782     /**
   1783      * Returns a new bitmap to show when the given View is being dragged around.
   1784      * Responsibility for the bitmap is transferred to the caller.
   1785      */
   1786     public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
   1787         Bitmap b;
   1788 
   1789         if (v instanceof TextView) {
   1790             Drawable d = ((TextView) v).getCompoundDrawables()[1];
   1791             b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
   1792                     d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
   1793         } else {
   1794             b = Bitmap.createBitmap(
   1795                     v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
   1796         }
   1797 
   1798         canvas.setBitmap(b);
   1799         drawDragView(v, canvas, padding, true);
   1800         canvas.setBitmap(null);
   1801 
   1802         return b;
   1803     }
   1804 
   1805     /**
   1806      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
   1807      * Responsibility for the bitmap is transferred to the caller.
   1808      */
   1809     private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
   1810         final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
   1811         final Bitmap b = Bitmap.createBitmap(
   1812                 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
   1813 
   1814         canvas.setBitmap(b);
   1815         drawDragView(v, canvas, padding, true);
   1816         mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
   1817         canvas.setBitmap(null);
   1818         return b;
   1819     }
   1820 
   1821     /**
   1822      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
   1823      * Responsibility for the bitmap is transferred to the caller.
   1824      */
   1825     private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
   1826             Paint alphaClipPaint) {
   1827         final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
   1828         final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
   1829         canvas.setBitmap(b);
   1830 
   1831         Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
   1832         float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
   1833                 (h - padding) / (float) orig.getHeight());
   1834         int scaledWidth = (int) (scaleFactor * orig.getWidth());
   1835         int scaledHeight = (int) (scaleFactor * orig.getHeight());
   1836         Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
   1837 
   1838         // center the image
   1839         dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
   1840 
   1841         canvas.drawBitmap(orig, src, dst, null);
   1842         mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
   1843                 alphaClipPaint);
   1844         canvas.setBitmap(null);
   1845 
   1846         return b;
   1847     }
   1848 
   1849     void startDrag(CellLayout.CellInfo cellInfo) {
   1850         View child = cellInfo.cell;
   1851 
   1852         // Make sure the drag was started by a long press as opposed to a long click.
   1853         if (!child.isInTouchMode()) {
   1854             return;
   1855         }
   1856 
   1857         mDragInfo = cellInfo;
   1858         child.setVisibility(INVISIBLE);
   1859         CellLayout layout = (CellLayout) child.getParent().getParent();
   1860         layout.prepareChildForDrag(child);
   1861 
   1862         child.clearFocus();
   1863         child.setPressed(false);
   1864 
   1865         final Canvas canvas = new Canvas();
   1866 
   1867         // The outline is used to visualize where the item will land if dropped
   1868         mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
   1869         beginDragShared(child, this);
   1870     }
   1871 
   1872     public void beginDragShared(View child, DragSource source) {
   1873         Resources r = getResources();
   1874 
   1875         // The drag bitmap follows the touch point around on the screen
   1876         final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
   1877 
   1878         final int bmpWidth = b.getWidth();
   1879         final int bmpHeight = b.getHeight();
   1880 
   1881         mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
   1882         int dragLayerX =
   1883                 Math.round(mTempXY[0] - (bmpWidth - child.getScaleX() * child.getWidth()) / 2);
   1884         int dragLayerY =
   1885                 Math.round(mTempXY[1] - (bmpHeight - child.getScaleY() * bmpHeight) / 2
   1886                         - DRAG_BITMAP_PADDING / 2);
   1887 
   1888         Point dragVisualizeOffset = null;
   1889         Rect dragRect = null;
   1890         if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
   1891             int iconSize = r.getDimensionPixelSize(R.dimen.app_icon_size);
   1892             int iconPaddingTop = r.getDimensionPixelSize(R.dimen.app_icon_padding_top);
   1893             int top = child.getPaddingTop();
   1894             int left = (bmpWidth - iconSize) / 2;
   1895             int right = left + iconSize;
   1896             int bottom = top + iconSize;
   1897             dragLayerY += top;
   1898             // Note: The drag region is used to calculate drag layer offsets, but the
   1899             // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
   1900             dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2,
   1901                     iconPaddingTop - DRAG_BITMAP_PADDING / 2);
   1902             dragRect = new Rect(left, top, right, bottom);
   1903         } else if (child instanceof FolderIcon) {
   1904             int previewSize = r.getDimensionPixelSize(R.dimen.folder_preview_size);
   1905             dragRect = new Rect(0, 0, child.getWidth(), previewSize);
   1906         }
   1907 
   1908         // Clear the pressed state if necessary
   1909         if (child instanceof BubbleTextView) {
   1910             BubbleTextView icon = (BubbleTextView) child;
   1911             icon.clearPressedOrFocusedBackground();
   1912         }
   1913 
   1914         mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
   1915                 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, child.getScaleX());
   1916         b.recycle();
   1917 
   1918         // Show the scrolling indicator when you pick up an item
   1919         showScrollingIndicator(false);
   1920     }
   1921 
   1922     void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, int screen,
   1923             int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
   1924         View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
   1925 
   1926         final int[] cellXY = new int[2];
   1927         target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
   1928         addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
   1929         LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen, cellXY[0],
   1930                 cellXY[1]);
   1931     }
   1932 
   1933     public boolean transitionStateShouldAllowDrop() {
   1934         return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
   1935     }
   1936 
   1937     /**
   1938      * {@inheritDoc}
   1939      */
   1940     public boolean acceptDrop(DragObject d) {
   1941         // If it's an external drop (e.g. from All Apps), check if it should be accepted
   1942         CellLayout dropTargetLayout = mDropToLayout;
   1943         if (d.dragSource != this) {
   1944             // Don't accept the drop if we're not over a screen at time of drop
   1945             if (dropTargetLayout == null) {
   1946                 return false;
   1947             }
   1948             if (!transitionStateShouldAllowDrop()) return false;
   1949 
   1950             mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
   1951                     d.dragView, mDragViewVisualCenter);
   1952 
   1953             // We want the point to be mapped to the dragTarget.
   1954             if (mLauncher.isHotseatLayout(dropTargetLayout)) {
   1955                 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
   1956             } else {
   1957                 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
   1958             }
   1959 
   1960             int spanX = 1;
   1961             int spanY = 1;
   1962             if (mDragInfo != null) {
   1963                 final CellLayout.CellInfo dragCellInfo = mDragInfo;
   1964                 spanX = dragCellInfo.spanX;
   1965                 spanY = dragCellInfo.spanY;
   1966             } else {
   1967                 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
   1968                 spanX = dragInfo.spanX;
   1969                 spanY = dragInfo.spanY;
   1970             }
   1971 
   1972             int minSpanX = spanX;
   1973             int minSpanY = spanY;
   1974             if (d.dragInfo instanceof PendingAddWidgetInfo) {
   1975                 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
   1976                 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
   1977             }
   1978 
   1979             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
   1980                     (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
   1981                     mTargetCell);
   1982             float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
   1983                     mDragViewVisualCenter[1], mTargetCell);
   1984             if (willCreateUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
   1985                     mTargetCell, distance, true)) {
   1986                 return true;
   1987             }
   1988             if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
   1989                     mTargetCell, distance)) {
   1990                 return true;
   1991             }
   1992 
   1993             int[] resultSpan = new int[2];
   1994             mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
   1995                     (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
   1996                     null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
   1997             boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
   1998 
   1999             // Don't accept the drop if there's no room for the item
   2000             if (!foundCell) {
   2001                 // Don't show the message if we are dropping on the AllApps button and the hotseat
   2002                 // is full
   2003                 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
   2004                 if (mTargetCell != null && isHotseat) {
   2005                     Hotseat hotseat = mLauncher.getHotseat();
   2006                     if (hotseat.isAllAppsButtonRank(
   2007                             hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
   2008                         return false;
   2009                     }
   2010                 }
   2011 
   2012                 mLauncher.showOutOfSpaceMessage(isHotseat);
   2013                 return false;
   2014             }
   2015         }
   2016         return true;
   2017     }
   2018 
   2019     boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
   2020             distance, boolean considerTimeout) {
   2021         if (distance > mMaxDistanceForFolderCreation) return false;
   2022         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
   2023 
   2024         if (dropOverView != null) {
   2025             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
   2026             if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
   2027                 return false;
   2028             }
   2029         }
   2030 
   2031         boolean hasntMoved = false;
   2032         if (mDragInfo != null) {
   2033             hasntMoved = dropOverView == mDragInfo.cell;
   2034         }
   2035 
   2036         if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
   2037             return false;
   2038         }
   2039 
   2040         boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
   2041         boolean willBecomeShortcut =
   2042                 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
   2043                 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
   2044 
   2045         return (aboveShortcut && willBecomeShortcut);
   2046     }
   2047 
   2048     boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
   2049             float distance) {
   2050         if (distance > mMaxDistanceForFolderCreation) return false;
   2051         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
   2052 
   2053         if (dropOverView != null) {
   2054             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
   2055             if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
   2056                 return false;
   2057             }
   2058         }
   2059 
   2060         if (dropOverView instanceof FolderIcon) {
   2061             FolderIcon fi = (FolderIcon) dropOverView;
   2062             if (fi.acceptDrop(dragInfo)) {
   2063                 return true;
   2064             }
   2065         }
   2066         return false;
   2067     }
   2068 
   2069     boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
   2070             int[] targetCell, float distance, boolean external, DragView dragView,
   2071             Runnable postAnimationRunnable) {
   2072         if (distance > mMaxDistanceForFolderCreation) return false;
   2073         View v = target.getChildAt(targetCell[0], targetCell[1]);
   2074 
   2075         boolean hasntMoved = false;
   2076         if (mDragInfo != null) {
   2077             CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
   2078             hasntMoved = (mDragInfo.cellX == targetCell[0] &&
   2079                     mDragInfo.cellY == targetCell[1]) && (cellParent == target);
   2080         }
   2081 
   2082         if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
   2083         mCreateUserFolderOnDrop = false;
   2084         final int screen = (targetCell == null) ? mDragInfo.screen : indexOfChild(target);
   2085 
   2086         boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
   2087         boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
   2088 
   2089         if (aboveShortcut && willBecomeShortcut) {
   2090             ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
   2091             ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
   2092             // if the drag started here, we need to remove it from the workspace
   2093             if (!external) {
   2094                 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
   2095             }
   2096 
   2097             Rect folderLocation = new Rect();
   2098             float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
   2099             target.removeView(v);
   2100 
   2101             FolderIcon fi =
   2102                 mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);
   2103             destInfo.cellX = -1;
   2104             destInfo.cellY = -1;
   2105             sourceInfo.cellX = -1;
   2106             sourceInfo.cellY = -1;
   2107 
   2108             // If the dragView is null, we can't animate
   2109             boolean animate = dragView != null;
   2110             if (animate) {
   2111                 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
   2112                         postAnimationRunnable);
   2113             } else {
   2114                 fi.addItem(destInfo);
   2115                 fi.addItem(sourceInfo);
   2116             }
   2117             return true;
   2118         }
   2119         return false;
   2120     }
   2121 
   2122     boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
   2123             float distance, DragObject d, boolean external) {
   2124         if (distance > mMaxDistanceForFolderCreation) return false;
   2125 
   2126         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
   2127         if (!mAddToExistingFolderOnDrop) return false;
   2128         mAddToExistingFolderOnDrop = false;
   2129 
   2130         if (dropOverView instanceof FolderIcon) {
   2131             FolderIcon fi = (FolderIcon) dropOverView;
   2132             if (fi.acceptDrop(d.dragInfo)) {
   2133                 fi.onDrop(d);
   2134 
   2135                 // if the drag started here, we need to remove it from the workspace
   2136                 if (!external) {
   2137                     getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
   2138                 }
   2139                 return true;
   2140             }
   2141         }
   2142         return false;
   2143     }
   2144 
   2145     public void onDrop(final DragObject d) {
   2146         mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
   2147                 mDragViewVisualCenter);
   2148 
   2149         CellLayout dropTargetLayout = mDropToLayout;
   2150 
   2151         // We want the point to be mapped to the dragTarget.
   2152         if (dropTargetLayout != null) {
   2153             if (mLauncher.isHotseatLayout(dropTargetLayout)) {
   2154                 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
   2155             } else {
   2156                 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
   2157             }
   2158         }
   2159 
   2160         int snapScreen = -1;
   2161         boolean resizeOnDrop = false;
   2162         if (d.dragSource != this) {
   2163             final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
   2164                     (int) mDragViewVisualCenter[1] };
   2165             onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
   2166         } else if (mDragInfo != null) {
   2167             final View cell = mDragInfo.cell;
   2168 
   2169             Runnable resizeRunnable = null;
   2170             if (dropTargetLayout != null) {
   2171                 // Move internally
   2172                 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
   2173                 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
   2174                 long container = hasMovedIntoHotseat ?
   2175                         LauncherSettings.Favorites.CONTAINER_HOTSEAT :
   2176                         LauncherSettings.Favorites.CONTAINER_DESKTOP;
   2177                 int screen = (mTargetCell[0] < 0) ?
   2178                         mDragInfo.screen : indexOfChild(dropTargetLayout);
   2179                 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
   2180                 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
   2181                 // First we find the cell nearest to point at which the item is
   2182                 // dropped, without any consideration to whether there is an item there.
   2183 
   2184                 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
   2185                         mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
   2186                 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
   2187                         mDragViewVisualCenter[1], mTargetCell);
   2188 
   2189                 // If the item being dropped is a shortcut and the nearest drop
   2190                 // cell also contains a shortcut, then create a folder with the two shortcuts.
   2191                 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
   2192                         dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
   2193                     return;
   2194                 }
   2195 
   2196                 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
   2197                         distance, d, false)) {
   2198                     return;
   2199                 }
   2200 
   2201                 // Aside from the special case where we're dropping a shortcut onto a shortcut,
   2202                 // we need to find the nearest cell location that is vacant
   2203                 ItemInfo item = (ItemInfo) d.dragInfo;
   2204                 int minSpanX = item.spanX;
   2205                 int minSpanY = item.spanY;
   2206                 if (item.minSpanX > 0 && item.minSpanY > 0) {
   2207                     minSpanX = item.minSpanX;
   2208                     minSpanY = item.minSpanY;
   2209                 }
   2210 
   2211                 int[] resultSpan = new int[2];
   2212                 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
   2213                         (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
   2214                         mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
   2215 
   2216                 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
   2217                 if (foundCell && (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
   2218                     resizeOnDrop = true;
   2219                     item.spanX = resultSpan[0];
   2220                     item.spanY = resultSpan[1];
   2221                     AppWidgetHostView awhv = (AppWidgetHostView) cell;
   2222                     AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
   2223                             resultSpan[1]);
   2224                 }
   2225 
   2226                 if (mCurrentPage != screen && !hasMovedIntoHotseat) {
   2227                     snapScreen = screen;
   2228                     snapToPage(screen);
   2229                 }
   2230 
   2231                 if (foundCell) {
   2232                     final ItemInfo info = (ItemInfo) cell.getTag();
   2233                     if (hasMovedLayouts) {
   2234                         // Reparent the view
   2235                         getParentCellLayoutForView(cell).removeView(cell);
   2236                         addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
   2237                                 info.spanX, info.spanY);
   2238                     }
   2239 
   2240                     // update the item's position after drop
   2241                     CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
   2242                     lp.cellX = lp.tmpCellX = mTargetCell[0];
   2243                     lp.cellY = lp.tmpCellY = mTargetCell[1];
   2244                     lp.cellHSpan = item.spanX;
   2245                     lp.cellVSpan = item.spanY;
   2246                     lp.isLockedToGrid = true;
   2247                     cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
   2248                             mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
   2249 
   2250                     if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
   2251                             cell instanceof LauncherAppWidgetHostView) {
   2252                         final CellLayout cellLayout = dropTargetLayout;
   2253                         // We post this call so that the widget has a chance to be placed
   2254                         // in its final location
   2255 
   2256                         final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
   2257                         AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
   2258                         if (pinfo != null &&
   2259                                 pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
   2260                             final Runnable addResizeFrame = new Runnable() {
   2261                                 public void run() {
   2262                                     DragLayer dragLayer = mLauncher.getDragLayer();
   2263                                     dragLayer.addResizeFrame(info, hostView, cellLayout);
   2264                                 }
   2265                             };
   2266                             resizeRunnable = (new Runnable() {
   2267                                 public void run() {
   2268                                     if (!isPageMoving()) {
   2269                                         addResizeFrame.run();
   2270                                     } else {
   2271                                         mDelayedResizeRunnable = addResizeFrame;
   2272                                     }
   2273                                 }
   2274                             });
   2275                         }
   2276                     }
   2277 
   2278                     LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, lp.cellX,
   2279                             lp.cellY);
   2280                 } else {
   2281                     // If we can't find a drop location, we return the item to its original position
   2282                     CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
   2283                     mTargetCell[0] = lp.cellX;
   2284                     mTargetCell[1] = lp.cellY;
   2285                     CellLayout layout = (CellLayout) cell.getParent().getParent();
   2286                     layout.markCellsAsOccupiedForView(cell);
   2287                 }
   2288             }
   2289 
   2290             final CellLayout parent = (CellLayout) cell.getParent().getParent();
   2291             final Runnable finalResizeRunnable = resizeRunnable;
   2292             // Prepare it to be animated into its new position
   2293             // This must be called after the view has been re-parented
   2294             final Runnable onCompleteRunnable = new Runnable() {
   2295                 @Override
   2296                 public void run() {
   2297                     mAnimatingViewIntoPlace = false;
   2298                     updateChildrenLayersEnabled();
   2299                     if (finalResizeRunnable != null) {
   2300                         finalResizeRunnable.run();
   2301                     }
   2302                 }
   2303             };
   2304             mAnimatingViewIntoPlace = true;
   2305             if (d.dragView.hasDrawn()) {
   2306                 final ItemInfo info = (ItemInfo) cell.getTag();
   2307                 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
   2308                     int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
   2309                             ANIMATE_INTO_POSITION_AND_DISAPPEAR;
   2310                     animateWidgetDrop(info, parent, d.dragView,
   2311                             onCompleteRunnable, animationType, cell, false);
   2312                 } else {
   2313                     int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
   2314                     mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
   2315                             onCompleteRunnable, this);
   2316                 }
   2317             } else {
   2318                 d.deferDragViewCleanupPostAnimation = false;
   2319                 cell.setVisibility(VISIBLE);
   2320             }
   2321             parent.onDropChild(cell);
   2322         }
   2323     }
   2324 
   2325     public void setFinalScrollForPageChange(int screen) {
   2326         if (screen >= 0) {
   2327             mSavedScrollX = getScrollX();
   2328             CellLayout cl = (CellLayout) getChildAt(screen);
   2329             mSavedTranslationX = cl.getTranslationX();
   2330             mSavedRotationY = cl.getRotationY();
   2331             final int newX = getChildOffset(screen) - getRelativeChildOffset(screen);
   2332             setScrollX(newX);
   2333             cl.setTranslationX(0f);
   2334             cl.setRotationY(0f);
   2335         }
   2336     }
   2337 
   2338     public void resetFinalScrollForPageChange(int screen) {
   2339         if (screen >= 0) {
   2340             CellLayout cl = (CellLayout) getChildAt(screen);
   2341             setScrollX(mSavedScrollX);
   2342             cl.setTranslationX(mSavedTranslationX);
   2343             cl.setRotationY(mSavedRotationY);
   2344         }
   2345     }
   2346 
   2347     public void getViewLocationRelativeToSelf(View v, int[] location) {
   2348         getLocationInWindow(location);
   2349         int x = location[0];
   2350         int y = location[1];
   2351 
   2352         v.getLocationInWindow(location);
   2353         int vX = location[0];
   2354         int vY = location[1];
   2355 
   2356         location[0] = vX - x;
   2357         location[1] = vY - y;
   2358     }
   2359 
   2360     public void onDragEnter(DragObject d) {
   2361         mDragEnforcer.onDragEnter();
   2362         mCreateUserFolderOnDrop = false;
   2363         mAddToExistingFolderOnDrop = false;
   2364 
   2365         mDropToLayout = null;
   2366         CellLayout layout = getCurrentDropLayout();
   2367         setCurrentDropLayout(layout);
   2368         setCurrentDragOverlappingLayout(layout);
   2369 
   2370         // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
   2371         // don't need to show the outlines
   2372         if (LauncherApplication.isScreenLarge()) {
   2373             showOutlines();
   2374         }
   2375     }
   2376 
   2377     static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
   2378         Resources res = launcher.getResources();
   2379         Display display = launcher.getWindowManager().getDefaultDisplay();
   2380         Point smallestSize = new Point();
   2381         Point largestSize = new Point();
   2382         display.getCurrentSizeRange(smallestSize, largestSize);
   2383         if (orientation == CellLayout.LANDSCAPE) {
   2384             if (mLandscapeCellLayoutMetrics == null) {
   2385                 int paddingLeft = res.getDimensionPixelSize(R.dimen.workspace_left_padding_land);
   2386                 int paddingRight = res.getDimensionPixelSize(R.dimen.workspace_right_padding_land);
   2387                 int paddingTop = res.getDimensionPixelSize(R.dimen.workspace_top_padding_land);
   2388                 int paddingBottom = res.getDimensionPixelSize(R.dimen.workspace_bottom_padding_land);
   2389                 int width = largestSize.x - paddingLeft - paddingRight;
   2390                 int height = smallestSize.y - paddingTop - paddingBottom;
   2391                 mLandscapeCellLayoutMetrics = new Rect();
   2392                 CellLayout.getMetrics(mLandscapeCellLayoutMetrics, res,
   2393                         width, height, LauncherModel.getCellCountX(), LauncherModel.getCellCountY(),
   2394                         orientation);
   2395             }
   2396             return mLandscapeCellLayoutMetrics;
   2397         } else if (orientation == CellLayout.PORTRAIT) {
   2398             if (mPortraitCellLayoutMetrics == null) {
   2399                 int paddingLeft = res.getDimensionPixelSize(R.dimen.workspace_left_padding_land);
   2400                 int paddingRight = res.getDimensionPixelSize(R.dimen.workspace_right_padding_land);
   2401                 int paddingTop = res.getDimensionPixelSize(R.dimen.workspace_top_padding_land);
   2402                 int paddingBottom = res.getDimensionPixelSize(R.dimen.workspace_bottom_padding_land);
   2403                 int width = smallestSize.x - paddingLeft - paddingRight;
   2404                 int height = largestSize.y - paddingTop - paddingBottom;
   2405                 mPortraitCellLayoutMetrics = new Rect();
   2406                 CellLayout.getMetrics(mPortraitCellLayoutMetrics, res,
   2407                         width, height, LauncherModel.getCellCountX(), LauncherModel.getCellCountY(),
   2408                         orientation);
   2409             }
   2410             return mPortraitCellLayoutMetrics;
   2411         }
   2412         return null;
   2413     }
   2414 
   2415     public void onDragExit(DragObject d) {
   2416         mDragEnforcer.onDragExit();
   2417 
   2418         // Here we store the final page that will be dropped to, if the workspace in fact
   2419         // receives the drop
   2420         if (mInScrollArea) {
   2421             mDropToLayout = mDragOverlappingLayout;
   2422         } else {
   2423             mDropToLayout = mDragTargetLayout;
   2424         }
   2425 
   2426         if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
   2427             mCreateUserFolderOnDrop = true;
   2428         } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
   2429             mAddToExistingFolderOnDrop = true;
   2430         }
   2431 
   2432         // Reset the scroll area and previous drag target
   2433         onResetScrollArea();
   2434         setCurrentDropLayout(null);
   2435         setCurrentDragOverlappingLayout(null);
   2436 
   2437         mSpringLoadedDragController.cancel();
   2438 
   2439         if (!mIsPageMoving) {
   2440             hideOutlines();
   2441         }
   2442     }
   2443 
   2444     void setCurrentDropLayout(CellLayout layout) {
   2445         if (mDragTargetLayout != null) {
   2446             mDragTargetLayout.revertTempState();
   2447             mDragTargetLayout.onDragExit();
   2448         }
   2449         mDragTargetLayout = layout;
   2450         if (mDragTargetLayout != null) {
   2451             mDragTargetLayout.onDragEnter();
   2452         }
   2453         cleanupReorder(true);
   2454         cleanupFolderCreation();
   2455         setCurrentDropOverCell(-1, -1);
   2456     }
   2457 
   2458     void setCurrentDragOverlappingLayout(CellLayout layout) {
   2459         if (mDragOverlappingLayout != null) {
   2460             mDragOverlappingLayout.setIsDragOverlapping(false);
   2461         }
   2462         mDragOverlappingLayout = layout;
   2463         if (mDragOverlappingLayout != null) {
   2464             mDragOverlappingLayout.setIsDragOverlapping(true);
   2465         }
   2466         invalidate();
   2467     }
   2468 
   2469     void setCurrentDropOverCell(int x, int y) {
   2470         if (x != mDragOverX || y != mDragOverY) {
   2471             mDragOverX = x;
   2472             mDragOverY = y;
   2473             setDragMode(DRAG_MODE_NONE);
   2474         }
   2475     }
   2476 
   2477     void setDragMode(int dragMode) {
   2478         if (dragMode != mDragMode) {
   2479             if (dragMode == DRAG_MODE_NONE) {
   2480                 cleanupAddToFolder();
   2481                 // We don't want to cancel the re-order alarm every time the target cell changes
   2482                 // as this feels to slow / unresponsive.
   2483                 cleanupReorder(false);
   2484                 cleanupFolderCreation();
   2485             } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
   2486                 cleanupReorder(true);
   2487                 cleanupFolderCreation();
   2488             } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
   2489                 cleanupAddToFolder();
   2490                 cleanupReorder(true);
   2491             } else if (dragMode == DRAG_MODE_REORDER) {
   2492                 cleanupAddToFolder();
   2493                 cleanupFolderCreation();
   2494             }
   2495             mDragMode = dragMode;
   2496         }
   2497     }
   2498 
   2499     private void cleanupFolderCreation() {
   2500         if (mDragFolderRingAnimator != null) {
   2501             mDragFolderRingAnimator.animateToNaturalState();
   2502         }
   2503         mFolderCreationAlarm.cancelAlarm();
   2504     }
   2505 
   2506     private void cleanupAddToFolder() {
   2507         if (mDragOverFolderIcon != null) {
   2508             mDragOverFolderIcon.onDragExit(null);
   2509             mDragOverFolderIcon = null;
   2510         }
   2511     }
   2512 
   2513     private void cleanupReorder(boolean cancelAlarm) {
   2514         // Any pending reorders are canceled
   2515         if (cancelAlarm) {
   2516             mReorderAlarm.cancelAlarm();
   2517         }
   2518         mLastReorderX = -1;
   2519         mLastReorderY = -1;
   2520     }
   2521 
   2522     public DropTarget getDropTargetDelegate(DragObject d) {
   2523         return null;
   2524     }
   2525 
   2526     /*
   2527     *
   2528     * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
   2529     * coordinate space. The argument xy is modified with the return result.
   2530     *
   2531     */
   2532    void mapPointFromSelfToChild(View v, float[] xy) {
   2533        mapPointFromSelfToChild(v, xy, null);
   2534    }
   2535 
   2536    /*
   2537     *
   2538     * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
   2539     * coordinate space. The argument xy is modified with the return result.
   2540     *
   2541     * if cachedInverseMatrix is not null, this method will just use that matrix instead of
   2542     * computing it itself; we use this to avoid redundant matrix inversions in
   2543     * findMatchingPageForDragOver
   2544     *
   2545     */
   2546    void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
   2547        if (cachedInverseMatrix == null) {
   2548            v.getMatrix().invert(mTempInverseMatrix);
   2549            cachedInverseMatrix = mTempInverseMatrix;
   2550        }
   2551        int scrollX = getScrollX();
   2552        if (mNextPage != INVALID_PAGE) {
   2553            scrollX = mScroller.getFinalX();
   2554        }
   2555        xy[0] = xy[0] + scrollX - v.getLeft();
   2556        xy[1] = xy[1] + getScrollY() - v.getTop();
   2557        cachedInverseMatrix.mapPoints(xy);
   2558    }
   2559 
   2560    /*
   2561     * Maps a point from the Workspace's coordinate system to another sibling view's. (Workspace
   2562     * covers the full screen)
   2563     */
   2564    void mapPointFromSelfToSibling(View v, float[] xy) {
   2565        xy[0] = xy[0] - v.getLeft();
   2566        xy[1] = xy[1] - v.getTop();
   2567    }
   2568 
   2569    void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
   2570        xy[0] = xy[0] - hotseat.getLeft() - hotseat.getLayout().getLeft();
   2571        xy[1] = xy[1] - hotseat.getTop() - hotseat.getLayout().getTop();
   2572    }
   2573 
   2574    /*
   2575     *
   2576     * Convert the 2D coordinate xy from this CellLayout's coordinate space to
   2577     * the parent View's coordinate space. The argument xy is modified with the return result.
   2578     *
   2579     */
   2580    void mapPointFromChildToSelf(View v, float[] xy) {
   2581        v.getMatrix().mapPoints(xy);
   2582        int scrollX = getScrollX();
   2583        if (mNextPage != INVALID_PAGE) {
   2584            scrollX = mScroller.getFinalX();
   2585        }
   2586        xy[0] -= (scrollX - v.getLeft());
   2587        xy[1] -= (getScrollY() - v.getTop());
   2588    }
   2589 
   2590    static private float squaredDistance(float[] point1, float[] point2) {
   2591         float distanceX = point1[0] - point2[0];
   2592         float distanceY = point2[1] - point2[1];
   2593         return distanceX * distanceX + distanceY * distanceY;
   2594    }
   2595 
   2596     /*
   2597      *
   2598      * Returns true if the passed CellLayout cl overlaps with dragView
   2599      *
   2600      */
   2601     boolean overlaps(CellLayout cl, DragView dragView,
   2602             int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
   2603         // Transform the coordinates of the item being dragged to the CellLayout's coordinates
   2604         final float[] draggedItemTopLeft = mTempDragCoordinates;
   2605         draggedItemTopLeft[0] = dragViewX;
   2606         draggedItemTopLeft[1] = dragViewY;
   2607         final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
   2608         draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth();
   2609         draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight();
   2610 
   2611         // Transform the dragged item's top left coordinates
   2612         // to the CellLayout's local coordinates
   2613         mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
   2614         float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
   2615         float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
   2616 
   2617         if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
   2618             // Transform the dragged item's bottom right coordinates
   2619             // to the CellLayout's local coordinates
   2620             mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
   2621             float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
   2622             float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
   2623 
   2624             if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
   2625                 float overlap = (overlapRegionRight - overlapRegionLeft) *
   2626                          (overlapRegionBottom - overlapRegionTop);
   2627                 if (overlap > 0) {
   2628                     return true;
   2629                 }
   2630              }
   2631         }
   2632         return false;
   2633     }
   2634 
   2635     /*
   2636      *
   2637      * This method returns the CellLayout that is currently being dragged to. In order to drag
   2638      * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
   2639      * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
   2640      *
   2641      * Return null if no CellLayout is currently being dragged over
   2642      *
   2643      */
   2644     private CellLayout findMatchingPageForDragOver(
   2645             DragView dragView, float originX, float originY, boolean exact) {
   2646         // We loop through all the screens (ie CellLayouts) and see which ones overlap
   2647         // with the item being dragged and then choose the one that's closest to the touch point
   2648         final int screenCount = getChildCount();
   2649         CellLayout bestMatchingScreen = null;
   2650         float smallestDistSoFar = Float.MAX_VALUE;
   2651 
   2652         for (int i = 0; i < screenCount; i++) {
   2653             CellLayout cl = (CellLayout) getChildAt(i);
   2654 
   2655             final float[] touchXy = {originX, originY};
   2656             // Transform the touch coordinates to the CellLayout's local coordinates
   2657             // If the touch point is within the bounds of the cell layout, we can return immediately
   2658             cl.getMatrix().invert(mTempInverseMatrix);
   2659             mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
   2660 
   2661             if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
   2662                     touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
   2663                 return cl;
   2664             }
   2665 
   2666             if (!exact) {
   2667                 // Get the center of the cell layout in screen coordinates
   2668                 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
   2669                 cellLayoutCenter[0] = cl.getWidth()/2;
   2670                 cellLayoutCenter[1] = cl.getHeight()/2;
   2671                 mapPointFromChildToSelf(cl, cellLayoutCenter);
   2672 
   2673                 touchXy[0] = originX;
   2674                 touchXy[1] = originY;
   2675 
   2676                 // Calculate the distance between the center of the CellLayout
   2677                 // and the touch point
   2678                 float dist = squaredDistance(touchXy, cellLayoutCenter);
   2679 
   2680                 if (dist < smallestDistSoFar) {
   2681                     smallestDistSoFar = dist;
   2682                     bestMatchingScreen = cl;
   2683                 }
   2684             }
   2685         }
   2686         return bestMatchingScreen;
   2687     }
   2688 
   2689     // This is used to compute the visual center of the dragView. This point is then
   2690     // used to visualize drop locations and determine where to drop an item. The idea is that
   2691     // the visual center represents the user's interpretation of where the item is, and hence
   2692     // is the appropriate point to use when determining drop location.
   2693     private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
   2694             DragView dragView, float[] recycle) {
   2695         float res[];
   2696         if (recycle == null) {
   2697             res = new float[2];
   2698         } else {
   2699             res = recycle;
   2700         }
   2701 
   2702         // First off, the drag view has been shifted in a way that is not represented in the
   2703         // x and y values or the x/yOffsets. Here we account for that shift.
   2704         x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
   2705         y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
   2706 
   2707         // These represent the visual top and left of drag view if a dragRect was provided.
   2708         // If a dragRect was not provided, then they correspond to the actual view left and
   2709         // top, as the dragRect is in that case taken to be the entire dragView.
   2710         // R.dimen.dragViewOffsetY.
   2711         int left = x - xOffset;
   2712         int top = y - yOffset;
   2713 
   2714         // In order to find the visual center, we shift by half the dragRect
   2715         res[0] = left + dragView.getDragRegion().width() / 2;
   2716         res[1] = top + dragView.getDragRegion().height() / 2;
   2717 
   2718         return res;
   2719     }
   2720 
   2721     private boolean isDragWidget(DragObject d) {
   2722         return (d.dragInfo instanceof LauncherAppWidgetInfo ||
   2723                 d.dragInfo instanceof PendingAddWidgetInfo);
   2724     }
   2725     private boolean isExternalDragWidget(DragObject d) {
   2726         return d.dragSource != this && isDragWidget(d);
   2727     }
   2728 
   2729     public void onDragOver(DragObject d) {
   2730         // Skip drag over events while we are dragging over side pages
   2731         if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return;
   2732 
   2733         Rect r = new Rect();
   2734         CellLayout layout = null;
   2735         ItemInfo item = (ItemInfo) d.dragInfo;
   2736 
   2737         // Ensure that we have proper spans for the item that we are dropping
   2738         if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
   2739         mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
   2740             d.dragView, mDragViewVisualCenter);
   2741 
   2742         final View child = (mDragInfo == null) ? null : mDragInfo.cell;
   2743         // Identify whether we have dragged over a side page
   2744         if (isSmall()) {
   2745             if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
   2746                 mLauncher.getHotseat().getHitRect(r);
   2747                 if (r.contains(d.x, d.y)) {
   2748                     layout = mLauncher.getHotseat().getLayout();
   2749                 }
   2750             }
   2751             if (layout == null) {
   2752                 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
   2753             }
   2754             if (layout != mDragTargetLayout) {
   2755 
   2756                 setCurrentDropLayout(layout);
   2757                 setCurrentDragOverlappingLayout(layout);
   2758 
   2759                 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
   2760                 if (isInSpringLoadedMode) {
   2761                     if (mLauncher.isHotseatLayout(layout)) {
   2762                         mSpringLoadedDragController.cancel();
   2763                     } else {
   2764                         mSpringLoadedDragController.setAlarm(mDragTargetLayout);
   2765                     }
   2766                 }
   2767             }
   2768         } else {
   2769             // Test to see if we are over the hotseat otherwise just use the current page
   2770             if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
   2771                 mLauncher.getHotseat().getHitRect(r);
   2772                 if (r.contains(d.x, d.y)) {
   2773                     layout = mLauncher.getHotseat().getLayout();
   2774                 }
   2775             }
   2776             if (layout == null) {
   2777                 layout = getCurrentDropLayout();
   2778             }
   2779             if (layout != mDragTargetLayout) {
   2780                 setCurrentDropLayout(layout);
   2781                 setCurrentDragOverlappingLayout(layout);
   2782             }
   2783         }
   2784 
   2785         // Handle the drag over
   2786         if (mDragTargetLayout != null) {
   2787             // We want the point to be mapped to the dragTarget.
   2788             if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
   2789                 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
   2790             } else {
   2791                 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
   2792             }
   2793 
   2794             ItemInfo info = (ItemInfo) d.dragInfo;
   2795 
   2796             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
   2797                     (int) mDragViewVisualCenter[1], item.spanX, item.spanY,
   2798                     mDragTargetLayout, mTargetCell);
   2799 
   2800             setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
   2801 
   2802             float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
   2803                     mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
   2804 
   2805             final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
   2806                     mTargetCell[1]);
   2807 
   2808             manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
   2809                     targetCellDistance, dragOverView);
   2810 
   2811             int minSpanX = item.spanX;
   2812             int minSpanY = item.spanY;
   2813             if (item.minSpanX > 0 && item.minSpanY > 0) {
   2814                 minSpanX = item.minSpanX;
   2815                 minSpanY = item.minSpanY;
   2816             }
   2817 
   2818             boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
   2819                     mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
   2820                     item.spanY, child, mTargetCell);
   2821 
   2822             if (!nearestDropOccupied) {
   2823                 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
   2824                         (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
   2825                         mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
   2826                         d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
   2827             } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
   2828                     && !mReorderAlarm.alarmPending() && (mLastReorderX != mTargetCell[0] ||
   2829                     mLastReorderY != mTargetCell[1])) {
   2830 
   2831                 // Otherwise, if we aren't adding to or creating a folder and there's no pending
   2832                 // reorder, then we schedule a reorder
   2833                 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
   2834                         minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
   2835                 mReorderAlarm.setOnAlarmListener(listener);
   2836                 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
   2837             }
   2838 
   2839             if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
   2840                     !nearestDropOccupied) {
   2841                 if (mDragTargetLayout != null) {
   2842                     mDragTargetLayout.revertTempState();
   2843                 }
   2844             }
   2845         }
   2846     }
   2847 
   2848     private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
   2849             int[] targetCell, float distance, View dragOverView) {
   2850         boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
   2851                 false);
   2852 
   2853         if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
   2854                 !mFolderCreationAlarm.alarmPending()) {
   2855             mFolderCreationAlarm.setOnAlarmListener(new
   2856                     FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
   2857             mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
   2858             return;
   2859         }
   2860 
   2861         boolean willAddToFolder =
   2862                 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
   2863 
   2864         if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
   2865             mDragOverFolderIcon = ((FolderIcon) dragOverView);
   2866             mDragOverFolderIcon.onDragEnter(info);
   2867             if (targetLayout != null) {
   2868                 targetLayout.clearDragOutlines();
   2869             }
   2870             setDragMode(DRAG_MODE_ADD_TO_FOLDER);
   2871             return;
   2872         }
   2873 
   2874         if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
   2875             setDragMode(DRAG_MODE_NONE);
   2876         }
   2877         if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
   2878             setDragMode(DRAG_MODE_NONE);
   2879         }
   2880 
   2881         return;
   2882     }
   2883 
   2884     class FolderCreationAlarmListener implements OnAlarmListener {
   2885         CellLayout layout;
   2886         int cellX;
   2887         int cellY;
   2888 
   2889         public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
   2890             this.layout = layout;
   2891             this.cellX = cellX;
   2892             this.cellY = cellY;
   2893         }
   2894 
   2895         public void onAlarm(Alarm alarm) {
   2896             if (mDragFolderRingAnimator == null) {
   2897                 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
   2898             }
   2899             mDragFolderRingAnimator.setCell(cellX, cellY);
   2900             mDragFolderRingAnimator.setCellLayout(layout);
   2901             mDragFolderRingAnimator.animateToAcceptState();
   2902             layout.showFolderAccept(mDragFolderRingAnimator);
   2903             layout.clearDragOutlines();
   2904             setDragMode(DRAG_MODE_CREATE_FOLDER);
   2905         }
   2906     }
   2907 
   2908     class ReorderAlarmListener implements OnAlarmListener {
   2909         float[] dragViewCenter;
   2910         int minSpanX, minSpanY, spanX, spanY;
   2911         DragView dragView;
   2912         View child;
   2913 
   2914         public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
   2915                 int spanY, DragView dragView, View child) {
   2916             this.dragViewCenter = dragViewCenter;
   2917             this.minSpanX = minSpanX;
   2918             this.minSpanY = minSpanY;
   2919             this.spanX = spanX;
   2920             this.spanY = spanY;
   2921             this.child = child;
   2922             this.dragView = dragView;
   2923         }
   2924 
   2925         public void onAlarm(Alarm alarm) {
   2926             int[] resultSpan = new int[2];
   2927             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
   2928                     (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell);
   2929             mLastReorderX = mTargetCell[0];
   2930             mLastReorderY = mTargetCell[1];
   2931 
   2932             mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
   2933                 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
   2934                 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
   2935 
   2936             if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
   2937                 mDragTargetLayout.revertTempState();
   2938             } else {
   2939                 setDragMode(DRAG_MODE_REORDER);
   2940             }
   2941 
   2942             boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
   2943             mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
   2944                 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
   2945                 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
   2946                 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
   2947         }
   2948     }
   2949 
   2950     @Override
   2951     public void getHitRect(Rect outRect) {
   2952         // We want the workspace to have the whole area of the display (it will find the correct
   2953         // cell layout to drop to in the existing drag/drop logic.
   2954         outRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
   2955     }
   2956 
   2957     /**
   2958      * Add the item specified by dragInfo to the given layout.
   2959      * @return true if successful
   2960      */
   2961     public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
   2962         if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
   2963             onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
   2964             return true;
   2965         }
   2966         mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
   2967         return false;
   2968     }
   2969 
   2970     private void onDropExternal(int[] touchXY, Object dragInfo,
   2971             CellLayout cellLayout, boolean insertAtFirst) {
   2972         onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
   2973     }
   2974 
   2975     /**
   2976      * Drop an item that didn't originate on one of the workspace screens.
   2977      * It may have come from Launcher (e.g. from all apps or customize), or it may have
   2978      * come from another app altogether.
   2979      *
   2980      * NOTE: This can also be called when we are outside of a drag event, when we want
   2981      * to add an item to one of the workspace screens.
   2982      */
   2983     private void onDropExternal(final int[] touchXY, final Object dragInfo,
   2984             final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
   2985         final Runnable exitSpringLoadedRunnable = new Runnable() {
   2986             @Override
   2987             public void run() {
   2988                 mLauncher.exitSpringLoadedDragModeDelayed(true, false, null);
   2989             }
   2990         };
   2991 
   2992         ItemInfo info = (ItemInfo) dragInfo;
   2993         int spanX = info.spanX;
   2994         int spanY = info.spanY;
   2995         if (mDragInfo != null) {
   2996             spanX = mDragInfo.spanX;
   2997             spanY = mDragInfo.spanY;
   2998         }
   2999 
   3000         final long container = mLauncher.isHotseatLayout(cellLayout) ?
   3001                 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
   3002                     LauncherSettings.Favorites.CONTAINER_DESKTOP;
   3003         final int screen = indexOfChild(cellLayout);
   3004         if (!mLauncher.isHotseatLayout(cellLayout) && screen != mCurrentPage
   3005                 && mState != State.SPRING_LOADED) {
   3006             snapToPage(screen);
   3007         }
   3008 
   3009         if (info instanceof PendingAddItemInfo) {
   3010             final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
   3011 
   3012             boolean findNearestVacantCell = true;
   3013             if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
   3014                 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
   3015                         cellLayout, mTargetCell);
   3016                 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
   3017                         mDragViewVisualCenter[1], mTargetCell);
   3018                 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
   3019                         distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
   3020                                 cellLayout, mTargetCell, distance)) {
   3021                     findNearestVacantCell = false;
   3022                 }
   3023             }
   3024 
   3025             final ItemInfo item = (ItemInfo) d.dragInfo;
   3026             if (findNearestVacantCell) {
   3027                 int minSpanX = item.spanX;
   3028                 int minSpanY = item.spanY;
   3029                 if (item.minSpanX > 0 && item.minSpanY > 0) {
   3030                     minSpanX = item.minSpanX;
   3031                     minSpanY = item.minSpanY;
   3032                 }
   3033                 int[] resultSpan = new int[2];
   3034                 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
   3035                         (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
   3036                         null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
   3037                 item.spanX = resultSpan[0];
   3038                 item.spanY = resultSpan[1];
   3039             }
   3040 
   3041             Runnable onAnimationCompleteRunnable = new Runnable() {
   3042                 @Override
   3043                 public void run() {
   3044                     // When dragging and dropping from customization tray, we deal with creating
   3045                     // widgets/shortcuts/folders in a slightly different way
   3046                     switch (pendingInfo.itemType) {
   3047                     case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
   3048                         int span[] = new int[2];
   3049                         span[0] = item.spanX;
   3050                         span[1] = item.spanY;
   3051                         mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
   3052                                 container, screen, mTargetCell, span, null);
   3053                         break;
   3054                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   3055                         mLauncher.processShortcutFromDrop(pendingInfo.componentName,
   3056                                 container, screen, mTargetCell, null);
   3057                         break;
   3058                     default:
   3059                         throw new IllegalStateException("Unknown item type: " +
   3060                                 pendingInfo.itemType);
   3061                     }
   3062                 }
   3063             };
   3064             View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
   3065                     ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
   3066             int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
   3067             if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
   3068                     ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
   3069                 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
   3070             }
   3071             animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
   3072                     animationStyle, finalView, true);
   3073         } else {
   3074             // This is for other drag/drop cases, like dragging from All Apps
   3075             View view = null;
   3076 
   3077             switch (info.itemType) {
   3078             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   3079             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   3080                 if (info.container == NO_ID && info instanceof ApplicationInfo) {
   3081                     // Came from all apps -- make a copy
   3082                     info = new ShortcutInfo((ApplicationInfo) info);
   3083                 }
   3084                 view = mLauncher.createShortcut(R.layout.application, cellLayout,
   3085                         (ShortcutInfo) info);
   3086                 break;
   3087             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   3088                 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
   3089                         (FolderInfo) info, mIconCache);
   3090                 break;
   3091             default:
   3092                 throw new IllegalStateException("Unknown item type: " + info.itemType);
   3093             }
   3094 
   3095             // First we find the cell nearest to point at which the item is
   3096             // dropped, without any consideration to whether there is an item there.
   3097             if (touchXY != null) {
   3098                 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
   3099                         cellLayout, mTargetCell);
   3100                 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
   3101                         mDragViewVisualCenter[1], mTargetCell);
   3102                 d.postAnimationRunnable = exitSpringLoadedRunnable;
   3103                 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
   3104                         true, d.dragView, d.postAnimationRunnable)) {
   3105                     return;
   3106                 }
   3107                 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
   3108                         true)) {
   3109                     return;
   3110                 }
   3111             }
   3112 
   3113             if (touchXY != null) {
   3114                 // when dragging and dropping, just find the closest free spot
   3115                 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
   3116                         (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
   3117                         null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
   3118             } else {
   3119                 cellLayout.findCellForSpan(mTargetCell, 1, 1);
   3120             }
   3121             addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
   3122                     info.spanY, insertAtFirst);
   3123             cellLayout.onDropChild(view);
   3124             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
   3125             cellLayout.getShortcutsAndWidgets().measureChild(view);
   3126 
   3127 
   3128             LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen,
   3129                     lp.cellX, lp.cellY);
   3130 
   3131             if (d.dragView != null) {
   3132                 // We wrap the animation call in the temporary set and reset of the current
   3133                 // cellLayout to its final transform -- this means we animate the drag view to
   3134                 // the correct final location.
   3135                 setFinalTransitionTransform(cellLayout);
   3136                 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
   3137                         exitSpringLoadedRunnable);
   3138                 resetTransitionTransform(cellLayout);
   3139             }
   3140         }
   3141     }
   3142 
   3143     public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
   3144         int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
   3145                 widgetInfo.spanY, widgetInfo, false);
   3146         int visibility = layout.getVisibility();
   3147         layout.setVisibility(VISIBLE);
   3148 
   3149         int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
   3150         int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
   3151         Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
   3152                 Bitmap.Config.ARGB_8888);
   3153         Canvas c = new Canvas(b);
   3154 
   3155         layout.measure(width, height);
   3156         layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
   3157         layout.draw(c);
   3158         c.setBitmap(null);
   3159         layout.setVisibility(visibility);
   3160         return b;
   3161     }
   3162 
   3163     private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
   3164             DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
   3165             boolean external, boolean scale) {
   3166         // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
   3167         // location and size on the home screen.
   3168         int spanX = info.spanX;
   3169         int spanY = info.spanY;
   3170 
   3171         Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
   3172         loc[0] = r.left;
   3173         loc[1] = r.top;
   3174 
   3175         setFinalTransitionTransform(layout);
   3176         float cellLayoutScale =
   3177                 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc);
   3178         resetTransitionTransform(layout);
   3179 
   3180         float dragViewScaleX;
   3181         float dragViewScaleY;
   3182         if (scale) {
   3183             dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
   3184             dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
   3185         } else {
   3186             dragViewScaleX = 1f;
   3187             dragViewScaleY = 1f;
   3188         }
   3189 
   3190         // The animation will scale the dragView about its center, so we need to center about
   3191         // the final location.
   3192         loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
   3193         loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
   3194 
   3195         scaleXY[0] = dragViewScaleX * cellLayoutScale;
   3196         scaleXY[1] = dragViewScaleY * cellLayoutScale;
   3197     }
   3198 
   3199     public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
   3200             final Runnable onCompleteRunnable, int animationType, final View finalView,
   3201             boolean external) {
   3202         Rect from = new Rect();
   3203         mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
   3204 
   3205         int[] finalPos = new int[2];
   3206         float scaleXY[] = new float[2];
   3207         boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
   3208         getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
   3209                 external, scalePreview);
   3210 
   3211         Resources res = mLauncher.getResources();
   3212         int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
   3213 
   3214         // In the case where we've prebound the widget, we remove it from the DragLayer
   3215         if (finalView instanceof AppWidgetHostView && external) {
   3216             Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView");
   3217             mLauncher.getDragLayer().removeView(finalView);
   3218         }
   3219         if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
   3220             Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
   3221             dragView.setCrossFadeBitmap(crossFadeBitmap);
   3222             dragView.crossFade((int) (duration * 0.8f));
   3223         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) {
   3224             scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0],  scaleXY[1]);
   3225         }
   3226 
   3227         DragLayer dragLayer = mLauncher.getDragLayer();
   3228         if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
   3229             mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
   3230                     DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
   3231         } else {
   3232             int endStyle;
   3233             if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
   3234                 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
   3235             } else {
   3236                 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
   3237             }
   3238 
   3239             Runnable onComplete = new Runnable() {
   3240                 @Override
   3241                 public void run() {
   3242                     if (finalView != null) {
   3243                         finalView.setVisibility(VISIBLE);
   3244                     }
   3245                     if (onCompleteRunnable != null) {
   3246                         onCompleteRunnable.run();
   3247                     }
   3248                 }
   3249             };
   3250             dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
   3251                     finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
   3252                     duration, this);
   3253         }
   3254     }
   3255 
   3256     public void setFinalTransitionTransform(CellLayout layout) {
   3257         if (isSwitchingState()) {
   3258             int index = indexOfChild(layout);
   3259             mCurrentScaleX = layout.getScaleX();
   3260             mCurrentScaleY = layout.getScaleY();
   3261             mCurrentTranslationX = layout.getTranslationX();
   3262             mCurrentTranslationY = layout.getTranslationY();
   3263             mCurrentRotationY = layout.getRotationY();
   3264             layout.setScaleX(mNewScaleXs[index]);
   3265             layout.setScaleY(mNewScaleYs[index]);
   3266             layout.setTranslationX(mNewTranslationXs[index]);
   3267             layout.setTranslationY(mNewTranslationYs[index]);
   3268             layout.setRotationY(mNewRotationYs[index]);
   3269         }
   3270     }
   3271     public void resetTransitionTransform(CellLayout layout) {
   3272         if (isSwitchingState()) {
   3273             mCurrentScaleX = layout.getScaleX();
   3274             mCurrentScaleY = layout.getScaleY();
   3275             mCurrentTranslationX = layout.getTranslationX();
   3276             mCurrentTranslationY = layout.getTranslationY();
   3277             mCurrentRotationY = layout.getRotationY();
   3278             layout.setScaleX(mCurrentScaleX);
   3279             layout.setScaleY(mCurrentScaleY);
   3280             layout.setTranslationX(mCurrentTranslationX);
   3281             layout.setTranslationY(mCurrentTranslationY);
   3282             layout.setRotationY(mCurrentRotationY);
   3283         }
   3284     }
   3285 
   3286     /**
   3287      * Return the current {@link CellLayout}, correctly picking the destination
   3288      * screen while a scroll is in progress.
   3289      */
   3290     public CellLayout getCurrentDropLayout() {
   3291         return (CellLayout) getChildAt(getNextPage());
   3292     }
   3293 
   3294     /**
   3295      * Return the current CellInfo describing our current drag; this method exists
   3296      * so that Launcher can sync this object with the correct info when the activity is created/
   3297      * destroyed
   3298      *
   3299      */
   3300     public CellLayout.CellInfo getDragInfo() {
   3301         return mDragInfo;
   3302     }
   3303 
   3304     /**
   3305      * Calculate the nearest cell where the given object would be dropped.
   3306      *
   3307      * pixelX and pixelY should be in the coordinate system of layout
   3308      */
   3309     private int[] findNearestArea(int pixelX, int pixelY,
   3310             int spanX, int spanY, CellLayout layout, int[] recycle) {
   3311         return layout.findNearestArea(
   3312                 pixelX, pixelY, spanX, spanY, recycle);
   3313     }
   3314 
   3315     void setup(DragController dragController) {
   3316         mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
   3317         mDragController = dragController;
   3318 
   3319         // hardware layers on children are enabled on startup, but should be disabled until
   3320         // needed
   3321         updateChildrenLayersEnabled();
   3322         setWallpaperDimension();
   3323     }
   3324 
   3325     /**
   3326      * Called at the end of a drag which originated on the workspace.
   3327      */
   3328     public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
   3329             boolean success) {
   3330         if (success) {
   3331             if (target != this) {
   3332                 if (mDragInfo != null) {
   3333                     getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
   3334                     if (mDragInfo.cell instanceof DropTarget) {
   3335                         mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
   3336                     }
   3337                 }
   3338             }
   3339         } else if (mDragInfo != null) {
   3340             CellLayout cellLayout;
   3341             if (mLauncher.isHotseatLayout(target)) {
   3342                 cellLayout = mLauncher.getHotseat().getLayout();
   3343             } else {
   3344                 cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
   3345             }
   3346             cellLayout.onDropChild(mDragInfo.cell);
   3347         }
   3348         if (d.cancelled &&  mDragInfo.cell != null) {
   3349                 mDragInfo.cell.setVisibility(VISIBLE);
   3350         }
   3351         mDragOutline = null;
   3352         mDragInfo = null;
   3353 
   3354         // Hide the scrolling indicator after you pick up an item
   3355         hideScrollingIndicator(false);
   3356     }
   3357 
   3358     void updateItemLocationsInDatabase(CellLayout cl) {
   3359         int count = cl.getShortcutsAndWidgets().getChildCount();
   3360 
   3361         int screen = indexOfChild(cl);
   3362         int container = Favorites.CONTAINER_DESKTOP;
   3363 
   3364         if (mLauncher.isHotseatLayout(cl)) {
   3365             screen = -1;
   3366             container = Favorites.CONTAINER_HOTSEAT;
   3367         }
   3368 
   3369         for (int i = 0; i < count; i++) {
   3370             View v = cl.getShortcutsAndWidgets().getChildAt(i);
   3371             ItemInfo info = (ItemInfo) v.getTag();
   3372             // Null check required as the AllApps button doesn't have an item info
   3373             if (info != null) {
   3374                 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screen, info.cellX,
   3375                         info.cellY, info.spanX, info.spanY);
   3376             }
   3377         }
   3378     }
   3379 
   3380     @Override
   3381     public boolean supportsFlingToDelete() {
   3382         return true;
   3383     }
   3384 
   3385     @Override
   3386     public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
   3387         // Do nothing
   3388     }
   3389 
   3390     @Override
   3391     public void onFlingToDeleteCompleted() {
   3392         // Do nothing
   3393     }
   3394 
   3395     public boolean isDropEnabled() {
   3396         return true;
   3397     }
   3398 
   3399     @Override
   3400     protected void onRestoreInstanceState(Parcelable state) {
   3401         super.onRestoreInstanceState(state);
   3402         Launcher.setScreen(mCurrentPage);
   3403     }
   3404 
   3405     @Override
   3406     public void scrollLeft() {
   3407         if (!isSmall() && !mIsSwitchingState) {
   3408             super.scrollLeft();
   3409         }
   3410         Folder openFolder = getOpenFolder();
   3411         if (openFolder != null) {
   3412             openFolder.completeDragExit();
   3413         }
   3414     }
   3415 
   3416     @Override
   3417     public void scrollRight() {
   3418         if (!isSmall() && !mIsSwitchingState) {
   3419             super.scrollRight();
   3420         }
   3421         Folder openFolder = getOpenFolder();
   3422         if (openFolder != null) {
   3423             openFolder.completeDragExit();
   3424         }
   3425     }
   3426 
   3427     @Override
   3428     public boolean onEnterScrollArea(int x, int y, int direction) {
   3429         // Ignore the scroll area if we are dragging over the hot seat
   3430         boolean isPortrait = !LauncherApplication.isScreenLandscape(getContext());
   3431         if (mLauncher.getHotseat() != null && isPortrait) {
   3432             Rect r = new Rect();
   3433             mLauncher.getHotseat().getHitRect(r);
   3434             if (r.contains(x, y)) {
   3435                 return false;
   3436             }
   3437         }
   3438 
   3439         boolean result = false;
   3440         if (!isSmall() && !mIsSwitchingState) {
   3441             mInScrollArea = true;
   3442 
   3443             final int page = getNextPage() +
   3444                        (direction == DragController.SCROLL_LEFT ? -1 : 1);
   3445 
   3446             // We always want to exit the current layout to ensure parity of enter / exit
   3447             setCurrentDropLayout(null);
   3448 
   3449             if (0 <= page && page < getChildCount()) {
   3450                 CellLayout layout = (CellLayout) getChildAt(page);
   3451                 setCurrentDragOverlappingLayout(layout);
   3452 
   3453                 // Workspace is responsible for drawing the edge glow on adjacent pages,
   3454                 // so we need to redraw the workspace when this may have changed.
   3455                 invalidate();
   3456                 result = true;
   3457             }
   3458         }
   3459         return result;
   3460     }
   3461 
   3462     @Override
   3463     public boolean onExitScrollArea() {
   3464         boolean result = false;
   3465         if (mInScrollArea) {
   3466             invalidate();
   3467             CellLayout layout = getCurrentDropLayout();
   3468             setCurrentDropLayout(layout);
   3469             setCurrentDragOverlappingLayout(layout);
   3470 
   3471             result = true;
   3472             mInScrollArea = false;
   3473         }
   3474         return result;
   3475     }
   3476 
   3477     private void onResetScrollArea() {
   3478         setCurrentDragOverlappingLayout(null);
   3479         mInScrollArea = false;
   3480     }
   3481 
   3482     /**
   3483      * Returns a specific CellLayout
   3484      */
   3485     CellLayout getParentCellLayoutForView(View v) {
   3486         ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
   3487         for (CellLayout layout : layouts) {
   3488             if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
   3489                 return layout;
   3490             }
   3491         }
   3492         return null;
   3493     }
   3494 
   3495     /**
   3496      * Returns a list of all the CellLayouts in the workspace.
   3497      */
   3498     ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
   3499         ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
   3500         int screenCount = getChildCount();
   3501         for (int screen = 0; screen < screenCount; screen++) {
   3502             layouts.add(((CellLayout) getChildAt(screen)));
   3503         }
   3504         if (mLauncher.getHotseat() != null) {
   3505             layouts.add(mLauncher.getHotseat().getLayout());
   3506         }
   3507         return layouts;
   3508     }
   3509 
   3510     /**
   3511      * We should only use this to search for specific children.  Do not use this method to modify
   3512      * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
   3513      * the hotseat and workspace pages
   3514      */
   3515     ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
   3516         ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
   3517                 new ArrayList<ShortcutAndWidgetContainer>();
   3518         int screenCount = getChildCount();
   3519         for (int screen = 0; screen < screenCount; screen++) {
   3520             childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
   3521         }
   3522         if (mLauncher.getHotseat() != null) {
   3523             childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
   3524         }
   3525         return childrenLayouts;
   3526     }
   3527 
   3528     public Folder getFolderForTag(Object tag) {
   3529         ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
   3530                 getAllShortcutAndWidgetContainers();
   3531         for (ShortcutAndWidgetContainer layout: childrenLayouts) {
   3532             int count = layout.getChildCount();
   3533             for (int i = 0; i < count; i++) {
   3534                 View child = layout.getChildAt(i);
   3535                 if (child instanceof Folder) {
   3536                     Folder f = (Folder) child;
   3537                     if (f.getInfo() == tag && f.getInfo().opened) {
   3538                         return f;
   3539                     }
   3540                 }
   3541             }
   3542         }
   3543         return null;
   3544     }
   3545 
   3546     public View getViewForTag(Object tag) {
   3547         ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
   3548                 getAllShortcutAndWidgetContainers();
   3549         for (ShortcutAndWidgetContainer layout: childrenLayouts) {
   3550             int count = layout.getChildCount();
   3551             for (int i = 0; i < count; i++) {
   3552                 View child = layout.getChildAt(i);
   3553                 if (child.getTag() == tag) {
   3554                     return child;
   3555                 }
   3556             }
   3557         }
   3558         return null;
   3559     }
   3560 
   3561     void clearDropTargets() {
   3562         ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
   3563                 getAllShortcutAndWidgetContainers();
   3564         for (ShortcutAndWidgetContainer layout: childrenLayouts) {
   3565             int childCount = layout.getChildCount();
   3566             for (int j = 0; j < childCount; j++) {
   3567                 View v = layout.getChildAt(j);
   3568                 if (v instanceof DropTarget) {
   3569                     mDragController.removeDropTarget((DropTarget) v);
   3570                 }
   3571             }
   3572         }
   3573     }
   3574 
   3575     void removeItems(final ArrayList<ApplicationInfo> apps) {
   3576         final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
   3577 
   3578         final HashSet<String> packageNames = new HashSet<String>();
   3579         final int appCount = apps.size();
   3580         for (int i = 0; i < appCount; i++) {
   3581             packageNames.add(apps.get(i).componentName.getPackageName());
   3582         }
   3583 
   3584         ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
   3585         for (final CellLayout layoutParent: cellLayouts) {
   3586             final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
   3587 
   3588             // Avoid ANRs by treating each screen separately
   3589             post(new Runnable() {
   3590                 public void run() {
   3591                     final ArrayList<View> childrenToRemove = new ArrayList<View>();
   3592                     childrenToRemove.clear();
   3593 
   3594                     int childCount = layout.getChildCount();
   3595                     for (int j = 0; j < childCount; j++) {
   3596                         final View view = layout.getChildAt(j);
   3597                         Object tag = view.getTag();
   3598 
   3599                         if (tag instanceof ShortcutInfo) {
   3600                             final ShortcutInfo info = (ShortcutInfo) tag;
   3601                             final Intent intent = info.intent;
   3602                             final ComponentName name = intent.getComponent();
   3603 
   3604                             if (name != null) {
   3605                                 if (packageNames.contains(name.getPackageName())) {
   3606                                     LauncherModel.deleteItemFromDatabase(mLauncher, info);
   3607                                     childrenToRemove.add(view);
   3608                                 }
   3609                             }
   3610                         } else if (tag instanceof FolderInfo) {
   3611                             final FolderInfo info = (FolderInfo) tag;
   3612                             final ArrayList<ShortcutInfo> contents = info.contents;
   3613                             final int contentsCount = contents.size();
   3614                             final ArrayList<ShortcutInfo> appsToRemoveFromFolder =
   3615                                     new ArrayList<ShortcutInfo>();
   3616 
   3617                             for (int k = 0; k < contentsCount; k++) {
   3618                                 final ShortcutInfo appInfo = contents.get(k);
   3619                                 final Intent intent = appInfo.intent;
   3620                                 final ComponentName name = intent.getComponent();
   3621 
   3622                                 if (name != null) {
   3623                                     if (packageNames.contains(name.getPackageName())) {
   3624                                         appsToRemoveFromFolder.add(appInfo);
   3625                                     }
   3626                                 }
   3627                             }
   3628                             for (ShortcutInfo item: appsToRemoveFromFolder) {
   3629                                 info.remove(item);
   3630                                 LauncherModel.deleteItemFromDatabase(mLauncher, item);
   3631                             }
   3632                         } else if (tag instanceof LauncherAppWidgetInfo) {
   3633                             final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
   3634                             final ComponentName provider = info.providerName;
   3635                             if (provider != null) {
   3636                                 if (packageNames.contains(provider.getPackageName())) {
   3637                                     LauncherModel.deleteItemFromDatabase(mLauncher, info);
   3638                                     childrenToRemove.add(view);
   3639                                 }
   3640                             }
   3641                         }
   3642                     }
   3643 
   3644                     childCount = childrenToRemove.size();
   3645                     for (int j = 0; j < childCount; j++) {
   3646                         View child = childrenToRemove.get(j);
   3647                         // Note: We can not remove the view directly from CellLayoutChildren as this
   3648                         // does not re-mark the spaces as unoccupied.
   3649                         layoutParent.removeViewInLayout(child);
   3650                         if (child instanceof DropTarget) {
   3651                             mDragController.removeDropTarget((DropTarget)child);
   3652                         }
   3653                     }
   3654 
   3655                     if (childCount > 0) {
   3656                         layout.requestLayout();
   3657                         layout.invalidate();
   3658                     }
   3659                 }
   3660             });
   3661         }
   3662 
   3663         // It is no longer the case the BubbleTextViews correspond 1:1 with the workspace items in
   3664         // the database (and LauncherModel) since shortcuts are not added and animated in until
   3665         // the user returns to launcher.  As a result, we really should be cleaning up the Db
   3666         // regardless of whether the item was added or not (unlike the logic above).  This is only
   3667         // relevant for direct workspace items.
   3668         post(new Runnable() {
   3669             @Override
   3670             public void run() {
   3671                 String spKey = LauncherApplication.getSharedPreferencesKey();
   3672                 SharedPreferences sp = getContext().getSharedPreferences(spKey,
   3673                         Context.MODE_PRIVATE);
   3674                 Set<String> newApps = sp.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY,
   3675                         null);
   3676 
   3677                 for (String packageName: packageNames) {
   3678                     // Remove all items that have the same package, but were not removed above
   3679                     ArrayList<ShortcutInfo> infos =
   3680                             mLauncher.getModel().getShortcutInfosForPackage(packageName);
   3681                     for (ShortcutInfo info : infos) {
   3682                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
   3683                     }
   3684                     // Remove all queued items that match the same package
   3685                     if (newApps != null) {
   3686                         synchronized (newApps) {
   3687                             Iterator<String> iter = newApps.iterator();
   3688                             while (iter.hasNext()) {
   3689                                 try {
   3690                                     Intent intent = Intent.parseUri(iter.next(), 0);
   3691                                     String pn = ItemInfo.getPackageName(intent);
   3692                                     if (packageNames.contains(pn)) {
   3693                                         iter.remove();
   3694                                     }
   3695                                 } catch (URISyntaxException e) {}
   3696                             }
   3697                         }
   3698                     }
   3699                 }
   3700             }
   3701         });
   3702     }
   3703 
   3704     void updateShortcuts(ArrayList<ApplicationInfo> apps) {
   3705         ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
   3706         for (ShortcutAndWidgetContainer layout: childrenLayouts) {
   3707             int childCount = layout.getChildCount();
   3708             for (int j = 0; j < childCount; j++) {
   3709                 final View view = layout.getChildAt(j);
   3710                 Object tag = view.getTag();
   3711                 if (tag instanceof ShortcutInfo) {
   3712                     ShortcutInfo info = (ShortcutInfo) tag;
   3713                     // We need to check for ACTION_MAIN otherwise getComponent() might
   3714                     // return null for some shortcuts (for instance, for shortcuts to
   3715                     // web pages.)
   3716                     final Intent intent = info.intent;
   3717                     final ComponentName name = intent.getComponent();
   3718                     if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
   3719                             Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
   3720                         final int appCount = apps.size();
   3721                         for (int k = 0; k < appCount; k++) {
   3722                             ApplicationInfo app = apps.get(k);
   3723                             if (app.componentName.equals(name)) {
   3724                                 BubbleTextView shortcut = (BubbleTextView) view;
   3725                                 info.updateIcon(mIconCache);
   3726                                 info.title = app.title.toString();
   3727                                 shortcut.applyFromShortcutInfo(info, mIconCache);
   3728                             }
   3729                         }
   3730                     }
   3731                 }
   3732             }
   3733         }
   3734     }
   3735 
   3736     void moveToDefaultScreen(boolean animate) {
   3737         if (!isSmall()) {
   3738             if (animate) {
   3739                 snapToPage(mDefaultPage);
   3740             } else {
   3741                 setCurrentPage(mDefaultPage);
   3742             }
   3743         }
   3744         getChildAt(mDefaultPage).requestFocus();
   3745     }
   3746 
   3747     @Override
   3748     public void syncPages() {
   3749     }
   3750 
   3751     @Override
   3752     public void syncPageItems(int page, boolean immediate) {
   3753     }
   3754 
   3755     @Override
   3756     protected String getCurrentPageDescription() {
   3757         int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
   3758         return String.format(getContext().getString(R.string.workspace_scroll_format),
   3759                 page + 1, getChildCount());
   3760     }
   3761 
   3762     public void getLocationInDragLayer(int[] loc) {
   3763         mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
   3764     }
   3765 
   3766     void setFadeForOverScroll(float fade) {
   3767         if (!isScrollingIndicatorEnabled()) return;
   3768 
   3769         mOverscrollFade = fade;
   3770         float reducedFade = 0.5f + 0.5f * (1 - fade);
   3771         final ViewGroup parent = (ViewGroup) getParent();
   3772         final ImageView qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
   3773         final ImageView dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
   3774         final View scrollIndicator = getScrollingIndicator();
   3775 
   3776         cancelScrollingIndicatorAnimations();
   3777         if (qsbDivider != null) qsbDivider.setAlpha(reducedFade);
   3778         if (dockDivider != null) dockDivider.setAlpha(reducedFade);
   3779         scrollIndicator.setAlpha(1 - fade);
   3780     }
   3781 }
   3782