expo-location 18.0.10 → 18.0.11-canary-20250402-161f57b

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,54 +8,49 @@
8
8
 
9
9
  ### 🐛 Bug fixes
10
10
 
11
- ### 💡 Others
12
-
13
- ## 18.0.10 — 2025-04-01
14
-
15
- ### 🐛 Bug fixes
16
-
17
11
  - [iOS] Fixed issue with some permission request flows resolving too soon on iOS. ([#35693](https://github.com/expo/expo/pull/35693) by [@chrfalch](https://github.com/chrfalch))
18
12
  - [iOS] Remove restarting all services when CLLocationManager reports an error ([#35478](https://github.com/expo/expo/pull/35478) by [@chrfalch](https://github.com/chrfalch))
13
+ - [Android] Add missing ProGuard rule to fix task consumer failed ([#34098](https://github.com/expo/expo/pull/34098) by [@cornejobarraza](https://github.com/cornejobarraza))
14
+ - [iOS] `startLocationUpdatesAsync` should not require background permissions ([#33617](https://github.com/expo/expo/pull/33617) by [@andrejpavlovic](https://github.com/andrejpavlovic)
19
15
 
20
- ## 18.0.9 — 2025-03-31
16
+ ### 💡 Others
17
+
18
+ - On Android, remove dependency on `smart-location-lib`. ([#33609](https://github.com/expo/expo/pull/33609) by [@alanjhughes](https://github.com/alanjhughes))
19
+ - [Android] Started using expo modules gradle plugin. ([#34176](https://github.com/expo/expo/pull/34176) by [@lukmccall](https://github.com/lukmccall))
20
+ - [Android] Added missing dependency. ([#35822](https://github.com/expo/expo/pull/35822) by [@lukmccall](https://github.com/lukmccall))
21
+
22
+ ## 18.0.9 - 2025-03-31
21
23
 
22
24
  _This version does not introduce any user-facing changes._
23
25
 
24
- ## 18.0.8 2025-03-14
26
+ ## 18.0.8 - 2025-03-14
25
27
 
26
28
  ### 💡 Others
27
29
 
28
30
  - 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))
29
31
 
30
- ## 18.0.7 — 2025-02-19
31
-
32
- - On Android, remove dependency on `smart-location-lib`. ([#33609](https://github.com/expo/expo/pull/33609) by [@alanjhughes](https://github.com/alanjhughes))
33
- - [Android] Started using expo modules gradle plugin. ([#34176](https://github.com/expo/expo/pull/34176) by [@lukmccall](https://github.com/lukmccall))
34
-
35
32
  ## 18.0.7 - 2025-02-19
36
33
 
37
- - ([iOS][location] Add scope to foreground and background iOS permissions as pr. documentation (#35452))
38
-
39
34
  ### 🐛 Bug fixes
40
35
 
41
36
  - [iOS] Added guards to avoid task options to crash the app. ([#35477](https://github.com/expo/expo/pull/35477) by [@chrfalch](https://github.com/chrfalch))
42
37
  - [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))
43
38
 
44
- ## 18.0.6 2025-02-10
39
+ ## 18.0.6 - 2025-02-10
45
40
 
46
41
  ### 🐛 Bug fixes
47
42
 
48
43
  - [Android] Use less specific exception in catch block of `resolveUserSettingsForRequest`. ([#34784](https://github.com/expo/expo/pull/34784) by [@alanjhughes](https://github.com/alanjhughes))
49
44
 
50
- ## 18.0.5 2025-01-10
45
+ ## 18.0.5 - 2025-01-10
51
46
 
52
47
  _This version does not introduce any user-facing changes._
53
48
 
54
- ## 18.0.4 2024-12-10
49
+ ## 18.0.4 - 2024-12-10
55
50
 
56
51
  _This version does not introduce any user-facing changes._
57
52
 
58
- ## 18.0.3 2024-11-29
53
+ ## 18.0.3 - 2024-11-29
59
54
 
60
55
  _This version does not introduce any user-facing changes._
61
56
 
@@ -1,24 +1,21 @@
1
- apply plugin: 'com.android.library'
1
+ plugins {
2
+ id 'com.android.library'
3
+ id 'expo-module-gradle-plugin'
4
+ }
2
5
 
3
6
  group = 'host.exp.exponent'
4
- version = '18.0.10'
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()
7
+ version = '18.0.11-canary-20250402-161f57b'
12
8
 
13
9
  android {
14
10
  namespace "expo.modules.location"
15
11
  defaultConfig {
16
12
  versionCode 29
17
- versionName "18.0.10"
13
+ versionName "18.0.11-canary-20250402-161f57b"
14
+ consumerProguardFiles("proguard-rules.pro")
18
15
  }
19
16
  }
20
17
 
21
18
  dependencies {
22
19
  api 'com.google.android.gms:play-services-location:21.0.1'
23
- implementation project(":${project.name}\$io.nlopez.smartlocation-jetified-aar")
20
+ implementation 'androidx.annotation:annotation:1.7.1'
24
21
  }
@@ -0,0 +1,3 @@
1
+ # https://github.com/expo/expo/issues/3918
2
+
3
+ -keep class expo.modules.location.taskConsumers.** { *; }
@@ -16,8 +16,6 @@ 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
21
19
  import kotlin.coroutines.resume
22
20
  import kotlin.coroutines.resumeWithException
23
21
  import kotlin.coroutines.suspendCoroutine
@@ -110,16 +108,16 @@ class LocationHelpers {
110
108
 
111
109
  private fun mapOptionsToLocationParams(options: LocationOptions): LocationParams {
112
110
  val accuracy = options.accuracy
113
- val locationParamsBuilder = buildLocationParamsForAccuracy(accuracy)
111
+ val locationParams = buildLocationParamsForAccuracy(accuracy)
114
112
 
115
113
  options.timeInterval?.let {
116
- locationParamsBuilder.setInterval(it)
114
+ locationParams.interval = it
117
115
  }
118
116
  options.distanceInterval?.let {
119
- locationParamsBuilder.setDistance(it.toFloat())
117
+ locationParams.distance = it.toFloat()
120
118
  }
121
119
 
122
- return locationParamsBuilder.build()
120
+ return locationParams
123
121
  }
124
122
 
125
123
  private fun mapAccuracyToPriority(accuracy: Int): Int {
@@ -131,42 +129,15 @@ class LocationHelpers {
131
129
  }
132
130
  }
133
131
 
134
- private fun buildLocationParamsForAccuracy(accuracy: Int): LocationParams.Builder {
132
+ private fun buildLocationParamsForAccuracy(accuracy: Int): LocationParams {
135
133
  return when (accuracy) {
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)
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)
170
141
  }
171
142
  }
172
143
 
@@ -4,6 +4,7 @@ import android.Manifest
4
4
  import android.app.Activity
5
5
  import android.content.Context
6
6
  import android.content.Intent
7
+ import android.content.pm.PackageManager
7
8
  import android.hardware.GeomagneticField
8
9
  import android.hardware.Sensor
9
10
  import android.hardware.SensorEvent
@@ -11,11 +12,14 @@ import android.hardware.SensorEventListener
11
12
  import android.hardware.SensorManager
12
13
  import android.location.Geocoder
13
14
  import android.location.Location
15
+ import android.location.LocationManager
14
16
  import android.os.Build
15
17
  import android.os.Bundle
16
18
  import android.os.Looper
17
19
  import android.util.Log
18
20
  import androidx.annotation.ChecksSdkIntAtLeast
21
+ import androidx.core.app.ActivityCompat
22
+ import androidx.core.location.LocationManagerCompat
19
23
  import androidx.core.os.bundleOf
20
24
  import com.google.android.gms.common.api.ApiException
21
25
  import com.google.android.gms.common.api.CommonStatusCodes
@@ -51,9 +55,6 @@ import expo.modules.location.records.ReverseGeocodeLocation
51
55
  import expo.modules.location.records.ReverseGeocodeResponse
52
56
  import expo.modules.location.taskConsumers.GeofencingTaskConsumer
53
57
  import expo.modules.location.taskConsumers.LocationTaskConsumer
54
- import io.nlopez.smartlocation.SmartLocation
55
- import io.nlopez.smartlocation.geocoding.utils.LocationAddress
56
- import io.nlopez.smartlocation.location.config.LocationParams
57
58
  import java.util.Locale
58
59
  import kotlin.coroutines.resume
59
60
  import kotlin.coroutines.resumeWithException
@@ -157,21 +158,12 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
157
158
  }
158
159
 
159
160
  AsyncFunction<LocationProviderStatus>("getProviderStatusAsync") {
160
- val state = SmartLocation.with(mContext).location().state()
161
-
162
- return@AsyncFunction LocationProviderStatus().apply {
163
- backgroundModeEnabled = state.locationServicesEnabled()
164
- gpsAvailable = state.isGpsAvailable
165
- networkAvailable = state.isNetworkAvailable
166
- locationServicesEnabled = state.locationServicesEnabled()
167
- passiveAvailable = state.isPassiveAvailable
168
- }
161
+ return@AsyncFunction getProviderStatus()
169
162
  }
170
163
 
171
164
  AsyncFunction("watchDeviceHeading") { watchId: Int ->
172
165
  mHeadingId = watchId
173
- startHeadingUpdate()
174
- return@AsyncFunction
166
+ return@AsyncFunction startHeadingUpdate()
175
167
  }
176
168
 
177
169
  AsyncFunction("watchPositionImplAsync") { watchId: Int, options: LocationOptions, promise: Promise ->
@@ -214,7 +206,6 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
214
206
  } else {
215
207
  removeLocationUpdatesForRequest(watchId)
216
208
  }
217
- return@AsyncFunction
218
209
  }
219
210
 
220
211
  AsyncFunction("geocodeAsync") Coroutine { address: String ->
@@ -341,6 +332,23 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
341
332
  } ?: throw NoPermissionsModuleException()
342
333
  }
343
334
 
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
+
344
352
  private suspend fun requestBackgroundPermissionsAsync(): PermissionRequestResponse {
345
353
  if (!isBackgroundPermissionInManifest()) {
346
354
  throw NoPermissionInManifestException("ACCESS_BACKGROUND_LOCATION")
@@ -500,27 +508,54 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
500
508
  }
501
509
 
502
510
  private fun startHeadingUpdate() {
503
- val locationControl = SmartLocation.with(mContext).location().oneFix().config(LocationParams.BEST_EFFORT)
504
- val currLoc = locationControl.lastLocation
505
- if (currLoc != null) {
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) {
506
522
  mGeofield = GeomagneticField(
507
- currLoc.latitude.toFloat(), currLoc.longitude.toFloat(), currLoc.altitude.toFloat(),
523
+ lastLocation.latitude.toFloat(),
524
+ lastLocation.longitude.toFloat(),
525
+ lastLocation.altitude.toFloat(),
508
526
  System.currentTimeMillis()
509
527
  )
510
528
  } else {
511
- locationControl.start { location: Location ->
512
- mGeofield = GeomagneticField(
513
- location.latitude.toFloat(), location.longitude.toFloat(), location.altitude.toFloat(),
514
- System.currentTimeMillis()
515
- )
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
+ }
516
546
  }
547
+ mLocationProvider.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
517
548
  }
518
549
  mSensorManager.registerListener(
519
550
  this,
520
551
  mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
521
552
  SensorManager.SENSOR_DELAY_NORMAL
522
553
  )
523
- mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL)
554
+ mSensorManager.registerListener(
555
+ this,
556
+ mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
557
+ SensorManager.SENSOR_DELAY_NORMAL
558
+ )
524
559
  }
525
560
 
526
561
  private fun sendUpdate() {
@@ -598,7 +633,6 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
598
633
  private fun stopWatching() {
599
634
  // if permissions not granted it won't work anyway, but this can be invoked when permission dialog appears
600
635
  if (Geocoder.isPresent() && !isMissingForegroundPermissions()) {
601
- SmartLocation.with(mContext).geocoding().stop()
602
636
  mGeocoderPaused = true
603
637
  }
604
638
  for (requestId in mLocationCallbacks.keys) {
@@ -665,8 +699,10 @@ class LocationModule : Module(), LifecycleEventListener, SensorEventListener, Ac
665
699
  locations?.let { location ->
666
700
  location.let {
667
701
  val results = it.mapNotNull { address ->
668
- val locationAddress = LocationAddress(address)
669
- GeocodeResponse.from(locationAddress.location)
702
+ val newLocation = Location(LocationManager.GPS_PROVIDER)
703
+ newLocation.latitude = address.latitude
704
+ newLocation.longitude = address.longitude
705
+ GeocodeResponse.from(newLocation)
670
706
  }
671
707
  continuation.resume(results)
672
708
  }
@@ -0,0 +1,14 @@
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
+ }
@@ -1,2 +1,2 @@
1
- export declare const LocationEventEmitter: import("expo-modules-core/types").EventEmitter<Record<never, never>>;
1
+ export declare const LocationEventEmitter: import("expo").EventEmitterType<Record<never, never>>;
2
2
  //# sourceMappingURL=LocationEventEmitter.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocationEventEmitter.web.d.ts","sourceRoot":"","sources":["../src/LocationEventEmitter.web.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,sEAAqB,CAAC"}
1
+ {"version":3,"file":"LocationEventEmitter.web.d.ts","sourceRoot":"","sources":["../src/LocationEventEmitter.web.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,uDAAqB,CAAC"}
@@ -1,22 +1,15 @@
1
1
  {
2
- "platforms": [
3
- "apple",
4
- "android"
5
- ],
2
+ "platforms": ["apple", "android"],
6
3
  "apple": {
7
- "modules": [
8
- "LocationModule"
9
- ]
4
+ "modules": ["LocationModule"]
10
5
  },
11
6
  "android": {
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
- ]
7
+ "modules": ["expo.modules.location.LocationModule"],
8
+ "publication": {
9
+ "groupId": "host.exp.exponent",
10
+ "artifactId": "expo.modules.location",
11
+ "version": "18.0.11-canary-20250402-161f57b",
12
+ "repository": "https://maven.pkg.github.com/expo/expo"
13
+ }
21
14
  }
22
15
  }
@@ -163,9 +163,13 @@ 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.
166
171
  try ensureLocationServicesEnabled()
167
172
  try ensureForegroundLocationPermissions(appContext)
168
- try ensureBackgroundLocationPermissions(appContext)
169
173
 
170
174
  guard CLLocationManager.significantLocationChangeMonitoringAvailable() else {
171
175
  throw Exceptions.LocationUpdatesUnavailable()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-location",
3
- "version": "18.0.10",
3
+ "version": "18.0.11-canary-20250402-161f57b",
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.4"
42
+ "expo-module-scripts": "4.0.5-canary-20250402-161f57b"
43
43
  },
44
44
  "peerDependencies": {
45
- "expo": "*"
45
+ "expo": "53.0.0-canary-20250402-161f57b"
46
46
  },
47
- "gitHead": "b08a0bd52965f85871c12c31da16a45e2cd26c4c"
47
+ "gitHead": "161f57bb5f579c84f8fa0337ec596034e21760f6"
48
48
  }