11package fr .free .nrw .commons .location ;
22
3+ import android .Manifest ;
4+ import android .app .Activity ;
35import android .content .Context ;
4- import android .location . Criteria ;
6+ import android .content . pm . PackageManager ;
57import android .location .Location ;
68import android .location .LocationListener ;
79import android .location .LocationManager ;
810import android .os .Bundle ;
11+ import android .support .v4 .app .ActivityCompat ;
12+ import android .support .v4 .content .ContextCompat ;
913
1014import java .util .List ;
1115import java .util .concurrent .CopyOnWriteArrayList ;
1721
1822@ Singleton
1923public class LocationServiceManager implements LocationListener {
24+ public static final int LOCATION_REQUEST = 1 ;
2025
21- private String provider ;
26+ private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 2 * 60 * 1000 ;
27+ private static final long MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 10 ;
28+
29+ private Context context ;
2230 private LocationManager locationManager ;
23- private LatLng lastLocation ;
24- private Float latestLocationAccuracy ;
31+ private Location lastLocation ;
2532 private final List <LocationUpdateListener > locationListeners = new CopyOnWriteArrayList <>();
33+ private boolean isLocationManagerRegistered = false ;
2634
2735 @ Inject
2836 public LocationServiceManager (Context context ) {
37+ this .context = context ;
2938 this .locationManager = (LocationManager ) context .getSystemService (Context .LOCATION_SERVICE );
30- provider = locationManager .getBestProvider (new Criteria (), true );
3139 }
3240
3341 public boolean isProviderEnabled () {
3442 return locationManager .isProviderEnabled (LocationManager .GPS_PROVIDER );
3543 }
3644
37- public LatLng getLastLocation () {
38- return lastLocation ;
45+ public boolean isLocationPermissionGranted () {
46+ return ContextCompat .checkSelfPermission (context ,
47+ Manifest .permission .ACCESS_FINE_LOCATION ) == PackageManager .PERMISSION_GRANTED ;
3948 }
4049
41- /**
42- * Returns the accuracy of the location. The measurement is
43- * given as a radius in meter of 68 % confidence.
44- *
45- * @return Float
46- */
47- public Float getLatestLocationAccuracy () {
48- return latestLocationAccuracy ;
50+ public void requestPermissions (Activity activity ) {
51+ if (activity .isFinishing ()) {
52+ return ;
53+ }
54+ ActivityCompat .requestPermissions (activity ,
55+ new String []{Manifest .permission .ACCESS_FINE_LOCATION },
56+ LOCATION_REQUEST );
57+ }
58+
59+ public boolean isPermissionExplanationRequired (Activity activity ) {
60+ if (activity .isFinishing ()) {
61+ return false ;
62+ }
63+ return ActivityCompat .shouldShowRequestPermissionRationale (activity ,
64+ Manifest .permission .ACCESS_FINE_LOCATION );
65+ }
66+
67+ public LatLng getLastLocation () {
68+ if (lastLocation == null ) {
69+ return null ;
70+ }
71+ return LatLng .from (lastLocation );
4972 }
5073
5174 /** Registers a LocationManager to listen for current location.
5275 */
5376 public void registerLocationManager () {
77+ if (!isLocationManagerRegistered )
78+ isLocationManagerRegistered = requestLocationUpdatesFromProvider (LocationManager .NETWORK_PROVIDER )
79+ && requestLocationUpdatesFromProvider (LocationManager .GPS_PROVIDER );
80+ }
81+
82+ private boolean requestLocationUpdatesFromProvider (String locationProvider ) {
5483 try {
55- locationManager .requestLocationUpdates (provider , 400 , 1 , this );
56- Location location = locationManager .getLastKnownLocation (provider );
57- //Location works, just need to 'send' GPS coords
58- // via emulator extended controls if testing on emulator
59- Timber .d ("Checking for location..." );
60- if (location != null ) {
61- this .onLocationChanged (location );
62- }
84+ locationManager .requestLocationUpdates (locationProvider ,
85+ MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS ,
86+ MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS ,
87+ this );
88+ return true ;
6389 } catch (IllegalArgumentException e ) {
6490 Timber .e (e , "Illegal argument exception" );
91+ return false ;
6592 } catch (SecurityException e ) {
6693 Timber .e (e , "Security exception" );
94+ return false ;
95+ }
96+ }
97+
98+ protected boolean isBetterLocation (Location location , Location currentBestLocation ) {
99+ if (currentBestLocation == null ) {
100+ // A new location is always better than no location
101+ return true ;
102+ }
103+
104+ // Check whether the new location fix is newer or older
105+ long timeDelta = location .getTime () - currentBestLocation .getTime ();
106+ boolean isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS ;
107+ boolean isSignificantlyOlder = timeDelta < -MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS ;
108+ boolean isNewer = timeDelta > 0 ;
109+
110+ // If it's been more than two minutes since the current location, use the new location
111+ // because the user has likely moved
112+ if (isSignificantlyNewer ) {
113+ return true ;
114+ // If the new location is more than two minutes older, it must be worse
115+ } else if (isSignificantlyOlder ) {
116+ return false ;
117+ }
118+
119+ // Check whether the new location fix is more or less accurate
120+ int accuracyDelta = (int ) (location .getAccuracy () - currentBestLocation .getAccuracy ());
121+ boolean isLessAccurate = accuracyDelta > 0 ;
122+ boolean isMoreAccurate = accuracyDelta < 0 ;
123+ boolean isSignificantlyLessAccurate = accuracyDelta > 200 ;
124+
125+ // Check if the old and new location are from the same provider
126+ boolean isFromSameProvider = isSameProvider (location .getProvider (),
127+ currentBestLocation .getProvider ());
128+
129+ // Determine location quality using a combination of timeliness and accuracy
130+ if (isMoreAccurate ) {
131+ return true ;
132+ } else if (isNewer && !isLessAccurate ) {
133+ return true ;
134+ } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider ) {
135+ return true ;
67136 }
137+ return false ;
138+ }
139+
140+ /**
141+ * Checks whether two providers are the same
142+ */
143+ private boolean isSameProvider (String provider1 , String provider2 ) {
144+ if (provider1 == null ) {
145+ return provider2 == null ;
146+ }
147+ return provider1 .equals (provider2 );
68148 }
69149
70150 /** Unregisters location manager.
71151 */
72152 public void unregisterLocationManager () {
153+ isLocationManagerRegistered = false ;
73154 try {
74155 locationManager .removeUpdates (this );
75156 } catch (SecurityException e ) {
@@ -89,15 +170,11 @@ public void removeLocationListener(LocationUpdateListener listener) {
89170
90171 @ Override
91172 public void onLocationChanged (Location location ) {
92- double currentLatitude = location .getLatitude ();
93- double currentLongitude = location .getLongitude ();
94- latestLocationAccuracy = location .getAccuracy ();
95- Timber .d ("Latitude: %f Longitude: %f Accuracy %f" ,
96- currentLatitude , currentLongitude , latestLocationAccuracy );
97- lastLocation = new LatLng (currentLatitude , currentLongitude , latestLocationAccuracy );
98-
99- for (LocationUpdateListener listener : locationListeners ) {
100- listener .onLocationChanged (lastLocation );
173+ if (isBetterLocation (location , lastLocation )) {
174+ lastLocation = location ;
175+ for (LocationUpdateListener listener : locationListeners ) {
176+ listener .onLocationChanged (LatLng .from (lastLocation ));
177+ }
101178 }
102179 }
103180
0 commit comments