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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,25 @@ The `taskRemovedBehavior` parameter of navigation session initialization defines

This parameter has only an effect on Android.

#### Android navigation notification

Configure the built-in Android navigation notification when creating the first
navigation session. The options are ignored on iOS.

```dart
await GoogleMapsNavigator.initializeNavigationSession(
notificationOptions: const NavigationNotificationOptions(
notificationId: 1234,
defaultMessage: 'Navigation is active',
resumeAppOnTap: true,
),
);
```

`resumeAppOnTap` makes a notification tap launch or resume the application.
These options retain the Navigation SDK's turn-by-turn notification content;
custom notification layouts are not supported.

### Add a map view

```dart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.google.maps.flutter.navigation

import android.app.Activity
import android.app.Application
import android.content.Intent
import android.content.res.Resources
import android.location.Location
import androidx.lifecycle.DefaultLifecycleObserver
Expand Down Expand Up @@ -61,6 +62,7 @@ constructor(
) : DefaultLifecycleObserver {
companion object {
var navigationReadyListener: NavigationReadyListener? = null
private var foregroundServiceManagerInitialized = false
}

private var arrivalListener: Navigator.ArrivalListener? = null
Expand Down Expand Up @@ -131,6 +133,7 @@ constructor(
fun createNavigationSession(
abnormalTerminationReportingEnabled: Boolean,
behavior: TaskRemovedBehaviorDto,
notificationOptions: NavigationNotificationOptionsDto?,
callback: (Result<Unit>) -> Unit,
) {
val currentState = GoogleMapsNavigatorHolder.getInitializationState()
Expand Down Expand Up @@ -178,6 +181,13 @@ constructor(
return
}

try {
initializeForegroundServiceManager(notificationOptions)
} catch (error: Throwable) {
callback(Result.failure(error))
return
}

// Enable or disable abnormal termination reporting.
NavigationApi.setAbnormalTerminationReportingEnabled(abnormalTerminationReportingEnabled)

Expand Down Expand Up @@ -288,6 +298,7 @@ constructor(
// As unregisterListeners() is removing all listeners, we need to re-register them when
// navigator is re-initialized. This is done in createNavigationSession() method.
GoogleMapsNavigatorHolder.reset()
clearForegroundServiceManager()
navigationReadyListener?.onNavigationReady(false)
}
}
Expand Down Expand Up @@ -417,6 +428,46 @@ constructor(
}
}

private fun initializeForegroundServiceManager(options: NavigationNotificationOptionsDto?) {
if (options == null || foregroundServiceManagerInitialized) return

val androidNotificationId =
options.notificationId?.let {
if (it !in Int.MIN_VALUE.toLong()..Int.MAX_VALUE.toLong()) {
throw FlutterError(
"invalidNotificationId",
"notificationId must fit in a 32-bit integer.",
)
}
it.toInt()
}

val resumeIntent =
if (options.resumeAppOnTap) {
application.packageManager
.getLaunchIntentForPackage(application.packageName)
?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
} else {
null
}

// This must happen before NavigationApi.getNavigator()
NavigationApi.initForegroundServiceManagerMessageAndIntent(
application,
androidNotificationId,
options.defaultMessage,
resumeIntent,
)
foregroundServiceManagerInitialized = true
}

private fun clearForegroundServiceManager() {
if (foregroundServiceManagerInitialized) {
NavigationApi.clearForegroundServiceManager()
foregroundServiceManagerInitialized = false
}
}

/**
* Wraps [Navigator.startGuidance]. See
* [Google Navigation SDK for Android](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#startGuidance()).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ class GoogleMapsNavigationSessionMessageHandler(
override fun createNavigationSession(
abnormalTerminationReportingEnabled: Boolean,
behavior: TaskRemovedBehaviorDto,
notificationOptions: NavigationNotificationOptionsDto?,
callback: (Result<Unit>) -> Unit,
) {
sessionManager.createNavigationSession(abnormalTerminationReportingEnabled, behavior, callback)
sessionManager.createNavigationSession(
abnormalTerminationReportingEnabled,
behavior,
notificationOptions,
callback,
)
}

override fun isInitialized(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2695,6 +2695,44 @@ data class TermsAndConditionsUIParamsDto(
override fun hashCode(): Int = toList().hashCode()
}

/**
* Android foreground-service notification configuration.
*
* This preserves the Navigation SDK's built-in turn-by-turn notification.
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class NavigationNotificationOptionsDto(
val notificationId: Long? = null,
val defaultMessage: String? = null,
val resumeAppOnTap: Boolean,
) {
companion object {
fun fromList(pigeonVar_list: List<Any?>): NavigationNotificationOptionsDto {
val notificationId = pigeonVar_list[0] as Long?
val defaultMessage = pigeonVar_list[1] as String?
val resumeAppOnTap = pigeonVar_list[2] as Boolean
return NavigationNotificationOptionsDto(notificationId, defaultMessage, resumeAppOnTap)
}
}

fun toList(): List<Any?> {
return listOf(notificationId, defaultMessage, resumeAppOnTap)
}

override fun equals(other: Any?): Boolean {
if (other !is NavigationNotificationOptionsDto) {
return false
}
if (this === other) {
return true
}
return MessagesPigeonUtils.deepEquals(toList(), other.toList())
}

override fun hashCode(): Int = toList().hashCode()
}

/**
* Options for step image generation in turn-by-turn navigation events.
*
Expand Down Expand Up @@ -2979,6 +3017,11 @@ private open class messagesPigeonCodec : StandardMessageCodec() {
}
}
203.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
NavigationNotificationOptionsDto.fromList(it)
}
}
204.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
StepImageGenerationOptionsDto.fromList(it)
}
Expand Down Expand Up @@ -3285,10 +3328,14 @@ private open class messagesPigeonCodec : StandardMessageCodec() {
stream.write(202)
writeValue(stream, value.toList())
}
is StepImageGenerationOptionsDto -> {
is NavigationNotificationOptionsDto -> {
stream.write(203)
writeValue(stream, value.toList())
}
is StepImageGenerationOptionsDto -> {
stream.write(204)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
Expand Down Expand Up @@ -6889,6 +6936,7 @@ interface NavigationSessionApi {
fun createNavigationSession(
abnormalTerminationReportingEnabled: Boolean,
behavior: TaskRemovedBehaviorDto,
notificationOptions: NavigationNotificationOptionsDto?,
callback: (Result<Unit>) -> Unit,
)

Expand Down Expand Up @@ -7011,8 +7059,12 @@ interface NavigationSessionApi {
val args = message as List<Any?>
val abnormalTerminationReportingEnabledArg = args[0] as Boolean
val behaviorArg = args[1] as TaskRemovedBehaviorDto
api.createNavigationSession(abnormalTerminationReportingEnabledArg, behaviorArg) {
result: Result<Unit> ->
val notificationOptionsArg = args[2] as NavigationNotificationOptionsDto?
api.createNavigationSession(
abnormalTerminationReportingEnabledArg,
behaviorArg,
notificationOptionsArg,
) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
Expand Down
8 changes: 7 additions & 1 deletion example/lib/pages/navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,13 @@ class _NavigationPageState extends ExamplePageState<NavigationPage> {
if (!_navigatorInitialized) {
debugPrint('Initializing new navigation session...');
try {
await GoogleMapsNavigator.initializeNavigationSession();
await GoogleMapsNavigator.initializeNavigationSession(
notificationOptions: const NavigationNotificationOptions(
notificationId: 1234,
defaultMessage: 'Navigation is active',
resumeAppOnTap: true,
),
);
} on SessionInitializationException catch (e) {
switch (e.code) {
case SessionInitializationError.termsNotAccepted:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi {

func createNavigationSession(
abnormalTerminationReportingEnabled: Bool,
// taskRemovedBehaviourValue is Android only value and not used on
// iOS.
// taskRemovedBehaviourValue and notificationOptions are Android only
// values and not used on iOS.
behavior: TaskRemovedBehaviorDto,
notificationOptions: NavigationNotificationOptionsDto?,
completion: @escaping (Result<Void, Error>) -> Void
) {
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2488,6 +2488,45 @@ struct TermsAndConditionsUIParamsDto: Hashable {
}
}

/// Android foreground-service notification configuration.
///
/// This preserves the Navigation SDK's built-in turn-by-turn notification.
///
/// Generated class from Pigeon that represents data sent in messages.
struct NavigationNotificationOptionsDto: Hashable {
var notificationId: Int64? = nil
var defaultMessage: String? = nil
var resumeAppOnTap: Bool

// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> NavigationNotificationOptionsDto? {
let notificationId: Int64? = nilOrValue(pigeonVar_list[0])
let defaultMessage: String? = nilOrValue(pigeonVar_list[1])
let resumeAppOnTap = pigeonVar_list[2] as! Bool

return NavigationNotificationOptionsDto(
notificationId: notificationId,
defaultMessage: defaultMessage,
resumeAppOnTap: resumeAppOnTap
)
}
func toList() -> [Any?] {
return [
notificationId,
defaultMessage,
resumeAppOnTap,
]
}
static func == (lhs: NavigationNotificationOptionsDto, rhs: NavigationNotificationOptionsDto)
-> Bool
{
return deepEqualsmessages(lhs.toList(), rhs.toList())
}
func hash(into hasher: inout Hasher) {
deepHashmessages(value: toList(), hasher: &hasher)
}
}

/// Options for step image generation in turn-by-turn navigation events.
///
/// Generated class from Pigeon that represents data sent in messages.
Expand Down Expand Up @@ -2780,6 +2819,8 @@ private class MessagesPigeonCodecReader: FlutterStandardReader {
case 202:
return TermsAndConditionsUIParamsDto.fromList(self.readValue() as! [Any?])
case 203:
return NavigationNotificationOptionsDto.fromList(self.readValue() as! [Any?])
case 204:
return StepImageGenerationOptionsDto.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
Expand Down Expand Up @@ -3011,9 +3052,12 @@ private class MessagesPigeonCodecWriter: FlutterStandardWriter {
} else if let value = value as? TermsAndConditionsUIParamsDto {
super.writeByte(202)
super.writeValue(value.toList())
} else if let value = value as? StepImageGenerationOptionsDto {
} else if let value = value as? NavigationNotificationOptionsDto {
super.writeByte(203)
super.writeValue(value.toList())
} else if let value = value as? StepImageGenerationOptionsDto {
super.writeByte(204)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
Expand Down Expand Up @@ -5880,6 +5924,7 @@ class ViewEventApi: ViewEventApiProtocol {
protocol NavigationSessionApi {
func createNavigationSession(
abnormalTerminationReportingEnabled: Bool, behavior: TaskRemovedBehaviorDto,
notificationOptions: NavigationNotificationOptionsDto?,
completion: @escaping (Result<Void, Error>) -> Void)
func isInitialized() throws -> Bool
func cleanup(resetSession: Bool) throws
Expand Down Expand Up @@ -5948,9 +5993,10 @@ class NavigationSessionApiSetup {
let args = message as! [Any?]
let abnormalTerminationReportingEnabledArg = args[0] as! Bool
let behaviorArg = args[1] as! TaskRemovedBehaviorDto
let notificationOptionsArg: NavigationNotificationOptionsDto? = nilOrValue(args[2])
api.createNavigationSession(
abnormalTerminationReportingEnabled: abnormalTerminationReportingEnabledArg,
behavior: behaviorArg
behavior: behaviorArg, notificationOptions: notificationOptionsArg
) { result in
switch result {
case .success:
Expand Down
12 changes: 12 additions & 0 deletions lib/src/method_channel/convert/navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ import 'dart:ui';
import '../../../google_navigation_flutter.dart';
import '../method_channel.dart';

/// [NavigationNotificationOptions] convert extension.
/// @nodoc
extension ConvertNavigationNotificationOptions
on NavigationNotificationOptions {
/// Converts [NavigationNotificationOptions] to its platform representation.
NavigationNotificationOptionsDto toDto() => NavigationNotificationOptionsDto(
notificationId: notificationId,
defaultMessage: defaultMessage,
resumeAppOnTap: resumeAppOnTap,
);
}

/// [SpeedAlertSeverityDto] convert extension.
/// @nodoc
extension ConvertSpeedAlertSeverityDto on SpeedAlertSeverityDto {
Expand Down
Loading
Loading