1 /*
2  * Copyright (C) 2019 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 package com.android.launcher3.touch;
17 
18 import android.content.Context;
19 import android.graphics.PointF;
20 import android.view.MotionEvent;
21 import android.view.ViewConfiguration;
22 
23 import androidx.annotation.NonNull;
24 import androidx.annotation.VisibleForTesting;
25 
26 import com.android.launcher3.Utilities;
27 
28 /**
29  * Two dimensional scroll/drag/swipe gesture detector that reports x and y displacement/velocity.
30  */
31 public class BothAxesSwipeDetector extends BaseSwipeDetector {
32 
33     public static final int DIRECTION_UP = 1 << 0;
34     // Note that this will track left instead of right in RTL.
35     public static final int DIRECTION_RIGHT = 1 << 1;
36     public static final int DIRECTION_DOWN = 1 << 2;
37     // Note that this will track right instead of left in RTL.
38     public static final int DIRECTION_LEFT = 1 << 3;
39 
40     /* Client of this gesture detector can register a callback. */
41     private final Listener mListener;
42 
43     private int mScrollDirections;
44 
BothAxesSwipeDetector(@onNull Context context, @NonNull Listener l)45     public BothAxesSwipeDetector(@NonNull Context context, @NonNull Listener l) {
46         this(ViewConfiguration.get(context), l, Utilities.isRtl(context.getResources()));
47     }
48 
49     @VisibleForTesting
BothAxesSwipeDetector(@onNull ViewConfiguration config, @NonNull Listener l, boolean isRtl)50     protected BothAxesSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
51             boolean isRtl) {
52         super(config, isRtl);
53         mListener = l;
54     }
55 
setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop)56     public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
57         mScrollDirections = scrollDirectionFlags;
58         mIgnoreSlopWhenSettling = ignoreSlop;
59     }
60 
61     @Override
shouldScrollStart(PointF displacement)62     protected boolean shouldScrollStart(PointF displacement) {
63         // Check if the client is interested in scroll in current direction.
64         boolean canScrollUp = (mScrollDirections & DIRECTION_UP) > 0
65                 && displacement.y <= -mTouchSlop;
66         boolean canScrollRight = (mScrollDirections & DIRECTION_RIGHT) > 0
67                 && displacement.x >= mTouchSlop;
68         boolean canScrollDown = (mScrollDirections & DIRECTION_DOWN) > 0
69                 && displacement.y >= mTouchSlop;
70         boolean canScrollLeft = (mScrollDirections & DIRECTION_LEFT) > 0
71                 && displacement.x <= -mTouchSlop;
72         return canScrollUp || canScrollRight || canScrollDown || canScrollLeft;
73     }
74 
75     @Override
reportDragStartInternal(boolean recatch)76     protected void reportDragStartInternal(boolean recatch) {
77         mListener.onDragStart(!recatch);
78     }
79 
80     @Override
reportDraggingInternal(PointF displacement, MotionEvent event)81     protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
82         mListener.onDrag(displacement, event);
83     }
84 
85     @Override
reportDragEndInternal(PointF velocity)86     protected void reportDragEndInternal(PointF velocity) {
87         mListener.onDragEnd(velocity);
88     }
89 
90     /** Listener to receive updates on the swipe. */
91     public interface Listener {
92         /** @param start whether this was the original drag start, as opposed to a recatch. */
onDragStart(boolean start)93         void onDragStart(boolean start);
94 
onDrag(PointF displacement, MotionEvent motionEvent)95         boolean onDrag(PointF displacement, MotionEvent motionEvent);
96 
onDragEnd(PointF velocity)97         void onDragEnd(PointF velocity);
98     }
99 }
100