/* * Copyright 2017 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.skar.examples.helloskar.helpers; import android.content.Context; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * Helper to detect taps using Android GestureDetector, and pass the taps between UI thread and * render thread. */ public final class TapHelper implements OnTouchListener { private final GestureDetector gestureDetector; private final BlockingQueue queuedSingleTaps = new ArrayBlockingQueue<>(16); private final BlockingQueue queuedFingerHold = new ArrayBlockingQueue<>(16); private boolean isScrolling = false; private boolean previousScroll = true; public static class ScrollEvent { public MotionEvent e; public boolean isStartOfScroll; public ScrollEvent(MotionEvent e, boolean isStartOfScroll) { this.e = e; this.isStartOfScroll = isStartOfScroll; } } /** * Creates the tap helper. * * @param context the application's context. */ public TapHelper(Context context) { gestureDetector = new GestureDetector( context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // Queue tap if there is space. Tap is lost if queue is full. queuedSingleTaps.offer(e); return true; } @Override public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // Queue motion events when scrolling if (e2.getPointerCount() == 1 && e1.getPointerCount() == 1) { previousScroll = isScrolling; isScrolling = true; queuedFingerHold.offer(new ScrollEvent(e2, startedScrolling())); return true; } return false; } @Override public boolean onDown(MotionEvent e) { return true; } }); } /** * Polls for a tap. * * @return if a tap was queued, a MotionEvent for the tap. Otherwise null if no taps are queued. */ public MotionEvent poll() { return queuedSingleTaps.poll(); } public ScrollEvent holdPoll() { return queuedFingerHold.poll(); } public boolean startedScrolling() { return isScrolling && !previousScroll; } @Override public boolean onTouch(View view, MotionEvent motionEvent) { boolean val = gestureDetector.onTouchEvent(motionEvent); // If finger is up + is scrolling: don't scroll anymore, and empty Touch Hold queue if (motionEvent.getAction() == MotionEvent.ACTION_UP && isScrolling) { previousScroll = true; isScrolling = false; queuedFingerHold.clear(); } return val; } }