Enable background geolocation updates

Enhanced geolocation plugin to support background updates. Added the `requestUpdatesInBackground` option to `watchPosition` and `requestPermissions` functions. Updated iOS handler to request appropriate location permissions based on this setting, and configured location manager for background updates. This change enables apps to continuously track location, improving functionality for applications requiring persistent geolocation data. Also introduced a default permissions configuration for the plugin.
pull/1889/head
Bradley Leatherwood 9 months ago
parent f445c704a1
commit 0ac7adfd10
No known key found for this signature in database

@ -93,7 +93,7 @@ export type PositionOptions = {
} }
export async function watchPosition( export async function watchPosition(
options: PositionOptions, options: PositionOptions & {requestUpdatesInBackground?: boolean},
cb: (location: Position | null, error?: string) => void cb: (location: Position | null, error?: string) => void
): Promise<number> { ): Promise<number> {
const channel = new Channel<Position | string>() const channel = new Channel<Position | string>()
@ -106,6 +106,7 @@ export async function watchPosition(
} }
await invoke('plugin:geolocation|watch_position', { await invoke('plugin:geolocation|watch_position', {
options, options,
requestUpdatesInBackground: options.requestUpdatesInBackground ?? false,
channel channel
}) })
return channel.id return channel.id
@ -130,9 +131,11 @@ export async function checkPermissions(): Promise<PermissionStatus> {
} }
export async function requestPermissions( export async function requestPermissions(
permissions: PermissionType[] | null permissions: PermissionType[] | null,
requestUpdatesInBackground: boolean = false
): Promise<PermissionStatus> { ): Promise<PermissionStatus> {
return await invoke('plugin:geolocation|request_permissions', { return await invoke('plugin:geolocation|request_permissions', {
permissions permissions,
requestUpdatesInBackground
}) })
} }

@ -14,6 +14,7 @@ class GetPositionArgs: Decodable {
class WatchPositionArgs: Decodable { class WatchPositionArgs: Decodable {
let options: GetPositionArgs let options: GetPositionArgs
let request_updates_in_background: Bool
let channel: Channel let channel: Channel
} }
@ -21,9 +22,20 @@ class ClearWatchArgs: Decodable {
let channelId: UInt32 let channelId: UInt32
} }
enum PermissionType: String, Decodable {
case location
case coarseLocation
}
class RequestPermissionsArgs: Decodable {
let permissions: [PermissionType]
let requestUpdatesInBackground: Bool
}
class GeolocationPlugin: Plugin, CLLocationManagerDelegate { class GeolocationPlugin: Plugin, CLLocationManagerDelegate {
private let locationManager = CLLocationManager() private let locationManager = CLLocationManager()
private var isUpdatingLocation: Bool = false private var isUpdatingLocation: Bool = false
private var backgroundUpdatesRequested: Bool = false
private var permissionRequests: [Invoke] = [] private var permissionRequests: [Invoke] = []
private var positionRequests: [Invoke] = [] private var positionRequests: [Invoke] = []
private var watcherChannels: [Channel] = [] private var watcherChannels: [Channel] = []
@ -61,6 +73,8 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate {
@objc public func watchPosition(_ invoke: Invoke) throws { @objc public func watchPosition(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(WatchPositionArgs.self) let args = try invoke.parseArgs(WatchPositionArgs.self)
self.backgroundUpdatesRequested = args.request_updates_in_background;
self.watcherChannels.append(args.channel) self.watcherChannels.append(args.channel)
DispatchQueue.main.async { DispatchQueue.main.async {
@ -70,12 +84,28 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate {
self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
} }
// TODO: Use the authorizationStatus instance property with locationManagerDidChangeAuthorization(_:) instead. // TODO: Use the authorizationStatus instance property with locationManagerDidChangeAuthorization(_:) instead.
if CLLocationManager.authorizationStatus() == .notDetermined { if CLLocationManager.authorizationStatus() == .notDetermined {
self.locationManager.requestWhenInUseAuthorization() if self.backgroundUpdatesRequested {
self.locationManager.requestAlwaysAuthorization()
} else {
self.locationManager.requestWhenInUseAuthorization()
}
} else { } else {
self.locationManager.startUpdatingLocation() if CLLocationManager.authorizationStatus() == .authorizedWhenInUse && self.backgroundUpdatesRequested {
self.isUpdatingLocation = true self.locationManager.requestAlwaysAuthorization()
} else {
self.locationManager.startUpdatingLocation()
self.isUpdatingLocation = true
// Enable background updates if enabled
if self.backgroundUpdatesRequested {
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.showsBackgroundLocationIndicator = true
}
}
} }
} }
@ -125,9 +155,19 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate {
// TODO: Use the authorizationStatus instance property with locationManagerDidChangeAuthorization(_:) instead. // TODO: Use the authorizationStatus instance property with locationManagerDidChangeAuthorization(_:) instead.
if CLLocationManager.authorizationStatus() == .notDetermined { if CLLocationManager.authorizationStatus() == .notDetermined {
self.permissionRequests.append(invoke) self.permissionRequests.append(invoke)
do {
DispatchQueue.main.async { let args = try invoke.parseArgs(RequestPermissionsArgs.self)
self.locationManager.requestWhenInUseAuthorization()
DispatchQueue.main.async {
self.locationManager.requestWhenInUseAuthorization()
if args.requestUpdatesInBackground {
// Request background permission if requested
self.locationManager.requestAlwaysAuthorization()
self.backgroundUpdatesRequested = true
}
}
} catch {
invoke.reject(error.localizedDescription)
} }
} else { } else {
checkPermissions(invoke) checkPermissions(invoke)
@ -224,6 +264,11 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate {
private func stopUpdating() { private func stopUpdating() {
self.locationManager.stopUpdatingLocation() self.locationManager.stopUpdatingLocation()
self.isUpdatingLocation = false self.isUpdatingLocation = false
if self.backgroundUpdatesRequested {
self.locationManager.allowsBackgroundLocationUpdates = false
self.locationManager.pausesLocationUpdatesAutomatically = true
self.locationManager.showsBackgroundLocationIndicator = false
}
} }
private func convertLocation(_ location: CLLocation) -> JsonObject { private func convertLocation(_ location: CLLocation) -> JsonObject {

@ -0,0 +1,3 @@
[default]
description = "Default permissions for the plugin"
permissions = ["allow-request-permissions", "allow-check-permissions", "allow-clear-watch", "allow-watch-position", "allow-get-current-position"]

@ -20,9 +20,10 @@ pub(crate) async fn get_current_position<R: Runtime>(
pub(crate) async fn watch_position<R: Runtime>( pub(crate) async fn watch_position<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
options: PositionOptions, options: PositionOptions,
request_updates_in_background: bool,
channel: Channel, channel: Channel,
) -> Result<()> { ) -> Result<()> {
app.geolocation().watch_position_inner(options, channel) app.geolocation().watch_position_inner(options, channel, request_updates_in_background)
} }
#[command] #[command]
@ -42,6 +43,7 @@ pub(crate) async fn check_permissions<R: Runtime>(app: AppHandle<R>) -> Result<P
pub(crate) async fn request_permissions<R: Runtime>( pub(crate) async fn request_permissions<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
permissions: Option<Vec<PermissionType>>, permissions: Option<Vec<PermissionType>>,
request_updates_in_background: bool,
) -> Result<PermissionStatus> { ) -> Result<PermissionStatus> {
app.geolocation().request_permissions(permissions) app.geolocation().request_permissions(permissions, request_updates_in_background)
} }

@ -32,6 +32,7 @@ impl<R: Runtime> Geolocation<R> {
pub fn watch_position<F: Fn(WatchEvent) + Send + Sync + 'static>( pub fn watch_position<F: Fn(WatchEvent) + Send + Sync + 'static>(
&self, &self,
options: PositionOptions, options: PositionOptions,
request_updates_in_background: bool,
callback: F, callback: F,
) -> crate::Result<u32> { ) -> crate::Result<u32> {
let channel = Channel::new(move |event| { let channel = Channel::new(move |event| {
@ -51,7 +52,7 @@ impl<R: Runtime> Geolocation<R> {
}); });
let id = channel.id(); let id = channel.id();
self.watch_position_inner(options, channel)?; self.watch_position_inner(options, request_updates_in_background, channel)?;
Ok(id) Ok(id)
} }
@ -59,6 +60,7 @@ impl<R: Runtime> Geolocation<R> {
pub(crate) fn watch_position_inner( pub(crate) fn watch_position_inner(
&self, &self,
_options: PositionOptions, _options: PositionOptions,
_request_updates_in_background: bool,
_callback_channel: Channel, _callback_channel: Channel,
) -> crate::Result<()> { ) -> crate::Result<()> {
Ok(()) Ok(())
@ -75,6 +77,7 @@ impl<R: Runtime> Geolocation<R> {
pub fn request_permissions( pub fn request_permissions(
&self, &self,
_permissions: Option<Vec<PermissionType>>, _permissions: Option<Vec<PermissionType>>,
_request_updates_in_background: bool,
) -> crate::Result<PermissionStatus> { ) -> crate::Result<PermissionStatus> {
Ok(PermissionStatus::default()) Ok(PermissionStatus::default())
} }

@ -47,6 +47,7 @@ impl<R: Runtime> Geolocation<R> {
pub fn watch_position<F: Fn(WatchEvent) + Send + Sync + 'static>( pub fn watch_position<F: Fn(WatchEvent) + Send + Sync + 'static>(
&self, &self,
options: PositionOptions, options: PositionOptions,
request_updates_in_background: bool,
callback: F, callback: F,
) -> crate::Result<u32> { ) -> crate::Result<u32> {
let channel = Channel::new(move |event| { let channel = Channel::new(move |event| {
@ -66,7 +67,7 @@ impl<R: Runtime> Geolocation<R> {
}); });
let id = channel.id(); let id = channel.id();
self.watch_position_inner(options, channel)?; self.watch_position_inner(options, request_updates_in_background, channel)?;
Ok(id) Ok(id)
} }
@ -74,10 +75,18 @@ impl<R: Runtime> Geolocation<R> {
pub(crate) fn watch_position_inner( pub(crate) fn watch_position_inner(
&self, &self,
options: PositionOptions, options: PositionOptions,
request_updates_in_background: bool,
channel: Channel, channel: Channel,
) -> crate::Result<()> { ) -> crate::Result<()> {
self.0 self.0
.run_mobile_plugin("watchPosition", WatchPayload { options, channel }) .run_mobile_plugin(
"watchPosition",
WatchPayload {
options,
channel,
request_updates_in_background,
},
)
.map_err(Into::into) .map_err(Into::into)
} }
@ -96,11 +105,15 @@ impl<R: Runtime> Geolocation<R> {
pub fn request_permissions( pub fn request_permissions(
&self, &self,
permissions: Option<Vec<PermissionType>>, permissions: Option<Vec<PermissionType>>,
request_updates_in_background: bool,
) -> crate::Result<PermissionStatus> { ) -> crate::Result<PermissionStatus> {
self.0 self.0
.run_mobile_plugin( .run_mobile_plugin(
"requestPermissions", "requestPermissions",
serde_json::json!({ "permissions": permissions }), serde_json::json!({
"permissions": permissions,
"requestUpdatesInBackground": request_updates_in_background,
}),
) )
.map_err(Into::into) .map_err(Into::into)
} }

Loading…
Cancel
Save