diff --git a/plugins/geolocation/guest-js/index.ts b/plugins/geolocation/guest-js/index.ts index 8ef5c533..b00ca664 100644 --- a/plugins/geolocation/guest-js/index.ts +++ b/plugins/geolocation/guest-js/index.ts @@ -93,7 +93,7 @@ export type PositionOptions = { } export async function watchPosition( - options: PositionOptions, + options: PositionOptions & {requestUpdatesInBackground?: boolean}, cb: (location: Position | null, error?: string) => void ): Promise { const channel = new Channel() @@ -106,6 +106,7 @@ export async function watchPosition( } await invoke('plugin:geolocation|watch_position', { options, + requestUpdatesInBackground: options.requestUpdatesInBackground ?? false, channel }) return channel.id @@ -130,9 +131,11 @@ export async function checkPermissions(): Promise { } export async function requestPermissions( - permissions: PermissionType[] | null + permissions: PermissionType[] | null, + requestUpdatesInBackground: boolean = false ): Promise { return await invoke('plugin:geolocation|request_permissions', { - permissions + permissions, + requestUpdatesInBackground }) } diff --git a/plugins/geolocation/ios/Sources/GeolocationPlugin.swift b/plugins/geolocation/ios/Sources/GeolocationPlugin.swift index 7a2b57a9..2595a996 100644 --- a/plugins/geolocation/ios/Sources/GeolocationPlugin.swift +++ b/plugins/geolocation/ios/Sources/GeolocationPlugin.swift @@ -14,6 +14,7 @@ class GetPositionArgs: Decodable { class WatchPositionArgs: Decodable { let options: GetPositionArgs + let request_updates_in_background: Bool let channel: Channel } @@ -21,9 +22,20 @@ class ClearWatchArgs: Decodable { let channelId: UInt32 } +enum PermissionType: String, Decodable { + case location + case coarseLocation +} + +class RequestPermissionsArgs: Decodable { + let permissions: [PermissionType] + let requestUpdatesInBackground: Bool +} + class GeolocationPlugin: Plugin, CLLocationManagerDelegate { private let locationManager = CLLocationManager() private var isUpdatingLocation: Bool = false + private var backgroundUpdatesRequested: Bool = false private var permissionRequests: [Invoke] = [] private var positionRequests: [Invoke] = [] private var watcherChannels: [Channel] = [] @@ -61,6 +73,8 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate { @objc public func watchPosition(_ invoke: Invoke) throws { let args = try invoke.parseArgs(WatchPositionArgs.self) + self.backgroundUpdatesRequested = args.request_updates_in_background; + self.watcherChannels.append(args.channel) DispatchQueue.main.async { @@ -70,12 +84,28 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate { self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer } + + // TODO: Use the authorizationStatus instance property with locationManagerDidChangeAuthorization(_:) instead. if CLLocationManager.authorizationStatus() == .notDetermined { - self.locationManager.requestWhenInUseAuthorization() + if self.backgroundUpdatesRequested { + self.locationManager.requestAlwaysAuthorization() + } else { + self.locationManager.requestWhenInUseAuthorization() + } } else { - self.locationManager.startUpdatingLocation() - self.isUpdatingLocation = true + if CLLocationManager.authorizationStatus() == .authorizedWhenInUse && self.backgroundUpdatesRequested { + 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. if CLLocationManager.authorizationStatus() == .notDetermined { self.permissionRequests.append(invoke) - - DispatchQueue.main.async { - self.locationManager.requestWhenInUseAuthorization() + do { + let args = try invoke.parseArgs(RequestPermissionsArgs.self) + + 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 { checkPermissions(invoke) @@ -224,6 +264,11 @@ class GeolocationPlugin: Plugin, CLLocationManagerDelegate { private func stopUpdating() { self.locationManager.stopUpdatingLocation() self.isUpdatingLocation = false + if self.backgroundUpdatesRequested { + self.locationManager.allowsBackgroundLocationUpdates = false + self.locationManager.pausesLocationUpdatesAutomatically = true + self.locationManager.showsBackgroundLocationIndicator = false + } } private func convertLocation(_ location: CLLocation) -> JsonObject { diff --git a/plugins/geolocation/permissions/default.toml b/plugins/geolocation/permissions/default.toml new file mode 100644 index 00000000..c4f75baf --- /dev/null +++ b/plugins/geolocation/permissions/default.toml @@ -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"] diff --git a/plugins/geolocation/src/commands.rs b/plugins/geolocation/src/commands.rs index 9f9b7aa0..a176214e 100644 --- a/plugins/geolocation/src/commands.rs +++ b/plugins/geolocation/src/commands.rs @@ -20,9 +20,10 @@ pub(crate) async fn get_current_position( pub(crate) async fn watch_position( app: AppHandle, options: PositionOptions, + request_updates_in_background: bool, channel: Channel, ) -> Result<()> { - app.geolocation().watch_position_inner(options, channel) + app.geolocation().watch_position_inner(options, channel, request_updates_in_background) } #[command] @@ -42,6 +43,7 @@ pub(crate) async fn check_permissions(app: AppHandle) -> Result

( app: AppHandle, permissions: Option>, + request_updates_in_background: bool, ) -> Result { - app.geolocation().request_permissions(permissions) + app.geolocation().request_permissions(permissions, request_updates_in_background) } diff --git a/plugins/geolocation/src/desktop.rs b/plugins/geolocation/src/desktop.rs index 00da1fad..5bebfd01 100644 --- a/plugins/geolocation/src/desktop.rs +++ b/plugins/geolocation/src/desktop.rs @@ -32,6 +32,7 @@ impl Geolocation { pub fn watch_position( &self, options: PositionOptions, + request_updates_in_background: bool, callback: F, ) -> crate::Result { let channel = Channel::new(move |event| { @@ -51,7 +52,7 @@ impl Geolocation { }); let id = channel.id(); - self.watch_position_inner(options, channel)?; + self.watch_position_inner(options, request_updates_in_background, channel)?; Ok(id) } @@ -59,6 +60,7 @@ impl Geolocation { pub(crate) fn watch_position_inner( &self, _options: PositionOptions, + _request_updates_in_background: bool, _callback_channel: Channel, ) -> crate::Result<()> { Ok(()) @@ -75,6 +77,7 @@ impl Geolocation { pub fn request_permissions( &self, _permissions: Option>, + _request_updates_in_background: bool, ) -> crate::Result { Ok(PermissionStatus::default()) } diff --git a/plugins/geolocation/src/mobile.rs b/plugins/geolocation/src/mobile.rs index 48a3f5de..a67047cf 100644 --- a/plugins/geolocation/src/mobile.rs +++ b/plugins/geolocation/src/mobile.rs @@ -47,6 +47,7 @@ impl Geolocation { pub fn watch_position( &self, options: PositionOptions, + request_updates_in_background: bool, callback: F, ) -> crate::Result { let channel = Channel::new(move |event| { @@ -66,7 +67,7 @@ impl Geolocation { }); let id = channel.id(); - self.watch_position_inner(options, channel)?; + self.watch_position_inner(options, request_updates_in_background, channel)?; Ok(id) } @@ -74,10 +75,18 @@ impl Geolocation { pub(crate) fn watch_position_inner( &self, options: PositionOptions, + request_updates_in_background: bool, channel: Channel, ) -> crate::Result<()> { self.0 - .run_mobile_plugin("watchPosition", WatchPayload { options, channel }) + .run_mobile_plugin( + "watchPosition", + WatchPayload { + options, + channel, + request_updates_in_background, + }, + ) .map_err(Into::into) } @@ -96,11 +105,15 @@ impl Geolocation { pub fn request_permissions( &self, permissions: Option>, + request_updates_in_background: bool, ) -> crate::Result { self.0 .run_mobile_plugin( "requestPermissions", - serde_json::json!({ "permissions": permissions }), + serde_json::json!({ + "permissions": permissions, + "requestUpdatesInBackground": request_updates_in_background, + }), ) .map_err(Into::into) }