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