diff --git a/README.md b/README.md index 8f96a62..16db60e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ This is the beta release of the Google Driver SDK package for React Native. It i | | Android | iOS | | ------------------------------- | ------- | --------- | -| **Minimum mobile OS supported** | SDK 24+ | iOS 16.0+ | +| **Minimum mobile OS supported** | SDK 26+ | iOS 16.0+ | +| **Compile/Target SDK** | SDK 36+ | - | * A React Native project * A Google Cloud project @@ -100,16 +101,18 @@ dependencies { #### Minimum SDK Requirements for Android -The `minSdkVersion` for your Android project must be set to 24 or higher in `android/app/build.gradle`: +The `minSdkVersion` for your Android project must be set to 26 or higher in `android/app/build.gradle`: ```groovy android { defaultConfig { - minSdkVersion 24 + minSdkVersion 26 } } ``` +The `compileSdkVersion` and `targetSdkVersion` should be set to 36 or higher. + #### Set Google Maps API Key To securely store your API key, it is recommended to use the [Google Maps Secrets Gradle Plugin](https://developers.google.com/maps/documentation/android-sdk/secrets-gradle-plugin). This plugin helps manage API keys without exposing them in your app's source code. diff --git a/android/build.gradle b/android/build.gradle index c299c59..cee74e0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -40,7 +40,7 @@ if (isNewArchitectureEnabled()) { android { namespace "com.google.android.react.driversdk" - compileSdkVersion 35 + compileSdkVersion 36 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -48,8 +48,8 @@ android { } defaultConfig { - minSdkVersion 23 - targetSdkVersion 34 + minSdkVersion 26 + targetSdkVersion 36 versionCode 1 // get version name from package.json version versionName "1.0" @@ -85,7 +85,7 @@ repositories { } dependencies { - implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-driver:6.2.0' + implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-driver:7.0.0' implementation 'com.facebook.react:react-native:+' implementation 'com.android.support:multidex:1.0.3' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/android/src/main/java/com/google/android/react/driversdk/lmfs/DeliveryDriverModule.java b/android/src/main/java/com/google/android/react/driversdk/lmfs/DeliveryDriverModule.java index 39e4918..d39edfc 100644 --- a/android/src/main/java/com/google/android/react/driversdk/lmfs/DeliveryDriverModule.java +++ b/android/src/main/java/com/google/android/react/driversdk/lmfs/DeliveryDriverModule.java @@ -137,7 +137,7 @@ public void setLocationReportingInterval(int interval, Promise promise) { return; } - vehicleReporter.setLocationReportingInterval(new Long(interval), TimeUnit.SECONDS); + vehicleReporter.setLocationReportingInterval(Long.valueOf(interval), TimeUnit.SECONDS); } catch (Exception e) { promise.reject(e.toString(), e.getMessage(), e); } diff --git a/android/src/main/java/com/google/android/react/driversdk/odrd/RidesharingModule.java b/android/src/main/java/com/google/android/react/driversdk/odrd/RidesharingModule.java index edaa70d..dfb7f65 100644 --- a/android/src/main/java/com/google/android/react/driversdk/odrd/RidesharingModule.java +++ b/android/src/main/java/com/google/android/react/driversdk/odrd/RidesharingModule.java @@ -161,7 +161,7 @@ public void setLocationReportingInterval(int interval, Promise promise) { return; } - vehicleReporter.setLocationReportingInterval(new Long(interval), TimeUnit.SECONDS); + vehicleReporter.setLocationReportingInterval(Long.valueOf(interval), TimeUnit.SECONDS); } catch (Exception e) { promise.reject(e.toString(), e.getMessage(), e); } diff --git a/example/.env.sample b/example/.env.sample index 5ade0a9..06a98fb 100644 --- a/example/.env.sample +++ b/example/.env.sample @@ -34,5 +34,9 @@ IOS_HOST=localhost LMFS_PORT=8091 ODRD_PORT=8092 +# Optional: Set vehicle IDs for the example apps (can also be set in the app UI or code) +# LMFS_VEHICLE_ID=your_lmfs_vehicle_id +# ODRD_VEHICLE_ID=your_odrd_vehicle_id + # This is typically `~/.config/gcloud` on MacOS and Linux, or `%APPDATA%\gcloud` on Windows. See ./tools/backend/README.md for more details. GCLOUD_CONFIG_DIR=~/.config/gcloud diff --git a/example/LMFS/README.md b/example/LMFS/README.md index b5081e5..0c3e314 100644 --- a/example/LMFS/README.md +++ b/example/LMFS/README.md @@ -8,7 +8,13 @@ This is a sample application that show cases developers how to integrate with th 1. This library depends on the LMFS backend available in https://github.com/googlemaps/last-mile-fleet-solution-samples/tree/main/backend. This package provides docker-compose files to run the backend services at `/example/tools/backend` folder. For more information, please refer to the [README](../tools/backend/README.md) file in the tools/backend folder of the example app. 2. Once the backend is setup, create a delivery vehicle and keep the vehicleId handy. In order to make it easier to create vehicles with tasks, you can use the `/upload-delivery-config.html` endpoint on the backend. [example json](https://raw.githubusercontent.com/googlemaps/last-mile-fleet-solution-samples/main/backend/src/test/resources/test.json) -3. Go to the [App.tsx](/example/LMFS/src/App.tsx) file and update the VEHICLE_ID from the endpoint response. +3. Configure the VEHICLE_ID using one of the following methods: + - **Option 1: .env file (Recommended)** - Add the vehicle ID to your `.env` file in the `example` folder: + ``` + LMFS_VEHICLE_ID=your_vehicle_id + ``` + - **Option 2: Direct Input** - When you start the app, you can enter the vehicle ID directly in the "VEHICLE_ID Not Configured" screen. + - **Option 3: Code Update** - Go to the [App.tsx](/example/LMFS/src/App.tsx) file and update the VEHICLE_ID_DEFAULT constant with your vehicle ID. ## Setup diff --git a/example/LMFS/android/build.gradle b/example/LMFS/android/build.gradle index 422f111..0921f81 100644 --- a/example/LMFS/android/build.gradle +++ b/example/LMFS/android/build.gradle @@ -20,7 +20,7 @@ def enableDesugaring = true buildscript { ext { buildToolsVersion = "36.0.0" - minSdkVersion = 34 + minSdkVersion = 26 compileSdkVersion = 36 targetSdkVersion = 36 ndkVersion = "27.1.12297006" diff --git a/example/LMFS/babel.config.js b/example/LMFS/babel.config.js index 26bda75..ae021e4 100644 --- a/example/LMFS/babel.config.js +++ b/example/LMFS/babel.config.js @@ -27,8 +27,14 @@ module.exports = getConfig( { envName: 'APP_ENV', path: '../.env', - allowlist: ['PROJECT_ID', 'ANDROID_HOST', 'IOS_HOST', 'LMFS_PORT'], - allowUndefined: false, + allowlist: [ + 'PROJECT_ID', + 'ANDROID_HOST', + 'IOS_HOST', + 'LMFS_PORT', + 'LMFS_VEHICLE_ID', + ], + allowUndefined: true, verbose: false, }, ], diff --git a/example/LMFS/env.d.ts b/example/LMFS/env.d.ts index f129924..e14f8eb 100644 --- a/example/LMFS/env.d.ts +++ b/example/LMFS/env.d.ts @@ -19,4 +19,5 @@ declare module '@env' { export const ANDROID_HOST: string; export const IOS_HOST: string; export const LMFS_PORT: number; + export const LMFS_VEHICLE_ID: string; } diff --git a/example/LMFS/src/App.tsx b/example/LMFS/src/App.tsx index be1a9e4..3e773e8 100644 --- a/example/LMFS/src/App.tsx +++ b/example/LMFS/src/App.tsx @@ -19,6 +19,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { StyleSheet, Text, + TextInput, View, Platform, Modal, @@ -46,6 +47,7 @@ import { ANDROID_HOST, IOS_HOST, LMFS_PORT, + LMFS_VEHICLE_ID, } from '@env'; import usePermissions from './checkPermissions'; @@ -56,7 +58,12 @@ const BASE_URL = : `http://${IOS_HOST}:${LMFS_PORT}`; // Update this vehicle id from the response from the /upload-delivery-config.html backend endpoint. -const VEHICLE_ID = ''; // ADD_VEHICLE_ID_HERE +// Can also be set via LMFS_VEHICLE_ID in .env file or as environment variable: +// See README.md for configuration options. +const VEHICLE_ID_DEFAULT = LMFS_VEHICLE_ID || ''; // ADD_VEHICLE_ID_HERE + +// New location reporting interval in seconds that is applied via menu action. +const NEW_LOCATION_REPORTING_INTERVAL_SECONDS = 20; const termsAndConditionsDialogOptions: TermsAndConditionsDialogOptions = { title: 'RN LMFS Sample', @@ -78,6 +85,9 @@ function LMFSSampleApp() { useState(false); const [authToken, setAuthToken] = useState(null); const [driverSdkVersion, setDriverSdkVersion] = useState(''); + const [vehicleId, setVehicleId] = useState(VEHICLE_ID_DEFAULT); + const [tempVehicleId, setTempVehicleId] = + useState(VEHICLE_ID_DEFAULT); const clearInstance = useCallback(async () => { await deliveryDriverApi.clearInstance(); @@ -153,7 +163,33 @@ function LMFSSampleApp() { [onArrival, onNavigationReady, onNavigationInitError, onRouteStatusResult] ); + const fetchAuthToken = useCallback(async () => { + if (!vehicleId) { + console.log('Vehicle ID not set, skipping auth token fetch'); + return; + } + try { + console.log('Fetching auth token...'); + const tokenUrl = BASE_URL + '/token/delivery_driver/' + vehicleId; + const response = await fetch(tokenUrl); + const { token } = await response.json(); + console.log('Got token:', token); + + setAuthToken(token); + } catch (error) { + console.log( + 'There has been a problem connecting to the provider, please make sure it is running. ', + error + ); + } + }, [vehicleId]); + useEffect(() => { + if (!vehicleId) { + console.log('Vehicle ID not set, skipping initialization'); + return; + } + console.log('Init LMFS Example app'); removeListeners(navigationCallbacks); addListeners(navigationCallbacks); @@ -167,31 +203,21 @@ function LMFSSampleApp() { removeListeners(navigationCallbacks); clearInstance(); }; - }, [clearInstance, navigationCallbacks, addListeners, removeListeners]); - - const fetchAuthToken = async () => { - try { - console.log('Fetching auth token...'); - const tokenUrl = BASE_URL + '/token/delivery_driver/' + VEHICLE_ID; - const response = await fetch(tokenUrl); - const { token } = await response.json(); - console.log('Got token:', token); - - setAuthToken(token); - } catch (error) { - console.log( - 'There has been a problem connecting to the provider, please make sure it is running. ', - error - ); - } - }; + }, [ + clearInstance, + navigationCallbacks, + addListeners, + removeListeners, + vehicleId, + fetchAuthToken, + ]); const createInstance = async () => { try { console.log('Creating LMFS instance'); await deliveryDriverApi.initialize( PROVIDER_ID, - VEHICLE_ID, + vehicleId, _tokenContext => { console.log('onGetToken call, return token: ', authToken); // Check if the token is expired, in such case request a new one. @@ -237,11 +263,14 @@ function LMFSSampleApp() { } }; - const setUpdateInterval = async () => { + const setLocationReportingInterval = async () => { try { await deliveryDriverApi .getDeliveryVehicleReporter() - .setLocationReportingInterval(20); + .setLocationReportingInterval(NEW_LOCATION_REPORTING_INTERVAL_SECONDS); + console.log( + `Location reporting interval set to ${NEW_LOCATION_REPORTING_INTERVAL_SECONDS} seconds` + ); } catch (e) { console.error(e); } @@ -313,19 +342,54 @@ function LMFSSampleApp() { const controlsButtonColor = '#d32f2f'; const controlsButtonColorPressed = '#a12020'; - if (!VEHICLE_ID) { + const handleSetVehicleId = () => { + if (tempVehicleId.trim()) { + setVehicleId(tempVehicleId.trim()); + } + }; + + if (!vehicleId) { return ( VEHICLE_ID Not Configured - Please set the VEHICLE_ID in App.tsx before running the app. + Please set the VEHICLE_ID to continue. - To configure: - {'\n'}1. Follow the setup instructions in the README - {'\n'}2. Create a delivery vehicle using the backend - {'\n'}3. Update VEHICLE_ID in example/LMFS/src/App.tsx + You can configure the vehicle ID in one of three ways: + {'\n'} + {'\n'}1. Add it to your .env file: + {'\n'} LMFS_VEHICLE_ID=your_vehicle_id + {'\n'} + {'\n'}2. Enter it directly below: + + + + + + + + + {'\n'}3. Update VEHICLE_ID_DEFAULT in example/LMFS/src/App.tsx + {'\n'} + {'\n'}To create a vehicle: + {'\n'}• Follow the setup instructions in the README + {'\n'}• Create a delivery vehicle using the backend + {'\n'}• Use the vehicle ID from the response {'\n'} {'\n'}See README.md for detailed instructions. @@ -355,7 +419,7 @@ function LMFSSampleApp() { numberOfLines={1} ellipsizeMode="tail" > - Vehicle ID: {VEHICLE_ID || 'Not set'} + Vehicle ID: {vehicleId || 'Not set'} @@ -642,6 +706,22 @@ const styles = StyleSheet.create({ color: '#0b1a2a', fontWeight: '600', }, + inputContainer: { + marginVertical: 16, + width: '100%', + maxWidth: 400, + }, + input: { + backgroundColor: '#ffffff', + borderWidth: 1, + borderColor: '#ccc', + borderRadius: 8, + paddingVertical: 12, + paddingHorizontal: 16, + fontSize: 16, + color: '#0b1a2a', + marginBottom: 12, + }, }); export default App; diff --git a/example/ODRD/README.md b/example/ODRD/README.md index 26c5148..7e7cafe 100644 --- a/example/ODRD/README.md +++ b/example/ODRD/README.md @@ -35,7 +35,13 @@ curl -X POST http://localhost:8092/vehicle/new \ }' ``` -1. Go to the [App.tsx](/example/ODRD/App.tsx) file and update the VEHICLE_ID according to your configuration. +1. Configure the VEHICLE_ID using one of the following methods: + - **Option 1: .env file (Recommended)** - Add the vehicle ID to your `.env` file in the `example` folder: + ``` + ODRD_VEHICLE_ID=your_vehicle_id + ``` + - **Option 2: Direct Input** - When you start the app, you can enter the vehicle ID directly in the "VEHICLE_ID Not Configured" screen. + - **Option 3: Code Update** - Go to the [App.tsx](/example/ODRD/src/App.tsx) file and update the VEHICLE_ID_DEFAULT constant with your vehicle ID. ## Setup diff --git a/example/ODRD/android/build.gradle b/example/ODRD/android/build.gradle index 422f111..0921f81 100644 --- a/example/ODRD/android/build.gradle +++ b/example/ODRD/android/build.gradle @@ -20,7 +20,7 @@ def enableDesugaring = true buildscript { ext { buildToolsVersion = "36.0.0" - minSdkVersion = 34 + minSdkVersion = 26 compileSdkVersion = 36 targetSdkVersion = 36 ndkVersion = "27.1.12297006" diff --git a/example/ODRD/babel.config.js b/example/ODRD/babel.config.js index 29ad7d7..d194e6a 100644 --- a/example/ODRD/babel.config.js +++ b/example/ODRD/babel.config.js @@ -27,8 +27,14 @@ module.exports = getConfig( { envName: 'APP_ENV', path: '../.env', - allowlist: ['PROJECT_ID', 'ANDROID_HOST', 'IOS_HOST', 'ODRD_PORT'], - allowUndefined: false, + allowlist: [ + 'PROJECT_ID', + 'ANDROID_HOST', + 'IOS_HOST', + 'ODRD_PORT', + 'ODRD_VEHICLE_ID', + ], + allowUndefined: true, verbose: false, }, ], diff --git a/example/ODRD/env.d.ts b/example/ODRD/env.d.ts index 7f30687..e7dc183 100644 --- a/example/ODRD/env.d.ts +++ b/example/ODRD/env.d.ts @@ -19,4 +19,5 @@ declare module '@env' { export const ANDROID_HOST: string; export const IOS_HOST: string; export const ODRD_PORT: number; + export const ODRD_VEHICLE_ID: string; } diff --git a/example/ODRD/src/App.tsx b/example/ODRD/src/App.tsx index a3d0a2b..92f9d2d 100644 --- a/example/ODRD/src/App.tsx +++ b/example/ODRD/src/App.tsx @@ -19,6 +19,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { StyleSheet, Text, + TextInput, View, Platform, Modal, @@ -48,6 +49,7 @@ import { ANDROID_HOST, IOS_HOST, ODRD_PORT, + ODRD_VEHICLE_ID, } from '@env'; import usePermissions from './checkPermissions'; @@ -58,7 +60,12 @@ const BASE_URL = : `http://${IOS_HOST}:${ODRD_PORT}`; // Update this with according your configuration sent to the backend/provider. -const VEHICLE_ID = ''; // ADD_VEHICLE_ID_HERE +// Can also be set via ODRD_VEHICLE_ID in .env file or as environment variable: +// See README.md for configuration options. +const VEHICLE_ID_DEFAULT = ODRD_VEHICLE_ID || ''; // ADD_VEHICLE_ID_HERE + +// New location reporting interval in seconds that is applied via menu action. +const NEW_LOCATION_REPORTING_INTERVAL_SECONDS = 20; const termsAndConditionsDialogOptions: TermsAndConditionsDialogOptions = { title: 'RN ODRD Sample', @@ -81,6 +88,9 @@ function ODRDSampleApp() { useState(false); const [authToken, setAuthToken] = useState(null); const [driverSdkVersion, setDriverSdkVersion] = useState(''); + const [vehicleId, setVehicleId] = useState(VEHICLE_ID_DEFAULT); + const [tempVehicleId, setTempVehicleId] = + useState(VEHICLE_ID_DEFAULT); const clearInstance = useCallback(async () => { await ridesharingDriverApi.clearInstance(); @@ -146,6 +156,27 @@ function ODRDSampleApp() { [navigationController] ); + const fetchAuthToken = useCallback(async () => { + if (!vehicleId) { + console.log('Vehicle ID not set, skipping auth token fetch'); + return; + } + try { + console.log('Fetching auth token...'); + const tokenUrl = BASE_URL + '/token/driver/' + vehicleId; + const response = await fetch(tokenUrl); + const token = await response.json(); + console.log('Got token:', token); + + setAuthToken(token.jwt); + } catch (error) { + console.log( + 'There has been a problem connecting to the provider, please make sure it is running. ', + error + ); + } + }, [vehicleId]); + const navigationCallbacks: NavigationCallbacks = useMemo( () => ({ onArrival, @@ -157,6 +188,11 @@ function ODRDSampleApp() { ); useEffect(() => { + if (!vehicleId) { + console.log('Vehicle ID not set, skipping initialization'); + return; + } + console.log('Init ODRD Example app'); removeListeners(navigationCallbacks); addListeners(navigationCallbacks); @@ -170,31 +206,21 @@ function ODRDSampleApp() { removeListeners(navigationCallbacks); clearInstance(); }; - }, [clearInstance, navigationCallbacks, addListeners, removeListeners]); - - const fetchAuthToken = async () => { - try { - console.log('Fetching auth token...'); - const tokenUrl = BASE_URL + '/token/driver/' + VEHICLE_ID; - const response = await fetch(tokenUrl); - const token = await response.json(); - console.log('Got token:', token); - - setAuthToken(token.jwt); - } catch (error) { - console.log( - 'There has been a problem connecting to the provider, please make sure it is running. ', - error - ); - } - }; + }, [ + clearInstance, + navigationCallbacks, + addListeners, + removeListeners, + vehicleId, + fetchAuthToken, + ]); const createInstance = async () => { try { console.log('Creating ODRD instance'); await ridesharingDriverApi.initialize( PROVIDER_ID, - VEHICLE_ID, + vehicleId, _tokenContext => { console.log('onGetToken call, return token: ', authToken); // Check if the token is expired, in such case request a new one. @@ -229,11 +255,14 @@ function ODRDSampleApp() { } }; - const setUpdateInterval = async () => { + const setLocationReportingInterval = async () => { try { await ridesharingDriverApi .getRidesharingVehicleReporter() - .setLocationReportingInterval(20); + .setLocationReportingInterval(NEW_LOCATION_REPORTING_INTERVAL_SECONDS); + console.log( + `Location reporting interval set to ${NEW_LOCATION_REPORTING_INTERVAL_SECONDS} seconds` + ); } catch (e) { console.error(e); } @@ -314,19 +343,54 @@ function ODRDSampleApp() { const controlsButtonColor = '#d32f2f'; const controlsButtonColorPressed = '#a12020'; - if (!VEHICLE_ID) { + const handleSetVehicleId = () => { + if (tempVehicleId.trim()) { + setVehicleId(tempVehicleId.trim()); + } + }; + + if (!vehicleId) { return ( VEHICLE_ID Not Configured - Please set the VEHICLE_ID in App.tsx before running the app. + Please set the VEHICLE_ID to continue. - To configure: - {'\n'}1. Follow the setup instructions in the README - {'\n'}2. Create a vehicle using the backend POST request - {'\n'}3. Update VEHICLE_ID in example/ODRD/src/App.tsx + You can configure the vehicle ID in one of three ways: + {'\n'} + {'\n'}1. Add it to your .env file: + {'\n'} ODRD_VEHICLE_ID=your_vehicle_id + {'\n'} + {'\n'}2. Enter it directly below: + + + + + + + + + {'\n'}3. Update VEHICLE_ID_DEFAULT in example/ODRD/src/App.tsx + {'\n'} + {'\n'}To create a vehicle: + {'\n'}• Follow the setup instructions in the README + {'\n'}• Create a vehicle using the backend POST request + {'\n'}• Use the vehicle ID from the response {'\n'} {'\n'}See README.md for detailed instructions. @@ -356,7 +420,7 @@ function ODRDSampleApp() { numberOfLines={1} ellipsizeMode="tail" > - Vehicle ID: {VEHICLE_ID || 'Not set'} + Vehicle ID: {vehicleId || 'Not set'} @@ -657,6 +721,22 @@ const styles = StyleSheet.create({ color: '#0b1a2a', fontWeight: '600', }, + inputContainer: { + marginVertical: 16, + width: '100%', + maxWidth: 400, + }, + input: { + backgroundColor: '#ffffff', + borderWidth: 1, + borderColor: '#ccc', + borderRadius: 8, + paddingVertical: 12, + paddingHorizontal: 16, + fontSize: 16, + color: '#0b1a2a', + marginBottom: 12, + }, }); export default App;