• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 Google Inc.
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.setupwizardlib.gesture;
18 
19 import android.graphics.Rect;
20 import android.view.MotionEvent;
21 import android.view.View;
22 import android.view.ViewConfiguration;
23 
24 /**
25  * Helper class to detect the consective-tap gestures on a view.
26  *
27  * <p>This class is instantiated and used similar to a GestureDetector, where onTouchEvent should be
28  * called when there are MotionEvents this detector should know about.
29  */
30 public final class ConsecutiveTapsGestureDetector {
31 
32   public interface OnConsecutiveTapsListener {
33     /** Callback method when the user tapped on the target view X number of times. */
onConsecutiveTaps(int numOfConsecutiveTaps)34     void onConsecutiveTaps(int numOfConsecutiveTaps);
35   }
36 
37   private final View view;
38   private final OnConsecutiveTapsListener listener;
39   private final int consecutiveTapTouchSlopSquare;
40   private final int consecutiveTapTimeout;
41 
42   private int consecutiveTapsCounter = 0;
43   private MotionEvent previousTapEvent;
44 
45   /**
46    * @param listener The listener that responds to the gesture.
47    * @param view The target view that associated with consecutive-tap gesture.
48    */
ConsecutiveTapsGestureDetector(OnConsecutiveTapsListener listener, View view)49   public ConsecutiveTapsGestureDetector(OnConsecutiveTapsListener listener, View view) {
50     this.listener = listener;
51     this.view = view;
52     int doubleTapSlop = ViewConfiguration.get(this.view.getContext()).getScaledDoubleTapSlop();
53     consecutiveTapTouchSlopSquare = doubleTapSlop * doubleTapSlop;
54     consecutiveTapTimeout = ViewConfiguration.getDoubleTapTimeout();
55   }
56 
57   /**
58    * This method should be called from the relevant activity or view, typically in onTouchEvent,
59    * onInterceptTouchEvent or dispatchTouchEvent.
60    *
61    * @param ev The motion event
62    */
onTouchEvent(MotionEvent ev)63   public void onTouchEvent(MotionEvent ev) {
64     if (ev.getAction() == MotionEvent.ACTION_UP) {
65       Rect viewRect = new Rect();
66       int[] leftTop = new int[2];
67       view.getLocationOnScreen(leftTop);
68       viewRect.set(
69           leftTop[0], leftTop[1], leftTop[0] + view.getWidth(), leftTop[1] + view.getHeight());
70       if (viewRect.contains((int) ev.getX(), (int) ev.getY())) {
71         if (isConsecutiveTap(ev)) {
72           consecutiveTapsCounter++;
73         } else {
74           consecutiveTapsCounter = 1;
75         }
76         listener.onConsecutiveTaps(consecutiveTapsCounter);
77       } else {
78         // Touch outside the target view. Reset counter.
79         consecutiveTapsCounter = 0;
80       }
81 
82       if (previousTapEvent != null) {
83         previousTapEvent.recycle();
84       }
85       previousTapEvent = MotionEvent.obtain(ev);
86     }
87   }
88 
89   /** Resets the consecutive-tap counter to zero. */
resetCounter()90   public void resetCounter() {
91     consecutiveTapsCounter = 0;
92   }
93 
94   /**
95    * Returns true if the distance between consecutive tap is within {@link
96    * #consecutiveTapTouchSlopSquare}. False, otherwise.
97    */
isConsecutiveTap(MotionEvent currentTapEvent)98   private boolean isConsecutiveTap(MotionEvent currentTapEvent) {
99     if (previousTapEvent == null) {
100       return false;
101     }
102 
103     double deltaX = previousTapEvent.getX() - currentTapEvent.getX();
104     double deltaY = previousTapEvent.getY() - currentTapEvent.getY();
105     long deltaTime = currentTapEvent.getEventTime() - previousTapEvent.getEventTime();
106     return (deltaX * deltaX + deltaY * deltaY <= consecutiveTapTouchSlopSquare)
107         && deltaTime < consecutiveTapTimeout;
108   }
109 }
110