/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.android.maps.inputhandling;
import org.mapsforge.android.maps.MapView;
import org.mapsforge.core.model.GeoPoint;
import android.content.Context;
import android.os.Build;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
/**
* Abstract base class for the single-touch and the multi-touch handler.
*/
public abstract class TouchEventHandler {
/**
* @param context
* a reference to the global application environment.
* @param mapView
* the MapView from which the touch events are coming from.
* @return a new TouchEventHandler instance, depending on the current Android version.
*/
public static TouchEventHandler getInstance(Context context, MapView mapView) {
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
return new SingleTouchHandler(context, mapView);
}
return new MultiTouchHandler(context, mapView);
}
/**
* Absolute threshold value for a double-tap event.
*/
final float doubleTapDelta;
/**
* Maximum time difference in milliseconds for a double-tap event.
*/
final int doubleTapTimeout;
/**
* Thread for detecting long press events.
*/
final LongPressDetector longPressDetector;
/**
* Duration in milliseconds for a long press event.
*/
final int longPressTimeout;
/**
* Absolute threshold value of a motion event to be interpreted as a move.
*/
final float mapMoveDelta;
/**
* The MapView from which the touch events are coming from.
*/
final MapView mapView;
/**
* Flag to indicate if the map movement threshold has been reached.
*/
boolean moveThresholdReached;
/**
* Flag to store if the previous event was a touch event.
*/
boolean previousEventTap;
/**
* Stores the x coordinate of the previous touch event.
*/
float previousPositionX;
/**
* Stores the y coordinate of the previous touch event.
*/
float previousPositionY;
/**
* Stores the time of the previous tap event.
*/
long previousTapTime;
/**
* Stores the X position of the previous tap event.
*/
float previousTapX;
/**
* Stores the Y position of the previous tap event.
*/
float previousTapY;
TouchEventHandler(Context context, MapView mapView) {
this.mapView = mapView;
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
this.mapMoveDelta = viewConfiguration.getScaledTouchSlop();
this.doubleTapDelta = viewConfiguration.getScaledDoubleTapSlop();
this.doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
this.longPressTimeout = ViewConfiguration.getLongPressTimeout();
this.longPressDetector = new LongPressDetector(this);
this.longPressDetector.start();
}
/**
* Destroys this TouchEventHandler.
*/
public final void destroy() {
this.longPressDetector.interrupt();
}
/**
* @param motionEvent
* the event to extract the action from.
* @return the action from the given motion event.
*/
public abstract int getAction(MotionEvent motionEvent);
/**
* Handles a motion event on the touch screen.
*
* @param motionEvent
* the motion event.
* @return true if the event was handled, false otherwise.
*/
public final boolean handleTouchEvent(MotionEvent motionEvent) {
if (!this.mapView.isClickable()) {
return true;
}
// round the event coordinates to integers
motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY());
return handleMotionEvent(motionEvent);
}
/**
* Forwards a long press event to all overlays until it has been handled.
*
* @return true if the long press event has been handled, false otherwise.
*/
final boolean forwardLongPressEvent() {
GeoPoint longPressPoint = this.mapView.getProjection().fromPixels((int) this.previousPositionX,
(int) this.previousPositionY);
if (longPressPoint != null) {
synchronized (this.mapView.getOverlays()) {
for (int i = this.mapView.getOverlays().size() - 1; i >= 0; --i) {
if (this.mapView.getOverlays().get(i).onLongPress(longPressPoint, this.mapView)) {
// the long press event has been handled
return true;
}
}
}
}
return false;
}
abstract boolean handleMotionEvent(MotionEvent motionEvent);
}