package org.opensatnav.services; import org.andnav.osm.util.constants.OpenStreetMapConstants; import org.opensatnav.OpenSatNavConstants; import org.opensatnav.R; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.util.Log; import android.widget.Toast; public class LocationHandler implements OpenSatNavConstants, OpenStreetMapConstants { protected LocationListenerAdaptor mGpsLocationListener; protected LocationListenerAdaptor mNetworkLocationListener; /** true if Gps Location is activated, false otherwise */ protected boolean gpsLocationActivated = false; /** true if Network Location is activated, false otherwise */ protected boolean networkLocationActivated = false; protected String lastLocation = ""; protected LocationManager mLocationManager; protected LocationListener mLocationReceiver; protected Context mContext; protected Location firstLocation; protected int mNumSatellites = NOT_SET; public LocationHandler(LocationManager lm, LocationListener dest, Context ctx) { if (OpenSatNavConstants.DEBUGMODE) Log.v(OpenSatNavConstants.LOG_TAG, "LocationHandler construtor. Context: " + ctx.getClass()); mLocationManager = lm; mLocationReceiver = dest; mContext = ctx; } public Location getFirstLocation() { return firstLocation; } public synchronized void start() { if (OpenSatNavConstants.DEBUGMODE) Log.v(OpenSatNavConstants.LOG_TAG, "LocationHandler start()"); // initialize state of location providers and launch location listeners if (!networkLocationActivated && mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { networkLocationActivated = true; mNetworkLocationListener = new LocationListenerAdaptor(); mLocationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, 0, 0, this.mNetworkLocationListener); } if (!gpsLocationActivated && mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { gpsLocationActivated = true; mGpsLocationListener = new LocationListenerAdaptor(); mLocationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, this.mGpsLocationListener); } // get the best location using bestProvider() try { firstLocation = mLocationManager.getLastKnownLocation( bestProvider()); } catch (Exception e) { Log.e(OpenSatNavConstants.LOG_TAG, "Error getting the first location"); } // test to see which location services are available if (!gpsLocationActivated) { if (!networkLocationActivated) { // no location providers are available, ask the user if they // want to go and change the setting AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setCancelable(true); builder.setMessage(R.string.location_services_disabled) .setCancelable(false).setPositiveButton( //FIXME ZeroG - 2009/12/28 replace by android.R.string.yes once it's fixed upstream R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { try { dialog.dismiss(); } catch (IllegalArgumentException e) { // if orientation change, thread continue but the dialog cannot be dismissed without exception } mContext.startActivity(new Intent( android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); } }).setNegativeButton( //FIXME ZeroG - 2009/12/28 replace by android.R.string.no once it's fixed upstream R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); } else { // we have network location but no GPS, tell the user that // accuracy is bad because of this Toast.makeText(mContext, R.string.gps_disabled, Toast.LENGTH_LONG) .show(); } } else if (!networkLocationActivated) { // we have GPS (but no network), this tells the user // that they might have to wait for a fix Toast.makeText(mContext, R.string.getting_gps_fix, Toast.LENGTH_LONG) .show(); } } public synchronized void stop() { if (OpenSatNavConstants.DEBUGMODE) Log.v(OpenSatNavConstants.LOG_TAG, "LocationHandler Stop"); try { mLocationManager.removeUpdates(mGpsLocationListener); networkLocationActivated = false; mGpsLocationListener = null; } catch (IllegalArgumentException e) { Log.d(OpenSatNavConstants.LOG_TAG, "Ignoring: " + e); // there's no gps location listener to disable } try { mLocationManager.removeUpdates(mNetworkLocationListener); gpsLocationActivated = false; mNetworkLocationListener = null; } catch (IllegalArgumentException e) { Log.v(OpenSatNavConstants.LOG_TAG, "Ignoring: " + e); // there's no network location listener to disable } } /** * Tests if the given provider is the best among all location providers * available * * @param myLocation * @return true if the location is the best choice, false otherwise */ private boolean isBestProvider(Location myLocation) { if (myLocation == null) return false; boolean isBestProvider = false; String myProvider = myLocation.getProvider(); boolean gpsCall = myProvider .equalsIgnoreCase(LocationManager.GPS_PROVIDER); boolean networkCall = myProvider .equalsIgnoreCase(LocationManager.NETWORK_PROVIDER); // get all location accuracy in meter; note that less is better! float gpsAccuracy = Float.MAX_VALUE; long gpsTime = 0; if (gpsLocationActivated) { Location lastGpsLocation = mLocationManager .getLastKnownLocation(LocationManager.GPS_PROVIDER); if (lastGpsLocation != null) { gpsAccuracy = lastGpsLocation.getAccuracy(); gpsTime = lastGpsLocation.getTime(); } } float networkAccuracy = Float.MAX_VALUE; if (networkLocationActivated) { Location lastNetworkLocation = mLocationManager .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); if (lastNetworkLocation != null) networkAccuracy = lastNetworkLocation.getAccuracy(); } float currentAccuracy = myLocation.getAccuracy(); long currentTime = myLocation.getTime(); // Use myLocation if: // 1. it's a gps location & network is disabled // 2. it's a gps loc & network activated // & gps accuracy is better than network // 3. it's a network loc & gps is disabled // 4. it's a network loc, gps enabled // & (network accuracy is better than gps // OR last network fix is newer than last gps fix+30seconds) boolean case1 = gpsCall && !networkLocationActivated; boolean case2 = gpsCall && networkLocationActivated && currentAccuracy < networkAccuracy; boolean case3 = networkCall && !gpsLocationActivated; boolean case4 = networkCall && gpsLocationActivated && (currentAccuracy < gpsAccuracy || currentTime > gpsTime + 30000); if (case1 || case2 || case3 || case4) { isBestProvider = true; } return isBestProvider; } /** * Defines the best location provider using isBestProvider() test * * @return LocationProvider or null if none are available */ protected String bestProvider() { String bestProvider = null; if (networkLocationActivated && isBestProvider(mLocationManager.getLastKnownLocation( LocationManager.NETWORK_PROVIDER))) { bestProvider = LocationManager.NETWORK_PROVIDER; } else if (gpsLocationActivated) { bestProvider = LocationManager.GPS_PROVIDER; } return bestProvider; } private class LocationListenerAdaptor implements LocationListener { public void onLocationChanged(final Location loc) { if (isBestProvider(loc)) { mLocationReceiver.onLocationChanged(loc); lastLocation = loc.getProvider(); } } public void onStatusChanged(String provider, int status, Bundle extras) { LocationHandler.this.mNumSatellites = extras.getInt( "satellites", NOT_SET); // TODO Check on an actual device if (provider.equals(bestProvider())) { mLocationReceiver.onStatusChanged(provider, status, extras); } } public void onProviderEnabled(String a) { /* ignore */ } public void onProviderDisabled(String a) { /* ignore */ } } }