/** AirCasting - Share your Air! Copyright (C) 2011-2012 HabitatMap, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. You can contact the authors by email at <info@habitatmap.org> */ package pl.llp.aircasting.helper; import pl.llp.aircasting.event.sensor.LocationEvent; import pl.llp.aircasting.util.Constants; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import com.google.common.eventbus.EventBus; import com.google.inject.Inject; import com.google.inject.Singleton; @Singleton public class LocationHelper implements LocationListener { public static final int ACCURACY_THRESHOLD = 200; @Inject LocationManager locationManager; @Inject EventBus eventBus; private Location lastLocation; private int starts; public synchronized void start() { locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); updateLocation(locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)); updateLocation(locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)); starts += 1; } public synchronized void stop() { starts -= 1; if (starts <= 0) { starts = 0; locationManager.removeUpdates(this); } } public Location getLastLocation() { return lastLocation; } @Override public void onLocationChanged(Location location) { updateLocation(location); } private void updateLocation(Location location) { if (isBetterLocation(location, lastLocation)) { lastLocation = location; LocationEvent locationEvent = new LocationEvent(); eventBus.post(locationEvent); } } @Override public void onStatusChanged(String s, int i, Bundle bundle) { } @Override public void onProviderEnabled(String s) { } @Override public void onProviderDisabled(String s) { } /** * Determines whether one Location reading is better than the current Location fix * <p/> * Source: http://developer.android.com/guide/topics/location/obtaining-user-location.html * * @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 */ protected boolean isBetterLocation(Location location, Location currentBestLocation) { if (currentBestLocation == null) { // A new location is always better than no location return true; } else if (location == null) { // A null location is not so good return false; } // Check whether the new location fix is newer or older long timeDelta = location.getTime() - currentBestLocation.getTime(); boolean isSignificantlyNewer = timeDelta > Constants.TWO_MINUTES; boolean isSignificantlyOlder = timeDelta < -Constants.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 > ACCURACY_THRESHOLD; // 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); } public boolean hasGPSFix() { return (lastLocation != null) && LocationManager.GPS_PROVIDER.equals(lastLocation.getProvider()); } public boolean hasNoGPSFix() { return !hasGPSFix(); } }