diff --git a/CHANGELOG_UNRELEASED.md b/CHANGELOG_UNRELEASED.md index e69de29b..8bd989f4 100644 --- a/CHANGELOG_UNRELEASED.md +++ b/CHANGELOG_UNRELEASED.md @@ -0,0 +1,8 @@ +## Unreleased + + ### Added + + - Added new methods to help users resolve permission and sensor-related issues within the app. With these new methods, the SDK can now automatically request the necessary permissions for positioning without the need to implement repetitive code: + - configureUserHelper(options): Automatically detects and explains configuration problems (such as missing Location or Bluetooth permissions, or disabled sensors), and guides the user through the steps to fix them. Accepts a configuration object to customize the behavior. + - enableUserHelper(): Shortcut to enable the user guidance with default settings + - disableUserHelper(): Shortcut to disable the user guidance with default settings \ No newline at end of file diff --git a/android/src/main/java/com/situm/plugin/PluginHelper.java b/android/src/main/java/com/situm/plugin/PluginHelper.java index b4dcbbf7..f33c706d 100644 --- a/android/src/main/java/com/situm/plugin/PluginHelper.java +++ b/android/src/main/java/com/situm/plugin/PluginHelper.java @@ -57,6 +57,7 @@ import es.situm.sdk.v1.SitumEvent; import es.situm.sdk.location.GeofenceListener; import es.situm.sdk.navigation.ExternalNavigation; +import es.situm.sdk.userhelper.UserHelperColorScheme; import static com.situm.plugin.SitumPlugin.EVENT_LOCATION_CHANGED; import static com.situm.plugin.SitumPlugin.EVENT_LOCATION_ERROR; @@ -1013,4 +1014,30 @@ public void onCancellation() { } }; } + + public void configureUserHelper(ReadableMap map, Callback success, Callback error) { + try { + JSONObject jsonUserHelperOptions = ReactNativeUtils.convertMapToJson(map); + boolean enabled = false; + if (jsonUserHelperOptions.has("enabled")) { + enabled = jsonUserHelperOptions.getBoolean("enabled"); + } + if (jsonUserHelperOptions.has("colorScheme")) { + JSONObject jsonColorScheme = jsonUserHelperOptions.getJSONObject("colorScheme"); + UserHelperColorScheme colorScheme = SitumMapper.jsonObjectToUserHelperColorScheme(jsonColorScheme); + SitumSdk.userHelperManager().setColorScheme(colorScheme); + } + SitumSdk.userHelperManager().autoManage(enabled); + + WritableMap response = Arguments.createMap(); + invokeCallback(success, response); + + } catch (Exception e) { + Log.d(TAG, "exception: " + e); + + WritableMap response = Arguments.createMap(); + response.putString("error", e.getMessage()); + invokeCallback(error, response); + } + } } diff --git a/android/src/main/java/com/situm/plugin/SitumMapper.java b/android/src/main/java/com/situm/plugin/SitumMapper.java index f4f32958..19f18f4d 100644 --- a/android/src/main/java/com/situm/plugin/SitumMapper.java +++ b/android/src/main/java/com/situm/plugin/SitumMapper.java @@ -69,6 +69,7 @@ import es.situm.sdk.v1.SitumEvent; import es.situm.sdk.location.ForegroundServiceNotificationOptions; import es.situm.sdk.location.ForegroundServiceNotificationOptions.TapAction; +import es.situm.sdk.userhelper.UserHelperColorScheme; class SitumMapper { @@ -1283,4 +1284,24 @@ public static ReadableArray convertListToReadableArray(List list) { } return response; } + + static UserHelperColorScheme jsonObjectToUserHelperColorScheme(JSONObject args) throws JSONException { + UserHelperColorScheme.Builder builder = new UserHelperColorScheme.Builder(); + + if (args.has("primaryColor")) { + String primaryColor = args.getString("primaryColor"); + if (primaryColor != null) { + builder.setPrimaryColor(primaryColor); + } + } + + if (args.has("secondaryColor")) { + String secondaryColor = args.getString("secondaryColor"); + if (secondaryColor != null) { + builder.setSecondaryColor(secondaryColor); + } + } + + return builder.build(); + } } diff --git a/android/src/main/java/com/situm/plugin/SitumPlugin.java b/android/src/main/java/com/situm/plugin/SitumPlugin.java index 86a0ba95..df13f348 100644 --- a/android/src/main/java/com/situm/plugin/SitumPlugin.java +++ b/android/src/main/java/com/situm/plugin/SitumPlugin.java @@ -82,11 +82,11 @@ public interface SitumPlugin { void invalidateCache(); - void requestAuthorization(); - void getDeviceId(Callback callback); void onEnterGeofences(); void onExitGeofences(); + + void configureUserHelper(ReadableMap map, Callback success, Callback error); } diff --git a/android/src/main/java/com/situm/plugin/SitumPluginImpl.java b/android/src/main/java/com/situm/plugin/SitumPluginImpl.java index 14509c45..7fe53acd 100755 --- a/android/src/main/java/com/situm/plugin/SitumPluginImpl.java +++ b/android/src/main/java/com/situm/plugin/SitumPluginImpl.java @@ -290,54 +290,6 @@ public void getDeviceId(Callback callback) { callback.invoke(response); } - @Override - @ReactMethod - public void requestAuthorization() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - final PermissionsModule perms = getReactApplicationContext().getNativeModule(PermissionsModule.class); - - final Callback onPermissionGranted = new Callback() { - @Override - public void invoke(Object... args) { - String result = (String) args[0]; - if (!result.equals("granted")) { - Log.e(TAG, "Location permission was not granted."); - } - } - }; - - final Callback onPermissionDenied = new Callback() { - @Override - public void invoke(Object... args) { - Log.e(TAG, "Failed to request location permission."); - } - }; - - Callback onPermissionCheckFailed = new Callback() { - @Override - public void invoke(Object... args) { - - Log.e(TAG, "Failed to check location permission."); - } - }; - - Callback onPermissionChecked = new Callback() { - @Override - public void invoke(Object... args) { - boolean hasPermission = (boolean) args[0]; - - if (!hasPermission) { - perms.requestPermission(Manifest.permission.ACCESS_FINE_LOCATION, - new PromiseImpl(onPermissionGranted, onPermissionDenied)); - } - } - }; - - perms.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, - new PromiseImpl(onPermissionChecked, onPermissionCheckFailed)); - } - } - @Override @ReactMethod public void onEnterGeofences() { @@ -351,4 +303,9 @@ public void onExitGeofences() { getPluginInstance().onExitGeofences( getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)); } + + @ReactMethod + public void configureUserHelper(ReadableMap map, Callback success, Callback error) { + getPluginInstance().configureUserHelper(map, success, error); + } } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 778b6273..696b07ef 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -75,7 +75,7 @@ PODS: - hermes-engine/Pre-built (0.72.12) - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - - Protobuf (3.29.2) + - Protobuf (3.29.4) - RCT-Folly (2021.07.22.00): - boost - DoubleConversion @@ -491,18 +491,18 @@ PODS: - React-jsi (= 0.72.12) - React-logger (= 0.72.12) - React-perflogger (= 0.72.12) - - ReactNativeSitumPlugin (3.12.5): + - ReactNativeSitumPlugin (3.14.14): - RCT-Folly (= 2021.07.22.00) - React - React-Core - - SitumSDK (= 3.21.1) + - SitumSDK (= 3.28.1) - RNPermissions (3.10.1): - React-Core - RNScreens (3.32.0): - RCT-Folly (= 2021.07.22.00) - React-Core - React-RCTImage - - SitumSDK (3.21.1): + - SitumSDK (3.28.1): - Protobuf (~> 3.7) - SSZipArchive (~> 2.4) - SocketRocket (0.6.1) @@ -710,7 +710,7 @@ SPEC CHECKSUMS: hermes-engine: e89344b9e9e54351c3c5cac075e0275148fb37ba libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - Protobuf: ba5d83b2201386fec27d484c099cac510ea5c169 + Protobuf: 2e6de032ba12b9efb390ae550d1a243a5b19ddfc RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: b6cea797b684c6d8d82ba0107cef58cbb679afdb RCTTypeSafety: d2eb5e0e8af9181b24034f5171f9b659994b4678 @@ -745,10 +745,10 @@ SPEC CHECKSUMS: React-runtimescheduler: 8aea338c561b2175f47018124c076d89d3808d30 React-utils: 9a24cb88f950d1020ee55bddacbc8c16a611e2dc ReactCommon: 76843a9bb140596351ac2786257ac9fe60cafabb - ReactNativeSitumPlugin: 389ee181d434a2c57d9598ee99103ab0ba5ea474 + ReactNativeSitumPlugin: 354a112a0a92d92dc1c67d55d235fe7e7a268b80 RNPermissions: 4377d1d869b3b32308932b8c11ba47880933d7c6 RNScreens: ad1c105ac9107cf1a613bf80889485458eb20bd7 - SitumSDK: dcc8e963d0d80c260786c3f76ea8160714a87d26 + SitumSDK: 974f20680e13f115caaa818b48bcc2340b768ffc SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef Yoga: 87e59f6d458e5061d2421086c5de994b3f7cd151 @@ -756,4 +756,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 5bbd3e6d4ded92b8497ea5f670af0806207e3ee6 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/example/package.json b/example/package.json index ca2842af..267dd475 100644 --- a/example/package.json +++ b/example/package.json @@ -15,6 +15,7 @@ "@react-navigation/native": "^6.1.1", "@react-navigation/native-stack": "^6.9.7", "@situm/react-native": "file:..", + "babel-plugin-module-resolver": "^5.0.2", "react": "18.2.0", "react-native": "0.72.12", "react-native-paper": "^5.10.0", @@ -60,4 +61,4 @@ "LocationWhenInUse", "Motion" ] -} \ No newline at end of file +} diff --git a/example/src/examples/Utils/requestPermission.tsx b/example/src/examples/Utils/requestPermission.tsx deleted file mode 100644 index 1b79f99c..00000000 --- a/example/src/examples/Utils/requestPermission.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Platform } from "react-native"; -import { - PERMISSIONS, - request, - requestMultiple, - RESULTS, -} from "react-native-permissions"; - -// TODO: can requestMultiple be used ? -const checkIOSPermissions = async () => { - const granted = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE); - - // Check if already denied - if (granted !== RESULTS.GRANTED) { - throw "Situm > permissions > ACCESS_FINE_LOCATION denied"; - } - - console.debug( - "Situm > permissions > LOCATION_WHEN_IN_USE permission granted" - ); - - return true; -}; - -const checkAndroidPermissions = async () => { - let granted; - - //@ts-ignore - if (Platform.Version <= 30) { - console.debug("Situm > permissions > ANDROID VERSION < 30"); - - granted = await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION); - - if (granted === RESULTS.GRANTED) { - console.debug("Situm > permissions > ACCESS_FINE_LOCATION granted"); - return true; - } else { - throw "Situm > permissions > ACCESS_FINE_LOCATION permission not granted"; - } - } - - console.debug("Situm > permissions > ANDROID VERSION > 30"); - granted = await requestMultiple([ - PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION, - PERMISSIONS.ANDROID.BLUETOOTH_CONNECT, - PERMISSIONS.ANDROID.BLUETOOTH_SCAN, - ]); - - if ( - granted["android.permission.ACCESS_FINE_LOCATION"] === RESULTS.GRANTED && - granted["android.permission.BLUETOOTH_CONNECT"] === RESULTS.GRANTED && - granted["android.permission.BLUETOOTH_SCAN"] === RESULTS.GRANTED - ) { - console.debug( - "Situm > permissions > ACCESS_FINE_LOCATION, BLUETOOTH_CONNECT and BLUETOOTH_SCAN permissions granted" - ); - return true; - } - - throw "Situm > permissions > ACCESS_FINE_LOCATION, BLUETOOTH_CONNECT or BLUETOOTH_SCAN permissions not granted"; -}; - -export const requestPermission = async () => { - console.debug( - "Situm > permissions > Retrieving permissions for platform " + Platform.OS - ); - - if (!["ios", "android"].includes(Platform.OS)) { - throw `Situm > permissions > Platform '${Platform.OS}' not supported`; - } - - return await (Platform.OS === "ios" - ? checkIOSPermissions() - : checkAndroidPermissions() - ) - .then(() => null) - .catch((e: string) => { - console.warn(e); - throw e; - }); -}; - -export default requestPermission; diff --git a/example/src/examples/sdk/Positioning.tsx b/example/src/examples/sdk/Positioning.tsx index 536cb9ce..78630499 100644 --- a/example/src/examples/sdk/Positioning.tsx +++ b/example/src/examples/sdk/Positioning.tsx @@ -9,7 +9,6 @@ import SitumPlugin, { } from '@situm/react-native'; import styles from '../styles/styles'; import {Button, Card, Divider, List} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; function PositioningScreen() { // State variables to store location, status, error, and geofences data @@ -23,6 +22,10 @@ function PositioningScreen() { SitumPlugin.setConfiguration({ useRemoteConfig: false, }); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + registerCallbacks(); return () => { @@ -32,20 +35,11 @@ function PositioningScreen() { }, []); const handlePermissionsButton = async () => { - try { - await requestPermission(); - } catch (e) { - console.error('Error requesting permissions:', e); - } + // Do nothing }; // Start positioning using Situm SDK const startPositioning = async () => { - try { - await requestPermission(); - } catch (e) { - console.warn('Situm > example > Error starting positioning:', e); - } console.log('Starting positioning'); setLocation(''); diff --git a/example/src/examples/sdk/RemoteConfig.tsx b/example/src/examples/sdk/RemoteConfig.tsx index 254dd80f..a0e0b17a 100644 --- a/example/src/examples/sdk/RemoteConfig.tsx +++ b/example/src/examples/sdk/RemoteConfig.tsx @@ -9,7 +9,6 @@ import SitumPlugin, { } from '@situm/react-native'; import styles from '../styles/styles'; import {Button, Card, Divider, List} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; export const RemoteConfig = () => { // State variables to store location, status, error, and geofences data @@ -96,21 +95,11 @@ export const RemoteConfig = () => { }; const handlePermissionsButton = async () => { - try { - await requestPermission(); - } catch (e) { - console.error('Error requesting permissions:', e); - } + // Do nothing. }; // Start positioning using Situm SDK const startPositioning = async () => { - try { - await requestPermission(); - } catch (e) { - console.warn('Situm > example > Error starting positioning:', e); - } - console.log('Situm > example > Starting positioning'); setLocation(''); setStatus(''); @@ -146,6 +135,10 @@ export const RemoteConfig = () => { SitumPlugin.setConfiguration({ useRemoteConfig: true, }); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + registerCallbacks(); return () => { diff --git a/example/src/examples/wayfinding/FollowUser.tsx b/example/src/examples/wayfinding/FollowUser.tsx index 3ac955af..adabf839 100644 --- a/example/src/examples/wayfinding/FollowUser.tsx +++ b/example/src/examples/wayfinding/FollowUser.tsx @@ -6,7 +6,6 @@ import SitumPlugin, {MapView, SitumProvider} from '@situm/react-native'; import type {MapViewRef} from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID} from '../../situm'; import {Button} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; import {getDefaultLocationOptions} from '../../settings'; const styles = StyleSheet.create({ @@ -40,18 +39,13 @@ const Screen: React.FC = () => { * Helper function that sets up the system to start positioning */ const initializeSitum = async () => { - try { - // Define your own configuration if needed - SitumPlugin.setConfiguration({useRemoteConfig: true}); - // Request permissions and start positioning - await requestPermission() - .then(() => { - SitumPlugin.requestLocationUpdates(); - }) - .catch(console.debug); - } catch (e) { - console.log(`Situm > example > Could not start positioning ${e}`); - } + // Define your own configuration if needed + SitumPlugin.setConfiguration({useRemoteConfig: true}); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + // Start positioning: + SitumPlugin.requestLocationUpdates(); }; /** @@ -83,12 +77,6 @@ const Screen: React.FC = () => { }, [mapViewRef]); const startPositioning = async () => { - try { - await requestPermission(); - } catch (e) { - console.warn('Situm > example > Error starting positioning:', e); - } - console.log('Starting positioning'); const locationOptions = getDefaultLocationOptions(); diff --git a/example/src/examples/wayfinding/NavigateToPoi.tsx b/example/src/examples/wayfinding/NavigateToPoi.tsx index 161f4c0a..2a9fefc7 100644 --- a/example/src/examples/wayfinding/NavigateToPoi.tsx +++ b/example/src/examples/wayfinding/NavigateToPoi.tsx @@ -6,7 +6,6 @@ import SitumPlugin, {MapView, SitumProvider} from '@situm/react-native'; import type {MapViewRef} from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID} from '../../situm'; import {Button, TextInput} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; const styles = StyleSheet.create({ viewer_container: { @@ -39,18 +38,9 @@ const Screen: React.FC = () => { * Helper function that sets up the system to start positioning */ const initializeSitum = async () => { - try { - // Define your own configuration if needed - SitumPlugin.setConfiguration({useRemoteConfig: true}); - // Request permissions and start positioning - await requestPermission() - .then(() => { - SitumPlugin.requestLocationUpdates(); - }) - .catch(console.debug); - } catch (e) { - console.log(`Situm > example > Could not start positioning ${e}`); - } + // Define your own configuration if needed + SitumPlugin.setConfiguration({useRemoteConfig: true}); + SitumPlugin.requestLocationUpdates(); }; /** diff --git a/example/src/examples/wayfinding/SelectFloor.tsx b/example/src/examples/wayfinding/SelectFloor.tsx index db50654d..2fea2229 100644 --- a/example/src/examples/wayfinding/SelectFloor.tsx +++ b/example/src/examples/wayfinding/SelectFloor.tsx @@ -6,7 +6,6 @@ import SitumPlugin, {MapView, SitumProvider} from '@situm/react-native'; import type {MapViewRef} from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID} from '../../situm'; import {Button, TextInput} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; const styles = StyleSheet.create({ viewer_container: { @@ -47,18 +46,13 @@ const Screen: React.FC = () => { * Helper function that sets up the system to start positioning */ const initializeSitum = async () => { - try { - // Define your own configuration if needed - SitumPlugin.setConfiguration({useRemoteConfig: true}); - // Request permissions and start positioning - await requestPermission() - .then(() => { - SitumPlugin.requestLocationUpdates(); - }) - .catch(console.debug); - } catch (e) { - console.log(`Situm > example > Could not start positioning ${e}`); - } + // Define your own configuration if needed + SitumPlugin.setConfiguration({useRemoteConfig: true}); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + // Start positioning: + SitumPlugin.requestLocationUpdates(); }; /** diff --git a/example/src/examples/wayfinding/SelectPoi.tsx b/example/src/examples/wayfinding/SelectPoi.tsx index abcc724e..3f1dd05c 100644 --- a/example/src/examples/wayfinding/SelectPoi.tsx +++ b/example/src/examples/wayfinding/SelectPoi.tsx @@ -6,7 +6,6 @@ import SitumPlugin, {MapView, SitumProvider} from '@situm/react-native'; import type {MapViewRef} from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID} from '../../situm'; import {Button, TextInput} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; const styles = StyleSheet.create({ viewer_container: { @@ -39,18 +38,13 @@ const Screen: React.FC = () => { * Helper function that sets up the system to start positioning */ const initializeSitum = async () => { - try { - // Define your own configuration if needed - SitumPlugin.setConfiguration({useRemoteConfig: true}); - // Request permissions and start positioning - await requestPermission() - .then(() => { - SitumPlugin.requestLocationUpdates(); - }) - .catch(console.debug); - } catch (e) { - console.log(`Situm > example > Could not start positioning ${e}`); - } + // Define your own configuration if needed + SitumPlugin.setConfiguration({useRemoteConfig: true}); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + // Start positioning: + SitumPlugin.requestLocationUpdates(); }; /** diff --git a/example/src/examples/wayfinding/SelectPoiCategory.tsx b/example/src/examples/wayfinding/SelectPoiCategory.tsx index 1a37e868..1c34d8f4 100644 --- a/example/src/examples/wayfinding/SelectPoiCategory.tsx +++ b/example/src/examples/wayfinding/SelectPoiCategory.tsx @@ -6,7 +6,6 @@ import SitumPlugin, {MapView, SitumProvider} from '@situm/react-native'; import type {MapViewRef} from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID} from '../../situm'; import {Button, TextInput} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; const styles = StyleSheet.create({ viewer_container: { @@ -40,18 +39,13 @@ const Screen: React.FC = () => { * Helper function that sets up the system to start positioning */ const initializeSitum = async () => { - try { - // Define your own configuration if needed - SitumPlugin.setConfiguration({useRemoteConfig: true}); - // Request permissions and start positioning - await requestPermission() - .then(() => { - SitumPlugin.requestLocationUpdates(); - }) - .catch(console.debug); - } catch (e) { - console.log(`Situm > example > Could not start positioning ${e}`); - } + // Define your own configuration if needed + SitumPlugin.setConfiguration({useRemoteConfig: true}); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + // Start positioning: + SitumPlugin.requestLocationUpdates(); }; /** diff --git a/example/src/examples/wayfinding/SetFavoritePois.tsx b/example/src/examples/wayfinding/SetFavoritePois.tsx index 22c27a25..ccfabef2 100644 --- a/example/src/examples/wayfinding/SetFavoritePois.tsx +++ b/example/src/examples/wayfinding/SetFavoritePois.tsx @@ -7,7 +7,6 @@ import type {MapViewRef} from '@situm/react-native'; import type {OnFavoritePoisUpdatedResult} from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID} from '../../situm'; import {Button, TextInput} from 'react-native-paper'; -import requestPermission from '../Utils/requestPermission'; const styles = StyleSheet.create({ viewer_container: { @@ -41,18 +40,13 @@ const Screen: React.FC = () => { * Helper function that sets up the system to start positioning */ const initializeSitum = async () => { - try { - // Define your own configuration if needed - SitumPlugin.setConfiguration({useRemoteConfig: true}); - // Request permissions and start positioning - await requestPermission() - .then(() => { - SitumPlugin.requestLocationUpdates(); - }) - .catch(console.debug); - } catch (e) { - console.log(`Situm > example > Could not start positioning ${e}`); - } + // Define your own configuration if needed + SitumPlugin.setConfiguration({useRemoteConfig: true}); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + // Start positioning: + SitumPlugin.requestLocationUpdates(); }; /** diff --git a/example/src/examples/wayfinding/Wayfinding.tsx b/example/src/examples/wayfinding/Wayfinding.tsx index 506bc11b..215bc4bf 100644 --- a/example/src/examples/wayfinding/Wayfinding.tsx +++ b/example/src/examples/wayfinding/Wayfinding.tsx @@ -16,7 +16,6 @@ import type { MapViewRef, } from '@situm/react-native'; import {SITUM_API_KEY, SITUM_BUILDING_ID, SITUM_PROFILE} from '../../situm'; -import requestPermission from '../Utils/requestPermission'; const styles = StyleSheet.create({ container: { @@ -58,23 +57,15 @@ const Screen: React.FC = () => { // Set positioning configuration SitumPlugin.setConfiguration({useRemoteConfig: true}); - //Request permissions and start positioning - requestPermission() - .then(() => { - if (!SitumPlugin.positioningIsRunning()) { - SitumPlugin.requestLocationUpdates(); - console.log('Situm > example > Starting positioning'); - } - }) - .catch(e => { - console.log(`Situm > example > Permissions rejected: ${e}`); - }) - .finally(() => { - //Register listener to react to the app comming from the background - appStateListener = registerAppStateListener(); - //Register callbacks - registerCallbacks(); - }); + // Register listener to react to the app comming from the background + appStateListener = registerAppStateListener(); + // Register callbacks + registerCallbacks(); + // Tells the underlying native SDKs to automatically manage permissions + // and sensor related issues. + SitumPlugin.enableUserHelper(); + // Start positioning: + SitumPlugin.requestLocationUpdates(); // When unmounting make sure to stop positioning and remove listeners return () => { diff --git a/example/tsconfig.json b/example/tsconfig.json index c5dea606..4d332b1f 100644 --- a/example/tsconfig.json +++ b/example/tsconfig.json @@ -1,6 +1,11 @@ { "compilerOptions": { - "jsx": "react" + "jsx": "react", + "baseUrl": ".", + "paths": { + "@situm/react-native": ["../src"], + "@situm/react-native-wayfinding": ["../src/wayfinding"] + } }, "extends": "@tsconfig/react-native/tsconfig.json" } diff --git a/example/yarn.lock b/example/yarn.lock index da8d2ab7..33965466 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -1852,7 +1852,7 @@ "@sinonjs/commons" "^3.0.0" "@situm/react-native@file:..": - version "3.8.5" + version "3.14.13" dependencies: react-dom "^18.2.0" react-native-webview "*" @@ -2361,6 +2361,17 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" +babel-plugin-module-resolver@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz#cdeac5d4aaa3b08dd1ac23ddbf516660ed2d293e" + integrity sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg== + dependencies: + find-babel-config "^2.1.1" + glob "^9.3.3" + pkg-up "^3.1.0" + reselect "^4.1.7" + resolve "^1.22.8" + babel-plugin-polyfill-corejs2@^0.4.10: version "0.4.11" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" @@ -2483,6 +2494,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -3488,6 +3506,13 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +find-babel-config@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-2.1.2.tgz#2841b1bfbbbcdb971e1e39df8cbc43dafa901716" + integrity sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg== + dependencies: + json5 "^2.2.3" + find-cache-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -3666,6 +3691,16 @@ glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^9.3.3: + version "9.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" + integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== + dependencies: + fs.realpath "^1.0.0" + minimatch "^8.0.2" + minipass "^4.2.4" + path-scurry "^1.6.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3935,6 +3970,13 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.2" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-view@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" @@ -4802,6 +4844,11 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5198,11 +5245,28 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatc dependencies: brace-expansion "^1.1.7" +minimatch@^8.0.2: + version "8.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" + integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" + integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -5535,6 +5599,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.6.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -5574,6 +5646,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + pod-install@0.1.38: version "0.1.38" resolved "https://registry.yarnpkg.com/pod-install/-/pod-install-0.1.38.tgz#1c16a800a5fc1abea0cafcc0e190f376368c76ab" @@ -5947,6 +6026,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +reselect@^4.1.7: + version "4.1.8" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" + integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -5983,6 +6067,15 @@ resolve@^1.14.2, resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.8: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.5: version "2.0.0-next.5" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" diff --git a/ios/SitumPlugin.m b/ios/SitumPlugin.m index afba5c63..02a9ca24 100755 --- a/ios/SitumPlugin.m +++ b/ios/SitumPlugin.m @@ -784,13 +784,53 @@ - (void)attachGeofenceListener { SITLocationManager.sharedInstance.geofenceDelegate = self; } +RCT_EXPORT_METHOD(configureUserHelper:(NSDictionary *)options + withSuccessCallback:(RCTResponseSenderBlock)successBlock + errorCallback:(RCTResponseSenderBlock)errorBlock) +{ + @try { + BOOL enabled = NO; + + if (options[@"enabled"]) { + enabled = [options[@"enabled"] boolValue]; + } + [[SITUserHelperManager sharedInstance] autoManage:enabled]; + + // Configure color scheme if necessary: + if (options[@"colorScheme"]) { + id colorSchemeValue = options[@"colorScheme"]; + + if ([colorSchemeValue isKindOfClass:[NSDictionary class]]) { + NSDictionary *colorScheme = options[@"colorScheme"]; + NSString *primaryColor = colorScheme[@"primaryColor"]; + NSString *secondaryColor = colorScheme[@"secondaryColor"]; + + SITUserHelperColorScheme *helperColorScheme = [[SITUserHelperColorScheme alloc] init]; + + if (primaryColor) { + helperColorScheme.primaryColor = primaryColor; + } + if (secondaryColor) { + helperColorScheme.secondaryColor = secondaryColor; + } + + [[SITUserHelperManager sharedInstance] setColorScheme:helperColorScheme]; + } + } + + successBlock(@[@"User helper configured"]); + } + @catch (NSException *exception) { + errorBlock(@[exception.reason]); + } +} + // SITRealtimeDelegate methods - (void)realTimeManager:(id _Nonnull)realTimeManager didUpdateUserLocations:(SITRealTimeData * _Nonnull)realTimeData { // SITRealTimeData to json NSDictionary *realtimeInfo = [SitumLocationWrapper.shared jsonFromRealtimeData:realTimeData]; - NSLog(@"ADDDDDED UPDATESSSS"); if (_realtimeUpdates) { [self sendEventWithName:@"realtimeUpdated" body:realtimeInfo.copy]; } diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 6f57482f..23f62997 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -29,6 +29,7 @@ import { type Point, type Route, type SdkVersion, + type UserHelperOptions, } from "./types"; import { InternalCallType, SdkNavigationUpdateType } from "./types/constants"; import { @@ -715,6 +716,56 @@ export default class SitumPlugin { }); }; + /** + * Automatically assists users in resolving app-related permission and sensor issues. + * + * This method tells the native SDKs to present a user interface that explains detected + * configuration issues and guides users through the required steps to resolve them, + * following best practices for runtime permission requests. + * + * Issues addressed include: + * - Missing permissions for Location or Bluetooth. + * - Disabled Location or Bluetooth sensors. + * + * Use the userHelperOptions parameter to configure the available options. + * Call {@link enableUserHelper} as a shortcut to enable the user helper with default configuration. + * Call {@link disableUserHelper} as a shortcut to disable the user helper. + * + * @param {UserHelperOptions} userHelperOptions - Options for the user helper. + * @param {function} cb - Cordova native callback to receive data. + * @param {function} error - Cordova native callback to receive errors. + */ + static configureUserHelper = (userHelperOptions: UserHelperOptions) => { + _registerCallbacks(); + return exceptionWrapper(({onSuccess, onError}) => { + RNCSitumPlugin.configureUserHelper(userHelperOptions, onSuccess, onError); + }); + }; + + /** + * Enables the user helper. + * + * Shortcut for {@link configureUserHelper} with {enabled: true}. + * + * @param {function} cb - Cordova native callback to receive data. + * @param {function} error - Cordova native callback to receive errors. + */ + static enableUserHelper = () => { + SitumPlugin.configureUserHelper({enabled: true, colorScheme: undefined}); + }; + + /** + * Disables the user helper. + * + * Shortcut for {@link configureUserHelper} with {enabled: false}. + * + * @param {function} cb - Cordova native callback to receive data. + * @param {function} error - Cordova native callback to receive errors. + */ + static disableUserHelper = () => { + SitumPlugin.configureUserHelper({enabled: false, colorScheme: undefined}); + }; + /** * INTERNAL METHOD. * diff --git a/src/sdk/nativeInterface.ts b/src/sdk/nativeInterface.ts index 5b536c1a..4a9353cc 100644 --- a/src/sdk/nativeInterface.ts +++ b/src/sdk/nativeInterface.ts @@ -19,6 +19,7 @@ import type { PoiCategory, PoiIcon, Point, + UserHelperOptions, } from "./types"; interface CartographyAPI { @@ -147,6 +148,11 @@ export interface SitumPluginInterface requestRealTimeUpdates: (options: any) => void; removeRealTimeUpdates: () => void; validateMapViewProjectSettings: () => void; + configureUserHelper: ( + userHelperOptions: UserHelperOptions, + success: (response: any) => void, + error: (response: any) => void + ) => void; } const { RNCSitumPlugin } = NativeModules; diff --git a/src/sdk/types/index.ts b/src/sdk/types/index.ts index b1b9d848..95683520 100644 --- a/src/sdk/types/index.ts +++ b/src/sdk/types/index.ts @@ -683,3 +683,31 @@ export class InternalCall { return this.data as unknown as T; } } + +/** + * @name + * UserHelperColorScheme + * @description + * Color scheme for the user helper UI. + * @property {string} primaryColor - Primary color for the user helper UI. Use HEX color code (e.g. "#ff5733"). + * @property {string} secondaryColor - Secondary color for the user helper UI. Use HEX color code (e.g. "#ff5733"). + */ + +export type UserHelperColorScheme = { + primaryColor: string, + secondaryColor: string, +}; + +/** + * @name + * UserHelperOptions + * @description + * Configuration options for the user helper. + * @property {boolean} enabled - Whether the user helper is enabled. Equivalent to the underlying native SitumSdk.userHelperManager#autoManage(true). + * @property {UserHelperColorScheme} colorScheme - Color scheme for the user helper UI. + */ + +export type UserHelperOptions = { + enabled: boolean, + colorScheme: UserHelperColorScheme | undefined, +}; \ No newline at end of file