expo-location 16.3.0 → 16.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/android/build.gradle +49 -32
  3. package/android/src/main/AndroidManifest.xml +1 -0
  4. package/android/src/main/java/expo/modules/location/LocationActivityResultListener.kt +5 -0
  5. package/android/src/main/java/expo/modules/location/LocationExceptions.kt +57 -0
  6. package/android/src/main/java/expo/modules/location/LocationHelpers.kt +220 -0
  7. package/android/src/main/java/expo/modules/location/LocationModule.kt +817 -0
  8. package/android/src/main/java/expo/modules/location/LocationRequestCallbacks.kt +11 -0
  9. package/android/src/main/java/expo/modules/location/records/LocationArguments.kt +101 -0
  10. package/android/src/main/java/expo/modules/location/records/LocationResults.kt +202 -0
  11. package/android/src/main/java/expo/modules/location/services/LocationTaskService.kt +120 -0
  12. package/android/src/main/java/expo/modules/location/taskConsumers/GeofencingTaskConsumer.kt +246 -0
  13. package/android/src/main/java/expo/modules/location/taskConsumers/LocationTaskConsumer.kt +322 -0
  14. package/expo-module.config.json +6 -0
  15. package/ios/EXLocation.podspec +1 -1
  16. package/package.json +2 -2
  17. package/android/src/main/java/expo/modules/location/LocationActivityResultListener.java +0 -5
  18. package/android/src/main/java/expo/modules/location/LocationHelpers.java +0 -267
  19. package/android/src/main/java/expo/modules/location/LocationModule.java +0 -1012
  20. package/android/src/main/java/expo/modules/location/LocationPackage.java +0 -16
  21. package/android/src/main/java/expo/modules/location/LocationRequestCallbacks.java +0 -12
  22. package/android/src/main/java/expo/modules/location/exceptions/LocationBackgroundUnauthorizedException.java +0 -15
  23. package/android/src/main/java/expo/modules/location/exceptions/LocationRequestRejectedException.java +0 -15
  24. package/android/src/main/java/expo/modules/location/exceptions/LocationSettingsUnsatisfiedException.java +0 -15
  25. package/android/src/main/java/expo/modules/location/exceptions/LocationUnauthorizedException.java +0 -15
  26. package/android/src/main/java/expo/modules/location/exceptions/LocationUnavailableException.java +0 -15
  27. package/android/src/main/java/expo/modules/location/services/LocationTaskService.java +0 -143
  28. package/android/src/main/java/expo/modules/location/taskConsumers/GeofencingTaskConsumer.java +0 -289
  29. package/android/src/main/java/expo/modules/location/taskConsumers/LocationTaskConsumer.java +0 -364
  30. package/unimodule.json +0 -4
package/CHANGELOG.md CHANGED
@@ -10,6 +10,28 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 16.5.0 — 2023-11-14
14
+
15
+ ### 🛠 Breaking changes
16
+
17
+ - Bumped iOS deployment target to 13.4. ([#25063](https://github.com/expo/expo/pull/25063) by [@gabrieldonadel](https://github.com/gabrieldonadel))
18
+ - On `Android` bump `compileSdkVersion` and `targetSdkVersion` to `34`. ([#24708](https://github.com/expo/expo/pull/24708) by [@alanjhughes](https://github.com/alanjhughes))
19
+
20
+ ### 💡 Others
21
+
22
+ - [Android] Moved to the new Modules API. ([#24737](https://github.com/expo/expo/pull/24737) by [@behenate](https://github.com/behenate))
23
+ - Remove `unimodule.json` in favour of `expo-module.config.json`. ([#25100](https://github.com/expo/expo/pull/25100) by [@reichhartd](https://github.com/reichhartd))
24
+
25
+ ### 📚 3rd party library updates
26
+
27
+ - Updated `com.google.android.gms:play-services-location` to `21.0.1`. ([#25028](https://github.com/expo/expo/pull/25028) by [@behenate](https://github.com/behenate))
28
+
29
+ ## 16.4.0 — 2023-10-17
30
+
31
+ ### 🛠 Breaking changes
32
+
33
+ - Dropped support for Android SDK 21 and 22. ([#24201](https://github.com/expo/expo/pull/24201) by [@behenate](https://github.com/behenate))
34
+
13
35
  ## 16.3.0 — 2023-09-04
14
36
 
15
37
  ### 🎉 New features
@@ -3,15 +3,20 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '16.3.0'
6
+ version = '16.5.0'
7
7
 
8
- buildscript {
9
- def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
- if (expoModulesCorePlugin.exists()) {
11
- apply from: expoModulesCorePlugin
12
- applyKotlinExpoModulesCorePlugin()
8
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
+ if (expoModulesCorePlugin.exists()) {
10
+ apply from: expoModulesCorePlugin
11
+ applyKotlinExpoModulesCorePlugin()
12
+ // Remove this check, but keep the contents after SDK49 support is dropped
13
+ if (safeExtGet("expoProvidesDefaultConfig", false)) {
14
+ useExpoPublishing()
15
+ useCoreDependencies()
13
16
  }
17
+ }
14
18
 
19
+ buildscript {
15
20
  // Simple helper that allows the root project to override versions declared by this library.
16
21
  ext.safeExtGet = { prop, fallback ->
17
22
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -35,23 +40,44 @@ buildscript {
35
40
  }
36
41
  }
37
42
 
38
- afterEvaluate {
39
- publishing {
40
- publications {
41
- release(MavenPublication) {
42
- from components.release
43
+ // Remove this if and it's contents, when support for SDK49 is dropped
44
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
45
+ afterEvaluate {
46
+ publishing {
47
+ publications {
48
+ release(MavenPublication) {
49
+ from components.release
50
+ }
43
51
  }
44
- }
45
- repositories {
46
- maven {
47
- url = mavenLocal().url
52
+ repositories {
53
+ maven {
54
+ url = mavenLocal().url
55
+ }
48
56
  }
49
57
  }
50
58
  }
51
59
  }
52
60
 
53
61
  android {
54
- compileSdkVersion safeExtGet("compileSdkVersion", 33)
62
+ // Remove this if and it's contents, when support for SDK49 is dropped
63
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
64
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
65
+
66
+ defaultConfig {
67
+ minSdkVersion safeExtGet("minSdkVersion", 23)
68
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
69
+ }
70
+
71
+ publishing {
72
+ singleVariant("release") {
73
+ withSourcesJar()
74
+ }
75
+ }
76
+
77
+ lintOptions {
78
+ abortOnError false
79
+ }
80
+ }
55
81
 
56
82
  def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
57
83
  if (agpVersion.tokenize('.')[0].toInteger() < 8) {
@@ -67,29 +93,20 @@ android {
67
93
 
68
94
  namespace "expo.modules.location"
69
95
  defaultConfig {
70
- minSdkVersion safeExtGet("minSdkVersion", 21)
71
- targetSdkVersion safeExtGet("targetSdkVersion", 33)
72
96
  versionCode 29
73
- versionName "16.3.0"
74
- }
75
- lintOptions {
76
- abortOnError false
77
- }
78
- publishing {
79
- singleVariant("release") {
80
- withSourcesJar()
81
- }
97
+ versionName "16.5.0"
82
98
  }
83
99
  }
84
100
 
85
101
  dependencies {
86
- implementation project(':expo-modules-core')
87
-
88
- api 'com.google.android.gms:play-services-location:20.0.0'
102
+ // Remove this if and it's contents, when support for SDK49 is dropped
103
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
104
+ implementation project(':expo-modules-core')
105
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
106
+ }
89
107
 
108
+ api 'com.google.android.gms:play-services-location:21.0.1'
90
109
  api('io.nlopez.smartlocation:library:3.3.3') {
91
110
  transitive = false
92
111
  }
93
-
94
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
95
112
  }
@@ -2,6 +2,7 @@
2
2
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
3
3
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
4
4
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
5
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
5
6
 
6
7
  <application>
7
8
  <service
@@ -0,0 +1,5 @@
1
+ package expo.modules.location
2
+
3
+ interface LocationActivityResultListener {
4
+ fun onResult(resultCode: Int)
5
+ }
@@ -0,0 +1,57 @@
1
+ package expo.modules.location
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ internal class NoPermissionsModuleException :
6
+ CodedException("Permissions module is null. Are you sure all the installed Expo modules are properly linked?")
7
+
8
+ internal class NoPermissionInManifestException(permissionName: String) :
9
+ CodedException("You need to add `$permissionName` to the AndroidManifest")
10
+
11
+ internal class LocationBackgroundUnauthorizedException :
12
+ CodedException("Not authorized to use background location services")
13
+
14
+ internal class LocationRequestRejectedException(cause: Exception) :
15
+ CodedException("Location request has been rejected: " + cause.message)
16
+
17
+ internal class LocationRequestCancelledException :
18
+ CodedException("Location request has been cancelled")
19
+
20
+ internal class LocationSettingsUnsatisfiedException :
21
+ CodedException("Location request failed due to unsatisfied device settings")
22
+
23
+ internal class LocationUnauthorizedException :
24
+ CodedException("Not authorized to use location services")
25
+
26
+ internal class LocationUnavailableException :
27
+ CodedException("Location is unavailable. Make sure that location services are enabled")
28
+
29
+ internal class LocationUnknownException :
30
+ CodedException("Current location is unknown")
31
+
32
+ internal class SensorManagerUnavailable :
33
+ CodedException("Sensor manager is unavailable")
34
+
35
+ internal class GeocodeException(message: String?, cause: Throwable? = null) :
36
+ CodedException("An exception occurred when accessing the geocode: ${message ?: ""} ${cause?.message ?: ""}")
37
+
38
+ internal class NoGeocodeException :
39
+ CodedException("Could not find the Geocoder")
40
+
41
+ internal class TaskManagerNotFoundException :
42
+ CodedException("Could not find the task manager")
43
+
44
+ internal class GeofencingException(message: String?, cause: Throwable? = null) :
45
+ CodedException("A geofencing exception has occurred: ${message ?: ""} ${cause?.message ?: ""}")
46
+
47
+ internal class MissingActivityManagerException :
48
+ CodedException("Activity manager is unavailable")
49
+
50
+ internal class MissingUIManagerException :
51
+ CodedException("UIManager is unavailable")
52
+
53
+ internal class ConversionException(fromClass: Class<*>, toClass: Class<*>, message: String? = "") :
54
+ CodedException("Couldn't cast from ${fromClass::class.simpleName} to ${toClass::class.java.simpleName}: $message")
55
+
56
+ internal class ForegroundServiceStartNotAllowedException :
57
+ CodedException("Couldn't start the foreground service. Foreground service cannot be started when the application is in the background.")
@@ -0,0 +1,220 @@
1
+ package expo.modules.location
2
+
3
+ import android.content.Context
4
+ import android.location.Location
5
+ import android.location.LocationManager
6
+ import android.os.Bundle
7
+ import com.google.android.gms.location.CurrentLocationRequest
8
+ import com.google.android.gms.location.FusedLocationProviderClient
9
+ import com.google.android.gms.location.Granularity
10
+ import com.google.android.gms.location.LocationRequest
11
+ import com.google.android.gms.location.Priority
12
+ import expo.modules.interfaces.permissions.Permissions
13
+ import expo.modules.kotlin.Promise
14
+ import expo.modules.kotlin.exception.CodedException
15
+ import expo.modules.location.records.LocationLastKnownOptions
16
+ import expo.modules.location.records.LocationOptions
17
+ import expo.modules.location.records.LocationResponse
18
+ import expo.modules.location.records.PermissionRequestResponse
19
+ import io.nlopez.smartlocation.location.config.LocationAccuracy
20
+ import io.nlopez.smartlocation.location.config.LocationParams
21
+ import kotlin.coroutines.resume
22
+ import kotlin.coroutines.resumeWithException
23
+ import kotlin.coroutines.suspendCoroutine
24
+
25
+ class LocationHelpers {
26
+ companion object {
27
+ /**
28
+ * Checks whether given location didn't exceed given `maxAge` and fits in the required accuracy.
29
+ */
30
+ internal fun isLocationValid(location: Location?, options: LocationLastKnownOptions): Boolean {
31
+ if (location == null) {
32
+ return false
33
+ }
34
+ val maxAge = options.maxAge ?: Double.MAX_VALUE
35
+ val requiredAccuracy = options.requiredAccuracy ?: Double.MAX_VALUE
36
+ val timeDiff = (System.currentTimeMillis() - location.time).toDouble()
37
+ return timeDiff <= maxAge && location.accuracy <= requiredAccuracy
38
+ }
39
+
40
+ fun hasNetworkProviderEnabled(context: Context?): Boolean {
41
+ if (context == null) {
42
+ return false
43
+ }
44
+ val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager
45
+ return locationManager != null && locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
46
+ }
47
+
48
+ internal fun prepareLocationRequest(options: LocationOptions): LocationRequest {
49
+ val locationParams = mapOptionsToLocationParams(options)
50
+
51
+ return LocationRequest.Builder(locationParams.interval)
52
+ .setMinUpdateIntervalMillis(locationParams.interval)
53
+ .setMaxUpdateDelayMillis(locationParams.interval)
54
+ .setMinUpdateDistanceMeters(locationParams.distance)
55
+ .setPriority(mapAccuracyToPriority(options.accuracy))
56
+ .build()
57
+ }
58
+
59
+ internal fun prepareCurrentLocationRequest(options: LocationOptions): CurrentLocationRequest {
60
+ val locationParams = mapOptionsToLocationParams(options)
61
+
62
+ return CurrentLocationRequest.Builder().apply {
63
+ setGranularity(Granularity.GRANULARITY_PERMISSION_LEVEL)
64
+ setPriority(mapAccuracyToPriority(options.accuracy))
65
+ setMaxUpdateAgeMillis(locationParams.interval)
66
+ }.build()
67
+ }
68
+
69
+ fun requestSingleLocation(locationProvider: FusedLocationProviderClient, locationRequest: CurrentLocationRequest, promise: Promise) {
70
+ try {
71
+ locationProvider.getCurrentLocation(locationRequest, null)
72
+ .addOnSuccessListener {
73
+ promise.resolve(LocationResponse(it))
74
+ }
75
+ .addOnFailureListener {
76
+ promise.reject(LocationRequestRejectedException(it))
77
+ }
78
+ .addOnCanceledListener {
79
+ promise.reject(LocationRequestCancelledException())
80
+ }
81
+ } catch (e: SecurityException) {
82
+ promise.reject(LocationRequestRejectedException(e))
83
+ }
84
+ }
85
+
86
+ fun requestContinuousUpdates(locationModule: LocationModule, locationRequest: LocationRequest, watchId: Int, promise: Promise) {
87
+ locationModule.requestLocationUpdates(
88
+ locationRequest,
89
+ watchId,
90
+ object : LocationRequestCallbacks {
91
+ override fun onLocationChanged(location: Location) {
92
+ locationModule.sendLocationResponse(watchId, LocationResponse(location))
93
+ }
94
+
95
+ override fun onRequestSuccess() {
96
+ promise.resolve(null)
97
+ }
98
+
99
+ override fun onRequestFailed(cause: CodedException) {
100
+ promise.reject(cause)
101
+ }
102
+ }
103
+ )
104
+ }
105
+
106
+ private fun mapOptionsToLocationParams(options: LocationOptions): LocationParams {
107
+ val accuracy = options.accuracy
108
+ val locationParamsBuilder = buildLocationParamsForAccuracy(accuracy)
109
+
110
+ options.timeInterval?.let {
111
+ locationParamsBuilder.setInterval(it)
112
+ }
113
+ options.distanceInterval?.let {
114
+ locationParamsBuilder.setDistance(it.toFloat())
115
+ }
116
+
117
+ return locationParamsBuilder.build()
118
+ }
119
+
120
+ private fun mapAccuracyToPriority(accuracy: Int): Int {
121
+ return when (accuracy) {
122
+ LocationModule.ACCURACY_BEST_FOR_NAVIGATION, LocationModule.ACCURACY_HIGHEST, LocationModule.ACCURACY_HIGH -> Priority.PRIORITY_HIGH_ACCURACY
123
+ LocationModule.ACCURACY_BALANCED, LocationModule.ACCURACY_LOW -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
124
+ LocationModule.ACCURACY_LOWEST -> Priority.PRIORITY_LOW_POWER
125
+ else -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
126
+ }
127
+ }
128
+
129
+ private fun buildLocationParamsForAccuracy(accuracy: Int): LocationParams.Builder {
130
+ return when (accuracy) {
131
+ LocationModule.ACCURACY_LOWEST -> LocationParams.Builder()
132
+ .setAccuracy(LocationAccuracy.LOWEST)
133
+ .setDistance(3000f)
134
+ .setInterval(10000)
135
+
136
+ LocationModule.ACCURACY_LOW -> LocationParams.Builder()
137
+ .setAccuracy(LocationAccuracy.LOW)
138
+ .setDistance(1000f)
139
+ .setInterval(5000)
140
+
141
+ LocationModule.ACCURACY_BALANCED -> LocationParams.Builder()
142
+ .setAccuracy(LocationAccuracy.MEDIUM)
143
+ .setDistance(100f)
144
+ .setInterval(3000)
145
+
146
+ LocationModule.ACCURACY_HIGH -> LocationParams.Builder()
147
+ .setAccuracy(LocationAccuracy.HIGH)
148
+ .setDistance(50f)
149
+ .setInterval(2000)
150
+
151
+ LocationModule.ACCURACY_HIGHEST -> LocationParams.Builder()
152
+ .setAccuracy(LocationAccuracy.HIGH)
153
+ .setDistance(25f)
154
+ .setInterval(1000)
155
+
156
+ LocationModule.ACCURACY_BEST_FOR_NAVIGATION -> LocationParams.Builder()
157
+ .setAccuracy(LocationAccuracy.HIGH)
158
+ .setDistance(0f)
159
+ .setInterval(500)
160
+
161
+ else -> LocationParams.Builder()
162
+ .setAccuracy(LocationAccuracy.MEDIUM)
163
+ .setDistance(100f)
164
+ .setInterval(3000)
165
+ }
166
+ }
167
+
168
+ fun isAnyProviderAvailable(context: Context?): Boolean {
169
+ val locationManager = context?.getSystemService(Context.LOCATION_SERVICE) as? LocationManager ?: return false
170
+ return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
171
+ }
172
+
173
+ // Decorator for Permissions.getPermissionsWithPermissionsManager, for use in Kotlin coroutines
174
+ internal suspend fun getPermissionsWithPermissionsManager(contextPermissions: Permissions, vararg permissionStrings: String): PermissionRequestResponse {
175
+ return suspendCoroutine { continuation ->
176
+ Permissions.getPermissionsWithPermissionsManager(
177
+ contextPermissions,
178
+ object : Promise {
179
+ override fun resolve(value: Any?) {
180
+ val result = value as? Bundle ?: throw ConversionException(Any::class.java, Bundle::class.java, "value returned by the permission promise is not a Bundle")
181
+ continuation.resume(PermissionRequestResponse(result))
182
+ }
183
+
184
+ override fun reject(code: String, message: String?, cause: Throwable?) {
185
+ continuation.resumeWithException(CodedException(code, message, cause))
186
+ }
187
+ },
188
+ *permissionStrings
189
+ )
190
+ }
191
+ }
192
+
193
+ // Decorator for Permissions.getPermissionsWithPermissionsManager, for use in Kotlin coroutines
194
+ internal suspend fun askForPermissionsWithPermissionsManager(contextPermissions: Permissions, vararg permissionStrings: String): Bundle {
195
+ return suspendCoroutine {
196
+ Permissions.askForPermissionsWithPermissionsManager(
197
+ contextPermissions,
198
+ object : Promise {
199
+ override fun resolve(value: Any?) {
200
+ it.resume(value as? Bundle ?: throw ConversionException(Any::class.java, Bundle::class.java, "value returned by the permission promise is not a Bundle"))
201
+ }
202
+
203
+ override fun reject(code: String, message: String?, cause: Throwable?) {
204
+ it.resumeWithException(CodedException(code, message, cause))
205
+ }
206
+ },
207
+ *permissionStrings
208
+ )
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * A singleton that keeps information about whether the app is in the foreground or not.
216
+ * This is a simple solution for passing current foreground information from the LocationModule to LocationTaskConsumer.
217
+ */
218
+ object AppForegroundedSingleton {
219
+ var isForegrounded = false
220
+ }