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