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