/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.ui.cordova.events.touch.swipe;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.shared.HasHandlers;
import org.jboss.errai.ui.cordova.events.touch.AbstractRecognizer;
import org.jboss.errai.ui.cordova.events.touch.TouchPoint;
import static org.jboss.errai.ui.cordova.events.touch.swipe.SwipeEvent.Direction;
public class SwipeRecognizer extends AbstractRecognizer {
private final HasHandlers source;
private final int minDistance;
private final int threshold;
private int touchCount;
private State state;
private Direction direction;
private int lastDistance;
private int x;
private int y;
/**
* construct a swipe recognizer
*
* @param source the source to fire events on
*/
public SwipeRecognizer(HasHandlers source) {
this(source, 40);
}
/**
* construct a swipe recognizer
*
* @param source the source to fire events on
* @param minDistance the minimum distance to cover before this counts as a swipe
*/
public SwipeRecognizer(HasHandlers source, int minDistance) {
this(source, minDistance, 10);
}
/**
* construct a swipe recognizer
*
* @param source the source to fire events on
* @param minDistance the minimum distance to cover before this counts as a swipe
* @param threshold the initial threshold before swipe start is fired
*/
public SwipeRecognizer(HasHandlers source, int minDistance, int threshold) {
if (source == null)
throw new IllegalArgumentException("source can not be null");
if (minDistance <= 0 || minDistance < threshold) {
throw new IllegalArgumentException("minDistance > 0 and minDistance > threshold");
}
if (threshold <= 0) {
throw new IllegalArgumentException("threshold > 0");
}
this.source = source;
this.minDistance = minDistance;
this.threshold = threshold;
this.touchCount = 0;
state = State.READY;
}
@Override
public void onTouchStart(TouchStartEvent event) {
touchCount++;
switch (state) {
case INVALID:
break;
case READY:
state = State.FINDER_DOWN;
x = event.getTouches().get(0).getPageX();
y = event.getTouches().get(0).getPageY();
break;
case FINDER_DOWN:
default:
state = State.INVALID;
break;
}
}
@Override
public void onTouchMove(TouchMoveEvent event) {
Touch touch = event.getTouches().get(0);
switch (state) {
case INVALID:
break;
case READY:
// WTF?
state = State.INVALID;
break;
case FINDER_DOWN:
// log(" X: " + touch.getPageX() + " old: " + touchStart.getPageX() + " test: " + x);
if (Math.abs(touch.getPageX() - x) >= threshold) {
state = State.FOUND_DIRECTION;
direction = touch.getPageX() - x > 0 ? Direction.LEFT_TO_RIGHT : Direction.RIGHT_TO_LEFT;
SwipeStartEvent swipeStartEvent =
new SwipeStartEvent(new TouchPoint(touch), touch.getPageX() - x, direction);
source.fireEvent(swipeStartEvent);
} else {
if (Math.abs(touch.getPageY() - y) >= threshold) {
state = State.FOUND_DIRECTION;
direction = touch.getPageY() - y > 0 ? Direction.TOP_TO_BOTTOM : Direction.BOTTOM_TO_TOP;
SwipeStartEvent swipeStartEvent =
new SwipeStartEvent(new TouchPoint(touch), touch.getPageY() - y, direction);
source.fireEvent(swipeStartEvent);
}
}
break;
case FOUND_DIRECTION:
switch (direction) {
case TOP_TO_BOTTOM:
case BOTTOM_TO_TOP:
lastDistance = Math.abs(touch.getPageY() - y);
source.fireEvent(
new SwipeMoveEvent(new TouchPoint(touch), lastDistance > minDistance,
lastDistance, direction));
break;
case LEFT_TO_RIGHT:
case RIGHT_TO_LEFT:
lastDistance = Math.abs(touch.getPageX() - x);
source.fireEvent(
new SwipeMoveEvent(new TouchPoint(touch), lastDistance > minDistance,
lastDistance, direction));
break;
default:
break;
}
break;
default:
break;
}
}
@Override
public void onTouchEnd(TouchEndEvent event) {
touchCount--;
switch (state) {
case FOUND_DIRECTION:
source.fireEvent(new SwipeEndEvent(lastDistance > minDistance, lastDistance, direction));
reset();
break;
default:
reset();
break;
}
}
@Override
public void onTouchCancel(TouchCancelEvent event) {
touchCount--;
if (touchCount <= 0) {
reset();
}
}
/**
* the threshold before an event is fired (deadzone)
*
* @return the threshold in px
*/
public int getThreshold() {
return threshold;
}
/**
* the distance that needs to be covered before counting as a swipe
*
* @return the distance in px
*/
public int getMinDistance() {
return minDistance;
}
private void reset() {
state = State.READY;
touchCount = 0;
}
}