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