Skip to content
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

Adding Background/Foreground Services to GeoLocator UPDATE #3803

Merged
merged 6 commits into from
Aug 27, 2024
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
11 changes: 8 additions & 3 deletions client/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion "25.1.8937393"

packagingOptions {
jniLibs {
useLegacyPackaging true
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand All @@ -42,11 +48,10 @@ android {

defaultConfig {
applicationId "com.appveyor.flet"
minSdkVersion flutter.minSdkVersion
minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
minSdkVersion 23
}

buildTypes {
Expand All @@ -60,4 +65,4 @@ flutter {
source '../..'
}

dependencies {}
dependencies {}
84 changes: 52 additions & 32 deletions client/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,38 +1,58 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.appveyor.flet">
<uses-permission android:name="android.permission.INTERNET" />
<!-- Media access permissions.
Android 13 or higher.
https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Storage access permissions. Android 12 or lower. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application android:label="Flet" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="true"/>
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
<uses-permission android:name="android.permission.INTERNET" />
<!-- Media access permissions.
Android 13 or higher.
https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Storage access permissions. Android 12 or lower. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Google TV -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

<application
android:label="Flet"
android:name="${applicationName}"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false"/>
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/> <!-- Google TV -->
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
4 changes: 2 additions & 2 deletions client/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '1.9.24'
repositories {
google()
mavenCentral()
Expand Down Expand Up @@ -27,4 +27,4 @@ subprojects {

tasks.register("clean", Delete) {
delete rootProject.buildDir
}
}
2 changes: 1 addition & 1 deletion client/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
4 changes: 2 additions & 2 deletions client/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "com.android.application" version "8.3.1" apply false
}

include ":app"
include ":app"
84 changes: 84 additions & 0 deletions packages/flet_geolocator/lib/src/geolocator.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'package:flet/flet.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
Expand All @@ -22,6 +24,8 @@ class GeolocatorControl extends StatefulWidget {

class _GeolocatorControlState extends State<GeolocatorControl>
with FletStoreMixin {
StreamSubscription<Position>? _positionStream;

@override
void initState() {
super.initState();
Expand All @@ -41,10 +45,81 @@ class _GeolocatorControlState extends State<GeolocatorControl>
super.deactivate();
}

void _onPosition(Position position) {
debugPrint("Geolocator onPosition: $position");
final jsonData = jsonEncode({
"latitude": position.latitude,
"longitude": position.longitude,
});
widget.backend.triggerControlEvent(widget.control.id, "position", jsonData);
}

Future<bool> _enableLocationService() async {
late LocationSettings locationSettings;
if (defaultTargetPlatform == TargetPlatform.android) {
locationSettings = AndroidSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
forceLocationManager: true,
intervalDuration: const Duration(seconds: 30),
// Needs this or when app goes in background, background service stops working
foregroundNotificationConfig: const ForegroundNotificationConfig(
notificationText:
"Location Updates",
notificationTitle: "Running in Background",
enableWakeLock: true,
)
);
} else if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) {
locationSettings = AppleSettings(
accuracy: LocationAccuracy.bestForNavigation,
activityType: ActivityType.automotiveNavigation,
distanceFilter: 0,
pauseLocationUpdatesAutomatically: false,
showBackgroundLocationIndicator: true,
allowBackgroundLocationUpdates: true,
);
} else if (kIsWeb) {
locationSettings = WebSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
// maximumAge: Duration(minutes: 5)
maximumAge: Duration.zero,
);
} else {
locationSettings = LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
);
}

_positionStream = Geolocator.getPositionStream(locationSettings: locationSettings,
).listen(
(Position? position) {
if (position != null) {
_onPosition(position);
debugPrint('Geolocator: ${position.latitude}, ${position.longitude}, ${position}');
} else {
debugPrint('Geolocator: Position is null.');
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Implement more robust error handling in _enableLocationService

The current error handling only logs the error. Consider implementing a more comprehensive strategy, such as propagating the error to the user or implementing a retry mechanism.

onError: (e) {
  debugPrint('Geolocator: Error getting stream position: $e');
  // Propagate error to user
  _showErrorToUser('Unable to get location. Please try again.');
  // Implement retry mechanism
  _retryLocationService();
},

onError: (e) {
debugPrint('Geolocator: Error getting stream position: $e');
},
);
return true;
}

Future<bool> _disableLocationService() async {
await _positionStream?.cancel();
return true;
}

@override
Widget build(BuildContext context) {
debugPrint(
"Geolocator build: ${widget.control.id} (${widget.control.hashCode})");
bool onPosition = widget.control.attrBool("onPosition", false)!;

() async {
widget.backend.subscribeMethods(widget.control.id,
Expand All @@ -59,6 +134,15 @@ class _GeolocatorControlState extends State<GeolocatorControl>
case "is_location_service_enabled":
var serviceEnabled = await Geolocator.isLocationServiceEnabled();
return serviceEnabled.toString();
case "service_enable":
var serviceEnabled = false;
if (onPosition) {
serviceEnabled = await _enableLocationService();
}
return serviceEnabled.toString();
case "service_disable":
var serviceDisabled = await _disableLocationService();
return serviceDisabled.toString();
case "open_app_settings":
if (!kIsWeb) {
var opened = await Geolocator.openAppSettings();
Expand Down
4 changes: 2 additions & 2 deletions packages/flet_geolocator/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ dependencies:
flutter:
sdk: flutter

geolocator: ^11.0.0
geolocator: ^13.0.1

flet:
path: ../flet/

dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter_lints: ^2.0.0
3 changes: 2 additions & 1 deletion packages/flet_permission_handler/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ dependencies:

collection: ^1.16.0
permission_handler: ^11.3.1
permission_handler_html: ^0.1.3+2

flet:
path: ../flet/

dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter_lints: ^2.0.0
Loading