add osmdroid overlays + bonuspack
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* 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.oscim.view;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public class DelayedTaskHandler extends Handler {
|
||||
public final int MESSAGE_UPDATE_POSITION = 1;
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_UPDATE_POSITION:
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
@@ -34,6 +36,9 @@ import org.oscim.database.OpenResult;
|
||||
import org.oscim.generator.JobQueue;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.generator.MapWorker;
|
||||
import org.oscim.overlay.LabelingOverlay;
|
||||
import org.oscim.overlay.Overlay;
|
||||
import org.oscim.overlay.OverlayManager;
|
||||
import org.oscim.renderer.GLRenderer;
|
||||
import org.oscim.renderer.GLView;
|
||||
import org.oscim.renderer.TileGenerator;
|
||||
@@ -43,29 +48,28 @@ import org.oscim.theme.InternalRenderTheme;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.RenderThemeHandler;
|
||||
import org.oscim.theme.Theme;
|
||||
import org.oscim.utils.AndroidUtils;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
/**
|
||||
* A MapView shows a map on the display of the device. It handles all user input
|
||||
* and touch gestures to move and zoom the map.
|
||||
*/
|
||||
public class MapView extends FrameLayout {
|
||||
public class MapView extends RelativeLayout {
|
||||
|
||||
final static String TAG = "MapView";
|
||||
|
||||
public static final boolean debugFrameTime = false;
|
||||
public static final boolean testRegionZoom = false;
|
||||
// public static final boolean staticLabeling = false;
|
||||
|
||||
private static final boolean debugDatabase = false;
|
||||
|
||||
RegionLookup mRegionLookup;
|
||||
// RegionLookup mRegionLookup;
|
||||
|
||||
public boolean enableRotation = false;
|
||||
public boolean enableCompass = false;
|
||||
@@ -81,10 +85,15 @@ public class MapView extends FrameLayout {
|
||||
private MapDatabases mMapDatabaseType;
|
||||
|
||||
private TileManager mTileManager;
|
||||
private GLView mGLView;
|
||||
private JobQueue mJobQueue;
|
||||
private MapWorker mMapWorkers[];
|
||||
private int mNumMapWorkers = 4;
|
||||
private final OverlayManager mOverlayManager;
|
||||
|
||||
private final GLView mGLView;
|
||||
private final JobQueue mJobQueue;
|
||||
|
||||
// TODO use 1 download and 1 generator thread instead
|
||||
private final MapWorker mMapWorkers[];
|
||||
private final int mNumMapWorkers = 4;
|
||||
|
||||
private DebugSettings debugSettings;
|
||||
private String mRenderTheme;
|
||||
private Map<String, String> mMapOptions;
|
||||
@@ -124,8 +133,10 @@ public class MapView extends FrameLayout {
|
||||
}
|
||||
|
||||
Log.d(TAG, "create MapView: " + mapDatabaseType.name());
|
||||
// this.setDrawingCacheEnabled(true);
|
||||
this.setWillNotDraw(true);
|
||||
|
||||
// TODO make this dpi dependent
|
||||
// TODO set tilesize, make this dpi dependent
|
||||
Tile.TILE_SIZE = 400;
|
||||
|
||||
MapActivity mapActivity = (MapActivity) context;
|
||||
@@ -138,6 +149,8 @@ public class MapView extends FrameLayout {
|
||||
|
||||
mMapViewPosition = new MapViewPosition(this);
|
||||
|
||||
mOverlayManager = new OverlayManager();
|
||||
|
||||
mTouchEventHandler = new TouchHandler(mapActivity, this);
|
||||
|
||||
mCompass = new Compass(mapActivity, this);
|
||||
@@ -145,6 +158,7 @@ public class MapView extends FrameLayout {
|
||||
mJobQueue = new JobQueue();
|
||||
|
||||
mTileManager = TileManager.create(this);
|
||||
|
||||
mGLView = new GLView(context, this);
|
||||
|
||||
mMapWorkers = new MapWorker[mNumMapWorkers];
|
||||
@@ -156,7 +170,7 @@ public class MapView extends FrameLayout {
|
||||
// .createMapDatabase(MapDatabases.TEST_READER);
|
||||
mapDatabase = MapDatabaseFactory
|
||||
.createMapDatabase(MapDatabases.MAP_READER);
|
||||
mNumMapWorkers = 1;
|
||||
// mNumMapWorkers = 1;
|
||||
} else {
|
||||
mapDatabase = MapDatabaseFactory.createMapDatabase(mapDatabaseType);
|
||||
}
|
||||
@@ -187,8 +201,8 @@ public class MapView extends FrameLayout {
|
||||
|
||||
addView(mGLView, params);
|
||||
|
||||
if (testRegionZoom)
|
||||
mRegionLookup = new RegionLookup(this);
|
||||
// if (testRegionZoom)
|
||||
// mRegionLookup = new RegionLookup(this);
|
||||
|
||||
mMapZoomControls = new MapZoomControls(mapActivity, this);
|
||||
mMapZoomControls.setShowMapZoomControls(true);
|
||||
@@ -197,6 +211,35 @@ public class MapView extends FrameLayout {
|
||||
|
||||
for (MapWorker worker : mMapWorkers)
|
||||
worker.start();
|
||||
|
||||
mOverlayManager.add(new LabelingOverlay(this));
|
||||
// mOverlayManager.add(new GenericOverlay(this, new OverlayGrid(this)));
|
||||
// mOverlayManager.add(new GenericOverlay(this, new OverlayTest(this)));
|
||||
|
||||
// ArrayList<OverlayItem> pList = new ArrayList<OverlayItem>();
|
||||
// pList.add(new OverlayItem("title", "description", new GeoPoint(53.067221, 8.78767)));
|
||||
// Overlay overlay = new ItemizedIconOverlay<OverlayItem>(this, context, pList, null);
|
||||
// mOverlayManager.add(overlay);
|
||||
|
||||
// ArrayList<OverlayItem> pList = new ArrayList<OverlayItem>();
|
||||
// pList.add(new ExtendedOverlayItem("Bremen", "description",
|
||||
// new GeoPoint(53.047221, 8.78767), context));
|
||||
// pList.add(new ExtendedOverlayItem("New York", "description",
|
||||
// new GeoPoint(40.4251, -74.021), context));
|
||||
// pList.add(new ExtendedOverlayItem("Tokyo", "description",
|
||||
// new GeoPoint(35.4122, 139.4130), context));
|
||||
// Overlay overlay = new ItemizedOverlayWithBubble<OverlayItem>(this, context, pList, null);
|
||||
// mOverlayManager.add(overlay);
|
||||
|
||||
// PathOverlay pathOverlay = new PathOverlay(this, Color.BLUE, context);
|
||||
// pathOverlay.addGreatCircle(
|
||||
// new GeoPoint(53.047221, 8.78767),
|
||||
// new GeoPoint(40.4251, -74.021));
|
||||
// // pathOverlay.addPoint(new GeoPoint(53.047221, 8.78767));
|
||||
// // pathOverlay.addPoint(new GeoPoint(53.067221, 8.78767));
|
||||
// mOverlayManager.add(pathOverlay);
|
||||
|
||||
mMapViewPosition.animateTo(new GeoPoint(53.067221, 8.78767));
|
||||
}
|
||||
|
||||
public void render() {
|
||||
@@ -259,6 +302,9 @@ public class MapView extends FrameLayout {
|
||||
if (mPausing || this.getWidth() == 0 || this.getHeight() == 0)
|
||||
return;
|
||||
|
||||
if (AndroidUtils.currentThreadIsUiThread())
|
||||
mOverlayManager.onUpdate(mMapViewPosition.getMapPosition());
|
||||
|
||||
mTileManager.updateMap(false);
|
||||
}
|
||||
|
||||
@@ -266,7 +312,8 @@ public class MapView extends FrameLayout {
|
||||
if (mPausing || this.getWidth() == 0 || this.getHeight() == 0)
|
||||
return;
|
||||
|
||||
mTileManager.updateMap(true);
|
||||
if (AndroidUtils.currentThreadIsUiThread())
|
||||
mTileManager.updateMap(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,7 +338,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the map file for this MapView.
|
||||
*
|
||||
* @param mapOptions
|
||||
* ...
|
||||
* @return true if the map file was set correctly, false otherwise.
|
||||
@@ -356,7 +402,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the MapDatabase for this MapView.
|
||||
*
|
||||
* @param mapDatabaseType
|
||||
* the new MapDatabase.
|
||||
*/
|
||||
@@ -396,7 +441,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the internal theme which is used for rendering the map.
|
||||
*
|
||||
* @param internalRenderTheme
|
||||
* the internal rendering theme.
|
||||
* @return ...
|
||||
@@ -418,7 +462,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the theme file which is used for rendering the map.
|
||||
*
|
||||
* @param renderThemePath
|
||||
* the path to the XML file which defines the rendering theme.
|
||||
* @throws IllegalArgumentException
|
||||
@@ -488,11 +531,11 @@ public class MapView extends FrameLayout {
|
||||
mapWorkersProceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
mMapZoomControls.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
// @Override
|
||||
// protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
// // super.onLayout(changed, left, top, right, bottom);
|
||||
// mMapZoomControls.onLayout(changed, left, top, right, bottom);
|
||||
// }
|
||||
|
||||
void destroy() {
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
@@ -577,7 +620,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the center and zoom level of this MapView and triggers a redraw.
|
||||
*
|
||||
* @param mapPosition
|
||||
* the new map position of this MapView.
|
||||
*/
|
||||
@@ -591,7 +633,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the center of the MapView and triggers a redraw.
|
||||
*
|
||||
* @param geoPoint
|
||||
* the new center point of the map.
|
||||
*/
|
||||
@@ -611,7 +652,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* add jobs and remember MapWorkers that stuff needs to be done
|
||||
*
|
||||
* @param jobs
|
||||
* tile jobs
|
||||
*/
|
||||
@@ -648,6 +688,35 @@ public class MapView extends FrameLayout {
|
||||
mapWorker.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* You can add/remove/reorder your Overlays using the List of
|
||||
* {@link Overlay}. The first (index 0) Overlay gets drawn first, the one
|
||||
* with the highest as the last one.
|
||||
* @return ...
|
||||
*/
|
||||
public List<Overlay> getOverlays() {
|
||||
return this.getOverlayManager();
|
||||
}
|
||||
|
||||
public OverlayManager getOverlayManager() {
|
||||
return mOverlayManager;
|
||||
}
|
||||
|
||||
public BoundingBox getBoundingBox() {
|
||||
return mMapViewPosition.getViewBox();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
// // TODO Auto-generated method stub
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void onMeasure()) {
|
||||
// // TODO Auto-generated method stub
|
||||
//
|
||||
// }
|
||||
// /**
|
||||
// * Sets the visibility of the zoom controls.
|
||||
// *
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org, 2012 Hannes Janetzek
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* 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
|
||||
@@ -14,30 +15,35 @@
|
||||
*/
|
||||
package org.oscim.view;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.utils.FastMath;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A MapPosition stores the latitude and longitude coordinate of a MapView
|
||||
* together with its zoom level.
|
||||
* together with its zoom level, rotation and tilt
|
||||
*/
|
||||
|
||||
// TODO use global coordinates that directly scale to pixel
|
||||
|
||||
public class MapViewPosition {
|
||||
|
||||
private static final String TAG = "MapViewPosition";
|
||||
private static final String TAG = MapViewPosition.class.getSimpleName();
|
||||
|
||||
public final static int MAX_ZOOMLEVEL = 17;
|
||||
public final static int MIN_ZOOMLEVEL = 2;
|
||||
|
||||
private final static float MAX_ANGLE = 35;
|
||||
private final static float MAX_ANGLE = 40;
|
||||
|
||||
private final MapView mMapView;
|
||||
|
||||
@@ -51,6 +57,12 @@ public class MapViewPosition {
|
||||
private float mRotation;
|
||||
public float mTilt;
|
||||
|
||||
// private static final int REF_ZOOM = 20;
|
||||
private double mPosX;
|
||||
private double mPosY;
|
||||
|
||||
private AnimationHandler mHandler;
|
||||
|
||||
MapViewPosition(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mLatitude = Double.NaN;
|
||||
@@ -60,6 +72,8 @@ public class MapViewPosition {
|
||||
mRotation = 0.0f;
|
||||
mTilt = 0;
|
||||
mMapScale = 1;
|
||||
|
||||
mHandler = new AnimationHandler(this);
|
||||
}
|
||||
|
||||
private float[] mProjMatrix = new float[16];
|
||||
@@ -69,19 +83,12 @@ public class MapViewPosition {
|
||||
private float[] mRotMatrix = new float[16];
|
||||
private float[] mTmpMatrix = new float[16];
|
||||
|
||||
private int mHeight, mWidth;
|
||||
private static int mHeight, mWidth;
|
||||
public final static float VIEW_SCALE = 1f / 2;
|
||||
public final static float VIEW_DISTANCE = 2;
|
||||
public final static float VIEW_NEAR = VIEW_DISTANCE;
|
||||
public final static float VIEW_FAR = VIEW_DISTANCE * 2;
|
||||
|
||||
public final static float DIST = 2;
|
||||
|
||||
// public final static float VIEW_SCALE = 1f / 2;
|
||||
// public final static float VIEW_DISTANCE = 1;
|
||||
// public final static float VIEW_NEAR = 1;
|
||||
// public final static float VIEW_FAR = 2;
|
||||
|
||||
void setViewport(int width, int height) {
|
||||
float sw = VIEW_SCALE;
|
||||
float sh = VIEW_SCALE;
|
||||
@@ -124,50 +131,211 @@ public class MapViewPosition {
|
||||
mapPosition.scale = mScale;
|
||||
mapPosition.zoomLevel = z;
|
||||
|
||||
mapPosition.x = MercatorProjection.longitudeToPixelX(mLongitude, z);
|
||||
mapPosition.y = MercatorProjection.latitudeToPixelY(mLatitude, z);
|
||||
mapPosition.x = mPosX;
|
||||
mapPosition.y = mPosY;
|
||||
|
||||
if (mapPosition.viewMatrix != null)
|
||||
System.arraycopy(mViewMatrix, 0, mapPosition.viewMatrix, 0, 16);
|
||||
|
||||
if (mapPosition.rotateMatrix != null)
|
||||
System.arraycopy(mRotMatrix, 0, mapPosition.rotateMatrix, 0, 16);
|
||||
|
||||
if (coords == null)
|
||||
return true;
|
||||
|
||||
// not so sure about this, but somehow works. weird z-values...
|
||||
float tilt = FloatMath.sin((float) Math.toRadians(mTilt
|
||||
// * 2.2f for dist = 1
|
||||
* 1.4f // for dist = 2
|
||||
// * 0.8f for dist = 4
|
||||
* ((float) mHeight / mWidth)));
|
||||
float tilt = getZ(1);
|
||||
|
||||
float d = 1f;
|
||||
unproject(-d, d, tilt, coords, 0); // bottom-left
|
||||
unproject(d, d, tilt, coords, 2); // bottom-right
|
||||
unproject(d, -d, -tilt, coords, 4); // top-right
|
||||
unproject(-d, -d, -tilt, coords, 6); // top-left
|
||||
unproject(-1, 1, tilt, coords, 0); // bottom-left
|
||||
unproject(1, 1, tilt, coords, 2); // bottom-right
|
||||
unproject(1, -1, -tilt, coords, 4); // top-right
|
||||
unproject(-1, -1, -tilt, coords, 6); // top-left
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return the current center point of the MapView. */
|
||||
public synchronized GeoPoint getMapCenter() {
|
||||
return new GeoPoint(mLatitude, mLongitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a MapPosition or null, if this map position is not valid.
|
||||
* @see #isValid()
|
||||
*/
|
||||
public synchronized MapPosition getMapPosition() {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
|
||||
}
|
||||
|
||||
/** @return the current zoom level of the MapView. */
|
||||
public synchronized byte getZoomLevel() {
|
||||
return mZoomLevel;
|
||||
}
|
||||
|
||||
/** @return the current scale of the MapView. */
|
||||
public synchronized float getScale() {
|
||||
return mScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @return BoundingBox containing view
|
||||
*/
|
||||
public synchronized BoundingBox getViewBox() {
|
||||
|
||||
float[] coords = mBBoxCoords;
|
||||
|
||||
float tilt = getZ(1);
|
||||
unproject(-1, 1, -tilt, coords, 0); // top-left
|
||||
unproject(1, 1, -tilt, coords, 2); // top-right
|
||||
unproject(1, -1, tilt, coords, 4); // bottom-right
|
||||
unproject(-1, -1, tilt, coords, 6); // bottom-left
|
||||
|
||||
byte z = mZoomLevel;
|
||||
double dx, dy;
|
||||
double minLat = 0, minLon = 0, maxLat = 0, maxLon = 0, lon, lat;
|
||||
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
|
||||
dx = mPosX - coords[i + 0] / mScale;
|
||||
dy = mPosY - coords[i + 1] / mScale;
|
||||
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
if (i == 0) {
|
||||
minLon = maxLon = lon;
|
||||
minLat = maxLat = lat;
|
||||
} else {
|
||||
if (lat > maxLat)
|
||||
maxLat = lat;
|
||||
else if (lat < minLat)
|
||||
minLat = lat;
|
||||
|
||||
if (lon > maxLon)
|
||||
maxLon = lon;
|
||||
else if (lon < minLon)
|
||||
minLon = lon;
|
||||
}
|
||||
}
|
||||
|
||||
return new BoundingBox(minLat, minLon, maxLat, maxLon);
|
||||
}
|
||||
|
||||
private float[] mv = { 0, 0, 0, 1 };
|
||||
// private float[] mu = { 0, 0, 0, 1 };
|
||||
private float[] mu = { 0, 0, 0, 1 };
|
||||
private float[] mBBoxCoords = new float[8];
|
||||
|
||||
private void unproject(float x, float y, float z, float[] coords, int position) {
|
||||
// mv[0] = x;
|
||||
// mv[1] = y;
|
||||
// mv[2] = z - 2f;
|
||||
// // mv[2] = 1f / (z - 2f);
|
||||
// mv[3] = 1;
|
||||
// Matrix.multiplyMV(mu, 0, mProjMatrix, 0, mv, 0);
|
||||
/* get the depth-value of the map for the current tilt, approximately.
|
||||
* needed to un-project a point on screen to the position on the map. not
|
||||
* so sure about this, but at least somehow works. */
|
||||
private float getZ(float y) {
|
||||
return FloatMath.sin((float) Math.toRadians(mTilt))
|
||||
// * 2.2f // for dist = 1
|
||||
* 1.3f // for dist = 2
|
||||
// * 0.8f // for dist = 4
|
||||
* ((float) mHeight / mWidth) * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* for x,y in screen coordinates get the point on the map in map-tile
|
||||
* coordinates
|
||||
* @param x ...
|
||||
* @param y ...
|
||||
* @param reuse ...
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized Point getScreenPointOnMap(float x, float y, Point reuse) {
|
||||
Point out = reuse == null ? new Point() : reuse;
|
||||
|
||||
float mx = ((mWidth / 2) - x) / (mWidth / 2);
|
||||
float my = ((mHeight / 2) - y) / (mHeight / 2);
|
||||
|
||||
unproject(-mx, my, getZ(my), mu, 0);
|
||||
|
||||
out.x = (int) (mPosX + mu[0] / mScale);
|
||||
out.y = (int) (mPosY + mu[1] / mScale);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the GeoPoint for x,y in screen coordinates
|
||||
* @param x screen pixel x
|
||||
* @param y screen pixel y
|
||||
* @return the corresponding GeoPoint
|
||||
*/
|
||||
public synchronized GeoPoint fromScreenPixels(float x, float y) {
|
||||
float mx = ((mWidth / 2) - x) / (mWidth / 2);
|
||||
float my = ((mHeight / 2) - y) / (mHeight / 2);
|
||||
|
||||
unproject(-mx, my, getZ(my), mu, 0);
|
||||
|
||||
double dx = mPosX + mu[0] / mScale;
|
||||
double dy = mPosY + mu[1] / mScale;
|
||||
|
||||
GeoPoint p = new GeoPoint(
|
||||
MercatorProjection.pixelYToLatitude(dy, mZoomLevel),
|
||||
MercatorProjection.pixelXToLongitude(dx, mZoomLevel));
|
||||
|
||||
// Log.d(">>>", "fromScreenPixels " + p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the screen pixel for a GeoPoint
|
||||
* @param geoPoint ...
|
||||
* @param reuse ...
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized Point project(GeoPoint geoPoint, Point reuse) {
|
||||
Point out = reuse == null ? new Point() : reuse;
|
||||
|
||||
double x = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(),
|
||||
mZoomLevel);
|
||||
double y = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(),
|
||||
mZoomLevel);
|
||||
|
||||
mv[0] = (float) (x - mPosX) * mScale;
|
||||
mv[1] = (float) (y - mPosY) * mScale;
|
||||
mv[2] = 0;
|
||||
mv[3] = 1;
|
||||
|
||||
Matrix.multiplyMV(mv, 0, mViewMatrix, 0, mv, 0);
|
||||
Matrix.multiplyMV(mv, 0, mProjMatrix, 0, mv, 0);
|
||||
|
||||
out.x = (int) (mv[0] / mv[3] * mWidth / 2);
|
||||
out.y = (int) (mv[1] / mv[3] * mHeight / 2);
|
||||
|
||||
// Log.d(">>>", "project: " + out.x + " " + out.y);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public synchronized void getMVP(float[] matrix) {
|
||||
Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, mViewMatrix, 0);
|
||||
}
|
||||
|
||||
// public static Point project(float x, float y, float[] matrix, float[] tmpVec, Point reuse) {
|
||||
// Point out = reuse == null ? new Point() : reuse;
|
||||
//
|
||||
// tmpVec[0] = x;
|
||||
// tmpVec[1] = y;
|
||||
// tmpVec[2] = 0;
|
||||
// tmpVec[3] = 1;
|
||||
//
|
||||
// Matrix.multiplyMV(tmpVec, 0, matrix, 0, tmpVec, 0);
|
||||
//
|
||||
// out.x = (int) (tmpVec[0] / tmpVec[3] * mWidth / 2);
|
||||
// out.y = (int) (tmpVec[1] / tmpVec[3] * mHeight / 2);
|
||||
//
|
||||
// return out;
|
||||
// }
|
||||
|
||||
private void unproject(float x, float y, float z, float[] coords, int position) {
|
||||
mv[0] = x;
|
||||
mv[1] = y;
|
||||
mv[2] = z - 1f;
|
||||
// mv[2] = -mu[2] / mu[3];
|
||||
mv[3] = 1;
|
||||
|
||||
Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0);
|
||||
@@ -175,17 +343,13 @@ public class MapViewPosition {
|
||||
if (mv[3] != 0) {
|
||||
coords[position] = mv[0] / mv[3];
|
||||
coords[position + 1] = mv[1] / mv[3];
|
||||
// Log.d(TAG, (z * 1.4f - 1) + " " + mu[2] / mu[3] + " - " + x + ":"
|
||||
// + y + " - "
|
||||
// + coords[position] + ":" + coords[position + 1] + " - " + mTilt);
|
||||
} else {
|
||||
// else what?
|
||||
Log.d(TAG, "... what?");
|
||||
Log.d(TAG, "uproject failed");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMatrix() {
|
||||
Matrix.setRotateM(mRotMatrix, 0, mRotation, 0, 0, 1);
|
||||
// - view matrix
|
||||
// 1. scale to window coordinates
|
||||
// 2. rotate
|
||||
@@ -195,11 +359,13 @@ public class MapViewPosition {
|
||||
// 4. translate to near-plane
|
||||
// 5. apply projection
|
||||
|
||||
Matrix.setRotateM(mRotMatrix, 0, mRotation, 0, 0, 1);
|
||||
|
||||
// tilt map
|
||||
float tilt = mTilt;
|
||||
Matrix.setRotateM(mTmpMatrix, 0, tilt, 1, 0, 0);
|
||||
|
||||
// apply first viewMatrix, then tilt
|
||||
// apply first rotation, then tilt
|
||||
Matrix.multiplyMM(mRotMatrix, 0, mTmpMatrix, 0, mRotMatrix, 0);
|
||||
|
||||
// scale to window coordinates
|
||||
@@ -231,91 +397,7 @@ public class MapViewPosition {
|
||||
Matrix.multiplyMM(mUnprojMatrix, 0, mTmpMatrix, 0, mProjMatrixI, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets viewBox to visible bounding box, (left,top,right,bottom)
|
||||
*
|
||||
* @param viewBox
|
||||
* ...
|
||||
*/
|
||||
public synchronized void getViewBox(final float[] viewBox) {
|
||||
|
||||
// updateMatrix();
|
||||
|
||||
float tilt = FloatMath.sin((float) Math.toRadians(mTilt)) * 4;
|
||||
|
||||
unproject(-1, 1, -tilt, mBBoxCoords, 0); // top-left
|
||||
unproject(1, 1, -tilt, mBBoxCoords, 2); // top-right
|
||||
unproject(1, -1, tilt, mBBoxCoords, 4); // bottom-right
|
||||
unproject(-1, -1, tilt, mBBoxCoords, 6); // bottom-left
|
||||
|
||||
byte z = mZoomLevel;
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, z);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, z);
|
||||
|
||||
double dx = pixelX - mBBoxCoords[0] / mScale;
|
||||
double dy = pixelY - mBBoxCoords[1] / mScale;
|
||||
double lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
double lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d(">>>", "bl:" + lon + " " + lat);
|
||||
|
||||
dx = pixelX - mBBoxCoords[2] / mScale;
|
||||
dy = pixelY - mBBoxCoords[3] / mScale;
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d("...", "br:" + lon + " " + lat);
|
||||
|
||||
dx = pixelX - mBBoxCoords[4] / mScale;
|
||||
dy = pixelY - mBBoxCoords[5] / mScale;
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d("...", "tl:" + lon + " " + lat);
|
||||
|
||||
dx = pixelX - mBBoxCoords[6] / mScale;
|
||||
dy = pixelY - mBBoxCoords[7] / mScale;
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d("...", "tr:" + lon + " " + lat);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current center point of the MapView.
|
||||
*/
|
||||
public synchronized GeoPoint getMapCenter() {
|
||||
return new GeoPoint(mLatitude, mLongitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an immutable MapPosition or null, if this map position is not
|
||||
* valid.
|
||||
* @see #isValid()
|
||||
*/
|
||||
public synchronized MapPosition getMapPosition() {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
// Log.d("MapViewPosition", "lat: " + mLatitude + " lon: " +
|
||||
// mLongitude);
|
||||
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current zoom level of the MapView.
|
||||
*/
|
||||
public synchronized byte getZoomLevel() {
|
||||
return mZoomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current scale of the MapView.
|
||||
*/
|
||||
public synchronized float getScale() {
|
||||
return mScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MapViewPosition is valid, false otherwise.
|
||||
*/
|
||||
/** @return true if this MapViewPosition is valid, false otherwise. */
|
||||
public synchronized boolean isValid() {
|
||||
if (Double.isNaN(mLatitude)) {
|
||||
return false;
|
||||
@@ -336,168 +418,43 @@ public class MapViewPosition {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GeoPoint for a pixel on screen
|
||||
*
|
||||
* @param x
|
||||
* ...
|
||||
* @param y
|
||||
* ...
|
||||
* @return the GeoPoint
|
||||
*/
|
||||
public GeoPoint getOffsetPoint(float x, float y) {
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
|
||||
double dx = ((mMapView.getWidth() >> 1) - x) / mScale;
|
||||
double dy = ((mMapView.getHeight() >> 1) - y) / mScale;
|
||||
|
||||
if (mMapView.enableRotation || mMapView.enableCompass) {
|
||||
double rad = Math.toRadians(mRotation);
|
||||
double xx = dx * Math.cos(rad) + dy * -Math.sin(rad);
|
||||
double yy = dx * Math.sin(rad) + dy * Math.cos(rad);
|
||||
|
||||
dx = pixelX - xx;
|
||||
dy = pixelY - yy;
|
||||
} else {
|
||||
dx = pixelX - dx;
|
||||
dy = pixelY - dy;
|
||||
}
|
||||
|
||||
double latitude = MercatorProjection.pixelYToLatitude(dy, mZoomLevel);
|
||||
latitude = MercatorProjection.limitLatitude(latitude);
|
||||
|
||||
double longitude = MercatorProjection.pixelXToLongitude(dx, mZoomLevel);
|
||||
longitude = MercatorProjection.limitLongitude(longitude);
|
||||
|
||||
return new GeoPoint(latitude, longitude);
|
||||
}
|
||||
|
||||
// public static double pixelXToLongitude(double pixelX, byte zoomLevel) {
|
||||
// return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5);
|
||||
// }
|
||||
//
|
||||
// public static double pixelYToLatitude(double pixelY, byte zoomLevel) {
|
||||
// double y = 0.5 - (pixelY / ((long) Tile.TILE_SIZE << zoomLevel));
|
||||
// return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Moves this MapViewPosition by the given amount of pixels.
|
||||
*
|
||||
* @param mx
|
||||
* the amount of pixels to move the map horizontally.
|
||||
* @param my
|
||||
* the amount of pixels to move the map vertically.
|
||||
* @param mx the amount of pixels to move the map horizontally.
|
||||
* @param my the amount of pixels to move the map vertically.
|
||||
*/
|
||||
public synchronized void moveMap(float mx, float my) {
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
|
||||
double dx = mx / mScale;
|
||||
double dy = my / mScale;
|
||||
|
||||
if (mMapView.enableRotation || mMapView.enableCompass) {
|
||||
double rad = Math.toRadians(mRotation);
|
||||
double x = dx * Math.cos(rad) + dy * Math.sin(rad);
|
||||
double y = dx * -Math.sin(rad) + dy * Math.cos(rad);
|
||||
double rcos = Math.cos(rad);
|
||||
double rsin = Math.sin(rad);
|
||||
double x = dx * rcos + dy * rsin;
|
||||
double y = dx * -rsin + dy * rcos;
|
||||
dx = x;
|
||||
dy = y;
|
||||
}
|
||||
|
||||
dx = pixelX - dx;
|
||||
dy = pixelY - dy;
|
||||
dx = mPosX - dx;
|
||||
dy = mPosY - dy;
|
||||
|
||||
mLatitude = MercatorProjection.pixelYToLatitude(dy, mZoomLevel);
|
||||
mLatitude = MercatorProjection.limitLatitude(mLatitude);
|
||||
|
||||
mLongitude = MercatorProjection.pixelXToLongitude(dx, mZoomLevel);
|
||||
|
||||
mLongitude = MercatorProjection.wrapLongitude(mLongitude);
|
||||
// mLongitude = MercatorProjection.limitLongitude(mLongitude);
|
||||
|
||||
// getViewBox(null);
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
public synchronized void rotateMap(float angle, float cx, float cy) {
|
||||
moveMap(cx, cy);
|
||||
// Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation -
|
||||
// angle));
|
||||
mRotation += angle;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public void setRotation(float f) {
|
||||
mRotation = f;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public boolean tilt(float move) {
|
||||
float tilt = mTilt + move;
|
||||
if (tilt > MAX_ANGLE)
|
||||
tilt = MAX_ANGLE;
|
||||
else if (tilt < 0)
|
||||
tilt = 0;
|
||||
|
||||
if (mTilt == tilt)
|
||||
return false;
|
||||
|
||||
setTilt(tilt);
|
||||
// mTilt = tilt;
|
||||
// updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTilt(float f) {
|
||||
mTilt = f;
|
||||
|
||||
// float sw = VIEW_SCALE;
|
||||
// float sh = VIEW_SCALE;
|
||||
// sh += (mTilt / 250);
|
||||
|
||||
// Matrix.frustumM(mProjMatrix, 0, -sw * mWidth, sw * mWidth,
|
||||
// sh * mHeight, -sh * mHeight, 1, 2);
|
||||
//
|
||||
// Matrix.translateM(mProjMatrix, 0, 0, 0, -VIEW_DISTANCE);
|
||||
//
|
||||
// Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0);
|
||||
// Matrix.invertM(mUnprojMatrix, 0, mProjMatrix, 0);
|
||||
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(GeoPoint geoPoint) {
|
||||
mLatitude = MercatorProjection.limitLatitude(geoPoint.getLatitude());
|
||||
mLongitude = MercatorProjection.limitLongitude(geoPoint.getLongitude());
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(MapPosition mapPosition) {
|
||||
mLatitude = MercatorProjection.limitLatitude(mapPosition.lat);
|
||||
mLongitude = MercatorProjection.limitLongitude(mapPosition.lon);
|
||||
mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
}
|
||||
|
||||
synchronized void setZoomLevel(byte zoomLevel) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
}
|
||||
|
||||
synchronized void setScale(float scale) {
|
||||
mScale = scale;
|
||||
}
|
||||
|
||||
// synchronized void zoomBoundingBox(GeoPoint p1, GeoPoint p2) {
|
||||
//
|
||||
// }
|
||||
|
||||
/**
|
||||
* @param scale
|
||||
* ...
|
||||
* @param pivotX
|
||||
* ...
|
||||
* @param pivotY
|
||||
* ...
|
||||
* -
|
||||
* @param scale ...
|
||||
* @param pivotX ...
|
||||
* @param pivotY ...
|
||||
* @return true if scale was changed
|
||||
*/
|
||||
public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) {
|
||||
@@ -512,13 +469,15 @@ public class MapViewPosition {
|
||||
if (z > MAX_ZOOMLEVEL) {
|
||||
// z17 shows everything, just increase scaling
|
||||
// need to fix this for ScanBox
|
||||
if (mScale * scale > 2) // 8)
|
||||
if (mScale * scale > 4)
|
||||
return false;
|
||||
|
||||
mScale *= scale;
|
||||
mMapScale = newScale;
|
||||
} else {
|
||||
mZoomLevel = (byte) z;
|
||||
updatePosition();
|
||||
|
||||
mScale = newScale / (1 << z);
|
||||
mMapScale = newScale;
|
||||
}
|
||||
@@ -530,4 +489,182 @@ public class MapViewPosition {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* rotate map around pivot cx,cy
|
||||
* @param angle ...
|
||||
* @param cx ...
|
||||
* @param cy ...
|
||||
*/
|
||||
public synchronized void rotateMap(float angle, float cx, float cy) {
|
||||
moveMap(cx, cy);
|
||||
mRotation += angle;
|
||||
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public synchronized void setRotation(float f) {
|
||||
mRotation = f;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public synchronized boolean tilt(float move) {
|
||||
float tilt = mTilt + move;
|
||||
if (tilt > MAX_ANGLE)
|
||||
tilt = MAX_ANGLE;
|
||||
else if (tilt < 0)
|
||||
tilt = 0;
|
||||
|
||||
if (mTilt == tilt)
|
||||
return false;
|
||||
|
||||
setTilt(tilt);
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void setTilt(float f) {
|
||||
mTilt = f;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
private void setMapCenter(double latitude, double longitude) {
|
||||
mLatitude = MercatorProjection.limitLatitude(latitude);
|
||||
mLongitude = MercatorProjection.limitLongitude(longitude);
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(GeoPoint geoPoint) {
|
||||
setMapCenter(geoPoint.getLatitude(), geoPoint.getLongitude());
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(MapPosition mapPosition) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
setMapCenter(mapPosition.lat, mapPosition.lon);
|
||||
}
|
||||
|
||||
synchronized void setZoomLevel(byte zoomLevel) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
synchronized void setScale(float scale) {
|
||||
mScale = scale;
|
||||
}
|
||||
|
||||
private void updatePosition() {
|
||||
mPosX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
mPosY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
}
|
||||
|
||||
private double mStartX;
|
||||
private double mStartY;
|
||||
private double mEndX;
|
||||
private double mEndY;
|
||||
private float mDuration = 500;
|
||||
private Point mTmpPoint;
|
||||
|
||||
public synchronized void animateTo(GeoPoint geoPoint) {
|
||||
MercatorProjection.projectPoint(geoPoint, mZoomLevel, mTmpPoint);
|
||||
|
||||
mEndX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mZoomLevel);
|
||||
mEndY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mZoomLevel);
|
||||
mStartX = mPosX;
|
||||
mStartY = mPosY;
|
||||
|
||||
mDuration = 300;
|
||||
mHandler.start((int) mDuration);
|
||||
}
|
||||
|
||||
synchronized void setMapPosition(double x, double y) {
|
||||
|
||||
mLatitude = MercatorProjection.pixelYToLatitude(y, mZoomLevel);
|
||||
mLatitude = MercatorProjection.limitLatitude(mLatitude);
|
||||
|
||||
mLongitude = MercatorProjection.pixelXToLongitude(x, mZoomLevel);
|
||||
mLongitude = MercatorProjection.wrapLongitude(mLongitude);
|
||||
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
void onTick(long millisLeft) {
|
||||
double adv = millisLeft / mDuration;
|
||||
double mx = (mStartX + (mEndX - mStartX) * (1.0 - adv));
|
||||
double my = (mStartY + (mEndY - mStartY) * (1.0 - adv));
|
||||
setMapPosition(mx, my);
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
void onFinish() {
|
||||
setMapPosition(mEndX, mEndY);
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
static class AnimationHandler extends Handler {
|
||||
private final WeakReference<MapViewPosition> mMapViewPosition;
|
||||
private static final int MSG = 1;
|
||||
|
||||
long mMillisInFuture;
|
||||
|
||||
long mInterval = 16;
|
||||
|
||||
long mStopTimeInFuture;
|
||||
|
||||
AnimationHandler(MapViewPosition mapAnimator) {
|
||||
mMapViewPosition = new WeakReference<MapViewPosition>(mapAnimator);
|
||||
}
|
||||
|
||||
public synchronized final void start(int millis) {
|
||||
mMillisInFuture = millis;
|
||||
MapViewPosition animator = mMapViewPosition.get();
|
||||
if (animator == null)
|
||||
return;
|
||||
|
||||
if (mMillisInFuture <= 0) {
|
||||
animator.onFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
|
||||
removeMessages(MSG);
|
||||
sendMessage(obtainMessage(MSG));
|
||||
}
|
||||
|
||||
public final void cancel() {
|
||||
removeMessages(MSG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
MapViewPosition animator = mMapViewPosition.get();
|
||||
if (animator == null)
|
||||
return;
|
||||
|
||||
final long millisLeft = mStopTimeInFuture
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
if (millisLeft <= 0) {
|
||||
animator.onFinish();
|
||||
} else if (millisLeft < mInterval) {
|
||||
// no tick, just delay until done
|
||||
sendMessageDelayed(obtainMessage(MSG), millisLeft);
|
||||
} else {
|
||||
long lastTickStart = SystemClock.elapsedRealtime();
|
||||
animator.onTick(millisLeft);
|
||||
|
||||
// take into account user's onTick taking time to
|
||||
// execute
|
||||
long delay = lastTickStart + mInterval
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
// special case: user's onTick took more than interval
|
||||
// to
|
||||
// complete, skip to next interval
|
||||
while (delay < 0)
|
||||
delay += mInterval;
|
||||
|
||||
sendMessageDelayed(obtainMessage(MSG), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* 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.oscim.view;
|
||||
|
||||
public class OverlayManager {
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* 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
|
||||
@@ -12,55 +13,52 @@
|
||||
* 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.oscim.view;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.overlay.OverlayManager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.GestureDetector.OnDoubleTapListener;
|
||||
import android.view.GestureDetector.OnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ScaleGestureDetector.OnScaleGestureListener;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Scroller;
|
||||
|
||||
/**
|
||||
* Implementation for multi-touch capable devices.
|
||||
*/
|
||||
|
||||
// TODO:
|
||||
// - write a AnimationTimer instead of using CountDownTimers
|
||||
// - fix recognition of tilt/rotate/scale state...
|
||||
|
||||
final class TouchHandler
|
||||
extends SimpleOnGestureListener
|
||||
implements ScaleGestureDetector.OnScaleGestureListener {
|
||||
final class TouchHandler implements OnGestureListener, OnScaleGestureListener, OnDoubleTapListener {
|
||||
|
||||
private static final float SCALE_DURATION = 450;
|
||||
private static final String TAG = TouchHandler.class.getSimpleName();
|
||||
|
||||
private static final float SCALE_DURATION = 500;
|
||||
private static final float ROTATION_DELAY = 200; // ms
|
||||
|
||||
private static final int INVALID_POINTER_ID = -1;
|
||||
|
||||
private final MapView mMapView;
|
||||
private final MapViewPosition mMapPosition;
|
||||
private final DecelerateInterpolator mInterpolator = new DecelerateInterpolator();
|
||||
private final OverlayManager mOverlayManager;
|
||||
|
||||
private final DecelerateInterpolator mInterpolator;
|
||||
private final DecelerateInterpolator mLinearInterpolator;
|
||||
private boolean mBeginScale;
|
||||
private float mSumScale;
|
||||
|
||||
private final float mMapMoveDelta;
|
||||
private boolean mMoveStart;
|
||||
private boolean mBeginRotate;
|
||||
private boolean mBeginTilt;
|
||||
private boolean mLongPress;
|
||||
// private long mLongPressTime;
|
||||
|
||||
private float mPosX;
|
||||
// private float mPosX;
|
||||
private float mPosY;
|
||||
private double mAngle;
|
||||
|
||||
@@ -76,16 +74,20 @@ final class TouchHandler
|
||||
* the MapView
|
||||
*/
|
||||
public TouchHandler(Context context, MapView mapView) {
|
||||
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
|
||||
mMapView = mapView;
|
||||
mMapPosition = mapView.getMapPosition();
|
||||
mMapMoveDelta = viewConfiguration.getScaledTouchSlop();
|
||||
mOverlayManager = mapView.getOverlayManager();
|
||||
// ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
|
||||
// mMapMoveDelta = viewConfiguration.getScaledTouchSlop();
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
mScaleGestureDetector = new ScaleGestureDetector(context, this);
|
||||
mGestureDetector = new GestureDetector(context, this);
|
||||
mGestureDetector.setOnDoubleTapListener(this);
|
||||
|
||||
mScroller = new Scroller(mMapView.getContext(),
|
||||
new android.view.animation.LinearInterpolator());
|
||||
mInterpolator = new DecelerateInterpolator(1.5f);
|
||||
|
||||
mScroller = new Scroller(mMapView.getContext(), mInterpolator);
|
||||
mLinearInterpolator = new DecelerateInterpolator(0.8f);//new android.view.animation.LinearInterpolator();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,16 +97,8 @@ final class TouchHandler
|
||||
*/
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
|
||||
// workaround for a bug in the ScaleGestureDetector, see Android issue
|
||||
// #12976
|
||||
// if (event.getAction() != MotionEvent.ACTION_MOVE
|
||||
// || event.getPointerCount() > 1) {
|
||||
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
mScaleGestureDetector.onTouchEvent(event);
|
||||
// }
|
||||
|
||||
if (!mScaling)
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
|
||||
int action = getAction(event);
|
||||
boolean ret = false;
|
||||
@@ -136,10 +130,10 @@ final class TouchHandler
|
||||
}
|
||||
|
||||
private boolean onActionDown(MotionEvent event) {
|
||||
mPosX = event.getX();
|
||||
// mPosX = event.getX();
|
||||
mPosY = event.getY();
|
||||
|
||||
mMoveStart = false;
|
||||
// mMoveStart = false;
|
||||
mBeginRotate = false;
|
||||
mBeginTilt = false;
|
||||
// save the ID of the pointer
|
||||
@@ -154,43 +148,19 @@ final class TouchHandler
|
||||
private boolean onActionMove(MotionEvent event) {
|
||||
int id = event.findPointerIndex(mActivePointerId);
|
||||
|
||||
// calculate the distance between previous and current position
|
||||
float moveX = event.getX(id) - mPosX;
|
||||
float moveY = event.getY(id) - mPosY;
|
||||
// save the position of the event
|
||||
|
||||
// Log.d("...", "mx " + moveX + " my " + moveY);
|
||||
|
||||
boolean scaling = mScaleGestureDetector.isInProgress();
|
||||
|
||||
if (!mScaling)
|
||||
mScaling = scaling;
|
||||
|
||||
if (!scaling && !mMoveStart) {
|
||||
|
||||
if (Math.abs(moveX) > mMapMoveDelta || Math.abs(moveY) > mMapMoveDelta) {
|
||||
// the map movement threshold has been reached
|
||||
// longPressDetector.pressStop();
|
||||
mMoveStart = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mPosX = event.getX(id);
|
||||
mPosY = event.getY(id);
|
||||
float py = event.getY(id);
|
||||
float moveY = py - mPosY;
|
||||
mPosY = py;
|
||||
|
||||
// double-tap + hold
|
||||
if (mLongPress) {
|
||||
mMapPosition.scaleMap(1 - moveY / 100, 0, 0);
|
||||
mMapView.redrawMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (multi == 0) {
|
||||
mMapPosition.moveMap(moveX, moveY);
|
||||
mMapView.redrawMap();
|
||||
if (multi == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.getEventTime() - mMultiTouchDownTime < ROTATION_DELAY)
|
||||
return true;
|
||||
@@ -206,18 +176,22 @@ final class TouchHandler
|
||||
double rad = Math.atan2(dy, dx);
|
||||
double r = rad - mAngle;
|
||||
|
||||
if (!mBeginRotate) {
|
||||
if (!mBeginRotate && !mBeginScale) {
|
||||
/* our naive gesture detector for rotation and tilt.. */
|
||||
|
||||
if (Math.abs(rad) < 0.25 || Math.abs(rad) > Math.PI - 0.25) {
|
||||
mBeginTilt = true;
|
||||
if (mMapPosition.tilt(moveY / 4)) {
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mBeginScale && !mBeginTilt) {
|
||||
if (!mBeginTilt) {
|
||||
if (Math.abs(r) > 0.05) {
|
||||
Log.d("...", "begin rotate");
|
||||
// Log.d(TAG, "begin rotate");
|
||||
mAngle = rad;
|
||||
mBeginRotate = true;
|
||||
}
|
||||
}
|
||||
@@ -246,7 +220,7 @@ final class TouchHandler
|
||||
private long mMultiTouchDownTime;
|
||||
|
||||
private boolean onActionPointerDown(MotionEvent event) {
|
||||
// longPressDetector.pressStop();
|
||||
|
||||
mMultiTouchDownTime = event.getEventTime();
|
||||
|
||||
multi++;
|
||||
@@ -256,7 +230,7 @@ final class TouchHandler
|
||||
double dy = event.getY(0) - event.getY(1);
|
||||
mAngle = Math.atan2(dy, dx);
|
||||
}
|
||||
Log.d("...", "multi down " + multi);
|
||||
// Log.d("...", "multi down " + multi);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -274,14 +248,14 @@ final class TouchHandler
|
||||
pointerIndex = 0;
|
||||
}
|
||||
// save the position of the event
|
||||
mPosX = motionEvent.getX(pointerIndex);
|
||||
// mPosX = motionEvent.getX(pointerIndex);
|
||||
mPosY = motionEvent.getY(pointerIndex);
|
||||
mActivePointerId = motionEvent.getPointerId(pointerIndex);
|
||||
}
|
||||
multi--;
|
||||
|
||||
mLongPress = false;
|
||||
Log.d("...", "multi up " + multi);
|
||||
// Log.d("...", "multi up " + multi);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -294,38 +268,32 @@ final class TouchHandler
|
||||
private boolean onActionUp(MotionEvent motionEvent) {
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
mScaling = false;
|
||||
multi = 0;
|
||||
|
||||
// if (mLongPress && SystemClock.uptimeMillis() - mLongPressTime < 150)
|
||||
// {
|
||||
// mScrollX = (mPosX - (mMapView.getWidth() >> 1)) * 2f;
|
||||
// mScrollY = (mPosY - (mMapView.getHeight() >> 1)) * 2f;
|
||||
// mPrevScale = 0;
|
||||
//
|
||||
// mTimer = new CountDownTimer((int) SCALE_DURATION, 30) {
|
||||
// @Override
|
||||
// public void onTick(long tick) {
|
||||
// scale2(tick);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFinish() {
|
||||
// scale2(0);
|
||||
// }
|
||||
// }.start();
|
||||
// }
|
||||
|
||||
mLongPress = false;
|
||||
|
||||
multi = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************* SimpleOnGestureListener *******************/
|
||||
/******************* GestureListener *******************/
|
||||
|
||||
private Scroller mScroller;
|
||||
private float mScrollX, mScrollY;
|
||||
private boolean fling = false;
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
Log.d(TAG, "show press");
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
Log.d(TAG, "single tap up");
|
||||
return mOverlayManager.onSingleTapUp(e, mMapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
if (fling) {
|
||||
@@ -338,6 +306,8 @@ final class TouchHandler
|
||||
fling = false;
|
||||
}
|
||||
|
||||
// Log.d(TAG, "tap");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -359,9 +329,32 @@ final class TouchHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
|
||||
final float distanceY) {
|
||||
|
||||
if (mOverlayManager.onScroll(e1, e2, distanceX, distanceY, mMapView)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mScaling)
|
||||
return true;
|
||||
|
||||
if (multi == 0) {
|
||||
mMapPosition.moveMap(-distanceX, -distanceY);
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
||||
float velocityY) {
|
||||
|
||||
if (mScaling)
|
||||
return true;
|
||||
|
||||
int w = Tile.TILE_SIZE * 20;
|
||||
int h = Tile.TILE_SIZE * 20;
|
||||
mScrollX = 0;
|
||||
@@ -376,7 +369,7 @@ final class TouchHandler
|
||||
-w, w, -h, h);
|
||||
|
||||
// animate for two seconds
|
||||
mTimer = new CountDownTimer(1500, 50) {
|
||||
mTimer = new CountDownTimer(1500, 16) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scroll();
|
||||
@@ -384,20 +377,26 @@ final class TouchHandler
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// do nothing
|
||||
}
|
||||
}.start();
|
||||
fling = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
if (MapView.testRegionZoom) {
|
||||
Log.d("mapsforge", "long press");
|
||||
mMapView.mRegionLookup.updateRegion(-1, null);
|
||||
if (mLongPress)
|
||||
return;
|
||||
|
||||
if (mOverlayManager.onLongPress(e, mMapView)) {
|
||||
return;
|
||||
}
|
||||
mLongPress = true;
|
||||
|
||||
// if (MapView.testRegionZoom) {
|
||||
// Log.d("mapsforge", "long press");
|
||||
// mMapView.mRegionLookup.updateRegion(-1, null);
|
||||
// }
|
||||
}
|
||||
|
||||
boolean scale2(long tick) {
|
||||
@@ -422,36 +421,30 @@ final class TouchHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************* DoubleTapListener ****************/
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
// Log.d(TAG, "single tap confirmed");
|
||||
return mOverlayManager.onSingleTapConfirmed(e, mMapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (MapView.testRegionZoom) {
|
||||
mMapView.mRegionLookup.updateRegion(1,
|
||||
mMapPosition.getOffsetPoint(mPosX, mPosY));
|
||||
} else {
|
||||
mLongPress = true;
|
||||
Log.d(TAG, "double tap");
|
||||
|
||||
// mLongPressTime = SystemClock.uptimeMillis();
|
||||
// mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f;
|
||||
// mScrollY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f;
|
||||
// mPrevScale = 0;
|
||||
//
|
||||
// mTimer = new CountDownTimer((int) SCALE_DURATION, 30) {
|
||||
// @Override
|
||||
// public void onTick(long tick) {
|
||||
// scale2(tick);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFinish() {
|
||||
// scale(0);
|
||||
// }
|
||||
// }.start();
|
||||
}
|
||||
return true;
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTapEvent(MotionEvent e) {
|
||||
mLongPress = true;
|
||||
Log.d(TAG, "double tap event");
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************* ScaleListener *******************/
|
||||
|
||||
private float mCenterX;
|
||||
private float mCenterY;
|
||||
private float mFocusX;
|
||||
@@ -475,7 +468,7 @@ final class TouchHandler
|
||||
|
||||
if (!mBeginScale) {
|
||||
if (mSumScale > 1.1 || mSumScale < 0.9) {
|
||||
Log.d("...", "begin scale " + mSumScale);
|
||||
// Log.d("...", "begin scale " + mSumScale);
|
||||
mBeginScale = true;
|
||||
// scale = mSumScale;
|
||||
}
|
||||
@@ -489,9 +482,11 @@ final class TouchHandler
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector gd) {
|
||||
mScaling = true;
|
||||
mBeginScale = false;
|
||||
|
||||
mTimeEnd = mTimeStart = SystemClock.elapsedRealtime();
|
||||
mSumScale = 1;
|
||||
mBeginScale = false;
|
||||
mCenterX = mMapView.getWidth() >> 1;
|
||||
mCenterY = mMapView.getHeight() >> 1;
|
||||
|
||||
@@ -514,19 +509,21 @@ final class TouchHandler
|
||||
|
||||
mZooutOut = mSumScale < 0.99;
|
||||
|
||||
mTimer = new CountDownTimer((int) SCALE_DURATION, 15) {
|
||||
mTimer = new CountDownTimer((int) SCALE_DURATION, 32) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scale(tick);
|
||||
scaleAnim(tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
scale(0);
|
||||
|
||||
scaleAnim(0);
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
mScaling = false;
|
||||
}
|
||||
|
||||
mBeginScale = false;
|
||||
}
|
||||
|
||||
@@ -534,7 +531,7 @@ final class TouchHandler
|
||||
private CountDownTimer mTimer;
|
||||
boolean mZooutOut;
|
||||
|
||||
boolean scale(long tick) {
|
||||
boolean scaleAnim(long tick) {
|
||||
|
||||
if (mPrevScale >= 1) {
|
||||
mTimer = null;
|
||||
@@ -542,7 +539,8 @@ final class TouchHandler
|
||||
}
|
||||
|
||||
float adv = (SCALE_DURATION - tick) / SCALE_DURATION;
|
||||
adv = mInterpolator.getInterpolation(adv);
|
||||
// adv = mInterpolator.getInterpolation(adv);
|
||||
adv = mLinearInterpolator.getInterpolation(adv);
|
||||
|
||||
float scale = adv - mPrevScale;
|
||||
mPrevScale += scale;
|
||||
@@ -560,120 +558,4 @@ final class TouchHandler
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* from CountDownTimer.java: Copyright (C) 2008 The Android Open Source
|
||||
* Project 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.
|
||||
*/
|
||||
|
||||
final static class Timer {
|
||||
|
||||
/**
|
||||
* Millis since epoch when alarm should stop.
|
||||
*/
|
||||
private final long mMillisInFuture;
|
||||
|
||||
/**
|
||||
* The interval in millis that the user receives callbacks
|
||||
*/
|
||||
final long mCountdownInterval;
|
||||
|
||||
long mStopTimeInFuture;
|
||||
|
||||
/**
|
||||
* @param millisInFuture
|
||||
* The number of millis in the future from the call to
|
||||
* {@link #start()} until the countdown is done and
|
||||
* {@link #onFinish()} is called.
|
||||
* @param countDownInterval
|
||||
* The interval along the way to receive
|
||||
* {@link #onTick(long)} callbacks.
|
||||
*/
|
||||
public Timer(long millisInFuture, long countDownInterval) {
|
||||
mMillisInFuture = millisInFuture;
|
||||
mCountdownInterval = countDownInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the countdown.
|
||||
*/
|
||||
public final void cancel() {
|
||||
mHandler.removeMessages(MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the countdown.
|
||||
*
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized final Timer start() {
|
||||
if (mMillisInFuture <= 0) {
|
||||
onFinish();
|
||||
return this;
|
||||
}
|
||||
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired on regular interval.
|
||||
*
|
||||
* @param millisUntilFinished
|
||||
* The amount of time until finished.
|
||||
*/
|
||||
public void onTick(long millisUntilFinished) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired when the time is up.
|
||||
*/
|
||||
public void onFinish() {
|
||||
}
|
||||
|
||||
private static final int MSG = 1;
|
||||
|
||||
// handles counting down
|
||||
private Handler mHandler = new Handler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
|
||||
synchronized (Timer.this) {
|
||||
final long millisLeft = mStopTimeInFuture
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
if (millisLeft <= 0) {
|
||||
onFinish();
|
||||
} else if (millisLeft < mCountdownInterval) {
|
||||
// no tick, just delay until done
|
||||
sendMessageDelayed(obtainMessage(MSG), millisLeft);
|
||||
} else {
|
||||
long lastTickStart = SystemClock.elapsedRealtime();
|
||||
onTick(millisLeft);
|
||||
|
||||
// take into account user's onTick taking time to
|
||||
// execute
|
||||
long delay = lastTickStart + mCountdownInterval
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
// special case: user's onTick took more than interval
|
||||
// to
|
||||
// complete, skip to next interval
|
||||
while (delay < 0)
|
||||
delay += mCountdownInterval;
|
||||
|
||||
sendMessageDelayed(obtainMessage(MSG), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user