expo-location 18.0.8-canary-20250305-0af9ad2 → 18.0.8

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.
package/CHANGELOG.md CHANGED
@@ -8,35 +8,42 @@
8
8
 
9
9
  ### 🐛 Bug fixes
10
10
 
11
- - [Android] Add missing ProGuard rule to fix task consumer failed ([#34098](https://github.com/expo/expo/pull/34098) by [@cornejobarraza](https://github.com/cornejobarraza))
12
- - [iOS] `startLocationUpdatesAsync` should not require background permissions ([#33617](https://github.com/expo/expo/pull/33617) by [@andrejpavlovic](https://github.com/andrejpavlovic)
11
+ ### 💡 Others
12
+
13
+ ## 18.0.8 — 2025-03-14
13
14
 
14
15
  ### 💡 Others
15
16
 
17
+ - On iOS, added setting the scope value as per our documentation. ([#35452](https://github.com/expo/expo/pull/35452) by [@chrfalch](https://github.com/chrfalch))
18
+
19
+ ## 18.0.7 — 2025-02-19
20
+
16
21
  - On Android, remove dependency on `smart-location-lib`. ([#33609](https://github.com/expo/expo/pull/33609) by [@alanjhughes](https://github.com/alanjhughes))
17
22
  - [Android] Started using expo modules gradle plugin. ([#34176](https://github.com/expo/expo/pull/34176) by [@lukmccall](https://github.com/lukmccall))
18
23
 
19
24
  ## 18.0.7 - 2025-02-19
20
25
 
26
+ - ([iOS][location] Add scope to foreground and background iOS permissions as pr. documentation (#35452))
27
+
21
28
  ### 🐛 Bug fixes
22
29
 
23
30
  - [iOS] Added error handler to the streaming location/heading methods since these can fail while streaming ([#35004](https://github.com/expo/expo/pull/35004) by [@chrfalch](https://github.com/chrfalch))
24
31
 
25
- ## 18.0.6 - 2025-02-10
32
+ ## 18.0.6 2025-02-10
26
33
 
27
34
  ### 🐛 Bug fixes
28
35
 
29
36
  - [Android] Use less specific exception in catch block of `resolveUserSettingsForRequest`. ([#34784](https://github.com/expo/expo/pull/34784) by [@alanjhughes](https://github.com/alanjhughes))
30
37
 
31
- ## 18.0.5 - 2025-01-10
38
+ ## 18.0.5 2025-01-10
32
39
 
33
40
  _This version does not introduce any user-facing changes._
34
41
 
35
- ## 18.0.4 - 2024-12-10
42
+ ## 18.0.4 2024-12-10
36
43
 
37
44
  _This version does not introduce any user-facing changes._
38
45
 
39
- ## 18.0.3 - 2024-11-29
46
+ ## 18.0.3 2024-11-29
40
47
 
41
48
  _This version does not introduce any user-facing changes._
42
49
 
@@ -1,20 +1,24 @@
1
- plugins {
2
- id 'com.android.library'
3
- id 'expo-module-gradle-plugin'
4
- }
1
+ apply plugin: 'com.android.library'
5
2
 
6
3
  group = 'host.exp.exponent'
7
- version = '18.0.2'
4
+ version = '18.0.8'
5
+
6
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
+ apply from: expoModulesCorePlugin
8
+ applyKotlinExpoModulesCorePlugin()
9
+ useCoreDependencies()
10
+ useDefaultAndroidSdkVersions()
11
+ useExpoPublishing()
8
12
 
9
13
  android {
10
14
  namespace "expo.modules.location"
11
15
  defaultConfig {
12
16
  versionCode 29
13
- versionName "18.0.2"
14
- consumerProguardFiles("proguard-rules.pro")
17
+ versionName "18.0.8"
15
18
  }
16
19
  }
17
20
 
18
21
  dependencies {
19
22
  api 'com.google.android.gms:play-services-location:21.0.1'
23
+ implementation project(":${project.name}\$io.nlopez.smartlocation-jetified-aar")
20
24
  }
@@ -16,6 +16,8 @@ import expo.modules.location.records.LocationLastKnownOptions
16
16
  import expo.modules.location.records.LocationOptions
17
17
  import expo.modules.location.records.LocationResponse
18
18
  import expo.modules.location.records.PermissionRequestResponse
19
+ import io.nlopez.smartlocation.location.config.LocationAccuracy
20
+ import io.nlopez.smartlocation.location.config.LocationParams
19
21
  import kotlin.coroutines.resume
20
22
  import kotlin.coroutines.resumeWithException
21
23
  import kotlin.coroutines.suspendCoroutine
@@ -108,16 +110,16 @@ class LocationHelpers {
108
110
 
109
111
  private fun mapOptionsToLocationParams(options: LocationOptions): LocationParams {
110
112
  val accuracy = options.accuracy
111
- val locationParams = buildLocationParamsForAccuracy(accuracy)
113
+ val locationParamsBuilder = buildLocationParamsForAccuracy(accuracy)
112
114
 
113
115
  options.timeInterval?.let {
114
- locationParams.interval = it
116
+ locationParamsBuilder.setInterval(it)
115
117
  }
116
118
  options.distanceInterval?.let {
117
- locationParams.distance = it.toFloat()
119
+ locationParamsBuilder.setDistance(it.toFloat())
118
120
  }
119
121
 
120
- return locationParams
122
+ return locationParamsBuilder.build()
121
123
  }
122
124
 
123
125
  private fun mapAccuracyToPriority(accuracy: Int): Int {
@@ -129,15 +131,42 @@ class LocationHelpers {
129
131
  }
130
132
  }
131
133
 
132
- private fun buildLocationParamsForAccuracy(accuracy: Int): LocationParams {
134
+ private fun buildLocationParamsForAccuracy(accuracy: Int): LocationParams.Builder {
133
135
  return when (accuracy) {
134
- LocationModule.ACCURACY_LOWEST -> LocationParams(accuracy = LocationAccuracy.LOWEST, distance = 3000f, interval = 10000)
135
- LocationModule.ACCURACY_LOW -> LocationParams(accuracy = LocationAccuracy.LOW, distance = 1000f, interval = 5000)
136
- LocationModule.ACCURACY_BALANCED -> LocationParams(accuracy = LocationAccuracy.MEDIUM, distance = 100f, interval = 3000)
137
- LocationModule.ACCURACY_HIGH -> LocationParams(accuracy = LocationAccuracy.HIGH, distance = 50f, interval = 2000)
138
- LocationModule.ACCURACY_HIGHEST -> LocationParams(accuracy = LocationAccuracy.HIGH, distance = 25f, interval = 1000)
139
- LocationModule.ACCURACY_BEST_FOR_NAVIGATION -> LocationParams(accuracy = LocationAccuracy.HIGH, distance = 0f, interval = 500)
140
- else -> LocationParams(accuracy = LocationAccuracy.MEDIUM, distance = 100f, interval = 3000)
136
+ LocationModule.ACCURACY_LOWEST -> LocationParams.Builder()
137
+ .setAccuracy(LocationAccuracy.LOWEST)
138
+ .setDistance(3000f)
139
+ .setInterval(10000)
140
+
141
+ LocationModule.ACCURACY_LOW -> LocationParams.Builder()
142
+ .setAccuracy(LocationAccuracy.LOW)
143
+ .setDistance(1000f)
144
+ .setInterval(5000)
145
+
146
+ LocationModule.ACCURACY_BALANCED -> LocationParams.Builder()
147
+ .setAccuracy(LocationAccuracy.MEDIUM)
148
+ .setDistance(100f)
149
+ .setInterval(3000)
150
+
151
+ LocationModule.ACCURACY_HIGH -> LocationParams.Builder()
152
+ .setAccuracy(LocationAccuracy.HIGH)
153
+ .setDistance(50f)
154
+ .setInterval(2000)
155
+
156
+ LocationModule.ACCURACY_HIGHEST -> LocationParams.Builder()
157
+ .setAccuracy(LocationAccuracy.HIGH)
158
+ .setDistance(25f)
159
+ .setInterval(1000)
160
+
161
+ LocationModule.ACCURACY_BEST_FOR_NAVIGATION -> LocationParams.Builder()
162
+ .setAccuracy(LocationAccuracy.HIGH)
163
+ .setDistance(0f)
164
+ .setInterval(500)
165
+
166
+ else -> LocationParams.Builder()
167
+ .setAccuracy(LocationAccuracy.MEDIUM)
168
+ .setDistance(100f)
169
+ .setInterval(3000)
141
170
  }
142
171
  }
143
172
 
@@ -12,14 +12,11 @@ import android.hardware.SensorEventListener
12
12
  import android.hardware.SensorManager
13
13
  import android.location.Geocoder
14
14
  import android.location.Location
15
- import android.location.LocationManager
16
15
  import android.os.Build
17
16
  import android.os.Bundle
18
17
  import android.os.Looper
19
18
  import android.util.Log
20
19
  import androidx.annotation.ChecksSdkIntAtLeast
21
- import androidx.core.app.ActivityCompat
22
- import androidx.core.location.LocationManagerCompat
23
20
  import androidx.core.os.bundleOf
24
21
  import com.google.android.gms.common.api.ApiException
25
22
  import com.google.android.gms.common.api.CommonStatusCodes
@@ -55,6 +52,9 @@ import expo.modules.location.records.ReverseGeocodeLocation
55
52
  import expo.modules.location.records.ReverseGeocodeResponse
56
53
  import expo.modules.location.taskConsumers.GeofencingTaskConsumer
57
54
  import expo.modules.location.taskConsumers.LocationTaskConsumer
55
+ import io.nlopez.smartlocation.SmartLocation
56
+ import io.nlopez.smartlocation.geocoding.utils.LocationAddress
57
+ import io.nlopez.smartlocation.location.config.LocationParams
58
58
  import java.util.Locale
59
59
  import kotlin.coroutines.resume
60
60
  import kotlin.coroutines.resumeWithException
@@ -158,12 +158,21 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
158
158
  }
159
159
 
160
160
  AsyncFunction<LocationProviderStatus>("getProviderStatusAsync") {
161
- return@AsyncFunction getProviderStatus()
161
+ val state = SmartLocation.with(mContext).location().state()
162
+
163
+ return@AsyncFunction LocationProviderStatus().apply {
164
+ backgroundModeEnabled = state.locationServicesEnabled()
165
+ gpsAvailable = state.isGpsAvailable
166
+ networkAvailable = state.isNetworkAvailable
167
+ locationServicesEnabled = state.locationServicesEnabled()
168
+ passiveAvailable = state.isPassiveAvailable
169
+ }
162
170
  }
163
171
 
164
172
  AsyncFunction("watchDeviceHeading") { watchId: Int ->
165
173
  mHeadingId = watchId
166
- return@AsyncFunction startHeadingUpdate()
174
+ startHeadingUpdate()
175
+ return@AsyncFunction
167
176
  }
168
177
 
169
178
  AsyncFunction("watchPositionImplAsync") { watchId: Int, options: LocationOptions, promise: Promise ->
@@ -206,6 +215,7 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
206
215
  } else {
207
216
  removeLocationUpdatesForRequest(watchId)
208
217
  }
218
+ return@AsyncFunction
209
219
  }
210
220
 
211
221
  AsyncFunction("geocodeAsync") Coroutine { address: String ->
@@ -332,23 +342,6 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
332
342
  } ?: throw NoPermissionsModuleException()
333
343
  }
334
344
 
335
- private fun getProviderStatus(): LocationProviderStatus {
336
- val manager = mContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
337
-
338
- val isGpsAvailable = manager.isProviderEnabled(LocationManager.GPS_PROVIDER)
339
- val isNetworkAvailable = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
340
- val isLocationServicesEnabled = LocationManagerCompat.isLocationEnabled(manager)
341
- val isPassiveAvailable = manager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER)
342
-
343
- return LocationProviderStatus().apply {
344
- backgroundModeEnabled = isLocationServicesEnabled
345
- gpsAvailable = isGpsAvailable
346
- networkAvailable = isNetworkAvailable
347
- locationServicesEnabled = isLocationServicesEnabled
348
- passiveAvailable = isPassiveAvailable
349
- }
350
- }
351
-
352
345
  private suspend fun requestBackgroundPermissionsAsync(): PermissionRequestResponse {
353
346
  if (!isBackgroundPermissionInManifest()) {
354
347
  throw NoPermissionInManifestException("ACCESS_BACKGROUND_LOCATION")
@@ -508,54 +501,27 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
508
501
  }
509
502
 
510
503
  private fun startHeadingUpdate() {
511
- val locationManager = mContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
512
- if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
513
- ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
514
- ) {
515
- return
516
- }
517
- val lastLocation =
518
- locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
519
- ?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
520
-
521
- if (lastLocation != null) {
504
+ val locationControl = SmartLocation.with(mContext).location().oneFix().config(LocationParams.BEST_EFFORT)
505
+ val currLoc = locationControl.lastLocation
506
+ if (currLoc != null) {
522
507
  mGeofield = GeomagneticField(
523
- lastLocation.latitude.toFloat(),
524
- lastLocation.longitude.toFloat(),
525
- lastLocation.altitude.toFloat(),
508
+ currLoc.latitude.toFloat(), currLoc.longitude.toFloat(), currLoc.altitude.toFloat(),
526
509
  System.currentTimeMillis()
527
510
  )
528
511
  } else {
529
- val locationRequest = LocationRequest.Builder(
530
- LocationRequest.PRIORITY_HIGH_ACCURACY,
531
- 0L
532
- ).setMaxUpdates(1)
533
- .build()
534
-
535
- val locationCallback = object : LocationCallback() {
536
- override fun onLocationResult(locationResult: LocationResult) {
537
- locationResult.lastLocation?.let {
538
- mGeofield = GeomagneticField(
539
- it.latitude.toFloat(),
540
- it.longitude.toFloat(),
541
- it.altitude.toFloat(),
542
- System.currentTimeMillis()
543
- )
544
- }
545
- }
512
+ locationControl.start { location: Location ->
513
+ mGeofield = GeomagneticField(
514
+ location.latitude.toFloat(), location.longitude.toFloat(), location.altitude.toFloat(),
515
+ System.currentTimeMillis()
516
+ )
546
517
  }
547
- mLocationProvider.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
548
518
  }
549
519
  mSensorManager.registerListener(
550
520
  this,
551
521
  mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
552
522
  SensorManager.SENSOR_DELAY_NORMAL
553
523
  )
554
- mSensorManager.registerListener(
555
- this,
556
- mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
557
- SensorManager.SENSOR_DELAY_NORMAL
558
- )
524
+ mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL)
559
525
  }
560
526
 
561
527
  private fun sendUpdate() {
@@ -633,6 +599,7 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
633
599
  private fun stopWatching() {
634
600
  // if permissions not granted it won't work anyway, but this can be invoked when permission dialog appears
635
601
  if (Geocoder.isPresent() && !isMissingForegroundPermissions()) {
602
+ SmartLocation.with(mContext).geocoding().stop()
636
603
  mGeocoderPaused = true
637
604
  }
638
605
  for (requestId in mLocationCallbacks.keys) {
@@ -699,10 +666,8 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
699
666
  locations?.let { location ->
700
667
  location.let {
701
668
  val results = it.mapNotNull { address ->
702
- val newLocation = Location(LocationManager.GPS_PROVIDER)
703
- newLocation.latitude = address.latitude
704
- newLocation.longitude = address.longitude
705
- GeocodeResponse.from(newLocation)
669
+ val locationAddress = LocationAddress(address)
670
+ GeocodeResponse.from(locationAddress.location)
706
671
  }
707
672
  continuation.resume(results)
708
673
  }
@@ -1,9 +1,22 @@
1
1
  {
2
- "platforms": ["apple", "android"],
2
+ "platforms": [
3
+ "apple",
4
+ "android"
5
+ ],
3
6
  "apple": {
4
- "modules": ["LocationModule"]
7
+ "modules": [
8
+ "LocationModule"
9
+ ]
5
10
  },
6
11
  "android": {
7
- "modules": ["expo.modules.location.LocationModule"]
12
+ "modules": [
13
+ "expo.modules.location.LocationModule"
14
+ ],
15
+ "gradleAarProjects": [
16
+ {
17
+ "name": "io.nlopez.smartlocation-jetified-aar",
18
+ "aarFilePath": "android/libs/io.nlopez.smartlocation-3.3.3-jetified.aar"
19
+ }
20
+ ]
8
21
  }
9
22
  }
@@ -163,13 +163,9 @@ public final class LocationModule: Module {
163
163
  // Background location
164
164
 
165
165
  AsyncFunction("startLocationUpdatesAsync") { (taskName: String, options: [String: Any]) in
166
- // There are two ways of starting this service.
167
- // 1. As a background location service, this requires the background location permission.
168
- // 2. As a user-initiated foreground service, this does NOT require the background location permission.
169
- // Unfortunately, we cannot distinguish between those cases.
170
- // So we only check foreground permission which needs to be granted in both cases.
171
166
  try ensureLocationServicesEnabled()
172
167
  try ensureForegroundLocationPermissions(appContext)
168
+ try ensureBackgroundLocationPermissions(appContext)
173
169
 
174
170
  guard CLLocationManager.significantLocationChangeMonitoringAvailable() else {
175
171
  throw Exceptions.LocationUpdatesUnavailable()
@@ -93,7 +93,7 @@ static SEL alwaysAuthorizationSelector;
93
93
  }
94
94
  }
95
95
 
96
- return @{ @"status": @(status) };
96
+ return @{ @"status": @(status), @"scope": @(systemStatus == kCLAuthorizationStatusAuthorizedWhenInUse ? "whenInUse" : systemStatus == kCLAuthorizationStatusAuthorizedAlways ? "always" : "none") };
97
97
  }
98
98
 
99
99
  @end
@@ -54,7 +54,7 @@ static SEL whenInUseAuthorizationSelector;
54
54
  }
55
55
  }
56
56
 
57
- return @{ @"status": @(status) };
57
+ return @{ @"status": @(status), @"scope": @(systemStatus == kCLAuthorizationStatusAuthorizedWhenInUse ? "whenInUse" : systemStatus == kCLAuthorizationStatusAuthorizedAlways ? "always" : "none") };
58
58
  }
59
59
 
60
60
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-location",
3
- "version": "18.0.8-canary-20250305-0af9ad2",
3
+ "version": "18.0.8",
4
4
  "description": "Allows reading geolocation information from the device. Your app can poll for the current location or subscribe to location update events.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -39,10 +39,10 @@
39
39
  "preset": "expo-module-scripts"
40
40
  },
41
41
  "devDependencies": {
42
- "expo-module-scripts": "4.0.5-canary-20250305-0af9ad2"
42
+ "expo-module-scripts": "^4.0.4"
43
43
  },
44
44
  "peerDependencies": {
45
- "expo": "53.0.0-canary-20250305-0af9ad2"
45
+ "expo": "*"
46
46
  },
47
- "gitHead": "0af9ad2afd326af47eae076f81a89105a026312d"
47
+ "gitHead": "ee33df50fbe6bf9bb5d772e7f341f85a27cf9993"
48
48
  }
@@ -1,3 +0,0 @@
1
- # https://github.com/expo/expo/issues/3918
2
-
3
- -keep class expo.modules.location.taskConsumers.** { *; }
@@ -1,14 +0,0 @@
1
- package expo.modules.location
2
-
3
- data class LocationParams(
4
- val accuracy: LocationAccuracy,
5
- var distance: Float,
6
- var interval: Long
7
- )
8
-
9
- enum class LocationAccuracy {
10
- LOWEST,
11
- LOW,
12
- MEDIUM,
13
- HIGH
14
- }