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