-
-
Notifications
You must be signed in to change notification settings - Fork 662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug]: getPositionStream causes exception when run on device without GMS (using default location manager) #1401
Comments
Dear @vanelizarov, I tried running your example on a GMS-less emulator but I am unable to reproduce this issue? Do you have the same issue when you run the example app? Kind regards, |
Yes, it's reproducible on AOSP-based API 31 (Android 12) image. You can run example from Error logW/GooglePlayServicesUtil( 4277): com.baseflow.geolocator_example requires the Google Play Store, but it is missing.
E/FlutterGeolocator( 4277): Geolocator position updates started
W/GooglePlayServicesUtil( 4277): com.baseflow.geolocator_example requires the Google Play Store, but it is missing.
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): Failed to open event stream
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): java.lang.IllegalStateException: passive location requests must have an explicit minimum update interval
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at androidx.core.util.Preconditions.checkState(Preconditions.java:169)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at androidx.core.location.LocationRequestCompat$Builder.build(LocationRequestCompat.java:488)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at com.baseflow.geolocator.location.LocationManagerClient.startPositionUpdates(LocationManagerClient.java:186)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at com.baseflow.geolocator.location.GeolocationManager.startPositionUpdates(GeolocationManager.java:54)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at com.baseflow.geolocator.StreamHandlerImpl.onListen(StreamHandlerImpl.java:137)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler.onListen(EventChannel.java:218)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler.onMessage(EventChannel.java:197)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at android.os.Handler.handleCallback(Handler.java:938)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at android.os.Handler.dispatchMessage(Handler.java:99)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at android.os.Looper.loopOnce(Looper.java:201)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at android.os.Looper.loop(Looper.java:288)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at android.app.ActivityThread.main(ActivityThread.java:7839)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at java.lang.reflect.Method.invoke(Native Method)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/EventChannel#flutter.baseflow.com/geolocator_updates_android( 4277): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
D/EGL_emulation( 4277): app_time_stats: avg=3383.37ms min=11.17ms max=6755.57ms count=2 Full example codeimport 'dart:async';
import 'dart:io' show Platform;
import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:flutter/material.dart';
import 'package:geolocator_android/geolocator_android.dart';
import 'package:geolocator_platform_interface/geolocator_platform_interface.dart';
/// Defines the main theme color.
final MaterialColor themeMaterialColor = BaseflowPluginExample.createMaterialColor(const Color.fromRGBO(48, 49, 60, 1));
void main() {
runApp(const GeolocatorWidget());
}
/// Example [Widget] showing the functionalities of the geolocator plugin.
class GeolocatorWidget extends StatefulWidget {
/// Creates a new GeolocatorWidget.
const GeolocatorWidget({Key? key}) : super(key: key);
/// Utility method to create a page with the Baseflow templating.
static ExamplePage createPage() {
return ExamplePage(Icons.location_on, (context) => const GeolocatorWidget());
}
@override
_GeolocatorWidgetState createState() => _GeolocatorWidgetState();
}
class _GeolocatorWidgetState extends State<GeolocatorWidget> {
static const String _kLocationServicesDisabledMessage = 'Location services are disabled.';
static const String _kPermissionDeniedMessage = 'Permission denied.';
static const String _kPermissionDeniedForeverMessage = 'Permission denied forever.';
static const String _kPermissionGrantedMessage = 'Permission granted.';
final GeolocatorPlatform geolocatorAndroid = GeolocatorPlatform.instance;
final List<_PositionItem> _positionItems = <_PositionItem>[];
StreamSubscription<Position>? _positionStreamSubscription;
StreamSubscription<ServiceStatus>? _serviceStatusStreamSubscription;
@override
void initState() {
super.initState();
_toggleServiceStatusStream();
}
PopupMenuButton _createActions() {
return PopupMenuButton(
elevation: 40,
onSelected: (value) async {
switch (value) {
case 1:
_getLocationAccuracy();
break;
case 2:
_requestTemporaryFullAccuracy();
break;
case 3:
_openAppSettings();
break;
case 4:
_openLocationSettings();
break;
case 5:
setState(_positionItems.clear);
break;
default:
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
child: Text("Get Location Accuracy"),
value: 1,
),
if (Platform.isIOS)
const PopupMenuItem(
child: Text("Request Temporary Full Accuracy"),
value: 2,
),
const PopupMenuItem(
child: Text("Open App Settings"),
value: 3,
),
if (Platform.isAndroid)
const PopupMenuItem(
child: Text("Open Location Settings"),
value: 4,
),
const PopupMenuItem(
child: Text("Clear"),
value: 5,
),
],
);
}
@override
Widget build(BuildContext context) {
const sizedBox = SizedBox(
height: 10,
);
return BaseflowPluginExample(
pluginName: 'Geolocator',
githubURL: 'https://github.com/Baseflow/flutter-geolocator',
pubDevURL: 'https://pub.dev/packages/geolocator',
appBarActions: [
_createActions()
],
pages: [
ExamplePage(
Icons.location_on,
(context) => Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: ListView.builder(
itemCount: _positionItems.length,
itemBuilder: (context, index) {
final positionItem = _positionItems[index];
if (positionItem.type == _PositionItemType.log) {
return ListTile(
title: Text(positionItem.displayValue,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
)),
);
} else {
return Card(
child: ListTile(
tileColor: themeMaterialColor,
title: Text(
positionItem.displayValue,
style: const TextStyle(color: Colors.white),
),
),
);
}
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
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'
: _positionStreamSubscription!.isPaused
? 'Resume'
: 'Pause',
backgroundColor: _determineButtonColor(),
),
sizedBox,
FloatingActionButton(
child: const Icon(Icons.my_location),
onPressed: _getCurrentPosition,
),
sizedBox,
FloatingActionButton(
child: const Icon(Icons.bookmark),
onPressed: _getLastKnownPosition,
),
],
),
),
)
]);
}
Future<void> _getCurrentPosition() async {
final hasPermission = await _handlePermission();
if (!hasPermission) {
return;
}
final position = await geolocatorAndroid.getCurrentPosition();
_updatePositionList(
_PositionItemType.position,
position.toString(),
);
}
Future<bool> _handlePermission() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await geolocatorAndroid.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
_updatePositionList(
_PositionItemType.log,
_kLocationServicesDisabledMessage,
);
return false;
}
permission = await geolocatorAndroid.checkPermission();
if (permission == LocationPermission.denied) {
permission = await geolocatorAndroid.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
_updatePositionList(
_PositionItemType.log,
_kPermissionDeniedMessage,
);
return false;
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
_updatePositionList(
_PositionItemType.log,
_kPermissionDeniedForeverMessage,
);
return false;
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
_updatePositionList(
_PositionItemType.log,
_kPermissionGrantedMessage,
);
return true;
}
void _updatePositionList(_PositionItemType type, String displayValue) {
_positionItems.add(_PositionItem(type, displayValue));
setState(() {});
}
bool _isListening() => !(_positionStreamSubscription == null || _positionStreamSubscription!.isPaused);
Color _determineButtonColor() {
return _isListening() ? Colors.green : Colors.red;
}
void _toggleServiceStatusStream() {
if (_serviceStatusStreamSubscription == null) {
final serviceStatusStream = geolocatorAndroid.getServiceStatusStream();
_serviceStatusStreamSubscription = serviceStatusStream.handleError((error) {
_serviceStatusStreamSubscription?.cancel();
_serviceStatusStreamSubscription = null;
}).listen((serviceStatus) {
String serviceStatusValue;
if (serviceStatus == ServiceStatus.enabled) {
serviceStatusValue = 'enabled';
} else {
serviceStatusValue = 'disabled';
}
_updatePositionList(
_PositionItemType.log,
'Location service has been $serviceStatusValue',
);
});
}
}
Future<void> _toggleListening() async {
final hasPermission = await _handlePermission();
if (!hasPermission) {
return;
}
if (_positionStreamSubscription == null) {
final androidSettings = AndroidSettings(
accuracy: LocationAccuracy.lowest,
timeLimit: const Duration(minutes: 1),
);
final positionStream = geolocatorAndroid.getPositionStream(locationSettings: androidSettings);
_positionStreamSubscription = positionStream.handleError((error) {
_positionStreamSubscription?.cancel();
_positionStreamSubscription = null;
}).listen((position) {
debugPrint(position.altitude.toString());
_updatePositionList(
_PositionItemType.position,
position.toString(),
);
});
_positionStreamSubscription?.pause();
}
setState(() {
if (_positionStreamSubscription == null) {
return;
}
String statusDisplayValue;
if (_positionStreamSubscription!.isPaused) {
_positionStreamSubscription!.resume();
statusDisplayValue = 'resumed';
} else {
_positionStreamSubscription!.pause();
statusDisplayValue = 'paused';
}
_updatePositionList(
_PositionItemType.log,
'Listening for position updates $statusDisplayValue',
);
});
}
@override
void dispose() {
if (_positionStreamSubscription != null) {
_positionStreamSubscription!.cancel();
_positionStreamSubscription = null;
}
super.dispose();
}
void _getLastKnownPosition() async {
final position = await geolocatorAndroid.getLastKnownPosition();
if (position != null) {
_updatePositionList(
_PositionItemType.position,
position.toString(),
);
} else {
_updatePositionList(
_PositionItemType.log,
'No last known position available',
);
}
}
void _getLocationAccuracy() async {
final status = await geolocatorAndroid.getLocationAccuracy();
_handleLocationAccuracyStatus(status);
}
void _requestTemporaryFullAccuracy() async {
final status = await geolocatorAndroid.requestTemporaryFullAccuracy(
purposeKey: "TemporaryPreciseAccuracy",
);
_handleLocationAccuracyStatus(status);
}
void _handleLocationAccuracyStatus(LocationAccuracyStatus status) {
String locationAccuracyStatusValue;
if (status == LocationAccuracyStatus.precise) {
locationAccuracyStatusValue = 'Precise';
} else if (status == LocationAccuracyStatus.reduced) {
locationAccuracyStatusValue = 'Reduced';
} else {
locationAccuracyStatusValue = 'Unknown';
}
_updatePositionList(
_PositionItemType.log,
'$locationAccuracyStatusValue location accuracy granted.',
);
}
void _openAppSettings() async {
final opened = await geolocatorAndroid.openAppSettings();
String displayValue;
if (opened) {
displayValue = 'Opened Application Settings.';
} else {
displayValue = 'Error opening Application Settings.';
}
_updatePositionList(
_PositionItemType.log,
displayValue,
);
}
void _openLocationSettings() async {
final opened = await geolocatorAndroid.openLocationSettings();
String displayValue;
if (opened) {
displayValue = 'Opened Location Settings';
} else {
displayValue = 'Error opening Location Settings';
}
_updatePositionList(
_PositionItemType.log,
displayValue,
);
}
}
enum _PositionItemType {
log,
position,
}
class _PositionItem {
_PositionItem(this.type, this.displayValue);
final _PositionItemType type;
final String displayValue;
} |
Dear @vanelizarov, However, the fix in your PR seems fine. I am still not able to reproduce the issue with the applied settings. But I am running a different AS version and emulator (although I don't think this will make a difference). I'll keep this issue open for so that the team can join me in the investigation. Linked PR |
Hi @vanelizarov, Thank you for reporting the issue and creating a PR with the solution. We have just merged your PR and published version 4.4.1 to pub.dev which should resolve this issue. |
Cool! |
Please check the following before submitting a new issue.
Please select affected platform(s)
Steps to reproduce
Run any example with getPositonStream on AOSP Android emulator
Expected results
No exception
Actual results
getPositionStream
causes anjava.lang.IllegalStateException: passive location requests must have an explicit minimum update interval
The problem is that
minUpdateIntervalMillis
property in location request is implicitly set to -1 by defaultCode sample
Code sample
Screenshots or video
Screenshots or video demonstration
[Upload media here]
Version
10.1.0
Flutter Doctor output
Doctor output
The text was updated successfully, but these errors were encountered: