Skip to content

Commit

Permalink
Added location priority and initial interval with callbacks and addit…
Browse files Browse the repository at this point in the history
…ional parameters on notifications
  • Loading branch information
BWMuller committed Apr 11, 2024
1 parent 2891e7a commit 0e280e4
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 17 deletions.
62 changes: 62 additions & 0 deletions lib/background_callback.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'dart:developer';
import 'dart:ui';

import 'package:background_location/background_location.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

const BACKGROUND_CHANNEL_ID = "almoullim.com/background_location_service";
const ARG_CALLBACK = "ARG_CALLBACK";
const ARG_LOCATION = "ARG_LOCATION";
const ARG_LOCATIONS = "ARG_LOCATIONS";
const BCM_LOCATION = "BCM_LOCATION";
const BCM_NOTIFICATION_ACTION = "BCM_NOTIFICATION_ACTION";

@pragma('vm:entry-point')
void callbackHandler() {
const MethodChannel _backgroundChannel = MethodChannel(BACKGROUND_CHANNEL_ID);
WidgetsFlutterBinding.ensureInitialized();

_backgroundChannel.setMethodCallHandler((MethodCall call) async {
if (BCM_LOCATION == call.method) {
final Map<dynamic, dynamic> args = call.arguments;

int callbackArg = args[ARG_CALLBACK] ?? 0;
if (callbackArg != 0) {
final Function? callback = PluginUtilities.getCallbackFromHandle(CallbackHandle.fromRawHandle(callbackArg));
if (callback != null) {
var locs = List.empty(growable: true);
var locations = args[ARG_LOCATIONS];
if (locations != null && '$locations' != '[]') {
for (var loc in locations) {
locs.add(Location.fromJson(loc));
}
} else {
locs.add(Location.fromJson(args[ARG_LOCATION]));
}
callback(locs);
}
} else {
log("BGLocationCallback: $args");
}
} else if (BCM_NOTIFICATION_ACTION == call.method) {
final Map<dynamic, dynamic> args = call.arguments;

log("BGActionCallback: $args");
int callbackArg = args[ARG_CALLBACK] ?? 0;
if (callbackArg != 0) {
final Function? callback = PluginUtilities.getCallbackFromHandle(CallbackHandle.fromRawHandle(callbackArg));
final dynamic locationJson = args[ARG_LOCATION];
Location? location = null;
if (locationJson != null) {
location = Location.fromJson(locationJson);
}
if (callback != null)
callback(location);
} else {
log("BGActionCallback: $args");
}
}
});
_backgroundChannel.invokeMethod("BackgroundLocationService.initialized");
}
132 changes: 115 additions & 17 deletions lib/background_location.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io' show Platform;
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'background_callback.dart';

typedef LocationCallback = void Function(List<Location> value);
typedef OptLocationCallback = void Function(Location? value);

enum LocationPriority {
/// The best level of accuracy available.
PRIORITY_HIGH_ACCURACY,
// Accurate to within one hundred meters.
PRIORITY_BALANCED_POWER_ACCURACY,
// Accurate to within ten meters of the desired target.
PRIORITY_LOW_POWER,
// The level of accuracy used when an app isn’t authorized for full accuracy location data.
PRIORITY_NO_POWER,
}

/// BackgroundLocation plugin to get background
/// lcoation updates in iOS and Android
class BackgroundLocation {
// The channel to be used for communication.
// This channel is also refrenced inside both iOS and Abdroid classes
// This channel is also referenced inside both iOS and Abdroid classes
static const MethodChannel _channel =
MethodChannel('com.almoullim.background_location/methods');

/// Stop receiving location updates
static stopLocationService() async {
static Future stopLocationService() async {
return await _channel.invokeMethod('stop_location_service');
}

Expand All @@ -24,22 +41,77 @@ class BackgroundLocation {
}

/// Start receiving location updated
static startLocationService({double distanceFilter = 0.0, bool forceAndroidLocationManager = false}) async {
return await _channel.invokeMethod('start_location_service',
<String, dynamic>{'distance_filter': distanceFilter, 'force_location_manager': forceAndroidLocationManager});
static Future startLocationService({
bool startOnBoot = false,
int interval = 1000,
int fastestInterval = 500,
double distanceFilter = 0.0,
bool forceAndroidLocationManager = false,
LocationPriority priority = LocationPriority.PRIORITY_HIGH_ACCURACY,
LocationCallback? backgroundCallback,
}) async {
var callbackHandle =
PluginUtilities.getCallbackHandle(callbackHandler)!.toRawHandle();
var locationCallback = 0;
if (backgroundCallback != null) {
try {
locationCallback =
PluginUtilities.getCallbackHandle(backgroundCallback)!
.toRawHandle();
} catch (ex, stack) {
log('Error getting callback handle', error: ex, stackTrace: stack);
}
}

return await _channel
.invokeMethod('start_location_service', <String, dynamic>{
'callbackHandle': callbackHandle,
'locationCallback': locationCallback,
'startOnBoot': startOnBoot,
'interval': interval,
'fastest_interval': fastestInterval,
'priority': priority.index,
'distance_filter': distanceFilter,
'force_location_manager': forceAndroidLocationManager,
});
}

static setAndroidNotification(
{String? title, String? message, String? icon}) async {

static Future setAndroidNotification({
String? channelID,
String? title,
String? message,
String? icon,
String? actionText,
OptLocationCallback? actionCallback,
}) async {
if (Platform.isAndroid) {
var callback = 0;
if (actionCallback != null) {
try {
callback =
PluginUtilities.getCallbackHandle(actionCallback)!
.toRawHandle();
} catch (ex, stack) {
log('Error getting callback handle', error: ex, stackTrace: stack);
}
}
return await _channel.invokeMethod('set_android_notification',
<String, dynamic>{'title': title, 'message': message, 'icon': icon});
<String, dynamic>{
'channelID': channelID,
'title': title,
'message': message,
'icon': icon,
'actionText': actionText,
'actionCallback': callback,
});
} else {
//return Promise.resolve();
}
}

static setAndroidConfiguration(int interval) async {

static Future setAndroidConfiguration(int interval) async {
if (Platform.isAndroid) {
return await _channel.invokeMethod('set_configuration', <String, dynamic>{
'interval': interval.toString(),
Expand All @@ -58,13 +130,11 @@ class BackgroundLocation {
return completer.future;
}



/// Register a function to recive location updates as long as the location
/// Register a function to receive location updates as long as the location
/// service has started
static getLocationUpdates(Function(Location) location) {
// add a handler on the channel to recive updates from the native classes
_channel.setMethodCallHandler((MethodCall methodCall) async {
static Future<void> getLocationUpdates(Function(Location) location) async {
// add a handler on the channel to receive updates from the native classes
return _channel.setMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'location') {
var locationData = Map.from(methodCall.arguments);
// Call the user passed function
Expand All @@ -82,6 +152,20 @@ class BackgroundLocation {
}
});
}

/// Register a function to receive location updates as long as the location
/// service has started
static StreamController<Location> getLocationUpdateStream() {
var streamController = StreamController<Location>();

getLocationUpdates((location) {
if (streamController.isClosed) return;

streamController.add(location);
});

return streamController;
}
}

/// about the user current location
Expand All @@ -106,7 +190,21 @@ class Location {
required this.isMock,
});

toMap() {
factory Location.fromJson(Map<dynamic, dynamic> json) {
bool isLocationMocked = Platform.isAndroid ? json['is_mock'] : false;
return Location(
latitude: json['latitude'],
longitude: json['longitude'],
altitude: json['altitude'],
bearing: json['bearing'],
accuracy: json['accuracy'],
speed: json['speed'],
time: json['time'],
isMock: isLocationMocked,
);
}

Map<String, Object> toMap() {
var obj = {
'latitude': latitude,
'longitude': longitude,
Expand Down

0 comments on commit 0e280e4

Please sign in to comment.