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