package net.osmand.plus;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import net.osmand.GeoidAltitudeCorrection;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.access.NavigationInfo;
import net.osmand.binary.GeocodingUtilities.GeocodingResult;
import net.osmand.binary.RouteDataObject;
import net.osmand.data.LatLon;
import net.osmand.data.QuadPoint;
import net.osmand.plus.OsmandSettings.OsmandPreference;
import net.osmand.plus.TargetPointsHelper.TargetPoint;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.router.RouteSegmentResult;
import net.osmand.util.MapUtils;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
public class OsmAndLocationProvider implements SensorEventListener {
public static final int REQUEST_LOCATION_PERMISSION = 100;
private static final String SIMULATED_PROVIDER = "OsmAnd";
public interface OsmAndLocationListener {
void updateLocation(net.osmand.Location location);
}
public interface OsmAndCompassListener {
void updateCompassValue(float value);
}
private static final int INTERVAL_TO_CLEAR_SET_LOCATION = 30 * 1000;
private static final int LOST_LOCATION_MSG_ID = OsmAndConstants.UI_HANDLER_LOCATION_SERVICE + 1;
private static final int START_SIMULATE_LOCATION_MSG_ID = OsmAndConstants.UI_HANDLER_LOCATION_SERVICE + 2;
private static final int RUN_SIMULATE_LOCATION_MSG_ID = OsmAndConstants.UI_HANDLER_LOCATION_SERVICE + 3;
private static final long LOST_LOCATION_CHECK_DELAY = 18000;
private static final long START_LOCATION_SIMULATION_DELAY = 2000;
private static final float ACCURACY_FOR_GPX_AND_ROUTING = 50;
private static final int GPS_TIMEOUT_REQUEST = 0;
private static final int GPS_DIST_REQUEST = 0;
private static final int NOT_SWITCH_TO_NETWORK_WHEN_GPS_LOST_MS = 12000;
private long lastTimeGPSLocationFixed = 0;
private boolean gpsSignalLost;
private SimulationProvider simulatePosition = null;
private boolean sensorRegistered = false;
private float[] mGravs = new float[3];
private float[] mGeoMags = new float[3];
private float previousCorrectionValue = 360;
private static final boolean USE_KALMAN_FILTER = true;
private static final float KALMAN_COEFFICIENT = 0.04f;
float avgValSin = 0;
float avgValCos = 0;
float lastValSin = 0;
float lastValCos = 0;
private float[] previousCompassValuesA = new float[50];
private float[] previousCompassValuesB = new float[50];
private int previousCompassIndA = 0;
private int previousCompassIndB = 0;
private boolean inUpdateValue = false;
private Float heading = null;
// Current screen orientation
private int currentScreenOrientation;
private OsmandApplication app;
private OsmandSettings settings;
private NavigationInfo navigationInfo;
private CurrentPositionHelper currentPositionHelper;
private OsmAndLocationSimulation locationSimulation;
private net.osmand.Location location = null;
private GPSInfo gpsInfo = new GPSInfo();
private List<OsmAndLocationListener> locationListeners = new ArrayList<OsmAndLocationProvider.OsmAndLocationListener>();
private List<OsmAndCompassListener> compassListeners = new ArrayList<OsmAndLocationProvider.OsmAndCompassListener>();
private Listener gpsStatusListener;
private float[] mRotationM = new float[9];
private OsmandPreference<Boolean> USE_MAGNETIC_FIELD_SENSOR_COMPASS;
private OsmandPreference<Boolean> USE_FILTER_FOR_COMPASS;
private static final long AGPS_TO_REDOWNLOAD = 16 * 60 * 60 * 1000; // 16 hours
public class SimulationProvider {
private int currentRoad;
private int currentSegment;
private QuadPoint currentPoint;
private net.osmand.Location startLocation;
private List<RouteSegmentResult> roads;
public void startSimulation(List<RouteSegmentResult> roads,
net.osmand.Location currentLocation) {
this.roads = roads;
startLocation = new net.osmand.Location(currentLocation);
long ms = System.currentTimeMillis();
if (ms - startLocation.getTime() > 5000 ||
ms < startLocation.getTime()) {
startLocation.setTime(ms);
}
currentRoad = -1;
int px = MapUtils.get31TileNumberX(currentLocation.getLongitude());
int py = MapUtils.get31TileNumberY(currentLocation.getLatitude());
double dist = 1000;
for (int i = 0; i < roads.size(); i++) {
RouteSegmentResult road = roads.get(i);
boolean plus = road.getStartPointIndex() < road.getEndPointIndex();
for (int j = road.getStartPointIndex() + 1; j <= road.getEndPointIndex(); ) {
RouteDataObject obj = road.getObject();
QuadPoint proj = MapUtils.getProjectionPoint31(px, py, obj.getPoint31XTile(j - 1), obj.getPoint31YTile(j - 1),
obj.getPoint31XTile(j), obj.getPoint31YTile(j));
double dd = MapUtils.squareRootDist31((int) proj.x, (int) proj.y, px, py);
if (dd < dist) {
dist = dd;
currentRoad = i;
currentSegment = j;
currentPoint = proj;
}
j += plus ? 1 : -1;
}
}
}
private float proceedMeters(float meters, net.osmand.Location l) {
for (int i = currentRoad; i < roads.size(); i++) {
RouteSegmentResult road = roads.get(i);
boolean firstRoad = i == currentRoad;
boolean plus = road.getStartPointIndex() < road.getEndPointIndex();
for (int j = firstRoad ? currentSegment : road.getStartPointIndex() + 1; j <= road.getEndPointIndex(); ) {
RouteDataObject obj = road.getObject();
int st31x = obj.getPoint31XTile(j - 1);
int st31y = obj.getPoint31YTile(j - 1);
int end31x = obj.getPoint31XTile(j);
int end31y = obj.getPoint31YTile(j);
boolean last = i == roads.size() - 1 && j == road.getEndPointIndex();
boolean first = firstRoad && j == currentSegment;
if (first) {
st31x = (int) currentPoint.x;
st31y = (int) currentPoint.y;
}
double dd = MapUtils.measuredDist31(st31x, st31y, end31x, end31y);
if (meters > dd && !last) {
meters -= dd;
} else {
int prx = (int) (st31x + (end31x - st31x) * (meters / dd));
int pry = (int) (st31y + (end31y - st31y) * (meters / dd));
l.setLongitude(MapUtils.get31LongitudeX(prx));
l.setLatitude(MapUtils.get31LatitudeY(pry));
return (float) Math.max(meters - dd, 0);
}
j += plus ? 1 : -1;
}
}
return -1;
}
/**
* @return null if it is not available of far from boundaries
*/
public net.osmand.Location getSimulatedLocation() {
if (!isSimulatedDataAvailable()) {
return null;
}
net.osmand.Location loc = new net.osmand.Location(SIMULATED_PROVIDER);
loc.setSpeed(startLocation.getSpeed());
loc.setAltitude(startLocation.getAltitude());
loc.setTime(System.currentTimeMillis());
float meters = startLocation.getSpeed() * ((System.currentTimeMillis() - startLocation.getTime()) / 1000);
float proc = proceedMeters(meters, loc);
if (proc < 0 || proc >= 100) {
return null;
}
return loc;
}
public boolean isSimulatedDataAvailable() {
return startLocation != null && startLocation.getSpeed() > 0 && currentRoad >= 0;
}
}
public OsmAndLocationProvider(OsmandApplication app) {
this.app = app;
navigationInfo = new NavigationInfo(app);
settings = app.getSettings();
USE_MAGNETIC_FIELD_SENSOR_COMPASS = settings.USE_MAGNETIC_FIELD_SENSOR_COMPASS;
USE_FILTER_FOR_COMPASS = settings.USE_KALMAN_FILTER_FOR_COMPASS;
currentPositionHelper = new CurrentPositionHelper(app);
locationSimulation = new OsmAndLocationSimulation(app, this);
addLocationListener(navigationInfo);
addCompassListener(navigationInfo);
}
public void resumeAllUpdates() {
final LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
if (app.getSettings().isInternetConnectionAvailable()) {
if (System.currentTimeMillis() - app.getSettings().AGPS_DATA_LAST_TIME_DOWNLOADED.get() > AGPS_TO_REDOWNLOAD) {
//force an updated check for internet connectivity here before destroying A-GPS-data
if (app.getSettings().isInternetConnectionAvailable(true)) {
redownloadAGPS();
}
}
}
if (isLocationPermissionAvailable(app)) {
service.addGpsStatusListener(getGpsStatusListener(service));
try {
service.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_TIMEOUT_REQUEST, GPS_DIST_REQUEST, gpsListener);
} catch (IllegalArgumentException e) {
Log.d(PlatformUtil.TAG, "GPS location provider not available"); //$NON-NLS-1$
}
// try to always ask for network provide : it is faster way to find location
List<String> providers = service.getProviders(true);
if (providers == null) {
return;
}
for (String provider : providers) {
if (provider == null || provider.equals(LocationManager.GPS_PROVIDER)) {
continue;
}
try {
NetworkListener networkListener = new NetworkListener();
service.requestLocationUpdates(provider, GPS_TIMEOUT_REQUEST, GPS_DIST_REQUEST, networkListener);
networkListeners.add(networkListener);
} catch (IllegalArgumentException e) {
Log.d(PlatformUtil.TAG, provider + " location provider not available"); //$NON-NLS-1$
}
}
}
}
public void redownloadAGPS() {
try {
final LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
service.sendExtraCommand(LocationManager.GPS_PROVIDER,"delete_aiding_data", null);
Bundle bundle = new Bundle();
service.sendExtraCommand("gps", "force_xtra_injection", bundle);
service.sendExtraCommand("gps", "force_time_injection", bundle);
app.getSettings().AGPS_DATA_LAST_TIME_DOWNLOADED.set(System.currentTimeMillis());
} catch (Exception e) {
app.getSettings().AGPS_DATA_LAST_TIME_DOWNLOADED.set(0L);
e.printStackTrace();
}
}
private Listener getGpsStatusListener(final LocationManager service) {
gpsStatusListener = new Listener() {
private GpsStatus gpsStatus;
@Override
public void onGpsStatusChanged(int event) {
gpsStatus = service.getGpsStatus(gpsStatus);
updateGPSInfo(gpsStatus);
updateLocation(location);
}
};
return gpsStatusListener;
}
private void updateGPSInfo(GpsStatus s) {
boolean fixed = false;
int n = 0;
int u = 0;
if (s != null) {
Iterator<GpsSatellite> iterator = s.getSatellites().iterator();
while (iterator.hasNext()) {
GpsSatellite g = iterator.next();
n++;
if (g.usedInFix()) {
u++;
fixed = true;
}
}
}
gpsInfo.fixed = fixed;
gpsInfo.foundSatellites = n;
gpsInfo.usedSatellites = u;
}
public GPSInfo getGPSInfo(){
return gpsInfo;
}
public void updateScreenOrientation(int orientation) {
currentScreenOrientation = orientation;
}
public void addLocationListener(OsmAndLocationListener listener){
if(!locationListeners.contains(listener)) {
locationListeners.add(listener);
}
}
public void removeLocationListener(OsmAndLocationListener listener){
locationListeners.remove(listener);
}
public void addCompassListener(OsmAndCompassListener listener){
if(!compassListeners.contains(listener)) {
compassListeners.add(listener);
}
}
public void removeCompassListener(OsmAndCompassListener listener){
compassListeners.remove(listener);
}
public net.osmand.Location getFirstTimeRunDefaultLocation() {
if (!isLocationPermissionAvailable(app)) {
return null;
}
LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
List<String> ps = service.getProviders(true);
if(ps == null) {
return null;
}
List<String> providers = new ArrayList<String>(ps);
// note, passive provider is from API_LEVEL 8 but it is a constant, we can check for it.
// constant should not be changed in future
int passiveFirst = providers.indexOf("passive"); // LocationManager.PASSIVE_PROVIDER
// put passive provider to first place
if (passiveFirst > -1) {
providers.add(0, providers.remove(passiveFirst));
}
// find location
for (String provider : providers) {
net.osmand.Location location = convertLocation(service.getLastKnownLocation(provider), app);
if (location != null) {
return location;
}
}
return null;
}
public synchronized void registerOrUnregisterCompassListener(boolean register) {
if (sensorRegistered && !register) {
Log.d(PlatformUtil.TAG, "Disable sensor"); //$NON-NLS-1$
((SensorManager) app.getSystemService(Context.SENSOR_SERVICE)).unregisterListener(this);
sensorRegistered = false;
heading = null;
} else if (!sensorRegistered && register) {
Log.d(PlatformUtil.TAG, "Enable sensor"); //$NON-NLS-1$
SensorManager sensorMgr = (SensorManager) app.getSystemService(Context.SENSOR_SERVICE);
if (USE_MAGNETIC_FIELD_SENSOR_COMPASS.get()) {
Sensor s = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (s == null || !sensorMgr.registerListener(this, s, SensorManager.SENSOR_DELAY_UI)) {
Log.e(PlatformUtil.TAG, "Sensor accelerometer could not be enabled");
}
s = sensorMgr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
if (s == null || !sensorMgr.registerListener(this, s, SensorManager.SENSOR_DELAY_UI)) {
Log.e(PlatformUtil.TAG, "Sensor magnetic field could not be enabled");
}
} else {
Sensor s = sensorMgr.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (s == null || !sensorMgr.registerListener(this, s, SensorManager.SENSOR_DELAY_UI)) {
Log.e(PlatformUtil.TAG, "Sensor orientation could not be enabled");
}
}
sensorRegistered = true;
}
}
// location not null!
private void updateSpeedEmulator(net.osmand.Location location) {
// For network/gps it's bad way (not accurate). It's widely used for testing purposes
// possibly keep using only for emulator case
if (location != null) {
if (location.distanceTo(location) > 3) {
float d = location.distanceTo(location);
long time = location.getTime() - location.getTime();
float speed;
if (time == 0) {
speed = 0;
} else {
speed = ((float) d * 1000) / time;
}
// Be aware only for emulator ! code is incorrect in case of airplane
if (speed > 100) {
speed = 100;
}
location.setSpeed(speed);
}
}
}
public static boolean isPointAccurateForRouting(net.osmand.Location loc) {
return loc != null && (!loc.hasAccuracy() || loc.getAccuracy() < ACCURACY_FOR_GPX_AND_ROUTING * 3 / 2);
}
private boolean isRunningOnEmulator() {
if (Build.DEVICE.equals("generic")) { //$NON-NLS-1$
return true;
}
return false;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
// Attention : sensor produces a lot of events & can hang the system
if(inUpdateValue) {
return;
}
synchronized (this) {
if (!sensorRegistered) {
return;
}
inUpdateValue = true;
try {
float val = 0;
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
System.arraycopy(event.values, 0, mGravs, 0, 3);
break;
case Sensor.TYPE_MAGNETIC_FIELD:
System.arraycopy(event.values, 0, mGeoMags, 0, 3);
break;
case Sensor.TYPE_ORIENTATION:
val = event.values[0];
break;
default:
return;
}
if (USE_MAGNETIC_FIELD_SENSOR_COMPASS.get()) {
if (mGravs != null && mGeoMags != null) {
boolean success = SensorManager.getRotationMatrix(mRotationM, null, mGravs, mGeoMags);
if (!success) {
return;
}
float[] orientation = SensorManager.getOrientation(mRotationM, new float[3]);
val = (float) Math.toDegrees(orientation[0]);
} else {
return;
}
}
val = calcScreenOrientationCorrection(val);
val = calcGeoMagneticCorrection(val);
float valRad = (float) (val / 180f * Math.PI);
lastValSin = (float) Math.sin(valRad);
lastValCos = (float) Math.cos(valRad);
// lastHeadingCalcTime = System.currentTimeMillis();
boolean filter = USE_FILTER_FOR_COMPASS.get(); //USE_MAGNETIC_FIELD_SENSOR_COMPASS.get();
if (filter) {
filterCompassValue();
} else {
avgValSin = lastValSin;
avgValCos = lastValCos;
}
updateCompassVal();
} finally {
inUpdateValue = false;
}
}
}
private float calcGeoMagneticCorrection(float val) {
if (previousCorrectionValue == 360 && getLastKnownLocation() != null) {
net.osmand.Location l = getLastKnownLocation();
GeomagneticField gf = new GeomagneticField((float) l.getLatitude(), (float) l.getLongitude(), (float) l.getAltitude(),
System.currentTimeMillis());
previousCorrectionValue = gf.getDeclination();
}
if (previousCorrectionValue != 360) {
val += previousCorrectionValue;
}
return val;
}
private float calcScreenOrientationCorrection(float val) {
if (currentScreenOrientation == 1) {
val += 90;
} else if (currentScreenOrientation == 2) {
val += 180;
} else if (currentScreenOrientation == 3) {
val -= 90;
}
return val;
}
private void filterCompassValue() {
if(heading == null && previousCompassIndA == 0) {
Arrays.fill(previousCompassValuesA, lastValSin);
Arrays.fill(previousCompassValuesB, lastValCos);
avgValSin = lastValSin;
avgValCos = lastValCos;
} else {
if (USE_KALMAN_FILTER) {
avgValSin = KALMAN_COEFFICIENT * lastValSin + avgValSin * (1 - KALMAN_COEFFICIENT);
avgValCos = KALMAN_COEFFICIENT * lastValCos + avgValCos * (1 - KALMAN_COEFFICIENT);
} else {
int l = previousCompassValuesA.length;
previousCompassIndA = (previousCompassIndA + 1) % l;
previousCompassIndB = (previousCompassIndB + 1) % l;
// update average
avgValSin = avgValSin + (-previousCompassValuesA[previousCompassIndA] + lastValSin) / l;
previousCompassValuesA[previousCompassIndA] = lastValSin;
avgValCos = avgValCos + (-previousCompassValuesB[previousCompassIndB] + lastValCos) / l;
previousCompassValuesB[previousCompassIndB] = lastValCos;
}
}
}
private void updateCompassVal() {
heading = (float) getAngle(avgValSin, avgValCos);
for(OsmAndCompassListener c : compassListeners){
c.updateCompassValue(heading);
}
}
public synchronized Float getHeading() {
// if (heading != null && lastValSin != avgValSin && System.currentTimeMillis() - lastHeadingCalcTime > 700) {
// avgValSin = lastValSin;
// avgValCos = lastValCos;
// Arrays.fill(previousCompassValuesA, avgValSin);
// Arrays.fill(previousCompassValuesB, avgValCos);
// updateCompassVal();
// }
return heading;
}
private float getAngle(float sinA, float cosA) {
return MapUtils.unifyRotationTo360((float) (Math.atan2(sinA, cosA) * 180 / Math.PI));
}
private void updateLocation(net.osmand.Location loc) {
for (OsmAndLocationListener l : locationListeners) {
l.updateLocation(loc);
}
}
private LocationListener gpsListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (location != null) {
lastTimeGPSLocationFixed = location.getTime();
}
if(!locationSimulation.isRouteAnimating()) {
setLocation(convertLocation(location, app));
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
private LinkedList<LocationListener> networkListeners = new LinkedList<LocationListener>();
private boolean useOnlyGPS() {
if(app.getRoutingHelper().isFollowingMode()) {
return true;
}
if((System.currentTimeMillis() - lastTimeGPSLocationFixed) < NOT_SWITCH_TO_NETWORK_WHEN_GPS_LOST_MS) {
return true;
}
if(isRunningOnEmulator()) {
return true;
}
return false;
}
// Working with location checkListeners
private class NetworkListener implements LocationListener {
@Override
public void onLocationChanged(Location location) {
if (!useOnlyGPS() && !locationSimulation.isRouteAnimating()) {
setLocation(convertLocation(location, app));
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
private void stopLocationRequests() {
LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
service.removeGpsStatusListener(gpsStatusListener);
service.removeUpdates(gpsListener);
while(!networkListeners.isEmpty()) {
service.removeUpdates(networkListeners.poll());
}
}
public void pauseAllUpdates() {
stopLocationRequests();
registerOrUnregisterCompassListener(false);
}
public static net.osmand.Location convertLocation(Location l, OsmandApplication app) {
if (l == null) {
return null;
}
net.osmand.Location r = new net.osmand.Location(l.getProvider());
r.setLatitude(l.getLatitude());
r.setLongitude(l.getLongitude());
r.setTime(l.getTime());
if (l.hasAccuracy()) {
r.setAccuracy(l.getAccuracy());
}
if (l.hasSpeed()) {
r.setSpeed(l.getSpeed());
}
if (l.hasAltitude()) {
r.setAltitude(l.getAltitude());
}
if (l.hasBearing()) {
r.setBearing(l.getBearing());
}
if (l.hasAltitude() && app != null) {
double alt = l.getAltitude();
final GeoidAltitudeCorrection geo = app.getResourceManager().getGeoidAltitudeCorrection();
if (geo != null) {
alt -= geo.getGeoidHeight(l.getLatitude(), l.getLongitude());
r.setAltitude(alt);
}
}
return r;
}
private void scheduleCheckIfGpsLost(final net.osmand.Location location) {
final RoutingHelper routingHelper = app.getRoutingHelper();
if (location != null) {
final long fixTime = location.getTime();
app.runMessageInUIThreadAndCancelPrevious(LOST_LOCATION_MSG_ID, new Runnable() {
@Override
public void run() {
net.osmand.Location lastKnown = getLastKnownLocation();
if (lastKnown != null && lastKnown.getTime() > fixTime) {
// false positive case, still strange how we got here with removeMessages
return;
}
gpsSignalLost = true;
if (routingHelper.isFollowingMode() && routingHelper.getLeftDistance() > 0) {
routingHelper.getVoiceRouter().gpsLocationLost();
}
setLocation(null);
}
}, LOST_LOCATION_CHECK_DELAY);
if (routingHelper.isFollowingMode() && routingHelper.getLeftDistance() > 0 && simulatePosition == null) {
app.runMessageInUIThreadAndCancelPrevious(START_SIMULATE_LOCATION_MSG_ID, new Runnable() {
@Override
public void run() {
net.osmand.Location lastKnown = getLastKnownLocation();
if (lastKnown != null && lastKnown.getTime() > fixTime) {
// false positive case, still strange how we got here with removeMessages
return;
}
List<RouteSegmentResult> tunnel = routingHelper.getUpcomingTunnel(1000);
if(tunnel != null) {
simulatePosition = new SimulationProvider();
simulatePosition.startSimulation(tunnel, location);
simulatePositionImpl();
}
}
}, START_LOCATION_SIMULATION_DELAY);
}
}
}
public void simulatePosition() {
app.runMessageInUIThreadAndCancelPrevious(RUN_SIMULATE_LOCATION_MSG_ID, new Runnable() {
@Override
public void run() {
simulatePositionImpl();
}
}, 600);
}
private void simulatePositionImpl() {
if(simulatePosition != null){
net.osmand.Location loc = simulatePosition.getSimulatedLocation();
if(loc != null){
setLocation(loc);
simulatePosition();
} else {
simulatePosition = null;
}
}
}
public void setLocationFromService(net.osmand.Location location, boolean continuous) {
// if continuous notify about lost location
if (continuous) {
scheduleCheckIfGpsLost(location);
}
app.getSavingTrackHelper().updateLocation(location);
OsmandPlugin.updateLocationPlugins(location);
app.getRoutingHelper().updateLocation(location);
app.getWaypointHelper().locationChanged(location);
}
public void setLocationFromSimulation(net.osmand.Location location) {
setLocation(location);
}
private void setLocation(net.osmand.Location location) {
if(location == null){
updateGPSInfo(null);
}
if(location != null) {
simulatePosition = null;
if(gpsSignalLost) {
gpsSignalLost = false;
final RoutingHelper routingHelper = app.getRoutingHelper();
if (routingHelper.isFollowingMode() && routingHelper.getLeftDistance() > 0) {
routingHelper.getVoiceRouter().gpsLocationRecover();
}
}
}
enhanceLocation(location);
scheduleCheckIfGpsLost(location);
final RoutingHelper routingHelper = app.getRoutingHelper();
// 1. Logging services
if (location != null) {
app.getSavingTrackHelper().updateLocation(location);
OsmandPlugin.updateLocationPlugins(location);
}
// 2. routing
net.osmand.Location updatedLocation = location;
if (routingHelper.isFollowingMode()) {
if (location == null || isPointAccurateForRouting(location)) {
// Update routing position and get location for sticking mode
updatedLocation = routingHelper.setCurrentLocation(location, settings.SNAP_TO_ROAD.get());
}
} else if(routingHelper.isRoutePlanningMode() && settings.getPointToStart() == null) {
routingHelper.setCurrentLocation(location, false);
} else if(getLocationSimulation().isRouteAnimating()) {
routingHelper.setCurrentLocation(location, false);
}
app.getWaypointHelper().locationChanged(location);
this.location = updatedLocation;
// Update information
updateLocation(this.location);
}
private void enhanceLocation(net.osmand.Location location) {
if (location != null && isRunningOnEmulator()) {
// only for emulator
updateSpeedEmulator(location);
}
}
public void checkIfLastKnownLocationIsValid() {
net.osmand.Location loc = getLastKnownLocation();
if (loc != null && (System.currentTimeMillis() - loc.getTime()) > INTERVAL_TO_CLEAR_SET_LOCATION) {
setLocation(null);
}
}
public NavigationInfo getNavigationInfo() {
return navigationInfo;
}
public String getNavigationHint(TargetPoint point) {
String hint = navigationInfo.getDirectionString(point == null ? null : point.point, getHeading());
if (hint == null)
hint = app.getString(R.string.no_info);
return hint;
}
public String getNavigationHint(LatLon point) {
String hint = navigationInfo.getDirectionString(point, getHeading());
if (hint == null)
hint = app.getString(R.string.no_info);
return hint;
}
public void emitNavigationHint() {
final TargetPoint point = app.getTargetPointsHelper().getPointToNavigate();
if (point != null) {
if (app.getRoutingHelper().isRouteCalculated()) {
app.getRoutingHelper().getVoiceRouter().announceCurrentDirection(getLastKnownLocation());
} else {
app.showToastMessage(getNavigationHint(point));
}
} else {
app.showShortToastMessage(R.string.access_no_destination);
}
}
public RouteDataObject getLastKnownRouteSegment() {
return currentPositionHelper.getLastKnownRouteSegment(getLastKnownLocation());
}
public boolean getRouteSegment(net.osmand.Location loc, ResultMatcher<RouteDataObject> result) {
return currentPositionHelper.getRouteSegment(loc, result);
}
public boolean getGeocodingResult(net.osmand.Location loc, ResultMatcher<GeocodingResult> result) {
return currentPositionHelper.getGeocodingResult(loc, result);
}
public net.osmand.Location getLastKnownLocation() {
return location;
}
public void showNavigationInfo(TargetPoint pointToNavigate, Context uiActivity) {
getNavigationInfo().show(pointToNavigate, getHeading(), uiActivity);
}
public OsmAndLocationSimulation getLocationSimulation() {
return locationSimulation;
}
public static class GPSInfo {
public int foundSatellites = 0;
public int usedSatellites = 0;
public boolean fixed = false;
}
public static boolean isNotSimulatedLocation(net.osmand.Location l) {
if(l != null) {
return !SIMULATED_PROVIDER.equals(l.getProvider());
}
return true;
}
public boolean checkGPSEnabled(final Context context) {
LocationManager lm = (LocationManager)app.getSystemService(Context.LOCATION_SERVICE);
boolean gpsenabled = false;
boolean networkenabled = false;
try {
gpsenabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch(Exception ex) {}
try {
networkenabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch(Exception ex) {}
if(!gpsenabled && !networkenabled) {
// notify user
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setMessage(context.getResources().getString(R.string.gps_network_not_enabled));
dialog.setPositiveButton(context.getResources().getString(R.string.shared_string_settings), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
Intent myIntent = new Intent( Settings.ACTION_LOCATION_SOURCE_SETTINGS);
context.startActivity(myIntent);
}
});
dialog.setNegativeButton(context.getString(R.string.shared_string_cancel), null);
dialog.show();
return false;
}
return true;
}
public static boolean isLocationPermissionAvailable(Context context) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
return true;
}
}