From 03569b29cbd8d2279a07ce74a9d3afdf3530e6d6 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Sat, 10 Jul 2010 22:01:33 +0000 Subject: [PATCH] add first multitouch suppor git-svn-id: https://osmand.googlecode.com/svn/trunk@314 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8 --- .../src/com/osmand/ToDoConstants.java | 3 +- .../com/osmand/activities/MapActivity.java | 16 +- .../views/AnimateDraggingMapThread.java | 1 + .../com/osmand/views/MultiTouchSupport.java | 114 +++++++++ .../com/osmand/views/OsmandMapTileView.java | 228 ++++++++---------- OsmAnd/src/com/osmand/views/POIMapLayer.java | 4 - .../com/osmand/views/PointLocationLayer.java | 4 +- .../com/osmand/views/TransportStopsLayer.java | 4 - 8 files changed, 222 insertions(+), 152 deletions(-) create mode 100644 OsmAnd/src/com/osmand/views/MultiTouchSupport.java diff --git a/DataExtractionOSM/src/com/osmand/ToDoConstants.java b/DataExtractionOSM/src/com/osmand/ToDoConstants.java index 112039ffb6..0f4fe7cb0a 100644 --- a/DataExtractionOSM/src/com/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/com/osmand/ToDoConstants.java @@ -19,7 +19,8 @@ public class ToDoConstants { // 60. Audio guidance for routing ! // 68. Implement service to app work with screen offline // (introduce special settings how often update location to monitoring & audio guidance) - // 69. Multitouch zoom + // 69. Multitouch zoom, animated zoom, animate map shift! + // check everywhere float zoom // Improvement : Show stops in the transport route on the map // Not clear if it is really needed diff --git a/OsmAnd/src/com/osmand/activities/MapActivity.java b/OsmAnd/src/com/osmand/activities/MapActivity.java index a3d0ed3ab7..1fbc13d379 100644 --- a/OsmAnd/src/com/osmand/activities/MapActivity.java +++ b/OsmAnd/src/com/osmand/activities/MapActivity.java @@ -279,13 +279,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso @Override public boolean onLongPressEvent(PointF point) { - float dx = point.x - mapView.getCenterPointX(); - float dy = point.y - mapView.getCenterPointY(); - float fy = mapView.calcDiffTileY(dx, dy); - float fx = mapView.calcDiffTileX(dx, dy); - double latitude = MapUtils.getLatitudeFromTile(mapView.getZoom(), mapView.getYTile() + fy); - double longitude = MapUtils.getLongitudeFromTile(mapView.getZoom(), mapView.getXTile() + fx); - contextMenuPoint(latitude, longitude, false); + LatLon l = mapView.getLatLonFromScreenPoint(point.x, point.y); + contextMenuPoint(l.getLatitude(), l.getLongitude(), false); return true; } @@ -308,11 +303,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso if(event.getAction() == MotionEvent.ACTION_MOVE){ float x = event.getX(); float y = event.getY(); - x = (float) (MapUtils.getTileNumberX(mapView.getZoom() , mapView.getLongitude()) + x / 3); - y = (float) (MapUtils.getTileNumberY(mapView.getZoom(), mapView.getLatitude()) + y / 3); - double lat = MapUtils.getLatitudeFromTile(mapView.getZoom(), y); - double lon = MapUtils.getLongitudeFromTile(mapView.getZoom(), x); - setMapLocation(lat, lon); + LatLon l = mapView.getLatLonFromScreenPoint(mapView.getCenterPointX() + x * 15, mapView.getCenterPointY() + y * 15); + setMapLocation(l.getLatitude(), l.getLongitude()); return true; } return super.onTrackballEvent(event); diff --git a/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java b/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java index 1189c3acef..0f0c55e416 100644 --- a/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java +++ b/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java @@ -7,6 +7,7 @@ package com.osmand.views; public class AnimateDraggingMapThread implements Runnable { public interface AnimateDraggingCallback { public void dragTo(float curX, float curY, float newX, float newY); + } private float curX; diff --git a/OsmAnd/src/com/osmand/views/MultiTouchSupport.java b/OsmAnd/src/com/osmand/views/MultiTouchSupport.java new file mode 100644 index 0000000000..cde0a6ceb7 --- /dev/null +++ b/OsmAnd/src/com/osmand/views/MultiTouchSupport.java @@ -0,0 +1,114 @@ +package com.osmand.views; + +import java.lang.reflect.Method; + +import org.apache.commons.logging.Log; + +import android.content.Context; +import android.util.FloatMath; +import android.view.MotionEvent; + +import com.osmand.LogUtil; + +public class MultiTouchSupport { + + private static final Log log = LogUtil.getLog(MultiTouchSupport.class); + + public static final int ACTION_MASK = 255; + public static final int ACTION_POINTER_ID_SHIFT = 8; + public static final int ACTION_POINTER_DOWN = 5; + public static final int ACTION_POINTER_UP = 6; + + public interface MultiTouchZoomListener { + + public void onZoomStarted(float distance); + + public void onZooming(float distance, float relativeToStart); + + public void onZoomEnded(float distance, float relativeToStart); + + } + + private boolean multiTouchAPISupported = false; + private final MultiTouchZoomListener listener; + protected final Context ctx; + + protected Method getPointerCount; + protected Method getX; + protected Method getY; + protected Method getPointerId; + + + + public MultiTouchSupport(Context ctx, MultiTouchZoomListener listener){ + this.ctx = ctx; + this.listener = listener; + initMethods(); + } + + public boolean isMultiTouchSupported(){ + return multiTouchAPISupported; + } + + public boolean isInZoomMode(){ + return inZoomMode; + } + + private void initMethods(){ + try { + getPointerCount = MotionEvent.class.getMethod("getPointerCount"); //$NON-NLS-1$ + getPointerId = MotionEvent.class.getMethod("getPointerId", Integer.TYPE); //$NON-NLS-1$ + getX = MotionEvent.class.getMethod("getX", Integer.TYPE); //$NON-NLS-1$ + getY = MotionEvent.class.getMethod("getY", Integer.TYPE); //$NON-NLS-1$ + multiTouchAPISupported = true; + } catch (Exception e) { + multiTouchAPISupported = false; + log.info("Multi touch not supported", e); //$NON-NLS-1$ + } + } + + private boolean inZoomMode = false; + private float zoomStartedDistance = 100; + private float previousZoom = 1; + + public boolean onTouchEvent(MotionEvent event){ + if(!isMultiTouchSupported()){ + return false; + } + int actionCode = event.getAction() & ACTION_MASK; + try { + Integer pointCount = (Integer) getPointerCount.invoke(event); + if(pointCount < 2){ + if(inZoomMode){ + listener.onZoomEnded(zoomStartedDistance * previousZoom, previousZoom); + } + return false; + } + Float x1 = (Float) getX.invoke(event, 0); + Float x2 = (Float) getX.invoke(event, 1); + Float y1 = (Float) getY.invoke(event, 0); + Float y2 = (Float) getY.invoke(event, 1); + float distance = FloatMath.sqrt((x2 - x1)*(x2 -x1) + (y2-y1)*(y2-y1)); + previousZoom = distance / zoomStartedDistance; + if (actionCode == ACTION_POINTER_DOWN) { + listener.onZoomStarted(distance); + zoomStartedDistance = distance; + inZoomMode = true; + return true; + } else if(actionCode == ACTION_POINTER_UP){ + if(inZoomMode){ + listener.onZoomEnded(distance, previousZoom); + inZoomMode = false; + } + return true; + } else if(inZoomMode && actionCode == MotionEvent.ACTION_MOVE){ + listener.onZooming(distance, previousZoom); + return true; + } + } catch (Exception e) { + log.debug("Multi touch exception" , e); //$NON-NLS-1$ + } + return false; + } + +} diff --git a/OsmAnd/src/com/osmand/views/OsmandMapTileView.java b/OsmAnd/src/com/osmand/views/OsmandMapTileView.java index 86f2f5448e..332a24009d 100644 --- a/OsmAnd/src/com/osmand/views/OsmandMapTileView.java +++ b/OsmAnd/src/com/osmand/views/OsmandMapTileView.java @@ -3,8 +3,6 @@ package com.osmand.views; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.commons.logging.Log; @@ -38,11 +36,13 @@ import com.osmand.data.preparation.MapTileDownloader.DownloadRequest; import com.osmand.data.preparation.MapTileDownloader.IMapDownloaderCallback; import com.osmand.map.IMapLocationListener; import com.osmand.map.ITileSource; +import com.osmand.osm.LatLon; import com.osmand.osm.MapUtils; import com.osmand.views.AnimateDraggingMapThread.AnimateDraggingCallback; +import com.osmand.views.MultiTouchSupport.MultiTouchZoomListener; public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, - Callback, AnimateDraggingCallback, OnGestureListener, OnDoubleTapListener { + Callback, AnimateDraggingCallback, OnGestureListener, OnDoubleTapListener, MultiTouchZoomListener { protected final int emptyTileDivisor = 16; protected final int timeForDraggingAnimation = 300; @@ -63,9 +63,10 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall protected static final Log log = LogUtil.getLog(OsmandMapTileView.class); /** - * zoom level + * zoom level - could be float to show zoomed tiles */ - private int zoom = 3; + // TODO rotated zoom calculation check where it is could be needed??? + private float zoom = 3; private double longitude = 0d; @@ -96,8 +97,12 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall private AnimateDraggingMapThread animatedDraggingThread; + private float initialMultiTouchZoom; + private GestureDetector gestureDetector; + private MultiTouchSupport multiTouchSupport; + Paint paintGrayFill; Paint paintWhiteFill; Paint paintCenter; @@ -146,6 +151,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall animatedDraggingThread = new AnimateDraggingMapThread(); animatedDraggingThread.setCallback(this); gestureDetector = new GestureDetector(getContext(), this); + multiTouchSupport = new MultiTouchSupport(getContext(), this); gestureDetector.setOnDoubleTapListener(this); } @@ -191,8 +197,13 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall /////////////////////////// NON UI PART (could be extracted in common) ///////////////////////////// - public int getTileSize() { - return map == null ? 256 : map.getTileSize(); + public float getTileSize() { + int tileSize = map == null ? 256 : map.getTileSize(); + if(zoom == (int) zoom){ + return tileSize; + } + float m = (float) Math.pow(2, zoom - (int) zoom); + return m * tileSize; } @@ -205,7 +216,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } - public void setZoom(int zoom){ + public void setZoom(float zoom){ if (map == null || (map.getMaximumZoomSupported() >= zoom && map.getMinimumZoomSupported() <= zoom)) { animatedDraggingThread.stopDragging(); this.zoom = zoom; @@ -266,9 +277,14 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } public int getZoom() { + return (int) zoom; + } + + public float getFloatZoom(){ return zoom; } + public void setMapLocationListener(IMapLocationListener l){ locationListener = l; } @@ -284,10 +300,10 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall //////////////////////////////// DRAWING MAP PART ///////////////////////////////////////////// protected void drawEmptyTile(Canvas cvs, float x, float y){ - int tileDiv = getTileSize() / emptyTileDivisor; + int tileDiv = (int) (getTileSize() / emptyTileDivisor); for (int k1 = 0; k1 < emptyTileDivisor; k1++) { for (int k2 = 0; k2 < emptyTileDivisor; k2++) { - float xk = x + tileDiv* k1; + float xk = x + tileDiv* k1 ; float yk = y + tileDiv* k2; if ((k1 + k2) % 2 == 0) { cvs.drawRect(xk, yk, xk + tileDiv, yk + tileDiv, paintGrayFill); @@ -378,7 +394,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall if (OsmandSettings.isUsingInternetToDownloadTiles(getContext())) { MapTileDownloader.getInstance().refuseAllPreviousRequests(); } - int tileSize = getTileSize(); + float ftileSize = getTileSize(); + int tileSize = map == null ? 256 : map.getTileSize(); float tileX = getXTile(); float tileY = getYTile(); float w = getCenterPointX(); @@ -386,6 +403,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall SurfaceHolder holder = getHolder(); synchronized (holder) { + int nzoom = (int) zoom; Canvas canvas = holder.lockCanvas(); if (canvas != null) { ResourceManager mgr = ResourceManager.getResourceManager(); @@ -402,21 +420,21 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall int height = (int) (FloatMath.ceil(tilesRect.bottom) - top); for (int i = 0; i 0) { - if (mtStart == null) { - mtStart = new PointF(event.getX(), event.getY()); - mtStart2 = new PointF(x2, y2); - mtZoom = zoom; - mtDist = dist; - log.debug("Multitouch dist="+mtDist); - } else { - if(dist < mtDist){ - dist *= 0.6f; - } else { - dist *= 1.3f; - } - int dz = (int) (Math.log(dist/mtDist) / Math.log(2)); - log.debug("Mt dist=" +dist); - if (mtZoom + dz != zoom) { - setZoom(mtZoom + dz); - } - } - lastX2 = x2; - lastY2 = y2; -// log.debug("Multi touch x=" + event.getX() + " y=" + event.getY() + " x2=" + x2 + " y2=" + y2 + " dist=" -// + dist +" mt="+mtDist); - } - return true; - } else if(event.getAction() == MotionEvent.ACTION_UP){ - log.debug("UP"); - mtStart = null; - mtStart2 = null; - return true; - } - } - } - return mtStart != null; -// String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" , -// "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" }; -// StringBuilder sb = new StringBuilder(); -// int action = event.getAction(); -// int actionCode = action & 255; -// sb.append("event ACTION_" ).append(names[actionCode]); -// if (actionCode == 5 -// || actionCode == 6) { -// sb.append("(pid " ).append(action >> 8); -// sb.append(")" ); -// } -// sb.append("[" ); -// for (int i = 0; i < event.getPointerCount(); i++) { -// sb.append("#" ).append(i); -// sb.append("(pid " ).append(event.getPointerId(i)); -// sb.append(")=" ).append((int) event.getX(i)); -// sb.append("," ).append((int) event.getY(i)); -// if (i + 1 < event.getPointerCount()) -// sb.append(";" ); -// } - - } - @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if(trackBallDelegate != null && keyCode == KeyEvent.KEYCODE_DPAD_CENTER){ @@ -724,19 +674,37 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall animatedDraggingThread.stopDragging(); return false; } + + @Override + public void onZoomEnded(float distance, float relativeToStart) { + float dz = (float) (Math.log(relativeToStart) / Math.log(2) * 1.5); + initialMultiTouchZoom = (float) Math.round(initialMultiTouchZoom + dz); + setZoom(initialMultiTouchZoom); + } + + + @Override + public void onZoomStarted(float distance) { + initialMultiTouchZoom = zoom; + } + + @Override + public void onZooming(float distance, float relativeToStart) { + float dz = (float) (Math.log(relativeToStart) / Math.log(2) * 1.5); + if(Math.abs(initialMultiTouchZoom + dz - zoom) > 0.05){ + setZoom(initialMultiTouchZoom + dz); + } + } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if(mtStart != null){ - return true; - } animatedDraggingThread.startDragging(Math.abs(velocityX/1000), Math.abs(velocityY/1000), e1.getX(), e1.getY(), e2.getX(), e2.getY()); return true; } @Override public void onLongPress(MotionEvent e) { - if(mtStart != null){ + if(multiTouchSupport.isInZoomMode()){ return; } if(log.isDebugEnabled()){ @@ -752,14 +720,12 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall return; } } + + @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if(mtStart != null){ - return true; - } dragTo(e2.getX() + distanceX, e2.getY() + distanceY, e2.getX(), e2.getY()); - return true; } @@ -769,9 +735,6 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @Override public boolean onSingleTapUp(MotionEvent e) { - if(mtStart != null){ - return true; - } PointF point = new PointF(e.getX(), e.getY()); if(log.isDebugEnabled()){ log.debug("On click event "+ point.x + " " + point.y); //$NON-NLS-1$ //$NON-NLS-2$ @@ -786,16 +749,23 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } return false; } + + + + public LatLon getLatLonFromScreenPoint(float x, float y){ + float dx = x - getCenterPointX(); + float dy = y - getCenterPointY(); + float fy = calcDiffTileY(dx, dy); + float fx = calcDiffTileX(dx, dy); + double latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + fy); + double longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + fx); + return new LatLon(latitude, longitude); + } @Override public boolean onDoubleTap(MotionEvent e) { - float dx = e.getX() - getCenterPointX(); - float dy = e.getY() - getCenterPointY(); - float fy = calcDiffTileY(dx, dy); - float fx = calcDiffTileX(dx, dy); - double latitude = MapUtils.getLatitudeFromTile(getZoom(), getYTile() + fy); - double longitude = MapUtils.getLongitudeFromTile(getZoom(), getXTile() + fx); - setLatLon(latitude, longitude); + LatLon l = getLatLonFromScreenPoint(e.getX(), e.getY()); + setLatLon(l.getLatitude(), l.getLongitude()); setZoom(zoom + 1); return true; } diff --git a/OsmAnd/src/com/osmand/views/POIMapLayer.java b/OsmAnd/src/com/osmand/views/POIMapLayer.java index 7fb9de4f1c..8d7605a825 100644 --- a/OsmAnd/src/com/osmand/views/POIMapLayer.java +++ b/OsmAnd/src/com/osmand/views/POIMapLayer.java @@ -109,10 +109,6 @@ public class POIMapLayer implements OsmandMapLayer { return false; } - public int getTileSize(){ - return view.getTileSize(); - - } @Override public void initLayer(OsmandMapTileView view) { diff --git a/OsmAnd/src/com/osmand/views/PointLocationLayer.java b/OsmAnd/src/com/osmand/views/PointLocationLayer.java index ecfe5758f7..6ab4ef161d 100644 --- a/OsmAnd/src/com/osmand/views/PointLocationLayer.java +++ b/OsmAnd/src/com/osmand/views/PointLocationLayer.java @@ -80,7 +80,7 @@ public class PointLocationLayer implements OsmandMapLayer { if (isLocationVisible(lastKnownLocation)) { int locationX = view.getMapXForPoint(lastKnownLocation.getLongitude()); int locationY = view.getMapYForPoint(lastKnownLocation.getLatitude()); - int radius = MapUtils.getLengthXFromMeters(view.getZoom(), view.getLatitude(), view.getLongitude(), lastKnownLocation + int radius = MapUtils.getLengthXFromMeters(view.getFloatZoom(), view.getLatitude(), view.getLongitude(), lastKnownLocation .getAccuracy(), view.getTileSize(), view.getWidth()); if(appMode == ApplicationMode.CAR){ @@ -105,7 +105,7 @@ public class PointLocationLayer implements OsmandMapLayer { int radiusBearing = 30; if(lastKnownLocation.hasSpeed()){ radiusBearing = - Math.max(MapUtils.getLengthXFromMeters(view.getZoom(), view.getLatitude(), view.getLongitude(), + Math.max(MapUtils.getLengthXFromMeters(view.getFloatZoom(), view.getLatitude(), view.getLongitude(), lastKnownLocation.getSpeed(), view.getTileSize(), view.getWidth()) * 2, radiusBearing); } radiusBearing += RADIUS /2; diff --git a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java index 53877175d9..7a4a2beb75 100644 --- a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java +++ b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java @@ -67,10 +67,6 @@ public class TransportStopsLayer implements OsmandMapLayer { - public int getTileSize(){ - return view.getTileSize(); - } - @Override public boolean onTouchEvent(PointF point) {