Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,31 @@ constructor(
getNavigator().setAudioGuidance(audioGuidanceSettings)
}

/**
* Sets whether guidance notifications should be shown when the app is not in the foreground. On
* Android, this controls heads-up notifications for guidance events (turns, etc.). On iOS, this
* controls background notifications containing guidance information.
*
* This method must be called on the UI thread. Wraps [Navigator.setHeadsUpNotificationEnabled].
* See
* [Google Navigation SDK for Android](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#setHeadsUpNotificationEnabled(boolean)).
*/
@Throws(FlutterError::class)
fun setGuidanceNotificationsEnabled(enabled: Boolean) {
val activity = getActivity()
activity.runOnUiThread { GoogleMapsNavigatorHolder.setGuidanceNotificationsEnabled(enabled) }
}

/**
* Gets whether guidance notifications are enabled. On Android, returns the state of heads-up
* notifications. On iOS, returns the state of background notifications.
*
* @return true if guidance notifications are enabled, false otherwise.
*/
fun getGuidanceNotificationsEnabled(): Boolean {
return GoogleMapsNavigatorHolder.getGuidanceNotificationsEnabled()
}

fun setSpeedAlertOptions(options: SpeedAlertOptions) {
getNavigator().setSpeedAlertOptions(options)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ class GoogleMapsNavigationSessionMessageHandler(
sessionManager.setAudioGuidance(audioGuidanceSettings)
}

override fun setGuidanceNotificationsEnabled(enabled: Boolean) {
manager().setGuidanceNotificationsEnabled(enabled)
}

override fun getGuidanceNotificationsEnabled(): Boolean {
return manager().getGuidanceNotificationsEnabled()
}

override fun setSpeedAlertOptions(options: SpeedAlertOptionsDto) {
val newOptions = Convert.convertSpeedAlertOptionsFromDto(options)
sessionManager.setSpeedAlertOptions(newOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ object GoogleMapsNavigatorHolder {
private var turnByTurnServiceRegistered = false
private val navInfoObservers = mutableListOf<Observer<NavInfo>>()

// Guidance notifications management
private var isGuidanceNotificationsEnabled = true

@Synchronized fun getNavigator(): Navigator? = navigator

@Synchronized
Expand Down Expand Up @@ -138,6 +141,23 @@ object GoogleMapsNavigatorHolder {
return true
}

/** Sets whether guidance notifications should be shown when the app is not in the foreground. */
@Synchronized
fun setGuidanceNotificationsEnabled(enabled: Boolean) {
isGuidanceNotificationsEnabled = enabled
navigator?.setHeadsUpNotificationEnabled(enabled)
}

/**
* Gets whether guidance notifications are enabled. Returns the state of heads-up notifications.
*
* @return true if guidance notifications are enabled, false otherwise.
*/
@Synchronized
fun getGuidanceNotificationsEnabled(): Boolean {
return isGuidanceNotificationsEnabled
}

@Synchronized
fun reset() {
// Clean up turn-by-turn service
Expand All @@ -150,6 +170,11 @@ object GoogleMapsNavigatorHolder {
turnByTurnServiceRegistered = false
}

if (isGuidanceNotificationsEnabled == false) {
navigator?.setHeadsUpNotificationEnabled(true)
isGuidanceNotificationsEnabled = true
}

navigator = null
initializationState = GoogleNavigatorInitializationState.NOT_INITIALIZED
initializationCallbacks.clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5693,7 +5693,6 @@ class ViewEventApi(

/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface NavigationSessionApi {
/** General. */
fun createNavigationSession(
abnormalTerminationReportingEnabled: Boolean,
behavior: TaskRemovedBehaviorDto,
Expand All @@ -5717,7 +5716,6 @@ interface NavigationSessionApi {

fun getNavSDKVersion(): String

/** Navigation. */
fun isGuidanceRunning(): Boolean

fun startGuidance()
Expand All @@ -5742,7 +5740,10 @@ interface NavigationSessionApi {

fun getCurrentRouteSegment(): RouteSegmentDto?

/** Simulation */
fun setGuidanceNotificationsEnabled(enabled: Boolean)

fun getGuidanceNotificationsEnabled(): Boolean

fun setUserLocation(location: LatLngDto)

fun removeUserLocation()
Expand Down Expand Up @@ -5773,15 +5774,13 @@ interface NavigationSessionApi {

fun resumeSimulation()

/** Simulation (iOS only) */
/** iOS-only method. */
fun allowBackgroundLocationUpdates(allow: Boolean)

/** Road snapped location updates. */
fun enableRoadSnappedLocationUpdates()

fun disableRoadSnappedLocationUpdates()

/** Enable Turn-by-Turn navigation events. */
fun enableTurnByTurnNavigationEvents(numNextStepsToPreview: Long?)

fun disableTurnByTurnNavigationEvents()
Expand Down Expand Up @@ -6238,6 +6237,51 @@ interface NavigationSessionApi {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.google_navigation_flutter.NavigationSessionApi.setGuidanceNotificationsEnabled$separatedMessageChannelSuffix",
codec,
)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val enabledArg = args[0] as Boolean
val wrapped: List<Any?> =
try {
api.setGuidanceNotificationsEnabled(enabledArg)
listOf(null)
} catch (exception: Throwable) {
MessagesPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.google_navigation_flutter.NavigationSessionApi.getGuidanceNotificationsEnabled$separatedMessageChannelSuffix",
codec,
)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> =
try {
listOf(api.getGuidanceNotificationsEnabled())
} catch (exception: Throwable) {
MessagesPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
Expand Down
116 changes: 116 additions & 0 deletions example/integration_test/t10_guidance_notifications_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'package:flutter/material.dart';
import 'shared.dart';

void main() {
patrol('Test guidance notifications enable and disable', (
PatrolIntegrationTester $,
) async {
// Initialize the navigator
await checkLocationDialogAcceptance($);

/// Display navigation view.
final Key key = GlobalKey();
await pumpNavigationView(
$,
GoogleMapsNavigationView(
key: key,
onViewCreated: (GoogleNavigationViewController controller) {},
),
);

// Initialize navigation session
await GoogleMapsNavigator.initializeNavigationSession();
expect(await GoogleMapsNavigator.isInitialized(), true);

// Test default state - should be enabled by default on most platforms
final bool initialState =
await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
$.log('Initial guidance notifications state: $initialState');

// Test enabling guidance notifications
await GoogleMapsNavigator.setGuidanceNotificationsEnabled(true);
bool currentState =
await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(
currentState,
true,
reason: 'Guidance notifications should be enabled',
);
$.log('Successfully enabled guidance notifications');

// Test disabling guidance notifications
await GoogleMapsNavigator.setGuidanceNotificationsEnabled(false);
currentState = await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(
currentState,
false,
reason: 'Guidance notifications should be disabled',
);
$.log('Successfully disabled guidance notifications');

// Test re-enabling guidance notifications
await GoogleMapsNavigator.setGuidanceNotificationsEnabled(true);
currentState = await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(
currentState,
true,
reason: 'Guidance notifications should be enabled again',
);
$.log('Successfully re-enabled guidance notifications');
});

patrol('Test guidance notifications state persistence', (
PatrolIntegrationTester $,
) async {
// Initialize the navigator
await checkLocationDialogAcceptance($);

/// Display navigation view.
final Key key = GlobalKey();
await pumpNavigationView(
$,
GoogleMapsNavigationView(
key: key,
onViewCreated: (GoogleNavigationViewController controller) {},
),
);

// Initialize navigation session
await GoogleMapsNavigator.initializeNavigationSession();
expect(await GoogleMapsNavigator.isInitialized(), true);

// Set to a known state (disabled)
await GoogleMapsNavigator.setGuidanceNotificationsEnabled(false);
bool state = await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(state, false, reason: 'Initial state should be disabled');

// Verify state is still disabled
state = await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(state, false, reason: 'State should persist as disabled');
$.log('State persisted correctly as disabled');

// Change to enabled
await GoogleMapsNavigator.setGuidanceNotificationsEnabled(true);
state = await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(state, true, reason: 'State should be enabled');

// Verify state is still enabled
state = await GoogleMapsNavigator.getGuidanceNotificationsEnabled();
expect(state, true, reason: 'State should persist as enabled');
$.log('State persisted correctly as enabled');
});
}
18 changes: 18 additions & 0 deletions example/lib/pages/navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class _NavigationPageState extends ExamplePageState<NavigationPage> {
bool _termsAndConditionsAccepted = false;
bool _locationPermissionsAccepted = false;
bool _turnByTurnNavigationEventEnabled = false;
bool _guidanceNotificationsEnabled = true;

bool _isAutoScreenAvailable = false;

Expand Down Expand Up @@ -232,6 +233,11 @@ class _NavigationPageState extends ExamplePageState<NavigationPage> {
await _updateNavigatorInitializationState();
await _restorePossibleNavigatorState();
unawaited(_setDefaultUserLocationAfterDelay());

// Get the current guidance notifications state
_guidanceNotificationsEnabled =
await GoogleMapsNavigator.getGuidanceNotificationsEnabled();

debugPrint('Navigator has been initialized: $_navigatorInitialized');
}
setState(() {});
Expand Down Expand Up @@ -1580,6 +1586,18 @@ class _NavigationPageState extends ExamplePageState<NavigationPage> {
});
},
),
ExampleSwitch(
title: 'Guidance notifications',
initialValue: _guidanceNotificationsEnabled,
onChanged: (bool newValue) async {
await GoogleMapsNavigator.setGuidanceNotificationsEnabled(
newValue,
);
setState(() {
_guidanceNotificationsEnabled = newValue;
});
},
),
],
),
const SizedBox(height: 10),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,16 @@ class GoogleMapsNavigationSessionManager: NSObject {
}
}

/// Sets whether guidance notifications should be sent when the app is in the background.
func setGuidanceNotificationsEnabled(enabled: Bool) throws {
try getNavigator().sendsBackgroundNotifications = enabled
}

/// Gets whether guidance notifications are enabled.
func getGuidanceNotificationsEnabled() throws -> Bool {
try getNavigator().sendsBackgroundNotifications
}

/// Simulation
func setUserLocation(location: LatLngDto) throws {
try getSimulator().simulateLocation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi {
try GoogleMapsNavigationSessionManager.shared.setAudioGuidance(settings: settings)
}

func setGuidanceNotificationsEnabled(enabled: Bool) throws {
try GoogleMapsNavigationSessionManager.shared.setGuidanceNotificationsEnabled(enabled: enabled)
}

func getGuidanceNotificationsEnabled() throws -> Bool {
try GoogleMapsNavigationSessionManager.shared.getGuidanceNotificationsEnabled()
}

/// Simulation
func simulateLocationsAlongExistingRoute() throws {
try GoogleMapsNavigationSessionManager.shared.simulateLocationsAlongExistingRoute()
Expand Down
Loading
Loading