package net.osmand.plus.views;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PointF;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import net.osmand.PlatformUtil;
import net.osmand.data.RotatedTileBox;
import org.apache.commons.logging.Log;
public class DoubleTapScaleDetector {
private static final Log LOG = PlatformUtil.getLog(DoubleTapScaleDetector.class);
private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
public static final int SCALE_PER_SCREEN = 4;
private final DoubleTapZoomListener listener;
protected final OsmandMapTileView view;
private int displayHeightPx;
private PointF centerScreen;
private PointF zoomCenter;
private boolean mIsInZoomMode = false;
private float scale;
private MotionEvent firstDown;
private MotionEvent firstUp;
private MotionEvent secondDown;
private int mTouchSlopSquare;
private int mDoubleTapSlopSquare;
private boolean mIsDoubleTapping;
private boolean mScrolling;
public DoubleTapScaleDetector(OsmandMapTileView view, DoubleTapZoomListener listener) {
this.view = view;
this.listener = listener;
RotatedTileBox tileBox = view.getCurrentRotatedTileBox();
centerScreen = new PointF(tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
displayHeightPx = tileBox.getPixHeight();
zoomCenter = new PointF(centerScreen.x, centerScreen.y);
final ViewConfiguration configuration = ViewConfiguration.get(view.getContext());
int touchSlop = configuration.getScaledTouchSlop();
mTouchSlopSquare = touchSlop * touchSlop;
int doubleTapSlop = (int) (configuration.getScaledDoubleTapSlop() * 0.5);
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getPointerCount() != 1) {
resetEvents();
mIsDoubleTapping = false;
mScrolling = false;
mIsInZoomMode = false;
return false;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
boolean handled = false;
if (mIsInZoomMode) {
mIsInZoomMode = false;
listener.onZoomEnded(scale);
handled = true;
} else if (secondDown != null) {
if (calculateSqaredDistance(secondDown, event) < mDoubleTapSlopSquare
&& event.getEventTime() - secondDown.getEventTime() < LONG_PRESS_TIMEOUT) {
listener.onDoubleTap(event);
}
handled = true;
} else if (!mScrolling) {
firstUp = MotionEvent.obtain(event);
} else {
resetEvents();
}
if (handled) {
resetEvents();
}
mIsDoubleTapping = false;
mScrolling = false;
return handled;
} else {
if (event.getAction() == MotionEvent.ACTION_DOWN && !mIsInZoomMode) {
if (isConsideredDoubleTap(firstDown, firstUp, event)) {
mIsDoubleTapping = true;
secondDown = MotionEvent.obtain(event);
float x = event.getX();
float y = event.getY();
listener.onGestureInit(x, y, x, y);
zoomCenter = isXLargeDevice(view.getContext()) ? new PointF(x, y) : centerScreen;
listener.onZoomStarted(zoomCenter);
return true;
} else {
firstDown = MotionEvent.obtain(event);
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (!mScrolling && secondDown == null && firstDown != null) {
mScrolling = calculateSqaredDistance(firstDown, event) > mTouchSlopSquare;
}
if (isConfirmedScale(secondDown, event)) {
mIsInZoomMode = true;
}
if (mIsInZoomMode) {
float delta = convertPxToDp((int) (firstDown.getY() - event.getY()));
float scaleDelta = delta / (displayHeightPx / SCALE_PER_SCREEN);
scale = 1 - scaleDelta;
listener.onZooming(scale);
return true;
}
}
}
return false;
}
private void resetEvents() {
firstUp = null;
firstDown = null;
secondDown = null;
}
public boolean isInZoomMode() {
return mIsInZoomMode;
}
public boolean isDoubleTapping() {
return mIsDoubleTapping;
}
private int convertPxToDp(int px) {
return Math.round(px / (Resources.getSystem().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
if (firstDown == null || firstUp == null || secondDown == null) {
return false;
}
if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
return false;
}
return calculateSqaredDistance(firstDown, secondDown) < mDoubleTapSlopSquare;
}
private int calculateSqaredDistance(MotionEvent first,
MotionEvent second) {
int deltaXDown = (int) first.getX() - (int) second.getX();
int deltaYDown = (int) first.getY() - (int) second.getY();
return deltaXDown * deltaXDown + deltaYDown * deltaYDown;
}
private boolean isConfirmedScale(MotionEvent secondDown, MotionEvent moveEvent) {
if (secondDown == null || moveEvent == null) {
return false;
}
return calculateSqaredDistance(secondDown, moveEvent) > mDoubleTapSlopSquare;
}
private boolean isXLargeDevice(Context ctx) {
int lt = (ctx.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK);
return lt == Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
public float getCenterX() {
return zoomCenter.x;
}
public float getCenterY() {
return zoomCenter.y;
}
public interface DoubleTapZoomListener {
void onZoomStarted(PointF centerPoint);
void onZooming(double relativeToStart);
void onZoomEnded(double relativeToStart);
void onGestureInit(float x1, float y1, float x2, float y2);
boolean onDoubleTap(MotionEvent e);
}
}