Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/geolocator_watchos/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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**
Expand Down
2 changes: 1 addition & 1 deletion packages/geolocator_watchos/PORTING_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion packages/geolocator_watchos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
29 changes: 27 additions & 2 deletions packages/geolocator_watchos/lib/geolocator_watchos.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<Position>? _positionStream;
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion packages/geolocator_watchos/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
68 changes: 68 additions & 0 deletions packages/geolocator_watchos/test/geolocator_watchos_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class _FakeBackend implements GeolocatorWatchosBackend {
int requestLocationCount = 0;
int stopRequestCount = 0;
int? lastRequestAccuracy;
final List<int> requestedAccuracies = <int>[];

/// What CoreLocation hands over as the stream starts, or null for nothing.
List<double>? fixOnStart;
Expand Down Expand Up @@ -73,6 +74,7 @@ class _FakeBackend implements GeolocatorWatchosBackend {
void requestLocation(int accuracyIndex) {
requestLocationCount++;
lastRequestAccuracy = accuracyIndex;
requestedAccuracies.add(accuracyIndex);
final List<double>? next = singleFix;
if (next != null) {
deliver(next);
Expand Down Expand Up @@ -263,6 +265,72 @@ void main() {
expect((await b).latitude, 12.0);
expect(fake.stopRequestCount, 1, reason: 'stopped once both are done');
});

Future<Position> 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<Future<Position>> pending = <Future<Position>>[
request(LocationAccuracy.low),
request(LocationAccuracy.best),
request(LocationAccuracy.medium),
];
await pumpEventQueue();
expect(fake.requestedAccuracies, <int>[
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<Future<Position>> pending = <Future<Position>>[
request(LocationAccuracy.reduced),
request(LocationAccuracy.lowest),
request(LocationAccuracy.bestForNavigation),
request(LocationAccuracy.best),
];
await pumpEventQueue();
expect(fake.requestedAccuracies, <int>[
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<Position> best = request(LocationAccuracy.best);
final Future<Position> short = geo.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.low,
timeLimit: Duration(milliseconds: 10)));
await expectLater(short, throwsA(isA<TimeoutException>()));
expect(fake.stopRequestCount, 0, reason: 'one call is still pending');
final Future<Position> medium = request(LocationAccuracy.medium);
await pumpEventQueue();
expect(fake.requestedAccuracies, <int>[
LocationAccuracy.best.index,
LocationAccuracy.best.index,
LocationAccuracy.best.index,
]);
fake.deliver(_fix());
await Future.wait(<Future<Position>>[best, medium]);
expect(fake.stopRequestCount, 1);
});
});

test('getLastKnownPosition returns null when there is no fix', () async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading