@@ -3,16 +3,19 @@ package com.thewizrd.common.location
3
3
import android.annotation.SuppressLint
4
4
import android.app.Activity
5
5
import android.content.Context
6
- import android.location.Criteria
7
6
import android.location.Location
8
7
import android.location.LocationManager
8
+ import android.os.Build
9
9
import android.util.Log
10
10
import androidx.core.location.LocationManagerCompat
11
11
import androidx.core.os.CancellationSignal
12
12
import com.google.android.gms.location.FusedLocationProviderClient
13
- import com.google.android.gms.location.LocationRequest
14
13
import com.google.android.gms.location.LocationServices
14
+ import com.google.android.gms.location.Priority
15
15
import com.google.android.gms.tasks.CancellationTokenSource
16
+ import com.google.android.gms.tasks.Task
17
+ import com.google.android.gms.tasks.Tasks
18
+ import com.google.android.gms.wearable.Wearable
16
19
import com.thewizrd.common.R
17
20
import com.thewizrd.common.helpers.locationPermissionEnabled
18
21
import com.thewizrd.common.utils.ErrorMessage
@@ -22,6 +25,7 @@ import com.thewizrd.shared_resources.exceptions.WeatherException
22
25
import com.thewizrd.shared_resources.locationdata.LocationData
23
26
import com.thewizrd.shared_resources.locationdata.toLocation
24
27
import com.thewizrd.shared_resources.locationdata.toLocationData
28
+ import com.thewizrd.shared_resources.utils.ContextUtils.isPhone
25
29
import com.thewizrd.shared_resources.utils.ConversionMethods
26
30
import com.thewizrd.shared_resources.utils.Logger
27
31
import com.thewizrd.weather_api.weatherModule
@@ -65,16 +69,12 @@ class LocationProvider {
65
69
suspend fun getLastLocation (): Location ? {
66
70
if (! checkPermissions()) return null
67
71
68
- if (isGMSAvailable) {
72
+ val isFusedLocationAvailable = canUseFusedLocation().await()
73
+
74
+ if (isFusedLocationAvailable) {
69
75
return mFusedLocationProviderClient.lastLocation.await()
70
76
} else {
71
- val locCriteria = Criteria ().apply {
72
- accuracy = Criteria .ACCURACY_COARSE
73
- isCostAllowed = false
74
- powerRequirement = Criteria .POWER_LOW
75
- }
76
-
77
- val provider = mLocationMgr.getBestProvider(locCriteria, true ) ? : return null
77
+ val provider = getBestProvider() ? : return null
78
78
return mLocationMgr.getLastKnownLocation(provider)
79
79
}
80
80
}
@@ -89,74 +89,75 @@ class LocationProvider {
89
89
return @suspendCancellableCoroutine
90
90
}
91
91
92
- if (isGMSAvailable) {
93
- val cts = CancellationTokenSource ()
92
+ canUseFusedLocation()
93
+ .continueWith { task ->
94
+ if (! continuation.isActive) return @continueWith
94
95
95
- continuation.invokeOnCancellation {
96
- cts.cancel()
97
- }
96
+ val isFusedLocationAvailable = task.result
98
97
99
- mFusedLocationProviderClient.getCurrentLocation(
100
- LocationRequest .PRIORITY_BALANCED_POWER_ACCURACY ,
101
- cts.token
102
- ).addOnCompleteListener {
103
- if (it.isSuccessful) {
104
- Logger .writeLine(Log .INFO , " $TAG : Location update received..." )
98
+ if (isFusedLocationAvailable) {
99
+ val cts = CancellationTokenSource ()
105
100
106
- if (continuation.isActive) {
107
- continuation.resume(it.result)
108
- }
109
- } else {
110
- it.exception?.let { ex ->
111
- Logger .writeLine(Log .INFO , ex, " $TAG : Error retrieving location..." )
101
+ continuation.invokeOnCancellation {
102
+ cts.cancel()
112
103
}
113
104
114
- if (continuation.isActive) {
115
- continuation.resume(null )
105
+ mFusedLocationProviderClient.getCurrentLocation(
106
+ Priority .PRIORITY_BALANCED_POWER_ACCURACY ,
107
+ cts.token
108
+ ).addOnCompleteListener {
109
+ if (it.isSuccessful) {
110
+ Logger .writeLine(Log .INFO , " $TAG : Location update received..." )
111
+
112
+ if (continuation.isActive) {
113
+ continuation.resume(it.result)
114
+ }
115
+ } else {
116
+ it.exception?.let { ex ->
117
+ Logger .writeLine(Log .INFO , ex, " $TAG : Error retrieving location..." )
118
+ }
119
+
120
+ if (continuation.isActive) {
121
+ continuation.resume(null )
122
+ }
123
+ }
116
124
}
117
- }
118
- }
119
- } else {
120
- val locCriteria = Criteria ().apply {
121
- accuracy = Criteria .ACCURACY_COARSE
122
- isCostAllowed = false
123
- powerRequirement = Criteria .POWER_LOW
124
- }
125
-
126
- val cancelSignal = CancellationSignal ()
125
+ } else {
126
+ val cancelSignal = CancellationSignal ()
127
127
128
- continuation.invokeOnCancellation {
129
- cancelSignal.cancel()
130
- }
128
+ continuation.invokeOnCancellation {
129
+ cancelSignal.cancel()
130
+ }
131
131
132
- val provider = mLocationMgr. getBestProvider(locCriteria, true )
132
+ val provider = getBestProvider()
133
133
134
- if (provider == null ) {
135
- if (continuation.isActive) {
136
- continuation.resume(null )
137
- }
138
- return @suspendCancellableCoroutine
139
- }
134
+ if (provider == null ) {
135
+ if (continuation.isActive) {
136
+ continuation.resume(null )
137
+ }
138
+ return @continueWith
139
+ }
140
140
141
- runCatching {
142
- LocationManagerCompat .getCurrentLocation(
143
- mLocationMgr,
144
- provider,
145
- cancelSignal,
146
- Executors .newSingleThreadExecutor()
147
- ) {
148
- Logger .writeLine(Log .INFO , " $TAG : Location update received..." )
149
- if (continuation.isActive) {
150
- continuation.resume(it)
141
+ runCatching {
142
+ LocationManagerCompat .getCurrentLocation(
143
+ mLocationMgr,
144
+ provider,
145
+ cancelSignal,
146
+ Executors .newSingleThreadExecutor()
147
+ ) {
148
+ Logger .writeLine(Log .INFO , " $TAG : Location update received..." )
149
+ if (continuation.isActive) {
150
+ continuation.resume(it)
151
+ }
152
+ }
153
+ }.onFailure {
154
+ Logger .writeLine(Log .INFO , it, " $TAG : Error retrieving location..." )
155
+ if (continuation.isActive) {
156
+ continuation.resume(null )
157
+ }
151
158
}
152
159
}
153
- }.onFailure {
154
- Logger .writeLine(Log .INFO , it, " $TAG : Error retrieving location..." )
155
- if (continuation.isActive) {
156
- continuation.resume(null )
157
- }
158
160
}
159
- }
160
161
}
161
162
162
163
suspend fun getLatestLocationData (previousLocation : LocationData ? = null): LocationResult {
@@ -236,4 +237,80 @@ class LocationProvider {
236
237
237
238
return LocationResult .NotChanged (previousLocation, false )
238
239
}
240
+
241
+ private fun getBestProvider (): String? {
242
+ val enabledProviders = mLocationMgr.getProviders(true )
243
+
244
+ if (enabledProviders.isNotEmpty()) {
245
+ return if (mContext.isPhone() && Build .VERSION .SDK_INT >= Build .VERSION_CODES .S && enabledProviders.contains(
246
+ LocationManager .FUSED_PROVIDER
247
+ )
248
+ ) {
249
+ LocationManager .FUSED_PROVIDER
250
+ } else if (enabledProviders.contains(LocationManager .GPS_PROVIDER )) {
251
+ LocationManager .GPS_PROVIDER
252
+ } else if (enabledProviders.contains(LocationManager .NETWORK_PROVIDER )) {
253
+ LocationManager .NETWORK_PROVIDER
254
+ } else if (enabledProviders.contains(LocationManager .PASSIVE_PROVIDER )) {
255
+ LocationManager .PASSIVE_PROVIDER
256
+ } else {
257
+ enabledProviders.first()
258
+ }
259
+ }
260
+
261
+ return null
262
+ }
263
+
264
+ /* *
265
+ * On non-wearables: Check if Google Play Services are available. If so, return [com.google.android.gms.location.LocationAvailability.isLocationAvailable]
266
+ *
267
+ * On wearables: Check if Google Play Services are available. If so, check if any paired phone is available and nearby.
268
+ * If so, confirm fused location availability [com.google.android.gms.location.LocationAvailability.isLocationAvailable]
269
+ *
270
+ * @see [FusedLocationProviderClient.getLocationAvailability]
271
+ * @see [com.google.android.gms.wearable.NodeClient.getConnectedNodes]
272
+ * @return true if we can use fused location provider
273
+ */
274
+ private fun canUseFusedLocation (): Task <Boolean > {
275
+ return Tasks .forResult(isGMSAvailable)
276
+ .continueWithTask {
277
+ val isGMSAvailable = it.result
278
+
279
+ if (isGMSAvailable) {
280
+ if (! mContext.isPhone()) {
281
+ // Wearables: FusedLocationProvider will likely not be of use if phone is not connected nearby
282
+ // Verify phone status
283
+ Wearable .getNodeClient(mContext)
284
+ .connectedNodes
285
+ .continueWithTask { nodesTask ->
286
+ if (it.isSuccessful) {
287
+ val nodes = nodesTask.result
288
+ val isNearbyNodes = nodes.any { n -> n.isNearby }
289
+ if (isNearbyNodes) {
290
+ isFusedLocationAvailable()
291
+ } else {
292
+ Tasks .forResult(false )
293
+ }
294
+ } else {
295
+ Tasks .forResult(false )
296
+ }
297
+ }
298
+ } else {
299
+ isFusedLocationAvailable()
300
+ }
301
+ } else {
302
+ Tasks .forResult(false )
303
+ }
304
+ }
305
+ }
306
+
307
+ private fun isFusedLocationAvailable (): Task <Boolean > {
308
+ return mFusedLocationProviderClient.locationAvailability.continueWith { avail ->
309
+ if (avail.isSuccessful) {
310
+ avail.result.isLocationAvailable
311
+ } else {
312
+ false
313
+ }
314
+ }
315
+ }
239
316
}
0 commit comments