From 6f66729f0f3dd0af7312144e940582579d0eab93 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Thu, 18 Dec 2025 14:25:32 +0200 Subject: [PATCH 1/3] docs: improve how example app vehicle id is configured --- example/.env.sample | 4 ++ example/LMFS/README.md | 8 ++- example/LMFS/babel.config.js | 10 +++- example/LMFS/env.d.ts | 1 + example/LMFS/src/App.tsx | 95 +++++++++++++++++++++++++++++++----- example/ODRD/README.md | 8 ++- example/ODRD/babel.config.js | 10 +++- example/ODRD/env.d.ts | 1 + example/ODRD/src/App.tsx | 95 +++++++++++++++++++++++++++++++----- 9 files changed, 204 insertions(+), 28 deletions(-) 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/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..e1e471e 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,9 @@ 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 const termsAndConditionsDialogOptions: TermsAndConditionsDialogOptions = { title: 'RN LMFS Sample', @@ -78,6 +82,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(); @@ -154,6 +161,11 @@ function LMFSSampleApp() { ); useEffect(() => { + if (!vehicleId) { + console.log('Vehicle ID not set, skipping initialization'); + return; + } + console.log('Init LMFS Example app'); removeListeners(navigationCallbacks); addListeners(navigationCallbacks); @@ -167,12 +179,22 @@ function LMFSSampleApp() { removeListeners(navigationCallbacks); clearInstance(); }; - }, [clearInstance, navigationCallbacks, addListeners, removeListeners]); + }, [ + clearInstance, + navigationCallbacks, + addListeners, + removeListeners, + vehicleId, + ]); const fetchAuthToken = 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/' + VEHICLE_ID; + const tokenUrl = BASE_URL + '/token/delivery_driver/' + vehicleId; const response = await fetch(tokenUrl); const { token } = await response.json(); console.log('Got token:', token); @@ -191,7 +213,7 @@ function LMFSSampleApp() { 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. @@ -313,19 +335,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 +412,7 @@ function LMFSSampleApp() { numberOfLines={1} ellipsizeMode="tail" > - Vehicle ID: {VEHICLE_ID || 'Not set'} + Vehicle ID: {vehicleId || 'Not set'} (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(); @@ -157,6 +164,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,12 +182,22 @@ function ODRDSampleApp() { removeListeners(navigationCallbacks); clearInstance(); }; - }, [clearInstance, navigationCallbacks, addListeners, removeListeners]); + }, [ + clearInstance, + navigationCallbacks, + addListeners, + removeListeners, + vehicleId, + ]); const fetchAuthToken = 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/' + VEHICLE_ID; + const tokenUrl = BASE_URL + '/token/driver/' + vehicleId; const response = await fetch(tokenUrl); const token = await response.json(); console.log('Got token:', token); @@ -194,7 +216,7 @@ function ODRDSampleApp() { 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. @@ -314,19 +336,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 +413,7 @@ function ODRDSampleApp() { numberOfLines={1} ellipsizeMode="tail" > - Vehicle ID: {VEHICLE_ID || 'Not set'} + Vehicle ID: {vehicleId || 'Not set'} Date: Thu, 18 Dec 2025 14:48:35 +0200 Subject: [PATCH 2/3] feat: upgrade Android Driver SDK to version 7.0.0 --- README.md | 9 ++++++--- android/build.gradle | 8 ++++---- .../react/driversdk/lmfs/DeliveryDriverModule.java | 2 +- .../react/driversdk/odrd/RidesharingModule.java | 2 +- example/LMFS/android/build.gradle | 2 +- example/LMFS/src/App.tsx | 14 ++++++++++---- example/ODRD/android/build.gradle | 2 +- example/ODRD/src/App.tsx | 14 ++++++++++---- 8 files changed, 34 insertions(+), 19 deletions(-) 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/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/src/App.tsx b/example/LMFS/src/App.tsx index e1e471e..71c2e28 100644 --- a/example/LMFS/src/App.tsx +++ b/example/LMFS/src/App.tsx @@ -62,6 +62,9 @@ const BASE_URL = // 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', companyName: 'Sample Company', @@ -259,11 +262,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); } @@ -459,8 +465,8 @@ function LMFSSampleApp() { 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/src/App.tsx b/example/ODRD/src/App.tsx index 840bc72..438c93b 100644 --- a/example/ODRD/src/App.tsx +++ b/example/ODRD/src/App.tsx @@ -64,6 +64,9 @@ const BASE_URL = // 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', companyName: 'Sample Company', @@ -251,11 +254,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); } @@ -460,8 +466,8 @@ function ODRDSampleApp() { From afd5040af9f58c382fe7852d4b83da86ac2e577e Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Fri, 19 Dec 2025 10:32:50 +0200 Subject: [PATCH 3/3] docs: useEffect usage on example app --- example/LMFS/src/App.tsx | 43 ++++++++++++++++++++-------------------- example/ODRD/src/App.tsx | 43 ++++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/example/LMFS/src/App.tsx b/example/LMFS/src/App.tsx index 71c2e28..3e773e8 100644 --- a/example/LMFS/src/App.tsx +++ b/example/LMFS/src/App.tsx @@ -163,6 +163,27 @@ 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'); @@ -188,29 +209,9 @@ function LMFSSampleApp() { addListeners, removeListeners, vehicleId, + fetchAuthToken, ]); - const fetchAuthToken = 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 - ); - } - }; - const createInstance = async () => { try { console.log('Creating LMFS instance'); diff --git a/example/ODRD/src/App.tsx b/example/ODRD/src/App.tsx index 438c93b..92f9d2d 100644 --- a/example/ODRD/src/App.tsx +++ b/example/ODRD/src/App.tsx @@ -156,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, @@ -191,29 +212,9 @@ function ODRDSampleApp() { addListeners, removeListeners, vehicleId, + fetchAuthToken, ]); - const fetchAuthToken = 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 - ); - } - }; - const createInstance = async () => { try { console.log('Creating ODRD instance');