Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,34 @@ setOnArrival(null);
| `setOnReroutingRequestedByOffRoute` | `void` | Called when rerouting is triggered by off-route |
| `setOnTrafficUpdated` | `void` | Called when traffic data is updated |
| `setOnRemainingTimeOrDistanceChanged` | `void` | Called when remaining time or distance changes |
| `setOnTurnByTurn` | `{ navInfo: NavInfo }` | Called with turn-by-turn navigation info |
| `setOnTurnByTurn` | `TurnByTurnEvent[]` | Called with turn-by-turn navigation info |

Turn-by-turn events include navigation state, distance and time estimates, the current step, and
remaining steps. Step distances are in meters and durations are in seconds. `instruction`,
`exitNumber`, and `fullRoadName` can be absent; an arrival instruction can also be empty.
`roundaboutTurnNumber` is `-1` when the step is not in a roundabout.

```tsx
import { useEffect } from 'react';
import { Maneuver, useNavigation } from '@googlemaps/react-native-navigation-sdk';

const { setOnTurnByTurn } = useNavigation();

useEffect(() => {
setOnTurnByTurn(events => {
const step = events[0]?.currentStep;
if (!step) {
return;
}

if (step.maneuver === Maneuver.TURN_LEFT) {
console.log(step.instruction ?? 'Turn left');
}
});

return () => setOnTurnByTurn(null);
}, [setOnTurnByTurn]);
```

### MapViewAutoController (useNavigationAuto hook)

Expand Down
49 changes: 48 additions & 1 deletion example/src/screens/integration_tests/integration_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,46 @@ const NAVIGATOR_NOT_READY_ERROR_CODE = 'NO_NAVIGATOR_ERROR_CODE';
const NO_DESTINATIONS_ERROR_CODE = 'NO_DESTINATIONS';
export const NO_ERRORS_DETECTED_LABEL = 'No errors detected';

const validateTurnByTurnPayload = (
events: TurnByTurnEvent[]
): string | null => {
const event = events[0];
if (!event) {
return 'Turn-by-turn event payload was empty';
}
if (
typeof event.navState !== 'number' ||
typeof event.routeChanged !== 'boolean'
) {
return 'Turn-by-turn event had invalid navigation state fields';
}
if (!Array.isArray(event.getRemainingSteps)) {
return 'Turn-by-turn event had invalid remaining steps';
}

const step = event.currentStep ?? event.getRemainingSteps[0];
if (!step) {
return 'Turn-by-turn event did not include a step';
}

const numericStepFields = [
step.distanceFromPrevStepMeters,
step.timeFromPrevStepSeconds,
step.drivingSide,
step.stepNumber,
step.maneuver,
step.roundaboutTurnNumber,
];
if (!numericStepFields.every(field => typeof field === 'number')) {
return 'Turn-by-turn step had invalid numeric fields';
}
if (step.instruction != null && typeof step.instruction !== 'string') {
return 'Turn-by-turn step had an invalid instruction';
}

return null;
};

type NativeModuleError = {
code?: string;
};
Expand Down Expand Up @@ -1911,8 +1951,15 @@ export const testNavInfoEventsAfterCleanup = async (testTools: TestTools) => {

let phase: 'first' | 'second' = 'first';

setOnTurnByTurn(async (_events: TurnByTurnEvent[]) => {
setOnTurnByTurn(async (events: TurnByTurnEvent[]) => {
if (phase === 'first') {
const payloadError = validateTurnByTurnPayload(events);
if (payloadError) {
setOnTurnByTurn(null);
failTest(payloadError);
return;
}

// Received navInfo in first session — now cleanup and re-init
phase = 'second';
setOnTurnByTurn(null);
Expand Down
2 changes: 2 additions & 0 deletions ios/react-native-navigation-sdk/NavModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,8 @@ - (NSDictionary *)getStepInfo:(GMSNavigationStepInfo *)stepInfo {
[obj setValue:[NSNumber numberWithInteger:stepInfo.drivingSide] forKey:@"drivingSide"];
[obj setValue:[NSNumber numberWithInteger:stepInfo.stepNumber] forKey:@"stepNumber"];
[obj setValue:[NSNumber numberWithInteger:stepInfo.maneuver] forKey:@"maneuver"];
[obj setValue:[NSNumber numberWithInteger:stepInfo.roundaboutTurnNumber]
forKey:@"roundaboutTurnNumber"];
[obj setValue:stepInfo.exitNumber forKey:@"exitNumber"];
[obj setValue:stepInfo.fullRoadName forKey:@"fullRoadName"];
[obj setValue:stepInfo.fullInstructionText forKey:@"instruction"];
Expand Down
16 changes: 11 additions & 5 deletions src/native/NativeNavModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,18 @@ type TurnByTurnEventSpec = Readonly<{
getRemainingSteps: ReadonlyArray<StepInfoSpec>;
}>;

// This mirrors the fields emitted by Android's StepInfo and iOS's
// GMSNavigationStepInfo. Keep it aligned with the public TurnByTurnStep type.
type StepInfoSpec = Readonly<{
instruction: string;
distanceMeters: Double;
durationSeconds: Double;
maneuver: string;
position: LatLngSpec;
instruction?: string | null;
distanceFromPrevStepMeters: Double;
timeFromPrevStepSeconds: Double;
drivingSide: Double;
stepNumber: Double;
maneuver: Double;
roundaboutTurnNumber: Double;
exitNumber?: string | null;
fullRoadName?: string | null;
}>;

enum RouteStatusSpec {
Expand Down
53 changes: 48 additions & 5 deletions src/navigation/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import type {
TravelMode,
Waypoint,
TermsAndConditionsUIParams,
DrivingSide,
Maneuver,
NavState,
} from '../types';
import { NavigationSessionStatus } from '../types';

Expand Down Expand Up @@ -550,8 +553,48 @@ export enum TaskRemovedBehavior {
QUIT_SERVICE,
}

/**
* Defines the turn-by-turn event data.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface TurnByTurnEvent {}
/** Information about one step in a turn-by-turn navigation route. */
export interface TurnByTurnStep {
/** The step instruction. It can be absent or empty for arrival steps. */
instruction?: string | null;
/** Distance from the previous step, in meters. */
distanceFromPrevStepMeters: number;
/** Estimated duration of the step, in seconds. */
timeFromPrevStepSeconds: number;
/** The side of the road used for this step. */
drivingSide: DrivingSide;
/** The zero-based index of this step in the route. */
stepNumber: number;
/** The maneuver to perform for this step. */
maneuver: Maneuver;
/** The number of the exit after entering a roundabout, or -1 when not applicable. */
roundaboutTurnNumber: number;
/** The route exit number, when available. */
exitNumber?: string | null;
/** The full road name, when available. */
fullRoadName?: string | null;
}

/** Defines the turn-by-turn event data. */
export interface TurnByTurnEvent {
/** The current navigation state. */
navState: NavState;
/** Whether the route changed since the previous event. */
routeChanged: boolean;
/** Distance to the current step, in meters. */
distanceToCurrentStepMeters?: number;
/** Distance to the final destination, in meters. */
distanceToFinalDestinationMeters?: number;
/** Estimated time to the current step, in seconds. */
timeToCurrentStepSeconds?: number;
/** Distance to the next destination, in meters. */
distanceToNextDestinationMeters?: number;
/** Estimated time to the next destination, in seconds. */
timeToNextDestinationSeconds?: number;
/** Estimated time to the final destination, in seconds. */
timeToFinalDestinationSeconds?: number;
/** The step currently being navigated, when available. */
currentStep?: TurnByTurnStep;
/** The remaining route steps. This name mirrors the native event payload. */
getRemainingSteps: TurnByTurnStep[];
}
70 changes: 70 additions & 0 deletions src/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,76 @@ export enum NavState {
STOPPED = 3,
}

/** The maneuver for a turn-by-turn navigation step. */
export enum Maneuver {
UNKNOWN = 0,
DEPART = 1,
DESTINATION = 2,
DESTINATION_LEFT = 3,
DESTINATION_RIGHT = 4,
STRAIGHT = 5,
TURN_LEFT = 6,
TURN_RIGHT = 7,
TURN_KEEP_LEFT = 8,
TURN_KEEP_RIGHT = 9,
TURN_SLIGHT_LEFT = 10,
TURN_SLIGHT_RIGHT = 11,
TURN_SHARP_LEFT = 12,
TURN_SHARP_RIGHT = 13,
TURN_U_TURN_CLOCKWISE = 14,
TURN_U_TURN_COUNTERCLOCKWISE = 15,
MERGE_UNSPECIFIED = 16,
MERGE_LEFT = 17,
MERGE_RIGHT = 18,
FORK_LEFT = 19,
FORK_RIGHT = 20,
ON_RAMP_UNSPECIFIED = 21,
ON_RAMP_LEFT = 22,
ON_RAMP_RIGHT = 23,
ON_RAMP_KEEP_LEFT = 24,
ON_RAMP_KEEP_RIGHT = 25,
ON_RAMP_SLIGHT_LEFT = 26,
ON_RAMP_SLIGHT_RIGHT = 27,
ON_RAMP_SHARP_LEFT = 28,
ON_RAMP_SHARP_RIGHT = 29,
ON_RAMP_U_TURN_CLOCKWISE = 30,
ON_RAMP_U_TURN_COUNTERCLOCKWISE = 31,
OFF_RAMP_UNSPECIFIED = 32,
OFF_RAMP_LEFT = 33,
OFF_RAMP_RIGHT = 34,
OFF_RAMP_KEEP_LEFT = 35,
OFF_RAMP_KEEP_RIGHT = 36,
OFF_RAMP_SLIGHT_LEFT = 37,
OFF_RAMP_SLIGHT_RIGHT = 38,
OFF_RAMP_SHARP_LEFT = 39,
OFF_RAMP_SHARP_RIGHT = 40,
OFF_RAMP_U_TURN_CLOCKWISE = 41,
OFF_RAMP_U_TURN_COUNTERCLOCKWISE = 42,
ROUNDABOUT_CLOCKWISE = 43,
ROUNDABOUT_COUNTERCLOCKWISE = 44,
ROUNDABOUT_STRAIGHT_CLOCKWISE = 45,
ROUNDABOUT_STRAIGHT_COUNTERCLOCKWISE = 46,
ROUNDABOUT_LEFT_CLOCKWISE = 47,
ROUNDABOUT_LEFT_COUNTERCLOCKWISE = 48,
ROUNDABOUT_RIGHT_CLOCKWISE = 49,
ROUNDABOUT_RIGHT_COUNTERCLOCKWISE = 50,
ROUNDABOUT_SLIGHT_LEFT_CLOCKWISE = 51,
ROUNDABOUT_SLIGHT_LEFT_COUNTERCLOCKWISE = 52,
ROUNDABOUT_SLIGHT_RIGHT_CLOCKWISE = 53,
ROUNDABOUT_SLIGHT_RIGHT_COUNTERCLOCKWISE = 54,
ROUNDABOUT_SHARP_LEFT_CLOCKWISE = 55,
ROUNDABOUT_SHARP_LEFT_COUNTERCLOCKWISE = 56,
ROUNDABOUT_SHARP_RIGHT_CLOCKWISE = 57,
ROUNDABOUT_SHARP_RIGHT_COUNTERCLOCKWISE = 58,
ROUNDABOUT_U_TURN_CLOCKWISE = 59,
ROUNDABOUT_U_TURN_COUNTERCLOCKWISE = 60,
ROUNDABOUT_EXIT_CLOCKWISE = 61,
ROUNDABOUT_EXIT_COUNTERCLOCKWISE = 62,
FERRY_BOAT = 63,
FERRY_TRAIN = 64,
NAME_CHANGE = 65,
}

/**
* Specify that the Navigation SDK should determine the appropriate day or night mode according to
* user's location and local time.
Expand Down
Loading