package net.osmand.core.samples.android.sample1; 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.Bundle; import android.provider.Settings; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AlertDialog; import android.util.Log; import net.osmand.PlatformUtil; import net.osmand.ResultMatcher; import net.osmand.binary.GeocodingUtilities; import net.osmand.binary.GeocodingUtilities.GeocodingResult; import net.osmand.data.LatLon; import net.osmand.util.MapUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class SampleLocationProvider implements SensorEventListener { public interface SampleLocationListener { void updateLocation(net.osmand.Location location); } public interface SampleCompassListener { void updateCompassValue(float value); } private static final int INTERVAL_TO_CLEAR_SET_LOCATION = 30 * 1000; 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 boolean sensorRegistered = false; private float[] mGravs = new float[3]; private float[] mGeoMags = new float[3]; private float previousCorrectionValue = 360; 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 SampleApplication app; private CurrentPositionHelper currentPositionHelper; private net.osmand.Location location = null; private GPSInfo gpsInfo = new GPSInfo(); private List<SampleLocationListener> locationListeners = new ArrayList<>(); private List<SampleCompassListener> compassListeners = new ArrayList<>(); private Listener gpsStatusListener; private float[] mRotationM = new float[9]; private static final long AGPS_TO_REDOWNLOAD = 16 * 60 * 60 * 1000; // 16 hours private long agpsDataLastTimeDownloaded; private boolean useMagneticFieldSensorCompass = false; public SampleLocationProvider(SampleApplication app) { this.app = app; currentPositionHelper = new CurrentPositionHelper(app); } public void resumeAllUpdates() { final LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE); if (app.isInternetConnectionAvailable()) { if (System.currentTimeMillis() - agpsDataLastTimeDownloaded > AGPS_TO_REDOWNLOAD) { //force an updated check for internet connectivity here before destroying A-GPS-data if (app.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); agpsDataLastTimeDownloaded = System.currentTimeMillis(); } catch (Exception e) { agpsDataLastTimeDownloaded = 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(SampleLocationListener listener) { if (!locationListeners.contains(listener)) { locationListeners.add(listener); } } public void removeLocationListener(SampleLocationListener listener) { locationListeners.remove(listener); } public void addCompassListener(SampleCompassListener listener) { if (!compassListeners.contains(listener)) { compassListeners.add(listener); } } public void removeCompassListener(SampleCompassListener 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 (useMagneticFieldSensorCompass) { 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; } } @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 (useMagneticFieldSensorCompass) { 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); 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 { 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 = getAngle(avgValSin, avgValCos); for (SampleCompassListener 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 (SampleLocationListener l : locationListeners) { l.updateLocation(loc); } } private LocationListener gpsListener = new LocationListener() { @Override public void onLocationChanged(Location location) { if (location != null) { lastTimeGPSLocationFixed = location.getTime(); } 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 ((System.currentTimeMillis() - lastTimeGPSLocationFixed) < NOT_SWITCH_TO_NETWORK_WHEN_GPS_LOST_MS) { return true; } return false; } // Working with location checkListeners private class NetworkListener implements LocationListener { @Override public void onLocationChanged(Location location) { if (!useOnlyGPS()) { 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, SampleApplication 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()); } return r; } private void setLocation(net.osmand.Location location) { if (location == null) { updateGPSInfo(null); } if (location != null) { if (gpsSignalLost) { gpsSignalLost = false; } } this.location = location; // Update information updateLocation(this.location); } public void checkIfLastKnownLocationIsValid() { net.osmand.Location loc = getLastKnownLocation(); if (loc != null && (System.currentTimeMillis() - loc.getTime()) > INTERVAL_TO_CLEAR_SET_LOCATION) { setLocation(null); } } /* 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 LatLon getLastKnownLocationLatLon() { if (location != null) { return new LatLon(location.getLatitude(), location.getLongitude()); } else { return null; } } public static class GPSInfo { public int foundSatellites = 0; public int usedSatellites = 0; public boolean fixed = false; } 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(app.getString("gps_network_not_enabled")); dialog.setPositiveButton(app.getString("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) { return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; } }