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 ;
1620import timber .log .Timber ;
1721
1822public class LocationServiceManager implements LocationListener {
23+ public static final int LOCATION_REQUEST = 1 ;
1924
20- private String provider ;
25+ private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 2 * 60 * 1000 ;
26+ private static final long MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 10 ;
27+
28+ private Context context ;
2129 private LocationManager locationManager ;
22- private LatLng lastLocation ;
23- private Float latestLocationAccuracy ;
30+ private Location lastLocation ;
2431 private final List <LocationUpdateListener > locationListeners = new CopyOnWriteArrayList <>();
32+ private boolean isLocationManagerRegistered = false ;
2533
2634 public LocationServiceManager (Context context ) {
35+ this .context = context ;
2736 this .locationManager = (LocationManager ) context .getSystemService (Context .LOCATION_SERVICE );
28- provider = locationManager .getBestProvider (new Criteria (), true );
2937 }
3038
3139 public boolean isProviderEnabled () {
3240 return locationManager .isProviderEnabled (LocationManager .GPS_PROVIDER );
3341 }
3442
35- public LatLng getLastLocation () {
36- return lastLocation ;
43+ public boolean isLocationPermissionGranted () {
44+ return ContextCompat .checkSelfPermission (context ,
45+ Manifest .permission .ACCESS_FINE_LOCATION ) == PackageManager .PERMISSION_GRANTED ;
3746 }
3847
39- /**
40- * Returns the accuracy of the location. The measurement is
41- * given as a radius in meter of 68 % confidence.
42- *
43- * @return Float
44- */
45- public Float getLatestLocationAccuracy () {
46- return latestLocationAccuracy ;
48+ public void requestPermissions (Activity activity ) {
49+ if (activity .isFinishing ()) {
50+ return ;
51+ }
52+ ActivityCompat .requestPermissions (activity ,
53+ new String []{Manifest .permission .ACCESS_FINE_LOCATION },
54+ LOCATION_REQUEST );
55+ }
56+
57+ public boolean isPermissionExplanationRequired (Activity activity ) {
58+ if (activity .isFinishing ()) {
59+ return false ;
60+ }
61+ return ActivityCompat .shouldShowRequestPermissionRationale (activity ,
62+ Manifest .permission .ACCESS_FINE_LOCATION );
63+ }
64+
65+ public LatLng getLastLocation () {
66+ if (lastLocation == null ) {
67+ return null ;
68+ }
69+ return LatLng .from (lastLocation );
4770 }
4871
4972 /** Registers a LocationManager to listen for current location.
5073 */
5174 public void registerLocationManager () {
75+ if (!isLocationManagerRegistered )
76+ isLocationManagerRegistered = requestLocationUpdatesFromProvider (LocationManager .NETWORK_PROVIDER )
77+ && requestLocationUpdatesFromProvider (LocationManager .GPS_PROVIDER );
78+ }
79+
80+ private boolean requestLocationUpdatesFromProvider (String locationProvider ) {
5281 try {
53- locationManager .requestLocationUpdates (provider , 400 , 1 , this );
54- Location location = locationManager .getLastKnownLocation (provider );
55- //Location works, just need to 'send' GPS coords
56- // via emulator extended controls if testing on emulator
57- Timber .d ("Checking for location..." );
58- if (location != null ) {
59- this .onLocationChanged (location );
60- }
82+ locationManager .requestLocationUpdates (locationProvider ,
83+ MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS ,
84+ MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS ,
85+ this );
86+ return true ;
6187 } catch (IllegalArgumentException e ) {
6288 Timber .e (e , "Illegal argument exception" );
89+ return false ;
6390 } catch (SecurityException e ) {
6491 Timber .e (e , "Security exception" );
92+ return false ;
93+ }
94+ }
95+
96+ protected boolean isBetterLocation (Location location , Location currentBestLocation ) {
97+ if (currentBestLocation == null ) {
98+ // A new location is always better than no location
99+ return true ;
100+ }
101+
102+ // Check whether the new location fix is newer or older
103+ long timeDelta = location .getTime () - currentBestLocation .getTime ();
104+ boolean isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS ;
105+ boolean isSignificantlyOlder = timeDelta < -MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS ;
106+ boolean isNewer = timeDelta > 0 ;
107+
108+ // If it's been more than two minutes since the current location, use the new location
109+ // because the user has likely moved
110+ if (isSignificantlyNewer ) {
111+ return true ;
112+ // If the new location is more than two minutes older, it must be worse
113+ } else if (isSignificantlyOlder ) {
114+ return false ;
115+ }
116+
117+ // Check whether the new location fix is more or less accurate
118+ int accuracyDelta = (int ) (location .getAccuracy () - currentBestLocation .getAccuracy ());
119+ boolean isLessAccurate = accuracyDelta > 0 ;
120+ boolean isMoreAccurate = accuracyDelta < 0 ;
121+ boolean isSignificantlyLessAccurate = accuracyDelta > 200 ;
122+
123+ // Check if the old and new location are from the same provider
124+ boolean isFromSameProvider = isSameProvider (location .getProvider (),
125+ currentBestLocation .getProvider ());
126+
127+ // Determine location quality using a combination of timeliness and accuracy
128+ if (isMoreAccurate ) {
129+ return true ;
130+ } else if (isNewer && !isLessAccurate ) {
131+ return true ;
132+ } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider ) {
133+ return true ;
65134 }
135+ return false ;
136+ }
137+
138+ /**
139+ * Checks whether two providers are the same
140+ */
141+ private boolean isSameProvider (String provider1 , String provider2 ) {
142+ if (provider1 == null ) {
143+ return provider2 == null ;
144+ }
145+ return provider1 .equals (provider2 );
66146 }
67147
68148 /** Unregisters location manager.
69149 */
70150 public void unregisterLocationManager () {
151+ isLocationManagerRegistered = false ;
71152 try {
72153 locationManager .removeUpdates (this );
73154 } catch (SecurityException e ) {
@@ -87,15 +168,11 @@ public void removeLocationListener(LocationUpdateListener listener) {
87168
88169 @ Override
89170 public void onLocationChanged (Location location ) {
90- double currentLatitude = location .getLatitude ();
91- double currentLongitude = location .getLongitude ();
92- latestLocationAccuracy = location .getAccuracy ();
93- Timber .d ("Latitude: %f Longitude: %f Accuracy %f" ,
94- currentLatitude , currentLongitude , latestLocationAccuracy );
95- lastLocation = new LatLng (currentLatitude , currentLongitude , latestLocationAccuracy );
96-
97- for (LocationUpdateListener listener : locationListeners ) {
98- listener .onLocationChanged (lastLocation );
171+ if (isBetterLocation (location , lastLocation )) {
172+ lastLocation = location ;
173+ for (LocationUpdateListener listener : locationListeners ) {
174+ listener .onLocationChanged (LatLng .from (lastLocation ));
175+ }
99176 }
100177 }
101178
0 commit comments