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