From 7563040983ad397e28616246e7ed5ffce69727c2 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 8 Nov 2021 14:01:24 +0100 Subject: [PATCH] feat(geolocation): Add new alias for coarse location (#684) --- geolocation/README.md | 39 ++++++++++----- .../geolocation/GeolocationPlugin.java | 48 ++++++++++++++----- .../ios/Plugin/GeolocationPlugin.swift | 3 +- geolocation/src/definitions.ts | 38 ++++++++++++++- geolocation/src/web.ts | 2 +- 5 files changed, 103 insertions(+), 27 deletions(-) diff --git a/geolocation/README.md b/geolocation/README.md index 04cea52cc..01fbb8560 100644 --- a/geolocation/README.md +++ b/geolocation/README.md @@ -59,7 +59,7 @@ const printCurrentPosition = async () => { * [`watchPosition(...)`](#watchposition) * [`clearWatch(...)`](#clearwatch) * [`checkPermissions()`](#checkpermissions) -* [`requestPermissions()`](#requestpermissions) +* [`requestPermissions(...)`](#requestpermissions) * [Interfaces](#interfaces) * [Type Aliases](#type-aliases) @@ -140,14 +140,18 @@ Check location permissions -------------------- -### requestPermissions() +### requestPermissions(...) ```typescript -requestPermissions() => Promise +requestPermissions(permissions?: GeolocationPluginPermissions | undefined) => Promise ``` Request location permissions +| Param | Type | +| ----------------- | ------------------------------------------------------------------------------------- | +| **`permissions`** | GeolocationPluginPermissions | + **Returns:** Promise<PermissionStatus> **Since:** 1.0.0 @@ -168,11 +172,11 @@ Request location permissions #### PositionOptions -| Prop | Type | Description | Default | Since | -| ------------------------ | -------------------- | ------------------------------------------------------------------------------------------ | ------------------ | ----- | -| **`enableHighAccuracy`** | boolean | High accuracy mode (such as GPS, if available) | false | 1.0.0 | -| **`timeout`** | number | The maximum wait time in milliseconds for location updates | 10000 | 1.0.0 | -| **`maximumAge`** | number | The maximum age in milliseconds of a possible cached position that is acceptable to return | 0 | 1.0.0 | +| Prop | Type | Description | Default | Since | +| ------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- | +| **`enableHighAccuracy`** | boolean | High accuracy mode (such as GPS, if available) On Android 12+ devices it will be ignored if users didn't grant ACCESS_FINE_LOCATION permissions (can be checked with location alias). | false | 1.0.0 | +| **`timeout`** | number | The maximum wait time in milliseconds for location updates | 10000 | 1.0.0 | +| **`maximumAge`** | number | The maximum age in milliseconds of a possible cached position that is acceptable to return | 0 | 1.0.0 | #### ClearWatchOptions @@ -184,9 +188,17 @@ Request location permissions #### PermissionStatus -| Prop | Type | -| -------------- | ----------------------------------------------------------- | -| **`location`** | PermissionState | +| Prop | Type | Description | Since | +| -------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | +| **`location`** | PermissionState | Permission state for location alias. On Android it requests/checks both ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions. On iOS and web it requests/checks location permission. | 1.0.0 | +| **`coarseLocation`** | PermissionState | Permission state for coarseLocation alias. On Android it requests/checks ACCESS_COARSE_LOCATION. On Android 12+, users can choose between Approximate location (ACCESS_COARSE_LOCATION) or Precise location (ACCESS_FINE_LOCATION), so this alias can be used if the app doesn't need high accuracy. On iOS and web it will have the same value as location alias. | 1.2.0 | + + +#### GeolocationPluginPermissions + +| Prop | Type | +| ----------------- | ---------------------------------------- | +| **`permissions`** | GeolocationPermissionType[] | ### Type Aliases @@ -206,4 +218,9 @@ Request location permissions 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' + +#### GeolocationPermissionType + +'location' | 'coarseLocation' + diff --git a/geolocation/android/src/main/java/com/capacitorjs/plugins/geolocation/GeolocationPlugin.java b/geolocation/android/src/main/java/com/capacitorjs/plugins/geolocation/GeolocationPlugin.java index 766de8989..022fada70 100644 --- a/geolocation/android/src/main/java/com/capacitorjs/plugins/geolocation/GeolocationPlugin.java +++ b/geolocation/android/src/main/java/com/capacitorjs/plugins/geolocation/GeolocationPlugin.java @@ -17,11 +17,17 @@ @CapacitorPlugin( name = "Geolocation", permissions = { - @Permission(strings = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, alias = "location") + @Permission( + strings = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, + alias = GeolocationPlugin.LOCATION + ), + @Permission(strings = { Manifest.permission.ACCESS_COARSE_LOCATION }, alias = GeolocationPlugin.COARSE_LOCATION) } ) public class GeolocationPlugin extends Plugin { + static final String LOCATION = "location"; + static final String COARSE_LOCATION = "coarseLocation"; private Geolocation implementation; private Map watchingCalls = new HashMap<>(); @@ -38,8 +44,9 @@ public void load() { */ @PluginMethod public void getCurrentPosition(final PluginCall call) { - if (getPermissionState("location") != PermissionState.GRANTED) { - requestAllPermissions(call, "completeCurrentPosition"); + String alias = getAlias(call); + if (getPermissionState(alias) != PermissionState.GRANTED) { + requestPermissionForAlias(alias, call, "completeCurrentPosition"); } else { getPosition(call); } @@ -52,12 +59,11 @@ public void getCurrentPosition(final PluginCall call) { */ @PermissionCallback private void completeCurrentPosition(PluginCall call) { - if (getPermissionState("location") == PermissionState.GRANTED) { - boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false); + if (getPermissionState(GeolocationPlugin.COARSE_LOCATION) == PermissionState.GRANTED) { int timeout = call.getInt("timeout", 10000); implementation.sendLocation( - enableHighAccuracy, + isHighAccuracy(call), timeout, true, new LocationResultCallback() { @@ -86,8 +92,9 @@ public void error(String message) { @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK) public void watchPosition(PluginCall call) { call.setKeepAlive(true); - if (getPermissionState("location") != PermissionState.GRANTED) { - requestAllPermissions(call, "completeWatchPosition"); + String alias = getAlias(call); + if (getPermissionState(alias) != PermissionState.GRANTED) { + requestPermissionForAlias(alias, call, "completeWatchPosition"); } else { startWatch(call); } @@ -100,7 +107,7 @@ public void watchPosition(PluginCall call) { */ @PermissionCallback private void completeWatchPosition(PluginCall call) { - if (getPermissionState("location") == PermissionState.GRANTED) { + if (getPermissionState(GeolocationPlugin.COARSE_LOCATION) == PermissionState.GRANTED) { startWatch(call); } else { call.reject("Location permission was denied"); @@ -109,7 +116,6 @@ private void completeWatchPosition(PluginCall call) { @SuppressWarnings("MissingPermission") private void getPosition(PluginCall call) { - boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false); int timeout = call.getInt("timeout", 10000); int maximumAge = call.getInt("maximumAge", 0); Location location = implementation.getLastLocation(maximumAge); @@ -117,7 +123,7 @@ private void getPosition(PluginCall call) { call.resolve(getJSObjectForLocation(location)); } else { implementation.sendLocation( - enableHighAccuracy, + isHighAccuracy(call), timeout, true, new LocationResultCallback() { @@ -137,11 +143,10 @@ public void error(String message) { @SuppressWarnings("MissingPermission") private void startWatch(final PluginCall call) { - boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false); int timeout = call.getInt("timeout", 10000); implementation.requestLocationUpdates( - enableHighAccuracy, + isHighAccuracy(call), timeout, false, new LocationResultCallback() { @@ -202,4 +207,21 @@ private JSObject getJSObjectForLocation(Location location) { coords.put("heading", location.getBearing()); return ret; } + + private String getAlias(PluginCall call) { + String alias = GeolocationPlugin.LOCATION; + // TODO replace with Build.VERSION_CODES.S once we target SDK 31 + if (Build.VERSION.SDK_INT >= 31) { + boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false); + if (!enableHighAccuracy) { + alias = GeolocationPlugin.COARSE_LOCATION; + } + } + return alias; + } + + private boolean isHighAccuracy(PluginCall call) { + boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false); + return enableHighAccuracy && getPermissionState(GeolocationPlugin.LOCATION) == PermissionState.GRANTED; + } } diff --git a/geolocation/ios/Plugin/GeolocationPlugin.swift b/geolocation/ios/Plugin/GeolocationPlugin.swift index 20881620a..b3d0bbbd0 100644 --- a/geolocation/ios/Plugin/GeolocationPlugin.swift +++ b/geolocation/ios/Plugin/GeolocationPlugin.swift @@ -168,7 +168,8 @@ public class GeolocationPlugin: CAPPlugin, CLLocationManagerDelegate { } let result = [ - "location": status + "location": status, + "coarseLocation": status ] call.resolve(result) diff --git a/geolocation/src/definitions.ts b/geolocation/src/definitions.ts index 790f36aa3..69dca9354 100644 --- a/geolocation/src/definitions.ts +++ b/geolocation/src/definitions.ts @@ -3,7 +3,38 @@ import type { PermissionState } from '@capacitor/core'; export type CallbackID = string; export interface PermissionStatus { + /** + * Permission state for location alias. + * + * On Android it requests/checks both ACCESS_COARSE_LOCATION and + * ACCESS_FINE_LOCATION permissions. + * + * On iOS and web it requests/checks location permission. + * + * @since 1.0.0 + */ location: PermissionState; + + /** + * Permission state for coarseLocation alias. + * + * On Android it requests/checks ACCESS_COARSE_LOCATION. + * + * On Android 12+, users can choose between Approximate location (ACCESS_COARSE_LOCATION) or + * Precise location (ACCESS_FINE_LOCATION), so this alias can be used if the app doesn't + * need high accuracy. + * + * On iOS and web it will have the same value as location alias. + * + * @since 1.2.0 + */ + coarseLocation: PermissionState; +} + +export type GeolocationPermissionType = 'location' | 'coarseLocation'; + +export interface GeolocationPluginPermissions { + permissions: GeolocationPermissionType[]; } export interface GeolocationPlugin { @@ -44,7 +75,9 @@ export interface GeolocationPlugin { * * @since 1.0.0 */ - requestPermissions(): Promise; + requestPermissions( + permissions?: GeolocationPluginPermissions, + ): Promise; } export interface ClearWatchOptions { @@ -122,6 +155,9 @@ export interface PositionOptions { /** * High accuracy mode (such as GPS, if available) * + * On Android 12+ devices it will be ignored if users didn't grant + * ACCESS_FINE_LOCATION permissions (can be checked with location alias). + * * @default false * @since 1.0.0 */ diff --git a/geolocation/src/web.ts b/geolocation/src/web.ts index e3df53a61..6830085de 100644 --- a/geolocation/src/web.ts +++ b/geolocation/src/web.ts @@ -63,7 +63,7 @@ export class GeolocationWeb extends WebPlugin implements GeolocationPlugin { const permission = await window.navigator.permissions.query({ name: 'geolocation', }); - return { location: permission.state }; + return { location: permission.state, coarseLocation: permission.state }; } async requestPermissions(): Promise {