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 17package com.android.launcher3; 18 19import android.animation.TimeInterpolator; 20import android.content.Context; 21import android.graphics.PointF; 22import android.os.AsyncTask; 23import android.util.AttributeSet; 24import android.view.View; 25import android.view.animation.AnimationUtils; 26 27import com.android.launcher3.util.FlingAnimation; 28import com.android.launcher3.util.Thunk; 29 30public class DeleteDropTarget extends ButtonDropTarget { 31 32 public DeleteDropTarget(Context context, AttributeSet attrs) { 33 this(context, attrs, 0); 34 } 35 36 public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) { 37 super(context, attrs, defStyle); 38 } 39 40 @Override 41 protected void onFinishInflate() { 42 super.onFinishInflate(); 43 // Get the hover color 44 mHoverColor = getResources().getColor(R.color.delete_target_hover_tint); 45 46 setDrawable(R.drawable.ic_remove_launcher); 47 } 48 49 public static boolean supportsDrop(Object info) { 50 return (info instanceof ShortcutInfo) 51 || (info instanceof LauncherAppWidgetInfo) 52 || (info instanceof FolderInfo); 53 } 54 55 @Override 56 protected boolean supportsDrop(DragSource source, Object info) { 57 return source.supportsDeleteDropTarget() && supportsDrop(info); 58 } 59 60 @Override 61 @Thunk void completeDrop(DragObject d) { 62 ItemInfo item = (ItemInfo) d.dragInfo; 63 if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) { 64 removeWorkspaceOrFolderItem(mLauncher, item, null); 65 } 66 } 67 68 /** 69 * Removes the item from the workspace. If the view is not null, it also removes the view. 70 * @return true if the item was removed. 71 */ 72 public static boolean removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) { 73 if (item instanceof ShortcutInfo) { 74 LauncherModel.deleteItemFromDatabase(launcher, item); 75 } else if (item instanceof FolderInfo) { 76 FolderInfo folder = (FolderInfo) item; 77 launcher.removeFolder(folder); 78 LauncherModel.deleteFolderContentsFromDatabase(launcher, folder); 79 } else if (item instanceof LauncherAppWidgetInfo) { 80 final LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) item; 81 82 // Remove the widget from the workspace 83 launcher.removeAppWidget(widget); 84 LauncherModel.deleteItemFromDatabase(launcher, widget); 85 86 final LauncherAppWidgetHost appWidgetHost = launcher.getAppWidgetHost(); 87 88 if (appWidgetHost != null && !widget.isCustomWidget() 89 && widget.isWidgetIdValid()) { 90 // Deleting an app widget ID is a void call but writes to disk before returning 91 // to the caller... 92 new AsyncTask<Void, Void, Void>() { 93 public Void doInBackground(Void ... args) { 94 appWidgetHost.deleteAppWidgetId(widget.appWidgetId); 95 return null; 96 } 97 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 98 } 99 } else { 100 return false; 101 } 102 103 if (view != null) { 104 launcher.getWorkspace().removeWorkspaceItem(view); 105 launcher.getWorkspace().stripEmptyScreens(); 106 } 107 return true; 108 } 109 110 @Override 111 public void onFlingToDelete(final DragObject d, PointF vel) { 112 // Don't highlight the icon as it's animating 113 d.dragView.setColor(0); 114 d.dragView.updateInitialScaleToCurrentScale(); 115 116 final DragLayer dragLayer = mLauncher.getDragLayer(); 117 FlingAnimation fling = new FlingAnimation(d, vel, 118 getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), 119 mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()), 120 dragLayer); 121 122 final int duration = fling.getDuration(); 123 final long startTime = AnimationUtils.currentAnimationTimeMillis(); 124 125 // NOTE: Because it takes time for the first frame of animation to actually be 126 // called and we expect the animation to be a continuation of the fling, we have 127 // to account for the time that has elapsed since the fling finished. And since 128 // we don't have a startDelay, we will always get call to update when we call 129 // start() (which we want to ignore). 130 final TimeInterpolator tInterpolator = new TimeInterpolator() { 131 private int mCount = -1; 132 private float mOffset = 0f; 133 134 @Override 135 public float getInterpolation(float t) { 136 if (mCount < 0) { 137 mCount++; 138 } else if (mCount == 0) { 139 mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - 140 startTime) / duration); 141 mCount++; 142 } 143 return Math.min(1f, mOffset + t); 144 } 145 }; 146 147 Runnable onAnimationEndRunnable = new Runnable() { 148 @Override 149 public void run() { 150 mLauncher.exitSpringLoadedDragMode(); 151 completeDrop(d); 152 mLauncher.getDragController().onDeferredEndFling(d); 153 } 154 }; 155 156 dragLayer.animateView(d.dragView, fling, duration, tInterpolator, onAnimationEndRunnable, 157 DragLayer.ANIMATION_END_DISAPPEAR, null); 158 } 159 160 @Override 161 protected String getAccessibilityDropConfirmation() { 162 return getResources().getString(R.string.item_removed); 163 } 164} 165