Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2011 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.launcher3;
     18 
     19 import android.animation.TimeInterpolator;
     20 import android.animation.ValueAnimator;
     21 import android.animation.ValueAnimator.AnimatorUpdateListener;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.res.ColorStateList;
     25 import android.content.res.Configuration;
     26 import android.content.res.Resources;
     27 import android.graphics.PointF;
     28 import android.graphics.Rect;
     29 import android.graphics.drawable.TransitionDrawable;
     30 import android.os.AsyncTask;
     31 import android.os.Build;
     32 import android.os.Bundle;
     33 import android.os.UserManager;
     34 import android.util.AttributeSet;
     35 import android.view.View;
     36 import android.view.ViewConfiguration;
     37 import android.view.ViewGroup;
     38 import android.view.animation.AnimationUtils;
     39 import android.view.animation.DecelerateInterpolator;
     40 import android.view.animation.LinearInterpolator;
     41 
     42 import com.android.launcher3.compat.UserHandleCompat;
     43 
     44 public class DeleteDropTarget extends ButtonDropTarget {
     45     private static int DELETE_ANIMATION_DURATION = 285;
     46     private static int FLING_DELETE_ANIMATION_DURATION = 350;
     47     private static float FLING_TO_DELETE_FRICTION = 0.035f;
     48     private static int MODE_FLING_DELETE_TO_TRASH = 0;
     49     private static int MODE_FLING_DELETE_ALONG_VECTOR = 1;
     50 
     51     private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR;
     52 
     53     private ColorStateList mOriginalTextColor;
     54     private TransitionDrawable mUninstallDrawable;
     55     private TransitionDrawable mRemoveDrawable;
     56     private TransitionDrawable mCurrentDrawable;
     57 
     58     private boolean mWaitingForUninstall = false;
     59 
     60     public DeleteDropTarget(Context context, AttributeSet attrs) {
     61         this(context, attrs, 0);
     62     }
     63 
     64     public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) {
     65         super(context, attrs, defStyle);
     66     }
     67 
     68     @Override
     69     protected void onFinishInflate() {
     70         super.onFinishInflate();
     71 
     72         // Get the drawable
     73         mOriginalTextColor = getTextColors();
     74 
     75         // Get the hover color
     76         Resources r = getResources();
     77         mHoverColor = r.getColor(R.color.delete_target_hover_tint);
     78         mUninstallDrawable = (TransitionDrawable)
     79                 r.getDrawable(R.drawable.uninstall_target_selector);
     80         mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector);
     81 
     82         mRemoveDrawable.setCrossFadeEnabled(true);
     83         mUninstallDrawable.setCrossFadeEnabled(true);
     84 
     85         // The current drawable is set to either the remove drawable or the uninstall drawable
     86         // and is initially set to the remove drawable, as set in the layout xml.
     87         mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
     88 
     89         // Remove the text in the Phone UI in landscape
     90         int orientation = getResources().getConfiguration().orientation;
     91         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
     92             if (!LauncherAppState.getInstance().isScreenLarge()) {
     93                 setText("");
     94             }
     95         }
     96     }
     97 
     98     private boolean isAllAppsApplication(DragSource source, Object info) {
     99         return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
    100     }
    101     private boolean isAllAppsWidget(DragSource source, Object info) {
    102         if (source instanceof AppsCustomizePagedView) {
    103             if (info instanceof PendingAddItemInfo) {
    104                 PendingAddItemInfo addInfo = (PendingAddItemInfo) info;
    105                 switch (addInfo.itemType) {
    106                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
    107                     case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
    108                         return true;
    109                 }
    110             }
    111         }
    112         return false;
    113     }
    114     private boolean isDragSourceWorkspaceOrFolder(DragObject d) {
    115         return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder);
    116     }
    117     private boolean isWorkspaceOrFolderApplication(DragObject d) {
    118         return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof ShortcutInfo);
    119     }
    120     private boolean isWorkspaceOrFolderWidget(DragObject d) {
    121         return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof LauncherAppWidgetInfo);
    122     }
    123     private boolean isWorkspaceFolder(DragObject d) {
    124         return (d.dragSource instanceof Workspace) && (d.dragInfo instanceof FolderInfo);
    125     }
    126 
    127     private void setHoverColor() {
    128         if (mCurrentDrawable != null) {
    129             mCurrentDrawable.startTransition(mTransitionDuration);
    130         }
    131         setTextColor(mHoverColor);
    132     }
    133     private void resetHoverColor() {
    134         if (mCurrentDrawable != null) {
    135             mCurrentDrawable.resetTransition();
    136         }
    137         setTextColor(mOriginalTextColor);
    138     }
    139 
    140     @Override
    141     public boolean acceptDrop(DragObject d) {
    142         return willAcceptDrop(d.dragInfo);
    143     }
    144 
    145     public static boolean willAcceptDrop(Object info) {
    146         if (info instanceof ItemInfo) {
    147             ItemInfo item = (ItemInfo) info;
    148             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
    149                     item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
    150                 return true;
    151             }
    152 
    153             if (!LauncherAppState.isDisableAllApps() &&
    154                     item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
    155                 return true;
    156             }
    157 
    158             if (!LauncherAppState.isDisableAllApps() &&
    159                     item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
    160                     item instanceof AppInfo) {
    161                 AppInfo appInfo = (AppInfo) info;
    162                 return (appInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0;
    163             }
    164 
    165             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
    166                 item instanceof ShortcutInfo) {
    167                 if (LauncherAppState.isDisableAllApps()) {
    168                     ShortcutInfo shortcutInfo = (ShortcutInfo) info;
    169                     return (shortcutInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0;
    170                 } else {
    171                     return true;
    172                 }
    173             }
    174         }
    175         return false;
    176     }
    177 
    178     @Override
    179     public void onDragStart(DragSource source, Object info, int dragAction) {
    180         boolean isVisible = true;
    181         boolean useUninstallLabel = !LauncherAppState.isDisableAllApps() &&
    182                 isAllAppsApplication(source, info);
    183         boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();
    184 
    185         // If we are dragging an application from AppsCustomize, only show the control if we can
    186         // delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
    187         // Hide the delete target if it is a widget from AppsCustomize.
    188         if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
    189             isVisible = false;
    190         }
    191         if (useUninstallLabel) {
    192             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    193                 UserManager userManager = (UserManager)
    194                         getContext().getSystemService(Context.USER_SERVICE);
    195                 Bundle restrictions = userManager.getUserRestrictions();
    196                 if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
    197                         || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
    198                     isVisible = false;
    199                 }
    200             }
    201         }
    202 
    203         if (useUninstallLabel) {
    204             setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
    205         } else if (useDeleteLabel) {
    206             setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null);
    207         } else {
    208             isVisible = false;
    209         }
    210         mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
    211 
    212         mActive = isVisible;
    213         resetHoverColor();
    214         ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
    215         if (isVisible && getText().length() > 0) {
    216             setText(useUninstallLabel ? R.string.delete_target_uninstall_label
    217                 : R.string.delete_target_label);
    218         }
    219     }
    220 
    221     @Override
    222     public void onDragEnd() {
    223         super.onDragEnd();
    224         mActive = false;
    225     }
    226 
    227     public void onDragEnter(DragObject d) {
    228         super.onDragEnter(d);
    229 
    230         setHoverColor();
    231     }
    232 
    233     public void onDragExit(DragObject d) {
    234         super.onDragExit(d);
    235 
    236         if (!d.dragComplete) {
    237             resetHoverColor();
    238         } else {
    239             // Restore the hover color if we are deleting
    240             d.dragView.setColor(mHoverColor);
    241         }
    242     }
    243 
    244     private void animateToTrashAndCompleteDrop(final DragObject d) {
    245         final DragLayer dragLayer = mLauncher.getDragLayer();
    246         final Rect from = new Rect();
    247         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
    248 
    249         int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
    250         int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
    251         final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
    252                 width, height);
    253         final float scale = (float) to.width() / from.width();
    254 
    255         mSearchDropTargetBar.deferOnDragEnd();
    256         deferCompleteDropIfUninstalling(d);
    257 
    258         Runnable onAnimationEndRunnable = new Runnable() {
    259             @Override
    260             public void run() {
    261                 completeDrop(d);
    262                 mSearchDropTargetBar.onDragEnd();
    263                 mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
    264             }
    265         };
    266         dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
    267                 DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2),
    268                 new LinearInterpolator(), onAnimationEndRunnable,
    269                 DragLayer.ANIMATION_END_DISAPPEAR, null);
    270     }
    271 
    272     private void deferCompleteDropIfUninstalling(DragObject d) {
    273         mWaitingForUninstall = false;
    274         if (isUninstallFromWorkspace(d)) {
    275             if (d.dragSource instanceof Folder) {
    276                 ((Folder) d.dragSource).deferCompleteDropAfterUninstallActivity();
    277             } else if (d.dragSource instanceof Workspace) {
    278                 ((Workspace) d.dragSource).deferCompleteDropAfterUninstallActivity();
    279             }
    280             mWaitingForUninstall = true;
    281         }
    282     }
    283 
    284     private boolean isUninstallFromWorkspace(DragObject d) {
    285         if (LauncherAppState.isDisableAllApps() && isWorkspaceOrFolderApplication(d)) {
    286             ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo;
    287             // Only allow manifest shortcuts to initiate an un-install.
    288             return !InstallShortcutReceiver.isValidShortcutLaunchIntent(shortcut.intent);
    289         }
    290         return false;
    291     }
    292 
    293     private void completeDrop(DragObject d) {
    294         ItemInfo item = (ItemInfo) d.dragInfo;
    295         boolean wasWaitingForUninstall = mWaitingForUninstall;
    296         mWaitingForUninstall = false;
    297         if (isAllAppsApplication(d.dragSource, item)) {
    298             // Uninstall the application if it is being dragged from AppsCustomize
    299             AppInfo appInfo = (AppInfo) item;
    300             mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags,
    301                     appInfo.user);
    302         } else if (isUninstallFromWorkspace(d)) {
    303             ShortcutInfo shortcut = (ShortcutInfo) item;
    304             if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
    305                 final ComponentName componentName = shortcut.intent.getComponent();
    306                 final DragSource dragSource = d.dragSource;
    307                 final UserHandleCompat user = shortcut.user;
    308                 mWaitingForUninstall = mLauncher.startApplicationUninstallActivity(
    309                         componentName, shortcut.flags, user);
    310                 if (mWaitingForUninstall) {
    311                     final Runnable checkIfUninstallWasSuccess = new Runnable() {
    312                         @Override
    313                         public void run() {
    314                             mWaitingForUninstall = false;
    315                             String packageName = componentName.getPackageName();
    316                             boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
    317                                     getContext(), packageName, user);
    318                             if (dragSource instanceof Folder) {
    319                                 ((Folder) dragSource).
    320                                     onUninstallActivityReturned(uninstallSuccessful);
    321                             } else if (dragSource instanceof Workspace) {
    322                                 ((Workspace) dragSource).
    323                                     onUninstallActivityReturned(uninstallSuccessful);
    324                             }
    325                         }
    326                     };
    327                     mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
    328                 }
    329             }
    330         } else if (isWorkspaceOrFolderApplication(d)) {
    331             LauncherModel.deleteItemFromDatabase(mLauncher, item);
    332         } else if (isWorkspaceFolder(d)) {
    333             // Remove the folder from the workspace and delete the contents from launcher model
    334             FolderInfo folderInfo = (FolderInfo) item;
    335             mLauncher.removeFolder(folderInfo);
    336             LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo);
    337         } else if (isWorkspaceOrFolderWidget(d)) {
    338             // Remove the widget from the workspace
    339             mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
    340             LauncherModel.deleteItemFromDatabase(mLauncher, item);
    341 
    342             final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
    343             final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
    344             if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) {
    345                 // Deleting an app widget ID is a void call but writes to disk before returning
    346                 // to the caller...
    347                 new AsyncTask<Void, Void, Void>() {
    348                     public Void doInBackground(Void ... args) {
    349                         appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
    350                         return null;
    351                     }
    352                 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
    353             }
    354         }
    355         if (wasWaitingForUninstall && !mWaitingForUninstall) {
    356             if (d.dragSource instanceof Folder) {
    357                 ((Folder) d.dragSource).onUninstallActivityReturned(false);
    358             } else if (d.dragSource instanceof Workspace) {
    359                 ((Workspace) d.dragSource).onUninstallActivityReturned(false);
    360             }
    361         }
    362     }
    363 
    364     public void onDrop(DragObject d) {
    365         animateToTrashAndCompleteDrop(d);
    366     }
    367 
    368     /**
    369      * Creates an animation from the current drag view to the delete trash icon.
    370      */
    371     private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
    372             DragObject d, PointF vel, ViewConfiguration config) {
    373 
    374         int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
    375         int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
    376         final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
    377                 width, height);
    378         final Rect from = new Rect();
    379         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
    380 
    381         // Calculate how far along the velocity vector we should put the intermediate point on
    382         // the bezier curve
    383         float velocity = Math.abs(vel.length());
    384         float vp = Math.min(1f, velocity / (config.getScaledMaximumFlingVelocity() / 2f));
    385         int offsetY = (int) (-from.top * vp);
    386         int offsetX = (int) (offsetY / (vel.y / vel.x));
    387         final float y2 = from.top + offsetY;                        // intermediate t/l
    388         final float x2 = from.left + offsetX;
    389         final float x1 = from.left;                                 // drag view t/l
    390         final float y1 = from.top;
    391         final float x3 = to.left;                                   // delete target t/l
    392         final float y3 = to.top;
    393 
    394         final TimeInterpolator scaleAlphaInterpolator = new TimeInterpolator() {
    395             @Override
    396             public float getInterpolation(float t) {
    397                 return t * t * t * t * t * t * t * t;
    398             }
    399         };
    400         return new AnimatorUpdateListener() {
    401             @Override
    402             public void onAnimationUpdate(ValueAnimator animation) {
    403                 final DragView dragView = (DragView) dragLayer.getAnimatedView();
    404                 float t = ((Float) animation.getAnimatedValue()).floatValue();
    405                 float tp = scaleAlphaInterpolator.getInterpolation(t);
    406                 float initialScale = dragView.getInitialScale();
    407                 float finalAlpha = 0.5f;
    408                 float scale = dragView.getScaleX();
    409                 float x1o = ((1f - scale) * dragView.getMeasuredWidth()) / 2f;
    410                 float y1o = ((1f - scale) * dragView.getMeasuredHeight()) / 2f;
    411                 float x = (1f - t) * (1f - t) * (x1 - x1o) + 2 * (1f - t) * t * (x2 - x1o) +
    412                         (t * t) * x3;
    413                 float y = (1f - t) * (1f - t) * (y1 - y1o) + 2 * (1f - t) * t * (y2 - x1o) +
    414                         (t * t) * y3;
    415 
    416                 dragView.setTranslationX(x);
    417                 dragView.setTranslationY(y);
    418                 dragView.setScaleX(initialScale * (1f - tp));
    419                 dragView.setScaleY(initialScale * (1f - tp));
    420                 dragView.setAlpha(finalAlpha + (1f - finalAlpha) * (1f - tp));
    421             }
    422         };
    423     }
    424 
    425     /**
    426      * Creates an animation from the current drag view along its current velocity vector.
    427      * For this animation, the alpha runs for a fixed duration and we update the position
    428      * progressively.
    429      */
    430     private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
    431         private DragLayer mDragLayer;
    432         private PointF mVelocity;
    433         private Rect mFrom;
    434         private long mPrevTime;
    435         private boolean mHasOffsetForScale;
    436         private float mFriction;
    437 
    438         private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
    439 
    440         public FlingAlongVectorAnimatorUpdateListener(DragLayer dragLayer, PointF vel, Rect from,
    441                 long startTime, float friction) {
    442             mDragLayer = dragLayer;
    443             mVelocity = vel;
    444             mFrom = from;
    445             mPrevTime = startTime;
    446             mFriction = 1f - (dragLayer.getResources().getDisplayMetrics().density * friction);
    447         }
    448 
    449         @Override
    450         public void onAnimationUpdate(ValueAnimator animation) {
    451             final DragView dragView = (DragView) mDragLayer.getAnimatedView();
    452             float t = ((Float) animation.getAnimatedValue()).floatValue();
    453             long curTime = AnimationUtils.currentAnimationTimeMillis();
    454 
    455             if (!mHasOffsetForScale) {
    456                 mHasOffsetForScale = true;
    457                 float scale = dragView.getScaleX();
    458                 float xOffset = ((scale - 1f) * dragView.getMeasuredWidth()) / 2f;
    459                 float yOffset = ((scale - 1f) * dragView.getMeasuredHeight()) / 2f;
    460 
    461                 mFrom.left += xOffset;
    462                 mFrom.top += yOffset;
    463             }
    464 
    465             mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
    466             mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
    467 
    468             dragView.setTranslationX(mFrom.left);
    469             dragView.setTranslationY(mFrom.top);
    470             dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
    471 
    472             mVelocity.x *= mFriction;
    473             mVelocity.y *= mFriction;
    474             mPrevTime = curTime;
    475         }
    476     };
    477     private AnimatorUpdateListener createFlingAlongVectorAnimatorListener(final DragLayer dragLayer,
    478             DragObject d, PointF vel, final long startTime, final int duration,
    479             ViewConfiguration config) {
    480         final Rect from = new Rect();
    481         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
    482 
    483         return new FlingAlongVectorAnimatorUpdateListener(dragLayer, vel, from, startTime,
    484                 FLING_TO_DELETE_FRICTION);
    485     }
    486 
    487     public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) {
    488         final boolean isAllApps = d.dragSource instanceof AppsCustomizePagedView;
    489 
    490         // Don't highlight the icon as it's animating
    491         d.dragView.setColor(0);
    492         d.dragView.updateInitialScaleToCurrentScale();
    493         // Don't highlight the target if we are flinging from AllApps
    494         if (isAllApps) {
    495             resetHoverColor();
    496         }
    497 
    498         if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
    499             // Defer animating out the drop target if we are animating to it
    500             mSearchDropTargetBar.deferOnDragEnd();
    501             mSearchDropTargetBar.finishAnimations();
    502         }
    503 
    504         final ViewConfiguration config = ViewConfiguration.get(mLauncher);
    505         final DragLayer dragLayer = mLauncher.getDragLayer();
    506         final int duration = FLING_DELETE_ANIMATION_DURATION;
    507         final long startTime = AnimationUtils.currentAnimationTimeMillis();
    508 
    509         // NOTE: Because it takes time for the first frame of animation to actually be
    510         // called and we expect the animation to be a continuation of the fling, we have
    511         // to account for the time that has elapsed since the fling finished.  And since
    512         // we don't have a startDelay, we will always get call to update when we call
    513         // start() (which we want to ignore).
    514         final TimeInterpolator tInterpolator = new TimeInterpolator() {
    515             private int mCount = -1;
    516             private float mOffset = 0f;
    517 
    518             @Override
    519             public float getInterpolation(float t) {
    520                 if (mCount < 0) {
    521                     mCount++;
    522                 } else if (mCount == 0) {
    523                     mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
    524                             startTime) / duration);
    525                     mCount++;
    526                 }
    527                 return Math.min(1f, mOffset + t);
    528             }
    529         };
    530         AnimatorUpdateListener updateCb = null;
    531         if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
    532             updateCb = createFlingToTrashAnimatorListener(dragLayer, d, vel, config);
    533         } else if (mFlingDeleteMode == MODE_FLING_DELETE_ALONG_VECTOR) {
    534             updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
    535                     duration, config);
    536         }
    537         deferCompleteDropIfUninstalling(d);
    538 
    539         Runnable onAnimationEndRunnable = new Runnable() {
    540             @Override
    541             public void run() {
    542                 // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up
    543                 // itself, otherwise, complete the drop to initiate the deletion process
    544                 if (!isAllApps) {
    545                     mLauncher.exitSpringLoadedDragMode();
    546                     completeDrop(d);
    547                 }
    548                 mLauncher.getDragController().onDeferredEndFling(d);
    549             }
    550         };
    551         dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable,
    552                 DragLayer.ANIMATION_END_DISAPPEAR, null);
    553     }
    554 }
    555