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