1
1
package fr .free .nrw .commons .location ;
2
2
3
+ import android .Manifest ;
4
+ import android .app .Activity ;
3
5
import android .content .Context ;
4
- import android .location . Criteria ;
6
+ import android .content . pm . PackageManager ;
5
7
import android .location .Location ;
6
8
import android .location .LocationListener ;
7
9
import android .location .LocationManager ;
8
10
import android .os .Bundle ;
11
+ import android .support .v4 .app .ActivityCompat ;
12
+ import android .support .v4 .content .ContextCompat ;
9
13
10
14
import java .util .List ;
11
15
import java .util .concurrent .CopyOnWriteArrayList ;
17
21
18
22
@ Singleton
19
23
public class LocationServiceManager implements LocationListener {
24
+ public static final int LOCATION_REQUEST = 1 ;
20
25
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 ;
22
30
private LocationManager locationManager ;
23
- private LatLng lastLocation ;
24
- private Float latestLocationAccuracy ;
31
+ private Location lastLocation ;
25
32
private final List <LocationUpdateListener > locationListeners = new CopyOnWriteArrayList <>();
33
+ private boolean isLocationManagerRegistered = false ;
26
34
27
35
@ Inject
28
36
public LocationServiceManager (Context context ) {
37
+ this .context = context ;
29
38
this .locationManager = (LocationManager ) context .getSystemService (Context .LOCATION_SERVICE );
30
- provider = locationManager .getBestProvider (new Criteria (), true );
31
39
}
32
40
33
41
public boolean isProviderEnabled () {
34
42
return locationManager .isProviderEnabled (LocationManager .GPS_PROVIDER );
35
43
}
36
44
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 ;
39
48
}
40
49
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 );
49
72
}
50
73
51
74
/** Registers a LocationManager to listen for current location.
52
75
*/
53
76
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 ) {
54
83
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 ;
63
89
} catch (IllegalArgumentException e ) {
64
90
Timber .e (e , "Illegal argument exception" );
91
+ return false ;
65
92
} catch (SecurityException e ) {
66
93
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 ;
67
136
}
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 );
68
148
}
69
149
70
150
/** Unregisters location manager.
71
151
*/
72
152
public void unregisterLocationManager () {
153
+ isLocationManagerRegistered = false ;
73
154
try {
74
155
locationManager .removeUpdates (this );
75
156
} catch (SecurityException e ) {
@@ -89,15 +170,11 @@ public void removeLocationListener(LocationUpdateListener listener) {
89
170
90
171
@ Override
91
172
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
+ }
101
178
}
102
179
}
103
180
0 commit comments