47
47
48
48
import java .math .BigDecimal ;
49
49
import java .math .RoundingMode ;
50
+ import java .util .ArrayList ;
50
51
import java .util .Arrays ;
51
52
import java .util .HashMap ;
52
53
import java .util .List ;
53
54
import java .util .concurrent .ConcurrentHashMap ;
54
55
55
56
class LocationGMS {
56
-
57
+
57
58
static class LocationPoint {
58
59
Double lat ;
59
60
Double log ;
@@ -62,74 +63,122 @@ static class LocationPoint {
62
63
Boolean bg ;
63
64
Long timeStamp ;
64
65
}
65
-
66
+
66
67
private static final long TIME_FOREGROUND_SEC = 5 * 60 ;
67
68
private static final long TIME_BACKGROUND_SEC = 10 * 60 ;
68
69
private static final long FOREGROUND_UPDATE_TIME_MS = (TIME_FOREGROUND_SEC - 30 ) * 1_000 ;
69
70
private static final long BACKGROUND_UPDATE_TIME_MS = (TIME_BACKGROUND_SEC - 30 ) * 1_000 ;
70
-
71
+
71
72
private static GoogleApiClientCompatProxy mGoogleApiClient ;
72
73
private static Location mLastLocation ;
73
74
static String requestPermission ;
74
75
private static Context classContext ;
75
-
76
+
76
77
private static LocationHandlerThread locationHandlerThread ;
77
78
78
79
protected static final Object syncLock = new Object () {};
79
80
80
- enum CALLBACK_TYPE {
81
+ enum PermissionType {
81
82
STARTUP , PROMPT_LOCATION , SYNC_SERVICE
82
83
}
83
84
interface LocationHandler {
84
- CALLBACK_TYPE getType ();
85
+ PermissionType getType ();
85
86
void complete (LocationPoint point );
86
87
}
87
88
88
- private static ConcurrentHashMap <CALLBACK_TYPE , LocationHandler > locationHandlers = new ConcurrentHashMap <>();
89
+ abstract static class LocationPromptCompletionHandler implements LocationHandler {
90
+ void onAnswered (boolean accepted ) {}
91
+ }
92
+
93
+ private static ConcurrentHashMap <PermissionType , LocationHandler > locationHandlers = new ConcurrentHashMap <>();
94
+ private static final List <LocationPromptCompletionHandler > promptHandlers = new ArrayList <>();
89
95
90
96
private static Thread fallbackFailThread ;
91
97
92
98
private static boolean locationCoarse ;
93
-
99
+
94
100
static boolean scheduleUpdate (Context context ) {
95
101
if (!hasLocationPermission (context ) || !OneSignal .shareLocation )
96
102
return false ;
97
-
103
+
98
104
long lastTime = System .currentTimeMillis () - getLastLocationTime ();
99
105
long minTime = 1_000 * (OneSignal .isForeground () ? TIME_FOREGROUND_SEC : TIME_BACKGROUND_SEC );
100
106
long scheduleTime = minTime - lastTime ;
101
-
107
+
102
108
OneSignalSyncServiceUtils .scheduleLocationUpdateTask (context , scheduleTime );
103
109
return true ;
104
110
}
105
-
111
+
106
112
private static void setLastLocationTime (long time ) {
107
113
OneSignalPrefs .saveLong (OneSignalPrefs .PREFS_ONESIGNAL ,
108
114
OneSignalPrefs .PREFS_OS_LAST_LOCATION_TIME ,time );
109
115
}
110
-
116
+
111
117
private static long getLastLocationTime () {
112
118
return OneSignalPrefs .getLong (
113
119
OneSignalPrefs .PREFS_ONESIGNAL ,
114
120
OneSignalPrefs .PREFS_OS_LAST_LOCATION_TIME ,
115
121
TIME_BACKGROUND_SEC * -1_000
116
122
);
117
123
}
118
-
124
+
119
125
private static boolean hasLocationPermission (Context context ) {
120
126
return ContextCompat .checkSelfPermission (context , "android.permission.ACCESS_FINE_LOCATION" ) == PackageManager .PERMISSION_GRANTED
121
127
|| ContextCompat .checkSelfPermission (context , "android.permission.ACCESS_COARSE_LOCATION" ) == PackageManager .PERMISSION_GRANTED ;
122
128
}
123
129
130
+ static void addPromptHandlerIfAvailable (LocationHandler handler ) {
131
+ if (handler instanceof LocationPromptCompletionHandler ) {
132
+ synchronized (promptHandlers ) {
133
+ promptHandlers .add ((LocationPromptCompletionHandler ) handler );
134
+ }
135
+ }
136
+ }
137
+
138
+ static void sendAndClearPromptHandlers (boolean promptLocation , boolean accepted ) {
139
+ if (!promptLocation ) {
140
+ OneSignal .onesignalLog (OneSignal .LOG_LEVEL .DEBUG , "LocationGMS sendAndClearPromptHandlers from non prompt flow" );
141
+ return ;
142
+ }
143
+
144
+ synchronized (promptHandlers ) {
145
+ OneSignal .onesignalLog (OneSignal .LOG_LEVEL .DEBUG , "LocationGMS calling prompt handlers" );
146
+ for (LocationPromptCompletionHandler promptHandler : promptHandlers ) {
147
+ promptHandler .onAnswered (accepted );
148
+ }
149
+ // We only call the prompt handlers once
150
+ promptHandlers .clear ();
151
+ }
152
+ }
153
+
154
+ /**
155
+ * This method handle location and permission location flows and border cases.
156
+ * For each flow we need to trigger location prompts listener,
157
+ * in that way all listener will now that location request completed, even if its showing a prompt
158
+ *
159
+ * Cases managed:
160
+ * - If app doesn't have location sharing activated, then location will not attributed
161
+ * - For API less than 23, prompt permission aren't needed
162
+ * - For API greater or equal than 23
163
+ * - Ask for permission if needed, this will prompt PermissionActivity
164
+ * - If permission granted, then trigger location attribution
165
+ * - If permission denied, then trigger fail flow
166
+ * - If location service is disable, then trigger fail flow
167
+ * - If the user approved for location and has disable location this will continue triggering fails flows
168
+ *
169
+ * For all cases we are calling prompt listeners.
170
+ */
124
171
static void getLocation (Context context , boolean promptLocation , LocationHandler handler ) {
172
+ addPromptHandlerIfAvailable (handler );
125
173
classContext = context ;
126
174
locationHandlers .put (handler .getType (), handler );
127
-
175
+
128
176
if (!OneSignal .shareLocation ) {
177
+ sendAndClearPromptHandlers (promptLocation , false );
129
178
fireFailedComplete ();
130
179
return ;
131
180
}
132
-
181
+
133
182
int locationCoarsePermission = PackageManager .PERMISSION_DENIED ;
134
183
135
184
int locationFinePermission = ContextCompat .checkSelfPermission (context , "android.permission.ACCESS_FINE_LOCATION" );
@@ -140,10 +189,11 @@ static void getLocation(Context context, boolean promptLocation, LocationHandler
140
189
141
190
if (android .os .Build .VERSION .SDK_INT < Build .VERSION_CODES .M ) {
142
191
if (locationFinePermission != PackageManager .PERMISSION_GRANTED && locationCoarsePermission != PackageManager .PERMISSION_GRANTED ) {
192
+ sendAndClearPromptHandlers (promptLocation ,false );
143
193
handler .complete (null );
144
194
return ;
145
195
}
146
-
196
+ sendAndClearPromptHandlers ( promptLocation , true );
147
197
startGetLocation ();
148
198
}
149
199
else { // Android 6.0+
@@ -158,18 +208,24 @@ else if (permissionList.contains("android.permission.ACCESS_COARSE_LOCATION")) {
158
208
requestPermission = "android.permission.ACCESS_COARSE_LOCATION" ;
159
209
}
160
210
161
- if (requestPermission != null && promptLocation )
211
+ if (requestPermission != null && promptLocation ) {
162
212
PermissionsActivity .startPrompt ();
163
- else if (locationCoarsePermission == PackageManager .PERMISSION_GRANTED )
213
+ } else if (locationCoarsePermission == PackageManager .PERMISSION_GRANTED ) {
214
+ sendAndClearPromptHandlers (promptLocation , true );
164
215
startGetLocation ();
165
- else
216
+ } else {
217
+ sendAndClearPromptHandlers (promptLocation , false );
166
218
fireFailedComplete ();
219
+ }
167
220
} catch (PackageManager .NameNotFoundException e ) {
221
+ sendAndClearPromptHandlers (promptLocation , false );
168
222
e .printStackTrace ();
169
223
}
170
224
}
171
- else
225
+ else {
226
+ sendAndClearPromptHandlers (promptLocation , true );
172
227
startGetLocation ();
228
+ }
173
229
}
174
230
}
175
231
@@ -231,17 +287,16 @@ static void fireFailedComplete() {
231
287
PermissionsActivity .answered = false ;
232
288
233
289
synchronized (syncLock ) {
234
- if (mGoogleApiClient != null )
290
+ if (mGoogleApiClient != null )
235
291
mGoogleApiClient .disconnect ();
236
292
mGoogleApiClient = null ;
237
293
}
238
-
239
294
fireComplete (null );
240
295
}
241
296
242
297
private static void fireComplete (LocationPoint point ) {
243
298
// create local copies of fields in thread-safe way
244
- HashMap <CALLBACK_TYPE , LocationHandler > _locationHandlers = new HashMap <>();
299
+ HashMap <PermissionType , LocationHandler > _locationHandlers = new HashMap <>();
245
300
Thread _fallbackFailThread ;
246
301
synchronized (LocationGMS .class ) {
247
302
_locationHandlers .putAll (LocationGMS .locationHandlers );
@@ -250,7 +305,7 @@ private static void fireComplete(LocationPoint point) {
250
305
}
251
306
252
307
// execute race-independent logic
253
- for (CALLBACK_TYPE type : _locationHandlers .keySet ())
308
+ for (PermissionType type : _locationHandlers .keySet ())
254
309
_locationHandlers .get (type ).complete (point );
255
310
if (_fallbackFailThread != null && !Thread .currentThread ().equals (_fallbackFailThread ))
256
311
_fallbackFailThread .interrupt ();
@@ -265,15 +320,15 @@ private static void fireComplete(LocationPoint point) {
265
320
// Save last time so even if a failure we trigger the same schedule update
266
321
setLastLocationTime (System .currentTimeMillis ());
267
322
}
268
-
323
+
269
324
private static void fireCompleteForLocation (Location location ) {
270
325
LocationPoint point = new LocationPoint ();
271
-
326
+
272
327
point .accuracy = location .getAccuracy ();
273
328
point .bg = !OneSignal .isForeground ();
274
329
point .type = locationCoarse ? 0 : 1 ;
275
330
point .timeStamp = location .getTime ();
276
-
331
+
277
332
// Coarse always gives out 14 digits and has an accuracy 2000.
278
333
// Always rounding to 7 as this is what fine returns.
279
334
if (locationCoarse ) {
@@ -304,7 +359,7 @@ static void onFocusChange() {
304
359
}
305
360
306
361
static LocationUpdateListener locationUpdateListener ;
307
-
362
+
308
363
private static class GoogleApiClientListener implements GoogleApiClient .ConnectionCallbacks , GoogleApiClient .OnConnectionFailedListener {
309
364
@ Override
310
365
public void onConnected (Bundle bundle ) {
@@ -336,7 +391,7 @@ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
336
391
}
337
392
338
393
static class LocationUpdateListener implements LocationListener {
339
-
394
+
340
395
private GoogleApiClient mGoogleApiClient ;
341
396
342
397
// this initializer method is already synchronized from LocationGMS with respect to the GoogleApiClient lock
@@ -352,17 +407,17 @@ static class LocationUpdateListener implements LocationListener {
352
407
.setInterval (updateInterval )
353
408
.setMaxWaitTime ((long )(updateInterval * 1.5 ))
354
409
.setPriority (LocationRequest .PRIORITY_BALANCED_POWER_ACCURACY );
355
-
410
+
356
411
FusedLocationApiWrapper .requestLocationUpdates (mGoogleApiClient , locationRequest , this );
357
412
}
358
-
413
+
359
414
@ Override
360
415
public void onLocationChanged (Location location ) {
361
416
mLastLocation = location ;
362
417
OneSignal .Log (OneSignal .LOG_LEVEL .INFO , "Location Change Detected" );
363
418
}
364
419
}
365
-
420
+
366
421
static class FusedLocationApiWrapper {
367
422
@ SuppressWarnings ("MissingPermission" )
368
423
static void requestLocationUpdates (GoogleApiClient googleApiClient , LocationRequest locationRequest , LocationListener locationListener ) {
@@ -375,7 +430,7 @@ static void requestLocationUpdates(GoogleApiClient googleApiClient, LocationRequ
375
430
OneSignal .Log (OneSignal .LOG_LEVEL .WARN , "FusedLocationApi.requestLocationUpdates failed!" , t );
376
431
}
377
432
}
378
-
433
+
379
434
@ SuppressWarnings ("MissingPermission" )
380
435
static Location getLastLocation (GoogleApiClient googleApiClient ) {
381
436
synchronized (LocationGMS .syncLock ) {
@@ -385,10 +440,10 @@ static Location getLastLocation(GoogleApiClient googleApiClient) {
385
440
return null ;
386
441
}
387
442
}
388
-
443
+
389
444
private static class LocationHandlerThread extends HandlerThread {
390
445
Handler mHandler ;
391
-
446
+
392
447
LocationHandlerThread () {
393
448
super ("OSH_LocationHandlerThread" );
394
449
start ();
0 commit comments