/* * Copyright 2010, 2011, 2012, 2013 mapsforge.org * Copyright 2014 Ludwig M Brinckmann * Copyright © 2014 devemux86 * Copyright © 2015 M. Dietrich * * 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.pyneo.tabulae.locus; import android.content.Context; import android.location.Location; import android.location.LocationProvider; import android.os.Build; import android.os.SystemClock; import android.util.Log; import org.mapsforge.core.graphics.Canvas; import org.mapsforge.core.graphics.GraphicFactory; import org.mapsforge.core.graphics.Paint; import org.mapsforge.core.graphics.Style; import org.mapsforge.core.model.BoundingBox; import org.mapsforge.core.model.LatLong; import org.mapsforge.core.model.Point; import org.mapsforge.map.android.graphics.AndroidGraphicFactory; import org.mapsforge.map.layer.Layer; import org.mapsforge.map.layer.overlay.Circle; import org.mapsforge.map.layer.overlay.Marker; import org.pyneo.tabulae.R; import static org.pyneo.tabulae.locus.Constants.*; /** * A thread-safe {@link Layer} implementation to display the current location. */ class ThreeStateLocationOverlay extends Layer { protected static final GraphicFactory GRAPHIC_FACTORY = AndroidGraphicFactory.INSTANCE; protected final Circle circle; protected final Marker map_needle_pinned; protected final Marker map_needle_off; protected final RotatingMarker map_needle; protected float minDistance = 0.0f; protected long minTime = 0; protected boolean showAccuracy = true; protected Marker marker; protected Context context; /** * Constructs a new {@code ThreeStateLocationOverlay} with the default circle paints. * * @param context a reference to the application context. */ public ThreeStateLocationOverlay(Context context) { super(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { map_needle_pinned = new Marker(null, AndroidGraphicFactory.convertToBitmap( context.getResources().getDrawable(R.drawable.map_needle_pinned, null)), 0, 0); map_needle = new RotatingMarker(null, AndroidGraphicFactory.convertToBitmap( context.getResources().getDrawable(R.drawable.map_needle, null)), 0, 0); map_needle_off = new Marker(null, AndroidGraphicFactory.convertToBitmap( context.getResources().getDrawable(R.drawable.map_needle_off, null)), 0, 0); } else { map_needle_pinned = new Marker(null, AndroidGraphicFactory.convertToBitmap( context.getResources().getDrawable(R.drawable.map_needle_pinned)), 0, 0); map_needle = new RotatingMarker(null, AndroidGraphicFactory.convertToBitmap( context.getResources().getDrawable(R.drawable.map_needle)), 0, 0); map_needle_off = new Marker(null, AndroidGraphicFactory.convertToBitmap( context.getResources().getDrawable(R.drawable.map_needle_off)), 0, 0); } marker = map_needle_off; Paint circleFill = getPaint(GRAPHIC_FACTORY.createColor(48, 0, 0, 255), 0, Style.FILL); Paint circleStroke = getPaint(GRAPHIC_FACTORY.createColor(160, 0, 0, 255), 2, Style.STROKE); circle = new Circle(null, 0, circleFill, circleStroke); } protected static Paint getPaint(int color, int strokeWidth, Style style) { Paint paint = GRAPHIC_FACTORY.createPaint(); paint.setColor(color); paint.setStrokeWidth(strokeWidth); paint.setStyle(style); return paint; } @Override public synchronized void draw(BoundingBox boundingBox, byte zoomLevel, Canvas canvas, Point topLeftPoint) { if (showAccuracy) { circle.draw(boundingBox, zoomLevel, canvas, topLeftPoint); } marker.draw(boundingBox, zoomLevel, canvas, topLeftPoint); } /** * Enables the receiving of location updates from the most accurate {@link LocationProvider} available. * * @return true */ public synchronized boolean enable() { circle.setDisplayModel(displayModel); map_needle_pinned.setDisplayModel(displayModel); map_needle.setDisplayModel(displayModel); map_needle_off.setDisplayModel(displayModel); return true; } @Override public void onDestroy() { map_needle_pinned.onDestroy(); map_needle.onDestroy(); map_needle_off.onDestroy(); } public void onLocationChanged(Location location) { //if (DEBUG) Log.d(TAG, "ThreeStateLocationOverlay.onLocationChanged location=" + location); synchronized (this) { long age = (SystemClock.elapsedRealtimeNanos() - location.getElapsedRealtimeNanos()) / 1000000000; if (age > 3 || !location.hasAccuracy() || location.getAccuracy() == 0) { //if (DEBUG) Log.d(TAG, "ThreeStateLocationOverlay.onLocationChanged off: age=" + age); marker = map_needle_off; circle.setRadius(0); } else { float accuracy = location.getAccuracy(); //if (DEBUG) { Log.d(TAG, "ThreeStateLocationOverlay.onLocationChanged circle: accuracy=" + accuracy); } circle.setRadius(accuracy); if (!location.hasSpeed() || !location.hasBearing()) { marker = map_needle_pinned; //if (DEBUG) { Log.d(TAG, "ThreeStateLocationOverlay.onLocationChanged pinned: no speed or bearing"); } } else { float speed = location.getSpeed(); if (speed < 2.0) { //if (DEBUG) { Log.d(TAG, "ThreeStateLocationOverlay.onLocationChanged pinned: speed=" + speed); } marker = map_needle_pinned; } else { float bearing = location.getBearing(); map_needle.setDegree(bearing); marker = map_needle; } } } LatLong latLong = new LatLong(location.getLatitude(), location.getLongitude()); marker.setLatLong(latLong); circle.setLatLong(latLong); requestRedraw(); } } }