• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google LLC All Rights Reserved.
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.google.skar.examples.helloskar.helpers;
18 
19 import android.content.Context;
20 import android.view.GestureDetector;
21 import android.view.MotionEvent;
22 import android.view.View;
23 import android.view.View.OnTouchListener;
24 import java.util.concurrent.ArrayBlockingQueue;
25 import java.util.concurrent.BlockingQueue;
26 
27 /**
28  * Helper to detect gestures using Android GestureDetector, and pass the taps between UI thread and
29  * render thread.
30  */
31 
32 public final class GestureHelper implements OnTouchListener {
33     private final GestureDetector gestureDetector;
34     private final BlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
35     private final BlockingQueue<ScrollEvent> queuedFingerHold = new ArrayBlockingQueue<>(16);
36     private boolean isScrolling = false;
37     private boolean previousScroll = true;
38 
39     // Struct holding a MotionEvent obtained from onScroll() callbacks, and a boolean evaluating to
40     // true if the MotionEven was the start of the scrolling motion
41     public static class ScrollEvent {
42         public MotionEvent event;
43         public boolean isStartOfScroll;
44 
ScrollEvent(MotionEvent e, boolean isStartOfScroll)45         public ScrollEvent(MotionEvent e, boolean isStartOfScroll) {
46             this.event = e;
47             this.isStartOfScroll = isStartOfScroll;
48         }
49     }
50 
51     /**
52      * Creates the gesture helper.
53      *
54      * @param context the application's context.
55      */
GestureHelper(Context context)56     public GestureHelper(Context context) {
57         gestureDetector =
58                 new GestureDetector(
59                         context,
60                         new GestureDetector.SimpleOnGestureListener() {
61                             @Override
62                             public boolean onSingleTapUp(MotionEvent e) {
63                                 // Queue tap if there is space. Tap is lost if queue is full.
64                                 queuedSingleTaps.offer(e);
65                                 return true;
66                             }
67 
68                             @Override
69                             public boolean onScroll (MotionEvent e1, MotionEvent e2,
70                                                      float distanceX, float distanceY) {
71                                 // Queue motion events when scrolling
72                                 if (e2.getPointerCount() == 1 && e1.getPointerCount() == 1) {
73                                     previousScroll = isScrolling;
74                                     isScrolling = true;
75 
76                                     queuedFingerHold.offer(new ScrollEvent(e2,
77                                                                            isStartedScrolling()));
78 
79                                     return true;
80                                 }
81                                 return false;
82                             }
83 
84 
85                             @Override
86                             public boolean onDown(MotionEvent e) {
87                                 return true;
88                             }
89                         });
90     }
91 
92     /**
93      * Polls for a tap.
94      *
95      * @return if a tap was queued, a MotionEvent for the tap. Otherwise null if no taps are queued.
96      */
poll()97     public MotionEvent poll() {
98         return queuedSingleTaps.poll();
99     }
100 
101     /**
102      * Polls for a scrolling motion.
103      *
104      * @return if a scrolling event was queued, a ScrollEvent for the gesture. Otherwise null
105      */
holdPoll()106     public ScrollEvent holdPoll() { return queuedFingerHold.poll(); }
107 
108     @Override
onTouch(View view, MotionEvent motionEvent)109     public boolean onTouch(View view, MotionEvent motionEvent) {
110         boolean val = gestureDetector.onTouchEvent(motionEvent);
111 
112         // If finger is up + is scrolling: don't scroll anymore, and empty touch hold queue
113         if (motionEvent.getAction() == MotionEvent.ACTION_UP && isScrolling) {
114             previousScroll = true;
115             isScrolling = false;
116             queuedFingerHold.clear();
117         }
118         return val;
119     }
isStartedScrolling()120     private boolean isStartedScrolling() { return isScrolling && !previousScroll; }
121 }
122