From d6a0b447adeeb38406786274ec69938cc33524ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Ali=20Ustao=C4=9Flu?= Date: Sat, 24 Oct 2026 23:00:22 +0200 Subject: [PATCH] geolocator_watchos 0.2.1: pending requests share the most accurate accuracy With several getCurrentPosition calls pending, each call passed its own accuracy to the shared single-request manager, so the last call's won. geolocator_apple 2.4.3 runs the shared request at the most accurate of the pending ones (MIN of CLLocationAccuracy) until the last one ends, and so does this package now (#18). The rule lives in Dart, ranked by the desiredAccuracy native sets for each LocationAccuracy: bestForNavigation -2 m, best -1, high 10, medium 100, low 1000, lowest 3000, reduced 6380 km (kCLLocationAccuracyReduced, read on macOS). Native is unchanged; its header says what Dart passes. Three tests: the ranking across pending calls and the reset after the last one, reduced against lowest and bestForNavigation against best, and a call that times out leaving the others at the accuracy they share. All three fail with the old rule. --- packages/geolocator_watchos/CHANGELOG.md | 6 ++ packages/geolocator_watchos/PORTING_REPORT.md | 2 +- packages/geolocator_watchos/README.md | 2 +- .../lib/geolocator_watchos.dart | 29 +++++++- packages/geolocator_watchos/pubspec.yaml | 2 +- .../test/geolocator_watchos_test.dart | 68 +++++++++++++++++++ .../watchos/Classes/geolocator_watchos_ffi.h | 3 +- 7 files changed, 106 insertions(+), 6 deletions(-) diff --git a/packages/geolocator_watchos/CHANGELOG.md b/packages/geolocator_watchos/CHANGELOG.md index 38d87ea..1f3730a 100644 --- a/packages/geolocator_watchos/CHANGELOG.md +++ b/packages/geolocator_watchos/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.2.1 + +* With several `getCurrentPosition` calls pending, the shared request runs at + the most accurate accuracy any of them asked for, until the last one ends, + as geolocator_apple does. The last call's accuracy used to win (#18). + ## 0.2.0 **Breaking** diff --git a/packages/geolocator_watchos/PORTING_REPORT.md b/packages/geolocator_watchos/PORTING_REPORT.md index 7571f30..3aa1b63 100644 --- a/packages/geolocator_watchos/PORTING_REPORT.md +++ b/packages/geolocator_watchos/PORTING_REPORT.md @@ -80,7 +80,7 @@ watchOS" table lists the ✗ rows. | `checkPermission` / `requestPermission` | ✅ when-in-use authorization | | `isLocationServiceEnabled` | ✅ | | `getLastKnownPosition` | ✅ the last cached fix | -| `getCurrentPosition` | ✅ a `CLLocationManager` of its own, as in geolocator_apple; skips cached fixes more than 5 seconds old | +| `getCurrentPosition` | ✅ a `CLLocationManager` of its own, as in geolocator_apple; skips cached fixes more than 5 seconds old; calls pending together share it, at the most accurate accuracy any of them asked for (geolocator_apple 2.4.3's rule) | | `getPositionStream` | ✅ pushed by the `CLLocationManager` delegate, every fix; a second call while one runs gets that stream, as from geolocator_apple | | `distanceBetween` / `bearingBetween` | ✅ the interface's own Dart code | | `getServiceStatusStream` | ✅ starts with the current status, then each change CoreLocation reports | diff --git a/packages/geolocator_watchos/README.md b/packages/geolocator_watchos/README.md index 96105f2..a68f0cc 100644 --- a/packages/geolocator_watchos/README.md +++ b/packages/geolocator_watchos/README.md @@ -38,7 +38,7 @@ Add a location usage description to the watch app's `Info.plist`: |---|---| | `checkPermission` / `requestPermission` | supported (when-in-use) | | `isLocationServiceEnabled` | supported | -| `getCurrentPosition` | supported; skips cached fixes more than 5 seconds old, and does not wait on a running stream | +| `getCurrentPosition` | supported; skips cached fixes more than 5 seconds old, and does not wait on a running stream; calls pending together share one request, at the most accurate accuracy any of them asked for, as in geolocator_apple | | `getPositionStream` | supported; while a stream runs, a second call gets it, with its settings, as from geolocator_apple | | `getLastKnownPosition` | supported (last cached fix) | | `getLocationAccuracy` | supported (precise or reduced) | diff --git a/packages/geolocator_watchos/lib/geolocator_watchos.dart b/packages/geolocator_watchos/lib/geolocator_watchos.dart index ba9a519..15fba4b 100644 --- a/packages/geolocator_watchos/lib/geolocator_watchos.dart +++ b/packages/geolocator_watchos/lib/geolocator_watchos.dart @@ -113,6 +113,12 @@ base class GeolocatorWatchos extends GeolocatorPlatform { /// manager, which stops when the last of them ends. static int _singleRequests = 0; + /// The accuracy the single-request manager runs at: the most accurate one + /// the pending `getCurrentPosition` calls asked for, as in + /// geolocator_apple. It is set afresh by the first call after the last one + /// ended. + static LocationAccuracy _singleRequestAccuracy = LocationAccuracy.best; + /// The running position stream, which a second `getPositionStream` call /// gets too, with the first call's settings, as from geolocator_apple. Stream? _positionStream; @@ -126,6 +132,20 @@ base class GeolocatorWatchos extends GeolocatorPlatform { GeolocatorPlatform.instance = GeolocatorWatchos(); } + /// The `desiredAccuracy` native sets for [accuracy], in metres: + /// CoreLocation's constant, where smaller is more accurate. + /// `kCLLocationAccuracyReduced` is 6380 km, the least accurate of all. + static double _desiredAccuracy(LocationAccuracy accuracy) => + switch (accuracy) { + LocationAccuracy.bestForNavigation => -2, + LocationAccuracy.best => -1, + LocationAccuracy.high => 10, + LocationAccuracy.medium => 100, + LocationAccuracy.low => 1000, + LocationAccuracy.lowest => 3000, + LocationAccuracy.reduced => 6380000, + }; + static LocationPermission _mapStatus(int raw) { switch (raw) { case 3: // authorizedAlways @@ -234,9 +254,14 @@ base class GeolocatorWatchos extends GeolocatorPlatform { final void Function() stopListening = _Notifier.listen(_b, GeolocatorWatchosSignal.position, check); + final LocationAccuracy accuracy = + locationSettings?.accuracy ?? LocationAccuracy.best; + if (_singleRequests == 0 || + _desiredAccuracy(accuracy) < _desiredAccuracy(_singleRequestAccuracy)) { + _singleRequestAccuracy = accuracy; + } _singleRequests++; - _b.requestLocation( - (locationSettings?.accuracy ?? LocationAccuracy.best).index); + _b.requestLocation(_singleRequestAccuracy.index); try { return await completer.future.timeout( diff --git a/packages/geolocator_watchos/pubspec.yaml b/packages/geolocator_watchos/pubspec.yaml index 7a4a05e..d3cff8a 100644 --- a/packages/geolocator_watchos/pubspec.yaml +++ b/packages/geolocator_watchos/pubspec.yaml @@ -1,6 +1,6 @@ name: geolocator_watchos description: "watchOS implementation of the geolocator plugin, providing position and permission handling from CoreLocation (CLLocationManager) via dart:ffi." -version: 0.2.0 +version: 0.2.1 homepage: https://flutterwatch.dev repository: https://github.com/flutterwatch/plugins/tree/main/packages/geolocator_watchos issue_tracker: https://github.com/flutterwatch/plugins/issues diff --git a/packages/geolocator_watchos/test/geolocator_watchos_test.dart b/packages/geolocator_watchos/test/geolocator_watchos_test.dart index 9fb563e..1414e6b 100644 --- a/packages/geolocator_watchos/test/geolocator_watchos_test.dart +++ b/packages/geolocator_watchos/test/geolocator_watchos_test.dart @@ -32,6 +32,7 @@ class _FakeBackend implements GeolocatorWatchosBackend { int requestLocationCount = 0; int stopRequestCount = 0; int? lastRequestAccuracy; + final List requestedAccuracies = []; /// What CoreLocation hands over as the stream starts, or null for nothing. List? fixOnStart; @@ -73,6 +74,7 @@ class _FakeBackend implements GeolocatorWatchosBackend { void requestLocation(int accuracyIndex) { requestLocationCount++; lastRequestAccuracy = accuracyIndex; + requestedAccuracies.add(accuracyIndex); final List? next = singleFix; if (next != null) { deliver(next); @@ -263,6 +265,72 @@ void main() { expect((await b).latitude, 12.0); expect(fake.stopRequestCount, 1, reason: 'stopped once both are done'); }); + + Future request(LocationAccuracy accuracy) => + geo.getCurrentPosition( + locationSettings: LocationSettings(accuracy: accuracy)); + + test( + 'pending calls run at the most accurate accuracy any of them asked for', + () async { + final List> pending = >[ + request(LocationAccuracy.low), + request(LocationAccuracy.best), + request(LocationAccuracy.medium), + ]; + await pumpEventQueue(); + expect(fake.requestedAccuracies, [ + LocationAccuracy.low.index, + LocationAccuracy.best.index, + LocationAccuracy.best.index, + ]); + fake.deliver(_fix()); + await Future.wait(pending); + + // With none pending, the next call runs at its own accuracy. + fake.singleFix = _fix(); + await request(LocationAccuracy.low); + expect(fake.requestedAccuracies.last, LocationAccuracy.low.index); + }); + + test('reduced is the least accurate, bestForNavigation the most', () async { + final List> pending = >[ + request(LocationAccuracy.reduced), + request(LocationAccuracy.lowest), + request(LocationAccuracy.bestForNavigation), + request(LocationAccuracy.best), + ]; + await pumpEventQueue(); + expect(fake.requestedAccuracies, [ + LocationAccuracy.reduced.index, + LocationAccuracy.lowest.index, + LocationAccuracy.bestForNavigation.index, + LocationAccuracy.bestForNavigation.index, + ]); + fake.deliver(_fix()); + await Future.wait(pending); + }); + + test('a call that times out leaves the others at the accuracy they share', + () async { + final Future best = request(LocationAccuracy.best); + final Future short = geo.getCurrentPosition( + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.low, + timeLimit: Duration(milliseconds: 10))); + await expectLater(short, throwsA(isA())); + expect(fake.stopRequestCount, 0, reason: 'one call is still pending'); + final Future medium = request(LocationAccuracy.medium); + await pumpEventQueue(); + expect(fake.requestedAccuracies, [ + LocationAccuracy.best.index, + LocationAccuracy.best.index, + LocationAccuracy.best.index, + ]); + fake.deliver(_fix()); + await Future.wait(>[best, medium]); + expect(fake.stopRequestCount, 1); + }); }); test('getLastKnownPosition returns null when there is no fix', () async { diff --git a/packages/geolocator_watchos/watchos/Classes/geolocator_watchos_ffi.h b/packages/geolocator_watchos/watchos/Classes/geolocator_watchos_ffi.h index 1edb44e..3335121 100644 --- a/packages/geolocator_watchos/watchos/Classes/geolocator_watchos_ffi.h +++ b/packages/geolocator_watchos/watchos/Classes/geolocator_watchos_ffi.h @@ -51,7 +51,8 @@ void geolocator_watchos_start_updates(int accuracy, double distance_filter, // Starts the single-request manager at `accuracy` (as above), with no // distance filter. It skips cached fixes more than 5 seconds old and stops -// itself after the first fix it accepts. +// itself after the first fix it accepts. With several requests pending, Dart +// passes the most accurate accuracy any of them asked for. GEOLOCATOR_WATCHOS_EXPORT void geolocator_watchos_request_location(int accuracy);