1 /*
2  * Copyright (C) 2016 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.dragndrop;
18 
19 import android.graphics.PointF;
20 import android.view.MotionEvent;
21 import android.view.VelocityTracker;
22 import android.view.ViewConfiguration;
23 
24 import com.android.launcher3.ButtonDropTarget;
25 import com.android.launcher3.DropTarget;
26 import com.android.launcher3.Launcher;
27 import com.android.launcher3.R;
28 import com.android.launcher3.util.FlingAnimation;
29 
30 /**
31  * Utility class to manage fling to delete action during drag and drop.
32  */
33 public class FlingToDeleteHelper {
34 
35     private static final float MAX_FLING_DEGREES = 35f;
36 
37     private final Launcher mLauncher;
38     private final int mFlingToDeleteThresholdVelocity;
39 
40     private ButtonDropTarget mDropTarget;
41     private VelocityTracker mVelocityTracker;
42 
FlingToDeleteHelper(Launcher launcher)43     public FlingToDeleteHelper(Launcher launcher) {
44         mLauncher = launcher;
45         mFlingToDeleteThresholdVelocity = launcher.getResources()
46                 .getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
47     }
48 
recordMotionEvent(MotionEvent ev)49     public void recordMotionEvent(MotionEvent ev) {
50         if (mVelocityTracker == null) {
51             mVelocityTracker = VelocityTracker.obtain();
52         }
53         mVelocityTracker.addMovement(ev);
54     }
55 
releaseVelocityTracker()56     public void releaseVelocityTracker() {
57         if (mVelocityTracker != null) {
58             mVelocityTracker.recycle();
59             mVelocityTracker = null;
60         }
61     }
62 
getDropTarget()63     public DropTarget getDropTarget() {
64         return mDropTarget;
65     }
66 
getFlingAnimation(DropTarget.DragObject dragObject, DragOptions options)67     public Runnable getFlingAnimation(DropTarget.DragObject dragObject, DragOptions options) {
68         PointF vel = isFlingingToDelete();
69         options.isFlingToDelete = vel != null;
70         if (!options.isFlingToDelete) {
71             return null;
72         }
73         return new FlingAnimation(dragObject, vel, mDropTarget, mLauncher, options);
74     }
75 
76     /**
77      * Determines whether the user flung the current item to delete it.
78      *
79      * @return the vector at which the item was flung, or null if no fling was detected.
80      */
isFlingingToDelete()81     private PointF isFlingingToDelete() {
82         if (mVelocityTracker == null) return null;
83         if (mDropTarget == null) {
84             mDropTarget = (ButtonDropTarget) mLauncher.findViewById(R.id.delete_target_text);
85         }
86         if (mDropTarget == null || !mDropTarget.isDropEnabled()) return null;
87         ViewConfiguration config = ViewConfiguration.get(mLauncher);
88         mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
89         PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
90         float theta = MAX_FLING_DEGREES + 1;
91         if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
92             // Do a quick dot product test to ensure that we are flinging upwards
93             PointF upVec = new PointF(0f, -1f);
94             theta = getAngleBetweenVectors(vel, upVec);
95         } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
96                 mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
97             // Remove icon is on left side instead of top, so check if we are flinging to the left.
98             PointF leftVec = new PointF(-1f, 0f);
99             theta = getAngleBetweenVectors(vel, leftVec);
100         }
101         if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
102             return vel;
103         }
104         return null;
105     }
106 
getAngleBetweenVectors(PointF vec1, PointF vec2)107     private float getAngleBetweenVectors(PointF vec1, PointF vec2) {
108         return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) /
109                 (vec1.length() * vec2.length()));
110     }
111 }
112