diff --git a/geolocator/CHANGELOG.md b/geolocator/CHANGELOG.md
index dfe7dcd9..f29aa007 100644
--- a/geolocator/CHANGELOG.md
+++ b/geolocator/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 13.1.0
+
+- Adds updatePositionStream API for changing parameters while a position stream is running.
+
## 13.0.2
- Updates dependency on geolocator_apple to version 2.3.8.
diff --git a/geolocator/example/android/app/src/main/AndroidManifest.xml b/geolocator/example/android/app/src/main/AndroidManifest.xml
index 829fcb8c..2f76ce97 100644
--- a/geolocator/example/android/app/src/main/AndroidManifest.xml
+++ b/geolocator/example/android/app/src/main/AndroidManifest.xml
@@ -8,6 +8,11 @@
+
+
+
+
+
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleExecutable
- App
- CFBundleIdentifier
- io.flutter.flutter.app
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- App
- CFBundlePackageType
- FMWK
- CFBundleShortVersionString
- 1.0
- CFBundleSignature
- ????
- CFBundleVersion
- 1.0
- MinimumOSVersion
- 11.0
-
-
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 12.0
+
+
\ No newline at end of file
diff --git a/geolocator/example/ios/Runner.xcodeproj/project.pbxproj b/geolocator/example/ios/Runner.xcodeproj/project.pbxproj
index 19138798..6fd1a7ba 100644
--- a/geolocator/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/geolocator/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -149,6 +149,7 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 2169DAB2B55B50BFEA564873 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -165,7 +166,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1020;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -206,12 +207,31 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 2169DAB2B55B50BFEA564873 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@@ -244,6 +264,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -332,7 +353,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -415,7 +436,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -464,7 +485,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/geolocator/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/geolocator/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index a28140cf..e67b2808 100644
--- a/geolocator/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/geolocator/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
UIViewControllerBasedStatusBarAppearance
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
diff --git a/geolocator/example/lib/main.dart b/geolocator/example/lib/main.dart
index a2633dcc..444288df 100644
--- a/geolocator/example/lib/main.dart
+++ b/geolocator/example/lib/main.dart
@@ -1,8 +1,9 @@
import 'dart:async';
import 'dart:io' show Platform;
-import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:flutter/material.dart';
+
+import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:geolocator/geolocator.dart';
/// Defines the main theme color.
@@ -39,9 +40,11 @@ class _GeolocatorWidgetState extends State {
final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance;
final List<_PositionItem> _positionItems = <_PositionItem>[];
+ final ScrollController _scrollController = ScrollController();
StreamSubscription? _positionStreamSubscription;
StreamSubscription? _serviceStatusStreamSubscription;
bool positionStreamStarted = false;
+ bool fastUpdates = false;
@override
void initState() {
@@ -120,6 +123,7 @@ class _GeolocatorWidgetState extends State {
(context) => Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
body: ListView.builder(
+ controller: _scrollController,
itemCount: _positionItems.length,
itemBuilder: (context, index) {
final positionItem = _positionItems[index];
@@ -167,6 +171,17 @@ class _GeolocatorWidgetState extends State {
: const Icon(Icons.pause),
),
sizedBox,
+ if (_positionStreamSubscription != null)
+ Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ FloatingActionButton(
+ onPressed: _toggleUpdateSpeed,
+ child: const Icon(Icons.speed),
+ ),
+ sizedBox,
+ ],
+ ),
FloatingActionButton(
onPressed: _getCurrentPosition,
child: const Icon(Icons.my_location),
@@ -252,9 +267,12 @@ class _GeolocatorWidgetState extends State {
return true;
}
- void _updatePositionList(_PositionItemType type, String displayValue) {
+ void _updatePositionList(_PositionItemType type, String displayValue) async {
_positionItems.add(_PositionItem(type, displayValue));
setState(() {});
+ await Future.delayed(const Duration(milliseconds: 50));
+ if (!mounted) return;
+ _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
bool _isListening() => !(_positionStreamSubscription == null ||
@@ -299,7 +317,19 @@ class _GeolocatorWidgetState extends State {
void _toggleListening() {
if (_positionStreamSubscription == null) {
- final positionStream = _geolocatorPlatform.getPositionStream();
+ final settings = AndroidSettings(
+ distanceFilter: 50,
+ foregroundNotificationConfig: const ForegroundNotificationConfig(
+ notificationTitle: 'Geolocator is tracking your trip',
+ notificationText:
+ 'This is a persistent notification and is used to start a foreground service.',
+ enableWifiLock: true,
+ enableWakeLock: true,
+ setOngoing: true,
+ ),
+ );
+ final positionStream =
+ _geolocatorPlatform.getPositionStream(locationSettings: settings);
_positionStreamSubscription = positionStream.handleError((error) {
_positionStreamSubscription?.cancel();
_positionStreamSubscription = null;
@@ -331,6 +361,41 @@ class _GeolocatorWidgetState extends State {
});
}
+ Future _toggleUpdateSpeed() async {
+ fastUpdates = !fastUpdates;
+
+ final LocationSettings settings;
+ if (Platform.isAndroid) {
+ settings = AndroidSettings(
+ accuracy: LocationAccuracy.best,
+ distanceFilter: 0,
+ intervalDuration: Duration(seconds: fastUpdates ? 1 : 10),
+ forceLocationManager: false,
+ useMSLAltitude: true,
+ );
+ } else if (Platform.isIOS) {
+ settings = AppleSettings(
+ accuracy: LocationAccuracy.best,
+ distanceFilter: fastUpdates ? 0 : 50,
+ activityType: ActivityType.fitness,
+ showBackgroundLocationIndicator: false,
+ allowBackgroundLocationUpdates: false,
+ );
+ } else {
+ settings = LocationSettings(
+ accuracy: LocationAccuracy.best,
+ distanceFilter: fastUpdates ? 0 : 50,
+ );
+ }
+
+ await _geolocatorPlatform.updatePositionStream(locationSettings: settings);
+
+ _updatePositionList(
+ _PositionItemType.log,
+ 'Position updates ${fastUpdates ? 'sped up' : 'slowed down'}.',
+ );
+ }
+
@override
void dispose() {
if (_positionStreamSubscription != null) {
diff --git a/geolocator/example/pubspec.yaml b/geolocator/example/pubspec.yaml
index b5fc7b89..b38dd79c 100644
--- a/geolocator/example/pubspec.yaml
+++ b/geolocator/example/pubspec.yaml
@@ -9,7 +9,7 @@ environment:
sdk: ">=2.15.0 <3.0.0"
dependencies:
- baseflow_plugin_template: ^2.1.2
+ baseflow_plugin_template: ^2.2.0
flutter:
sdk: flutter
@@ -24,16 +24,16 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
- cupertino_icons: ^1.0.1+1
+ cupertino_icons: ^1.0.8
# The following adds the URL Launcher plugin which is used by
# the demo application to open the links in the web browser.
- url_launcher: ^6.0.0-nullsafety.1
+ url_launcher: ^6.3.1
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ^3.0.1
+ flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
diff --git a/geolocator/lib/geolocator.dart b/geolocator/lib/geolocator.dart
index 008796a3..4710287d 100644
--- a/geolocator/lib/geolocator.dart
+++ b/geolocator/lib/geolocator.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
+
import 'package:geolocator_android/geolocator_android.dart';
import 'package:geolocator_apple/geolocator_apple.dart';
import 'package:geolocator_platform_interface/geolocator_platform_interface.dart';
@@ -192,6 +193,17 @@ class Geolocator {
locationSettings: locationSettings,
);
+ /// Updates the parameters of the active position stream.
+ ///
+ /// Throws a [NotSubscribedException] when there is no active position stream
+ /// to update.
+ static Future updatePositionStream({
+ required LocationSettings locationSettings,
+ }) =>
+ GeolocatorPlatform.instance.updatePositionStream(
+ locationSettings: locationSettings,
+ );
+
/// Returns a [Future] containing a [LocationAccuracyStatus]
/// When the user has given permission for approximate location,
/// [LocationAccuracyStatus.reduced] will be returned, if the user has
diff --git a/geolocator/pubspec.yaml b/geolocator/pubspec.yaml
index 12d08022..0fb7b4e0 100644
--- a/geolocator/pubspec.yaml
+++ b/geolocator/pubspec.yaml
@@ -2,7 +2,7 @@ name: geolocator
description: Geolocation plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API for generic location (GPS etc.) functions.
repository: https://github.com/baseflow/flutter-geolocator/tree/main/geolocator
issue_tracker: https://github.com/baseflow/flutter-geolocator/issues?q=is%3Aissue+is%3Aopen
-version: 13.0.2
+version: 13.1.0
environment:
sdk: ">=2.15.0 <4.0.0"
@@ -26,15 +26,20 @@ dependencies:
flutter:
sdk: flutter
- geolocator_platform_interface: ^4.2.3
- geolocator_android: ^4.6.0
- geolocator_apple: ^2.3.8
- geolocator_web: ^4.1.1
- geolocator_windows: ^0.2.3
+ geolocator_platform_interface:
+ path: ../geolocator_platform_interface
+ geolocator_android:
+ path: ../geolocator_android
+ geolocator_apple:
+ path: ../geolocator_apple
+ geolocator_web:
+ path: ../geolocator_web
+ geolocator_windows:
+ path: ../geolocator_windows
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ">=3.0.1 <5.0.0"
- mockito: ^5.0.0-nullsafety.7
+ flutter_lints: ">=5.0.0"
+ mockito: ^5.4.4
plugin_platform_interface: ^2.1.8
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/GeolocatorLocationService.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/GeolocatorLocationService.java
index 4c0e161c..05bb989f 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/GeolocatorLocationService.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/GeolocatorLocationService.java
@@ -1,5 +1,7 @@
package com.baseflow.geolocator;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
@@ -30,8 +32,8 @@ public class GeolocatorLocationService extends Service {
private static final String TAG = "FlutterGeolocator";
private static final int ONGOING_NOTIFICATION_ID = 75415;
private static final String CHANNEL_ID = "geolocator_channel_01";
- private final String WAKELOCK_TAG = "GeolocatorLocationService:Wakelock";
- private final String WIFILOCK_TAG = "GeolocatorLocationService:WifiLock";
+ private static final String WAKELOCK_TAG = "GeolocatorLocationService:Wakelock";
+ private static final String WIFILOCK_TAG = "GeolocatorLocationService:WifiLock";
private final LocalBinder binder = new LocalBinder(this);
// Service is foreground
private boolean isForeground = false;
@@ -144,13 +146,16 @@ public void enableBackgroundMode(ForegroundNotificationOptions options) {
this.getApplicationContext(), CHANNEL_ID, ONGOING_NOTIFICATION_ID, options);
backgroundNotification.updateChannel(options.getNotificationChannelName());
Notification notification = backgroundNotification.build();
- startForeground(ONGOING_NOTIFICATION_ID, notification);
- isForeground = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ startForeground(ONGOING_NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_LOCATION);
+ } else {
+ startForeground(ONGOING_NOTIFICATION_ID, notification);
+ }
+ isForeground = true;
}
obtainWakeLocks(options);
}
- @SuppressWarnings("deprecation")
public void disableBackgroundMode() {
if (isForeground) {
Log.d(TAG, "Stop service in foreground.");
@@ -222,7 +227,7 @@ private int getWifiLockType() {
return WifiManager.WIFI_MODE_FULL_LOW_LATENCY;
}
- class LocalBinder extends Binder {
+ static class LocalBinder extends Binder {
private final GeolocatorLocationService locationService;
LocalBinder(GeolocatorLocationService locationService) {
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/MethodCallHandlerImpl.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/MethodCallHandlerImpl.java
index 297e12ac..e4e36601 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/MethodCallHandlerImpl.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/MethodCallHandlerImpl.java
@@ -82,6 +82,9 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
case "cancelGetCurrentPosition":
onCancelGetCurrentPosition(call, result);
break;
+ case "updatePositionStream":
+ onUpdatePositionStream(call, result);
+ break;
case "openAppSettings":
boolean hasOpenedAppSettings = Utils.openAppSettings(this.context);
result.success(hasOpenedAppSettings);
@@ -279,4 +282,21 @@ private void onCancelGetCurrentPosition(MethodCall call, MethodChannel.Result re
result.success(null);
}
+
+ private void onUpdatePositionStream(MethodCall call, MethodChannel.Result result) {
+ @SuppressWarnings("unchecked")
+ Map arguments = (Map) call.arguments;
+ LocationOptions locationOptions = LocationOptions.parseArguments(arguments);
+
+ boolean success = geolocationManager.updateLocationOptions(locationOptions);
+
+ if (success) {
+ result.success(null);
+ } else {
+ result.error(
+ ErrorCodes.locationSubscriptionInactive.toString(),
+ ErrorCodes.locationSubscriptionInactive.toDescription(),
+ null);
+ }
+ }
}
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/errors/ErrorCodes.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/errors/ErrorCodes.java
index cea4141e..62114280 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/errors/ErrorCodes.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/errors/ErrorCodes.java
@@ -1,13 +1,18 @@
package com.baseflow.geolocator.errors;
+import androidx.annotation.NonNull;
+
public enum ErrorCodes {
activityMissing,
errorWhileAcquiringPosition,
locationServicesDisabled,
+ locationSubscriptionInactive,
permissionDefinitionsNotFound,
permissionDenied,
permissionRequestInProgress;
+ @NonNull
+ @Override
public String toString() {
switch (this) {
case activityMissing:
@@ -16,6 +21,8 @@ public String toString() {
return "ERROR_WHILE_ACQUIRING_POSITION";
case locationServicesDisabled:
return "LOCATION_SERVICES_DISABLED";
+ case locationSubscriptionInactive:
+ return "LOCATION_SUBSCRIPTION_INACTIVE";
case permissionDefinitionsNotFound:
return "PERMISSION_DEFINITIONS_NOT_FOUND";
case permissionDenied:
@@ -35,6 +42,8 @@ public String toDescription() {
return "An unexpected error occurred while trying to acquire the device's position.";
case locationServicesDisabled:
return "Location services are disabled. To receive location updates the location services should be enabled.";
+ case locationSubscriptionInactive:
+ return "No active location stream subscription found.";
case permissionDefinitionsNotFound:
return "No location permissions are defined in the manifest. Make sure at least ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION are defined in the manifest.";
case permissionDenied:
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/FusedLocationClient.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/FusedLocationClient.java
index a5e3f2b1..38d9cebe 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/FusedLocationClient.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/FusedLocationClient.java
@@ -40,10 +40,11 @@ class FusedLocationClient implements LocationClient {
private final FusedLocationProviderClient fusedLocationProviderClient;
private final NmeaClient nmeaClient;
private final int activityRequestCode;
- @Nullable private final LocationOptions locationOptions;
+ @Nullable private LocationOptions locationOptions;
@Nullable private ErrorCallback errorCallback;
@Nullable private PositionChangedCallback positionChangedCallback;
+ private boolean running = false;
public FusedLocationClient(@NonNull Context context, @Nullable LocationOptions locationOptions) {
this.context = context;
@@ -74,8 +75,9 @@ public synchronized void onLocationResult(@NonNull LocationResult locationResult
if (location.getExtras() == null) {
location.setExtras(Bundle.EMPTY);
}
- if (locationOptions != null) {
- location.getExtras().putBoolean(LocationOptions.USE_MSL_ALTITUDE_EXTRA, locationOptions.isUseMSLAltitude());
+ if (FusedLocationClient.this.locationOptions != null) {
+ location.getExtras().putBoolean(LocationOptions.USE_MSL_ALTITUDE_EXTRA,
+ FusedLocationClient.this.locationOptions.isUseMSLAltitude());
}
nmeaClient.enrichExtrasWithNmea(location);
@@ -157,6 +159,7 @@ private void requestPositionUpdates(LocationOptions locationOptions) {
this.nmeaClient.start();
fusedLocationProviderClient.requestLocationUpdates(
locationRequest, locationCallback, Looper.getMainLooper());
+ running = true;
}
@Override
@@ -275,8 +278,17 @@ public void startPositionUpdates(
});
}
+ @Override
+ public void updateLocationOptions(LocationOptions options) {
+ this.locationOptions = options;
+ if (running) {
+ requestPositionUpdates(options);
+ }
+ }
+
public void stopPositionUpdates() {
this.nmeaClient.stop();
fusedLocationProviderClient.removeLocationUpdates(locationCallback);
+ running = false;
}
}
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/GeolocationManager.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/GeolocationManager.java
index 76a4bb26..dd360dd9 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/GeolocationManager.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/GeolocationManager.java
@@ -3,6 +3,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -17,8 +18,9 @@
public class GeolocationManager
implements io.flutter.plugin.common.PluginRegistry.ActivityResultListener {
+ private static final String TAG = "GeolocationManager";
- private static GeolocationManager geolocationManagerInstance = null;
+ private static GeolocationManager geolocationManagerInstance = null;
private final List locationClients;
@@ -64,6 +66,16 @@ public void startPositionUpdates(
locationClient.startPositionUpdates(activity, positionChangedCallback, errorCallback);
}
+ public boolean updateLocationOptions(LocationOptions options) {
+ int numClientsUpdated = 0;
+ for (LocationClient client : locationClients) {
+ client.updateLocationOptions(options);
+ numClientsUpdated += 1;
+ }
+ Log.d(TAG, String.format("Updated LocationOptions for %d LocationClient(s).", numClientsUpdated));
+ return numClientsUpdated > 0;
+ }
+
public void stopPositionUpdates(@NonNull LocationClient locationClient) {
locationClients.remove(locationClient);
locationClient.stopPositionUpdates();
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationClient.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationClient.java
index 181f33ec..2854ecb6 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationClient.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationClient.java
@@ -19,6 +19,8 @@ void startPositionUpdates(
PositionChangedCallback positionChangedCallback,
ErrorCallback errorCallback);
+ void updateLocationOptions(LocationOptions options);
+
void stopPositionUpdates();
default boolean checkLocationService(Context context){
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationManagerClient.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationManagerClient.java
index c8b3e59a..a3ecbfac 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationManagerClient.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/LocationManagerClient.java
@@ -9,6 +9,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -22,14 +23,15 @@
import java.util.List;
class LocationManagerClient implements LocationClient, LocationListenerCompat {
-
+ private static final String TAG = "LocationManagerClient";
private static final long TWO_MINUTES = 120000;
+
private final LocationManager locationManager;
private final NmeaClient nmeaClient;
- @Nullable private final LocationOptions locationOptions;
public Context context;
private boolean isListening = false;
+ @Nullable private LocationOptions locationOptions;
@Nullable private Location currentBestLocation;
@Nullable private String currentLocationProvider;
@Nullable private PositionChangedCallback positionChangedCallback;
@@ -143,57 +145,77 @@ public boolean onActivityResult(int requestCode, int resultCode) {
return false;
}
- @SuppressLint("MissingPermission")
@Override
public void startPositionUpdates(
Activity activity,
PositionChangedCallback positionChangedCallback,
ErrorCallback errorCallback) {
+ this.positionChangedCallback = positionChangedCallback;
+ this.errorCallback = errorCallback;
+
+ if (!checkLocationService(context)) {
+ errorCallback.onError(ErrorCodes.locationServicesDisabled);
+ return;
+ }
+
+ LocationAccuracy accuracy = LocationAccuracy.best;
+ if (this.locationOptions != null) {
+ accuracy = locationOptions.getAccuracy();
+ }
+
+ this.currentLocationProvider = determineProvider(this.locationManager, accuracy);
- if (!checkLocationService(context)) {
- errorCallback.onError(ErrorCodes.locationServicesDisabled);
+ if (this.currentLocationProvider == null) {
+ errorCallback.onError(ErrorCodes.locationServicesDisabled);
+ return;
+ }
+
+ requestPositionUpdates();
+ }
+
+ @SuppressLint("MissingPermission")
+ private void requestPositionUpdates() {
+ if (this.currentLocationProvider == null) {
+ Log.e(TAG, "Location provider was null.");
return;
}
- this.positionChangedCallback = positionChangedCallback;
- this.errorCallback = errorCallback;
-
- LocationAccuracy accuracy = LocationAccuracy.best;
long timeInterval = 0;
float distanceFilter = 0;
@LocationRequestCompat.Quality int quality = LocationRequestCompat.QUALITY_BALANCED_POWER_ACCURACY;
if (this.locationOptions != null) {
distanceFilter = locationOptions.getDistanceFilter();
- accuracy = locationOptions.getAccuracy();
+ LocationAccuracy accuracy = locationOptions.getAccuracy();
timeInterval = accuracy == LocationAccuracy.lowest
? LocationRequestCompat.PASSIVE_INTERVAL
: locationOptions.getTimeInterval();
quality = accuracyToQuality(accuracy);
}
- this.currentLocationProvider = determineProvider(this.locationManager, accuracy);
-
- if (this.currentLocationProvider == null) {
- errorCallback.onError(ErrorCodes.locationServicesDisabled);
- return;
- }
-
final LocationRequestCompat locationRequest = new LocationRequestCompat.Builder(timeInterval)
.setMinUpdateDistanceMeters(distanceFilter)
.setMinUpdateIntervalMillis(timeInterval)
.setQuality(quality)
.build();
- this.isListening = true;
this.nmeaClient.start();
-
LocationManagerCompat.requestLocationUpdates(
this.locationManager,
this.currentLocationProvider,
locationRequest,
this,
Looper.getMainLooper());
+
+ this.isListening = true;
+ }
+
+ @Override
+ public void updateLocationOptions(LocationOptions options) {
+ this.locationOptions = options;
+ if (isListening) {
+ requestPositionUpdates();
+ }
}
@SuppressLint("MissingPermission")
@@ -205,7 +227,7 @@ public void stopPositionUpdates() {
}
@Override
- public synchronized void onLocationChanged(Location location) {
+ public synchronized void onLocationChanged(@NonNull Location location) {
if (isBetterLocation(location, currentBestLocation)) {
this.currentBestLocation = location;
@@ -233,9 +255,6 @@ public void onStatusChanged(@NonNull String provider, int status, Bundle extras)
}
}
- @Override
- public void onProviderEnabled(@NonNull String provider) {}
-
@SuppressLint("MissingPermission")
@Override
public void onProviderDisabled(String provider) {
diff --git a/geolocator_android/example/android/app/build.gradle b/geolocator_android/example/android/app/build.gradle
index 7eeb09b5..49aa3480 100644
--- a/geolocator_android/example/android/app/build.gradle
+++ b/geolocator_android/example/android/app/build.gradle
@@ -1,5 +1,6 @@
plugins {
id "com.android.application"
+ id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
@@ -43,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.baseflow.geolocator_example"
- minSdkVersion flutter.minSdkVersion
+ minSdkVersion 24
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
diff --git a/geolocator_android/example/android/app/src/main/AndroidManifest.xml b/geolocator_android/example/android/app/src/main/AndroidManifest.xml
index 7870abc0..4221badd 100644
--- a/geolocator_android/example/android/app/src/main/AndroidManifest.xml
+++ b/geolocator_android/example/android/app/src/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
+
_GeolocatorWidgetState();
+ GeolocatorWidgetState createState() => GeolocatorWidgetState();
}
-class _GeolocatorWidgetState extends State {
+/// State for the [GeolocatorWidget].
+class GeolocatorWidgetState extends State {
static const String _kLocationServicesDisabledMessage =
'Location services are disabled.';
static const String _kPermissionDeniedMessage = 'Permission denied.';
@@ -38,8 +40,9 @@ class _GeolocatorWidgetState extends State {
'Permission denied forever.';
static const String _kPermissionGrantedMessage = 'Permission granted.';
- final GeolocatorPlatform geolocatorAndroid = GeolocatorPlatform.instance;
+ final GeolocatorPlatform _geolocatorAndroid = GeolocatorPlatform.instance;
final List<_PositionItem> _positionItems = <_PositionItem>[];
+ final ScrollController _scrollController = ScrollController();
StreamSubscription? _positionStreamSubscription;
StreamSubscription? _serviceStatusStreamSubscription;
@@ -75,26 +78,26 @@ class _GeolocatorWidgetState extends State {
},
itemBuilder: (context) => [
const PopupMenuItem(
- child: Text("Get Location Accuracy"),
value: 1,
+ child: Text("Get Location Accuracy"),
),
if (Platform.isIOS)
const PopupMenuItem(
- child: Text("Request Temporary Full Accuracy"),
value: 2,
+ child: Text("Request Temporary Full Accuracy"),
),
const PopupMenuItem(
- child: Text("Open App Settings"),
value: 3,
+ child: Text("Open App Settings"),
),
if (Platform.isAndroid)
const PopupMenuItem(
- child: Text("Open Location Settings"),
value: 4,
+ child: Text("Open Location Settings"),
),
const PopupMenuItem(
- child: Text("Clear"),
value: 5,
+ child: Text("Clear"),
),
],
);
@@ -119,6 +122,7 @@ class _GeolocatorWidgetState extends State {
(context) => Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
body: ListView.builder(
+ controller: _scrollController,
itemCount: _positionItems.length,
itemBuilder: (context, index) {
final positionItem = _positionItems[index];
@@ -150,10 +154,6 @@ class _GeolocatorWidgetState extends State {
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
- child: (_positionStreamSubscription == null ||
- _positionStreamSubscription!.isPaused)
- ? const Icon(Icons.play_arrow)
- : const Icon(Icons.pause),
onPressed: _toggleListening,
tooltip: (_positionStreamSubscription == null)
? 'Start position updates'
@@ -161,16 +161,31 @@ class _GeolocatorWidgetState extends State {
? 'Resume'
: 'Pause',
backgroundColor: _determineButtonColor(),
+ child: (_positionStreamSubscription == null ||
+ _positionStreamSubscription!.isPaused)
+ ? const Icon(Icons.play_arrow)
+ : const Icon(Icons.pause),
),
sizedBox,
+ if (_positionStreamSubscription != null)
+ Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ FloatingActionButton(
+ onPressed: _speedUpTracking,
+ child: const Icon(Icons.speed),
+ ),
+ sizedBox,
+ ],
+ ),
FloatingActionButton(
- child: const Icon(Icons.my_location),
onPressed: _getCurrentPosition,
+ child: const Icon(Icons.my_location),
),
sizedBox,
FloatingActionButton(
- child: const Icon(Icons.bookmark),
onPressed: _getLastKnownPosition,
+ child: const Icon(Icons.bookmark),
),
],
),
@@ -186,7 +201,7 @@ class _GeolocatorWidgetState extends State {
return;
}
- final position = await geolocatorAndroid.getCurrentPosition();
+ final position = await _geolocatorAndroid.getCurrentPosition();
_updatePositionList(
_PositionItemType.position,
position.toString(),
@@ -198,7 +213,7 @@ class _GeolocatorWidgetState extends State {
LocationPermission permission;
// Test if location services are enabled.
- serviceEnabled = await geolocatorAndroid.isLocationServiceEnabled();
+ serviceEnabled = await _geolocatorAndroid.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
@@ -211,9 +226,9 @@ class _GeolocatorWidgetState extends State {
return false;
}
- permission = await geolocatorAndroid.checkPermission();
+ permission = await _geolocatorAndroid.checkPermission();
if (permission == LocationPermission.denied) {
- permission = await geolocatorAndroid.requestPermission();
+ permission = await _geolocatorAndroid.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
@@ -248,9 +263,12 @@ class _GeolocatorWidgetState extends State {
return true;
}
- void _updatePositionList(_PositionItemType type, String displayValue) {
+ void _updatePositionList(_PositionItemType type, String displayValue) async {
_positionItems.add(_PositionItem(type, displayValue));
setState(() {});
+ await Future.delayed(const Duration(milliseconds: 100));
+ if (!mounted) return;
+ _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
bool _isListening() => !(_positionStreamSubscription == null ||
@@ -262,7 +280,7 @@ class _GeolocatorWidgetState extends State {
void _toggleServiceStatusStream() {
if (_serviceStatusStreamSubscription == null) {
- final serviceStatusStream = geolocatorAndroid.getServiceStatusStream();
+ final serviceStatusStream = _geolocatorAndroid.getServiceStatusStream();
_serviceStatusStreamSubscription =
serviceStatusStream.handleError((error) {
_serviceStatusStreamSubscription?.cancel();
@@ -292,8 +310,8 @@ class _GeolocatorWidgetState extends State {
if (_positionStreamSubscription == null) {
final androidSettings = AndroidSettings(
accuracy: LocationAccuracy.best,
- distanceFilter: 10,
- intervalDuration: const Duration(seconds: 1),
+ distanceFilter: 0,
+ intervalDuration: const Duration(seconds: 10),
forceLocationManager: false,
useMSLAltitude: true,
foregroundNotificationConfig: const ForegroundNotificationConfig(
@@ -308,7 +326,7 @@ class _GeolocatorWidgetState extends State {
color: Colors.amber,
),
);
- final positionStream = geolocatorAndroid.getPositionStream(
+ final positionStream = _geolocatorAndroid.getPositionStream(
locationSettings: androidSettings);
_positionStreamSubscription = positionStream.handleError((error) {
_positionStreamSubscription?.cancel();
@@ -344,6 +362,21 @@ class _GeolocatorWidgetState extends State {
});
}
+ Future _speedUpTracking() async {
+ await _geolocatorAndroid.updatePositionStream(
+ locationSettings: AndroidSettings(
+ accuracy: LocationAccuracy.best,
+ distanceFilter: 0,
+ intervalDuration: const Duration(seconds: 1),
+ forceLocationManager: false,
+ useMSLAltitude: true,
+ ));
+ _updatePositionList(
+ _PositionItemType.log,
+ 'Position updates interval set to 1 second.',
+ );
+ }
+
@override
void dispose() {
if (_positionStreamSubscription != null) {
@@ -355,7 +388,7 @@ class _GeolocatorWidgetState extends State {
}
void _getLastKnownPosition() async {
- final position = await geolocatorAndroid.getLastKnownPosition();
+ final position = await _geolocatorAndroid.getLastKnownPosition();
if (position != null) {
_updatePositionList(
_PositionItemType.position,
@@ -370,12 +403,12 @@ class _GeolocatorWidgetState extends State {
}
void _getLocationAccuracy() async {
- final status = await geolocatorAndroid.getLocationAccuracy();
+ final status = await _geolocatorAndroid.getLocationAccuracy();
_handleLocationAccuracyStatus(status);
}
void _requestTemporaryFullAccuracy() async {
- final status = await geolocatorAndroid.requestTemporaryFullAccuracy(
+ final status = await _geolocatorAndroid.requestTemporaryFullAccuracy(
purposeKey: "TemporaryPreciseAccuracy",
);
_handleLocationAccuracyStatus(status);
@@ -397,7 +430,7 @@ class _GeolocatorWidgetState extends State {
}
void _openAppSettings() async {
- final opened = await geolocatorAndroid.openAppSettings();
+ final opened = await _geolocatorAndroid.openAppSettings();
String displayValue;
if (opened) {
@@ -413,7 +446,7 @@ class _GeolocatorWidgetState extends State {
}
void _openLocationSettings() async {
- final opened = await geolocatorAndroid.openLocationSettings();
+ final opened = await _geolocatorAndroid.openLocationSettings();
String displayValue;
if (opened) {
diff --git a/geolocator_android/example/pubspec.yaml b/geolocator_android/example/pubspec.yaml
index 22febac1..2410da1e 100644
--- a/geolocator_android/example/pubspec.yaml
+++ b/geolocator_android/example/pubspec.yaml
@@ -9,7 +9,7 @@ environment:
sdk: ">=2.15.0 <3.0.0"
dependencies:
- baseflow_plugin_template: ^2.1.2
+ baseflow_plugin_template: ^2.2.0
flutter:
sdk: flutter
@@ -21,18 +21,21 @@ dependencies:
# the parent directory to use the current plugin's version.
path: ../
+ geolocator_platform_interface:
+ path: ../../geolocator_platform_interface/
+
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
- cupertino_icons: ^1.0.1+1
+ cupertino_icons: ^1.0.8
# The following adds the URL Launcher plugin which is used by
# the demo application to open the links in the web browser.
- url_launcher: ^6.0.0-nullsafety.1
+ url_launcher: ^6.3.1
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ^1.0.4
+ flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
diff --git a/geolocator_android/lib/src/geolocator_android.dart b/geolocator_android/lib/src/geolocator_android.dart
index 04c1ced8..081a8419 100644
--- a/geolocator_android/lib/src/geolocator_android.dart
+++ b/geolocator_android/lib/src/geolocator_android.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/services.dart';
+
import 'package:geolocator_android/geolocator_android.dart';
import 'package:geolocator_platform_interface/geolocator_platform_interface.dart';
import 'package:uuid/uuid.dart';
@@ -211,6 +212,22 @@ class GeolocatorAndroid extends GeolocatorPlatform {
});
}
+ @override
+ Future updatePositionStream(
+ {required LocationSettings locationSettings}) async {
+ if (_positionStream == null) {
+ throw const NotSubscribedException();
+ }
+ try {
+ await _methodChannel.invokeMethod(
+ 'updatePositionStream', locationSettings.toJson());
+ } on PlatformException catch (e) {
+ final error = _handlePlatformException(e);
+
+ throw error;
+ }
+ }
+
@override
Future requestTemporaryFullAccuracy({
required String purposeKey,
@@ -247,6 +264,8 @@ class GeolocatorAndroid extends GeolocatorPlatform {
return const LocationServiceDisabledException();
case 'LOCATION_SUBSCRIPTION_ACTIVE':
return const AlreadySubscribedException();
+ case 'LOCATION_SUBSCRIPTION_INACTIVE':
+ return const NotSubscribedException();
case 'PERMISSION_DEFINITIONS_NOT_FOUND':
return PermissionDefinitionsNotFoundException(exception.message);
case 'PERMISSION_DENIED':
diff --git a/geolocator_android/pubspec.yaml b/geolocator_android/pubspec.yaml
index 8841c116..319e7f6d 100644
--- a/geolocator_android/pubspec.yaml
+++ b/geolocator_android/pubspec.yaml
@@ -2,7 +2,7 @@ name: geolocator_android
description: Geolocation plugin for Flutter. This plugin provides the Android implementation for the geolocator.
repository: https://github.com/baseflow/flutter-geolocator/tree/main/geolocator_android
issue_tracker: https://github.com/baseflow/flutter-geolocator/issues?q=is%3Aissue+is%3Aopen
-version: 4.6.1
+version: 4.7.0
environment:
sdk: ">=2.15.0 <4.0.0"
@@ -20,14 +20,15 @@ flutter:
dependencies:
flutter:
sdk: flutter
- geolocator_platform_interface: ^4.1.0
- meta: ^1.10.0
- uuid: ">=4.0.0 <6.0.0"
+ geolocator_platform_interface:
+ path: ../geolocator_platform_interface
+ meta: ^1.15.0
+ uuid: ">=4.5.1"
dev_dependencies:
- async: ^2.8.2
+ async: ^2.11.0
flutter_test:
sdk: flutter
- flutter_lints: ">=3.0.0 <5.0.0"
- mockito: ^5.2.0
- plugin_platform_interface: ^2.1.2
+ flutter_lints: ">=5.0.0"
+ mockito: ^5.4.4
+ plugin_platform_interface: ^2.1.8
diff --git a/geolocator_apple/example/ios/Podfile b/geolocator_apple/example/ios/Podfile
index c654fd27..2bd06077 100644
--- a/geolocator_apple/example/ios/Podfile
+++ b/geolocator_apple/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '12.0'
+platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/geolocator_apple/example/ios/Runner.xcodeproj/project.pbxproj b/geolocator_apple/example/ios/Runner.xcodeproj/project.pbxproj
index 33880a1c..13585198 100644
--- a/geolocator_apple/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/geolocator_apple/example/ios/Runner.xcodeproj/project.pbxproj
@@ -192,7 +192,7 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 591FD69E94FC610DB997784E /* [CP] Copy Pods Resources */,
+ 7987087EF4670CEDBC08B555 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -358,6 +358,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
+ 7987087EF4670CEDBC08B555 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
diff --git a/geolocator_apple/example/lib/main.dart b/geolocator_apple/example/lib/main.dart
index 442e0b2c..5777666b 100644
--- a/geolocator_apple/example/lib/main.dart
+++ b/geolocator_apple/example/lib/main.dart
@@ -1,8 +1,9 @@
import 'dart:async';
import 'dart:io' show Platform;
-import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:flutter/material.dart';
+
+import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:geolocator_apple/geolocator_apple.dart';
import 'package:geolocator_platform_interface/geolocator_platform_interface.dart';
@@ -40,6 +41,7 @@ class _GeolocatorWidgetState extends State {
final GeolocatorPlatform geolocatorApple = GeolocatorPlatform.instance;
final List<_PositionItem> _positionItems = <_PositionItem>[];
+ final ScrollController _scrollController = ScrollController();
StreamSubscription? _positionStreamSubscription;
StreamSubscription? _serviceStatusStreamSubscription;
@@ -121,6 +123,7 @@ class _GeolocatorWidgetState extends State {
backgroundColor: Theme.of(context).colorScheme.surface,
body: ListView.builder(
itemCount: _positionItems.length,
+ controller: _scrollController,
itemBuilder: (context, index) {
final positionItem = _positionItems[index];
@@ -164,6 +167,17 @@ class _GeolocatorWidgetState extends State {
: const Icon(Icons.pause),
),
sizedBox,
+ if (_positionStreamSubscription != null)
+ Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ FloatingActionButton(
+ onPressed: _speedUpTracking,
+ child: const Icon(Icons.speed),
+ ),
+ sizedBox,
+ ],
+ ),
FloatingActionButton(
onPressed: _getCurrentPosition,
child: const Icon(Icons.my_location),
@@ -254,9 +268,12 @@ class _GeolocatorWidgetState extends State {
return true;
}
- void _updatePositionList(_PositionItemType type, String displayValue) {
+ void _updatePositionList(_PositionItemType type, String displayValue) async {
_positionItems.add(_PositionItem(type, displayValue));
setState(() {});
+ await Future.delayed(const Duration(milliseconds: 100));
+ if (!mounted) return;
+ _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
bool _isListening() => !(_positionStreamSubscription == null ||
@@ -301,7 +318,7 @@ class _GeolocatorWidgetState extends State {
final Stream positionStream = geolocatorApple.getPositionStream(
locationSettings: AppleSettings(
accuracy: LocationAccuracy.best,
- distanceFilter: 10,
+ distanceFilter: 100,
activityType: ActivityType.other,
// Only set showBackgroundLocationIndicator and
// allowBackgroundLocationUpdates to true if our app will be started up
@@ -346,6 +363,21 @@ class _GeolocatorWidgetState extends State {
});
}
+ Future _speedUpTracking() async {
+ await geolocatorApple.updatePositionStream(
+ locationSettings: AppleSettings(
+ accuracy: LocationAccuracy.best,
+ distanceFilter: 0,
+ activityType: ActivityType.fitness,
+ showBackgroundLocationIndicator: false,
+ allowBackgroundLocationUpdates: false,
+ ));
+ _updatePositionList(
+ _PositionItemType.log,
+ 'Position updates distance filter has been set to zero.',
+ );
+ }
+
@override
void dispose() {
if (_positionStreamSubscription != null) {
diff --git a/geolocator_apple/example/pubspec.yaml b/geolocator_apple/example/pubspec.yaml
index bdaed10f..db58c668 100644
--- a/geolocator_apple/example/pubspec.yaml
+++ b/geolocator_apple/example/pubspec.yaml
@@ -23,7 +23,8 @@ dependencies:
# the parent directory to use the current plugin's version.
path: ../
- geolocator_platform_interface: ^4.2.0
+ geolocator_platform_interface:
+ path: ../../geolocator_platform_interface
flutter:
sdk: flutter
diff --git a/geolocator_apple/ios/Classes/Constants/ErrorCodes.h b/geolocator_apple/ios/Classes/Constants/ErrorCodes.h
index fa87ca14..b852b86e 100644
--- a/geolocator_apple/ios/Classes/Constants/ErrorCodes.h
+++ b/geolocator_apple/ios/Classes/Constants/ErrorCodes.h
@@ -8,6 +8,7 @@
FOUNDATION_EXPORT NSString * const GeolocatorErrorLocationUpdateFailure;
FOUNDATION_EXPORT NSString * const GeolocatorErrorLocationServicesDisabled;
FOUNDATION_EXPORT NSString * const GeolocatorErrorLocationSubscriptionActive;
+FOUNDATION_EXPORT NSString * const GeolocatorErrorLocationSubscriptionInactive;
FOUNDATION_EXPORT NSString * const GeolocatorErrorPermissionDefinitionsNotFound;
FOUNDATION_EXPORT NSString * const GeolocatorErrorPermissionDenied;
FOUNDATION_EXPORT NSString * const GeolocatorErrorPermissionRequestInProgress;
diff --git a/geolocator_apple/ios/Classes/Constants/ErrorCodes.m b/geolocator_apple/ios/Classes/Constants/ErrorCodes.m
index b1ffe7a0..9e9e970d 100644
--- a/geolocator_apple/ios/Classes/Constants/ErrorCodes.m
+++ b/geolocator_apple/ios/Classes/Constants/ErrorCodes.m
@@ -10,6 +10,7 @@
NSString * const GeolocatorErrorLocationUpdateFailure = @"LOCATION_UPDATE_FAILURE";
NSString * const GeolocatorErrorLocationServicesDisabled = @"LOCATION_SERVICES_DISABLED";
NSString * const GeolocatorErrorLocationSubscriptionActive = @"LOCATION_SUBSCRIPTION_ACTIVE";
+NSString * const GeolocatorErrorLocationSubscriptionInactive = @"LOCATION_SUBSCRIPTION_INACTIVE";
NSString * const GeolocatorErrorPermissionDefinitionsNotFound = @"PERMISSION_DEFINITIONS_NOT_FOUND";
NSString * const GeolocatorErrorPermissionDenied = @"PERMISSION_DENIED";
NSString * const GeolocatorErrorPermissionRequestInProgress = @"PERMISSION_REQUEST_IN_PROGRESS";
diff --git a/geolocator_apple/ios/Classes/GeolocatorPlugin.m b/geolocator_apple/ios/Classes/GeolocatorPlugin.m
index 0e3687a9..b8a304f0 100644
--- a/geolocator_apple/ios/Classes/GeolocatorPlugin.m
+++ b/geolocator_apple/ios/Classes/GeolocatorPlugin.m
@@ -98,6 +98,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString* purposeKey = (NSString *)call.arguments[@"purposeKey"];
[[self createLocationAccuracyHandler] requestTemporaryFullAccuracyWithResult:result
purposeKey:purposeKey];
+ } else if ([@"updatePositionStream" isEqualToString:call.method]) {
+ [self onUpdatePositionStreamWithArguments:call.arguments result:result];
} else if ([@"openAppSettings" isEqualToString:call.method]) {
[self openSettings:result];
} else if ([@"openLocationSettings" isEqualToString:call.method]) {
@@ -168,6 +170,27 @@ - (void)onGetCurrentPositionWithArguments:(id _Nullable)arguments
}];
}
+- (void)onUpdatePositionStreamWithArguments:(id _Nullable)arguments
+ result:(FlutterResult)result {
+ if (![self.createGeolocationHandler listeningForPositionUpdates]) {
+ result([FlutterError errorWithCode: GeolocatorErrorLocationSubscriptionInactive
+ message:@"There is no active location stream to update."
+ details:nil]);
+ return;
+ }
+
+ CLLocationAccuracy accuracy = [LocationAccuracyMapper toCLLocationAccuracy:(NSNumber *)arguments[@"accuracy"]];
+ CLLocationDistance distanceFilter = [LocationDistanceMapper toCLLocationDistance:(NSNumber *)arguments[@"distanceFilter"]];
+ NSNumber* pauseLocationUpdatesAutomatically = arguments[@"pauseLocationUpdatesAutomatically"];
+ CLActivityType activityType = [ActivityTypeMapper toCLActivityType:(NSNumber *)arguments[@"activityType"]];
+ NSNumber* allowBackgroundLocationUpdates = arguments[@"allowBackgroundLocationUpdates"];
+ NSNumber* showBackgroundLocationIndicator = arguments[@"showBackgroundLocationIndicator"];
+
+ [self.createGeolocationHandler updateListenerWithDesiredAccuracy:accuracy distanceFilter:distanceFilter pauseLocationUpdatesAutomatically:pauseLocationUpdatesAutomatically && [pauseLocationUpdatesAutomatically boolValue] showBackgroundLocationIndicator:showBackgroundLocationIndicator && [showBackgroundLocationIndicator boolValue] activityType:activityType allowBackgroundLocationUpdates:[allowBackgroundLocationUpdates boolValue]];
+
+ result(nil);
+}
+
- (void)openSettings:(FlutterResult)result {
#if TARGET_OS_OSX
diff --git a/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.h b/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.h
index 43801fa5..0b91c64d 100644
--- a/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.h
+++ b/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.h
@@ -32,6 +32,16 @@ typedef void (^GeolocatorResult)(CLLocation *_Nullable location);
errorHandler:(GeolocatorError _Nonnull)errorHandler;
- (void)stopListening;
+
+- (BOOL)listeningForPositionUpdates;
+
+- (void)updateListenerWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy
+ distanceFilter:(CLLocationDistance)distanceFilter
+ pauseLocationUpdatesAutomatically:(BOOL)pauseLocationUpdatesAutomatically
+ showBackgroundLocationIndicator:(BOOL)showBackgroundLocationIndicator
+ activityType:(CLActivityType)activityType
+ allowBackgroundLocationUpdates:(BOOL)allowBackgroundLocationUpdates;
+
+ (BOOL) shouldEnableBackgroundLocationUpdates;
@end
diff --git a/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.m b/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.m
index e3808f18..de6e4015 100644
--- a/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.m
+++ b/geolocator_apple/ios/Classes/Handlers/GeolocationHandler.m
@@ -13,6 +13,8 @@
@interface GeolocationHandler()
+@property(assign, nonatomic) bool isListeningForPositionUpdates;
+
@property(strong, nonatomic, nonnull) CLLocationManager *locationManager;
@property(strong, nonatomic) GeolocatorError errorHandler;
@@ -32,6 +34,8 @@ - (id) init {
if (!self) {
return nil;
}
+
+ self.isListeningForPositionUpdates = NO;
return self;
}
@@ -107,6 +111,8 @@ - (void)startListeningWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy
isListeningForPositionUpdates:YES
showBackgroundLocationIndicator:showBackgroundLocationIndicator
allowBackgroundLocationUpdates:allowBackgroundLocationUpdates];
+
+ self.isListeningForPositionUpdates = YES;
}
- (void)startUpdatingLocationWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy
@@ -148,11 +154,42 @@ - (void)stopOneTimeLocationListening {
}
- (void)stopListening {
+ self.isListeningForPositionUpdates = NO;
[[self getLocationManager] stopUpdatingLocation];
self.errorHandler = nil;
self.listenerResultHandler = nil;
}
+- (BOOL)listeningForPositionUpdates {
+ return self.isListeningForPositionUpdates;
+}
+
+- (void)updateListenerWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy
+ distanceFilter:(CLLocationDistance)distanceFilter
+ pauseLocationUpdatesAutomatically:(BOOL)pauseLocationUpdatesAutomatically
+ showBackgroundLocationIndicator:(BOOL)showBackgroundLocationIndicator
+ activityType:(CLActivityType)activityType
+ allowBackgroundLocationUpdates:(BOOL)allowBackgroundLocationUpdates
+{
+ CLLocationManager *locationManager = [self getLocationManager];
+ locationManager.desiredAccuracy = desiredAccuracy;
+ locationManager.distanceFilter = distanceFilter;
+ if (@available(iOS 6.0, macOS 10.15, *)) {
+ locationManager.activityType = activityType;
+ locationManager.pausesLocationUpdatesAutomatically = pauseLocationUpdatesAutomatically;
+ }
+
+#if TARGET_OS_IOS
+ if (@available(iOS 9.0, macOS 11.0, *)) {
+ locationManager.allowsBackgroundLocationUpdates = allowBackgroundLocationUpdates
+ && [GeolocationHandler shouldEnableBackgroundLocationUpdates];
+ }
+ if (@available(iOS 11.0, macOS 11.0, *)) {
+ locationManager.showsBackgroundLocationIndicator = showBackgroundLocationIndicator;
+ }
+#endif
+}
+
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
if (!self.listenerResultHandler && !self.currentLocationResultHandler) return;
diff --git a/geolocator_apple/lib/src/geolocator_apple.dart b/geolocator_apple/lib/src/geolocator_apple.dart
index 8475bdd2..13920432 100644
--- a/geolocator_apple/lib/src/geolocator_apple.dart
+++ b/geolocator_apple/lib/src/geolocator_apple.dart
@@ -198,6 +198,22 @@ class GeolocatorApple extends GeolocatorPlatform {
});
}
+ @override
+ Future updatePositionStream(
+ {required LocationSettings locationSettings}) async {
+ if (_positionStream == null) {
+ throw const NotSubscribedException();
+ }
+ try {
+ await _methodChannel.invokeMethod(
+ 'updatePositionStream', locationSettings.toJson());
+ } on PlatformException catch (e) {
+ final error = _handlePlatformException(e);
+
+ throw error;
+ }
+ }
+
@override
Future requestTemporaryFullAccuracy({
required String purposeKey,
@@ -234,6 +250,8 @@ class GeolocatorApple extends GeolocatorPlatform {
return const LocationServiceDisabledException();
case 'LOCATION_SUBSCRIPTION_ACTIVE':
return const AlreadySubscribedException();
+ case 'LOCATION_SUBSCRIPTION_INACTIVE':
+ return const NotSubscribedException();
case 'PERMISSION_DEFINITIONS_NOT_FOUND':
return PermissionDefinitionsNotFoundException(exception.message);
case 'PERMISSION_DENIED':
diff --git a/geolocator_apple/pubspec.yaml b/geolocator_apple/pubspec.yaml
index 270b13a3..185ba701 100644
--- a/geolocator_apple/pubspec.yaml
+++ b/geolocator_apple/pubspec.yaml
@@ -2,7 +2,7 @@ name: geolocator_apple
description: Geolocation Apple plugin for Flutter. This plugin provides the Apple implementation for the geolocator.
repository: https://github.com/baseflow/flutter-geolocator/tree/main/geolocator_apple
issue_tracker: https://github.com/baseflow/flutter-geolocator/issues?q=is%3Aissue+is%3Aopen
-version: 2.3.8+1
+version: 2.4.0
environment:
sdk: ">=2.15.0 <4.0.0"
@@ -22,12 +22,13 @@ flutter:
dependencies:
flutter:
sdk: flutter
- geolocator_platform_interface: ^4.1.0
+ geolocator_platform_interface:
+ path: ../geolocator_platform_interface
dev_dependencies:
- async: ^2.8.2
+ async: ^2.11.0
flutter_test:
sdk: flutter
- flutter_lints: ">=3.0.1 <5.0.0"
- mockito: ^5.2.0
- plugin_platform_interface: ^2.1.2
+ flutter_lints: ">=5.0.0"
+ mockito: ^5.4.4
+ plugin_platform_interface: ^2.1.8
diff --git a/geolocator_linux/example/pubspec.yaml b/geolocator_linux/example/pubspec.yaml
index bb0d1c19..52c4ae40 100644
--- a/geolocator_linux/example/pubspec.yaml
+++ b/geolocator_linux/example/pubspec.yaml
@@ -28,7 +28,8 @@ dependencies:
# The following adds the URL Launcher plugin which is used by
# the demo application to open the links in the web browser.
url_launcher: ^6.0.18
- geolocator_platform_interface: ^4.2.0
+ geolocator_platform_interface:
+ path: ../../geolocator_platform_interface
dev_dependencies:
flutter_test:
diff --git a/geolocator_linux/pubspec.yaml b/geolocator_linux/pubspec.yaml
index 897f9026..91e05dfb 100644
--- a/geolocator_linux/pubspec.yaml
+++ b/geolocator_linux/pubspec.yaml
@@ -21,7 +21,8 @@ dependencies:
flutter:
sdk: flutter
geoclue: ^0.1.1
- geolocator_platform_interface: ^4.1.0
+ geolocator_platform_interface:
+ path: ../geolocator_platform_interface
gsettings: ^0.2.5
package_info_plus: "^8.0.0"
diff --git a/geolocator_platform_interface/CHANGELOG.md b/geolocator_platform_interface/CHANGELOG.md
index 245e8e0a..f9490c95 100644
--- a/geolocator_platform_interface/CHANGELOG.md
+++ b/geolocator_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 4.3.0
+
+- Adds `updatePositionStream` API.
+
## 4.2.4
- Correctly handle integer-like numbers when decoding `Position` from JSON.
diff --git a/geolocator_platform_interface/lib/src/errors/errors.dart b/geolocator_platform_interface/lib/src/errors/errors.dart
index 3bbfcfe5..c0468828 100644
--- a/geolocator_platform_interface/lib/src/errors/errors.dart
+++ b/geolocator_platform_interface/lib/src/errors/errors.dart
@@ -2,6 +2,7 @@ export 'activity_missing_exception.dart';
export 'already_subscribed_exception.dart';
export 'invalid_permission_exception.dart';
export 'location_service_disabled_exception.dart';
+export 'not_subscribed_exception.dart';
export 'permission_definitions_not_found_exception.dart';
export 'permission_denied_exception.dart';
export 'permission_request_in_progress_exception.dart';
diff --git a/geolocator_platform_interface/lib/src/errors/not_subscribed_exception.dart b/geolocator_platform_interface/lib/src/errors/not_subscribed_exception.dart
new file mode 100644
index 00000000..5c593673
--- /dev/null
+++ b/geolocator_platform_interface/lib/src/errors/not_subscribed_exception.dart
@@ -0,0 +1,12 @@
+/// An exception thrown when trying to update location stream settings without
+/// having an active subscription to the location stream.
+class NotSubscribedException implements Exception {
+ /// Constructs the [NotSubscribedException]
+ const NotSubscribedException();
+
+ @override
+ String toString() =>
+ 'The App is not listening to a stream of position updates and thus it is'
+ 'not possible to update location stream settings. Call '
+ 'getPositionStream() first.';
+}
diff --git a/geolocator_platform_interface/lib/src/geolocator_platform_interface.dart b/geolocator_platform_interface/lib/src/geolocator_platform_interface.dart
index e16e8ff0..8c557de5 100644
--- a/geolocator_platform_interface/lib/src/geolocator_platform_interface.dart
+++ b/geolocator_platform_interface/lib/src/geolocator_platform_interface.dart
@@ -172,6 +172,17 @@ abstract class GeolocatorPlatform extends PlatformInterface {
throw UnimplementedError('getPositionStream() has not been implemented.');
}
+ /// Updates the parameters of the active position stream.
+ ///
+ /// Throws a [NotSubscribedException] when there is no active position stream
+ /// to update.
+ Future updatePositionStream({
+ required LocationSettings locationSettings,
+ }) {
+ throw UnimplementedError(
+ 'updatePositionStream() has not been implemented.');
+ }
+
/// Asks the user for Temporary Precise location access (iOS 14 or above).
///
/// Returns [LocationAccuracyStatus.precise] if the user already gave
diff --git a/geolocator_platform_interface/lib/src/implementations/method_channel_geolocator.dart b/geolocator_platform_interface/lib/src/implementations/method_channel_geolocator.dart
index fefc185e..346f8b28 100644
--- a/geolocator_platform_interface/lib/src/implementations/method_channel_geolocator.dart
+++ b/geolocator_platform_interface/lib/src/implementations/method_channel_geolocator.dart
@@ -6,8 +6,8 @@ import '../enums/enums.dart';
import '../errors/errors.dart';
import '../extensions/extensions.dart';
import '../geolocator_platform_interface.dart';
-import '../models/position.dart';
import '../models/location_settings.dart';
+import '../models/position.dart';
/// An implementation of [GeolocatorPlatform] that uses method channels.
class MethodChannelGeolocator extends GeolocatorPlatform {
@@ -191,6 +191,22 @@ class MethodChannelGeolocator extends GeolocatorPlatform {
return _positionStream!;
}
+ @override
+ Future updatePositionStream(
+ {required LocationSettings locationSettings}) async {
+ if (_positionStream == null) {
+ throw const NotSubscribedException();
+ }
+ try {
+ await _methodChannel.invokeMethod(
+ 'updatePositionStream', locationSettings.toJson());
+ } on PlatformException catch (e) {
+ final error = _handlePlatformException(e);
+
+ throw error;
+ }
+ }
+
Stream _wrapStream(Stream incoming) {
return incoming.asBroadcastStream(onCancel: (subscription) {
subscription.cancel();
@@ -234,6 +250,8 @@ class MethodChannelGeolocator extends GeolocatorPlatform {
return const LocationServiceDisabledException();
case 'LOCATION_SUBSCRIPTION_ACTIVE':
return const AlreadySubscribedException();
+ case 'LOCATION_SUBSCRIPTION_INACTIVE':
+ return const NotSubscribedException();
case 'PERMISSION_DEFINITIONS_NOT_FOUND':
return PermissionDefinitionsNotFoundException(exception.message);
case 'PERMISSION_DENIED':
diff --git a/geolocator_platform_interface/pubspec.yaml b/geolocator_platform_interface/pubspec.yaml
index 736507cc..31a16428 100644
--- a/geolocator_platform_interface/pubspec.yaml
+++ b/geolocator_platform_interface/pubspec.yaml
@@ -3,22 +3,22 @@ description: A common platform interface for the geolocator plugin.
repository: https://github.com/baseflow/flutter-geolocator/tree/main/geolocator_platform_interface
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 4.2.4
+version: 4.3.0
dependencies:
flutter:
sdk: flutter
- plugin_platform_interface: ^2.1.6
+ plugin_platform_interface: ^2.1.8
vector_math: ^2.1.4
- meta: ^1.9.1
+ meta: ^1.15.0
dev_dependencies:
async: ^2.11.0
flutter_test:
sdk: flutter
- flutter_lints: ">=3.0.1 <5.0.0"
- mockito: ^5.4.2
+ flutter_lints: ">=5.0.0"
+ mockito: ^5.4.4
environment:
sdk: ">=2.15.0 <4.0.0"
diff --git a/geolocator_web/pubspec.yaml b/geolocator_web/pubspec.yaml
index c3b8dde2..c10039b8 100644
--- a/geolocator_web/pubspec.yaml
+++ b/geolocator_web/pubspec.yaml
@@ -17,7 +17,8 @@ dependencies:
sdk: flutter
flutter_web_plugins:
sdk: flutter
- geolocator_platform_interface: ^4.2.3
+ geolocator_platform_interface:
+ path: ../geolocator_platform_interface
web: ^1.0.0
dev_dependencies:
diff --git a/geolocator_windows/example/pubspec.yaml b/geolocator_windows/example/pubspec.yaml
index 90fa2905..b506f069 100644
--- a/geolocator_windows/example/pubspec.yaml
+++ b/geolocator_windows/example/pubspec.yaml
@@ -13,7 +13,8 @@ dependencies:
flutter:
sdk: flutter
- geolocator_platform_interface: ^4.0.7
+ geolocator_platform_interface:
+ path: ../../geolocator_platform_interface
geolocator_windows:
# When depending on this package from a real application you should use:
# geolocator_windows: ^x.y.z
diff --git a/geolocator_windows/pubspec.yaml b/geolocator_windows/pubspec.yaml
index 5ae18655..8e448c94 100644
--- a/geolocator_windows/pubspec.yaml
+++ b/geolocator_windows/pubspec.yaml
@@ -18,7 +18,8 @@ flutter:
dependencies:
flutter:
sdk: flutter
- geolocator_platform_interface: ^4.1.0
+ geolocator_platform_interface:
+ path: ../geolocator_platform_interface
dev_dependencies:
flutter_test: