Home | History | Annotate | Download | only in gesture
      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
     28  * be called when there are MotionEvents this detector should know about.
     29  */
     30 public final class ConsecutiveTapsGestureDetector {
     31 
     32     public interface OnConsecutiveTapsListener {
     33         /**
     34          * Callback method when the user tapped on the target view X number of times.
     35          */
     36         void onConsecutiveTaps(int numOfConsecutiveTaps);
     37     }
     38 
     39     private final View mView;
     40     private final OnConsecutiveTapsListener mListener;
     41     private final int mConsecutiveTapTouchSlopSquare;
     42 
     43     private int mConsecutiveTapsCounter = 0;
     44     private MotionEvent mPreviousTapEvent;
     45 
     46     /**
     47      * @param listener The listener that responds to the gesture.
     48      * @param view  The target view that associated with consecutive-tap gesture.
     49      */
     50     public ConsecutiveTapsGestureDetector(
     51             OnConsecutiveTapsListener listener,
     52             View view) {
     53         mListener = listener;
     54         mView = view;
     55         int doubleTapSlop = ViewConfiguration.get(mView.getContext()).getScaledDoubleTapSlop();
     56         mConsecutiveTapTouchSlopSquare = doubleTapSlop * doubleTapSlop;
     57     }
     58 
     59     /**
     60      * This method should be called from the relevant activity or view, typically in
     61      * onTouchEvent, onInterceptTouchEvent or dispatchTouchEvent.
     62      *
     63      * @param ev The motion event
     64      */
     65     public void onTouchEvent(MotionEvent ev) {
     66         if (ev.getAction() == MotionEvent.ACTION_UP) {
     67             Rect viewRect = new Rect();
     68             int[] leftTop = new int[2];
     69             mView.getLocationOnScreen(leftTop);
     70             viewRect.set(
     71                     leftTop[0],
     72                     leftTop[1],
     73                     leftTop[0] + mView.getWidth(),
     74                     leftTop[1] + mView.getHeight());
     75             if (viewRect.contains((int) ev.getX(), (int) ev.getY())) {
     76                 if (isConsecutiveTap(ev)) {
     77                     mConsecutiveTapsCounter++;
     78                 } else {
     79                     mConsecutiveTapsCounter = 1;
     80                 }
     81                 mListener.onConsecutiveTaps(mConsecutiveTapsCounter);
     82             } else {
     83                 // Touch outside the target view. Reset counter.
     84                 mConsecutiveTapsCounter = 0;
     85             }
     86 
     87             if (mPreviousTapEvent != null) {
     88                 mPreviousTapEvent.recycle();
     89             }
     90             mPreviousTapEvent = MotionEvent.obtain(ev);
     91         }
     92     }
     93 
     94     /**
     95      * Resets the consecutive-tap counter to zero.
     96      */
     97     public void resetCounter() {
     98         mConsecutiveTapsCounter = 0;
     99     }
    100 
    101     /**
    102      * Returns true if the distance between consecutive tap is within
    103      * {@link #mConsecutiveTapTouchSlopSquare}. False, otherwise.
    104      */
    105     private boolean isConsecutiveTap(MotionEvent currentTapEvent) {
    106         if (mPreviousTapEvent == null) {
    107             return false;
    108         }
    109 
    110         double deltaX = mPreviousTapEvent.getX() - currentTapEvent.getX();
    111         double deltaY = mPreviousTapEvent.getY() - currentTapEvent.getY();
    112         return (deltaX * deltaX + deltaY * deltaY <= mConsecutiveTapTouchSlopSquare);
    113     }
    114 }
    115