private Location currentBestLocation;
private ServiceLocationListener gpsLocationListener;
private ServiceLocationListener networkLocationListener;
private ServiceLocationListener passiveLocationListener;
private LocationManager locationManager;
private Handler handler = new Handler();
public static Device getInstance() {
if (device == null) device = new Device();
return device;
}
public Location fetchLocation(Context context) {
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
LocationProvider gpsProvider = locationManager.getProvider(LocationManager.GPS_PROVIDER);
LocationProvider networkProvider = locationManager.getProvider(LocationManager.NETWORK_PROVIDER);
LocationProvider passiveProvider = locationManager.getProvider(LocationManager.PASSIVE_PROVIDER);
if (gpsProvider != null ) {
Location lastKnownGPSLocation = locationManager.getLastKnownLocation(gpsProvider.getName());
if (isBetterLocation(lastKnownGPSLocation, currentBestLocation) )
currentBestLocation = lastKnownGPSLocation;
}
if (networkProvider != null ) {
Location lastKnownNetworkLocation = locationManager.getLastKnownLocation(networkProvider.getName());
if (isBetterLocation(lastKnownNetworkLocation, currentBestLocation) )
currentBestLocation = lastKnownNetworkLocation;
}
if (passiveProvider != null) {
Location lastKnownPassiveLocation = locationManager.getLastKnownLocation(passiveProvider.getName());
if (isBetterLocation(lastKnownPassiveLocation, currentBestLocation)) {
currentBestLocation = lastKnownPassiveLocation;
}
}
timerRunnable = new TimerRunnable(context);
gpsLocationListener = new ServiceLocationListener();
networkLocationListener = new ServiceLocationListener();
passiveLocationListener = new ServiceLocationListener();
if (gpsProvider != null) {
locationManager.requestLocationUpdates(gpsProvider.getName(), 0l, 0.0f, gpsLocationListener);
}
if (networkProvider != null) {
locationManager.requestLocationUpdates(networkProvider.getName(), 0l, 0.0f, networkLocationListener);
}
if (passiveProvider != null) {
locationManager.requestLocationUpdates(passiveProvider.getName(), 0l, 0.0f, passiveLocationListener);
}
if (gpsProvider != null || networkProvider != null || passiveProvider != null) {
handler.postDelayed(timerRunnable, 2 * 60 * 1000);
} else {
handler.post(timerRunnable);
}
} catch (SecurityException se) {
finish();
Logger.getLogger(Web.class.getName()).log(Level.SEVERE, null, se);
}
return currentBestLocation;
}
private class ServiceLocationListener implements android.location.LocationListener {
@Override
public void onLocationChanged(Location newLocation) {
synchronized (this) {
if (isBetterLocation(newLocation, currentBestLocation)) {
currentBestLocation = newLocation;
LocationRetrieverBroadcaster.getInstance().fireGotLocation(currentBestLocation);
if (currentBestLocation.hasAccuracy() && currentBestLocation.getAccuracy() <= 100) {
finish();
}
}
}
}
@Override
public void onStatusChanged(String s, int i, Bundle bundle) {}
@Override
public void onProviderEnabled(String s) {}
@Override
public void onProviderDisabled(String s) {}
}
private synchronized void finish() {
handler.removeCallbacks(timerRunnable);
handler.post(timerRunnable);
}
private static final int TWO_MINUTES = 1000 * 60 * 2;
/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
private boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
if (location == null)
return true;
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
private class TimerRunnable implements Runnable {
Context context;
TimerRunnable(Context context) {
this.context = context;
}
@Override
public void run() {
Intent intent = new Intent(context.getPackageName() + ".action.LOCATION_FOUND");
if (currentBestLocation != null) {
intent.putExtra(LocationManager.KEY_LOCATION_CHANGED, currentBestLocation);
locationManager.removeUpdates(gpsLocationListener);
locationManager.removeUpdates(networkLocationListener);
locationManager.removeUpdates(passiveLocationListener);
}
}
}
private TimerRunnable timerRunnable;