Skip to content

Commit 0c358cb

Browse files
authored
feat!: upgrade to latest native SDK's (#526)
* Upgrades Android SDK to version 7.2.0 * Upgrades iOS SDK to version 10.6.0 * Adds support for Map ID (cloud-based styling) * Adds support for controlling building visibility * Adds support for controlling incident panel visibility and detecting visibility changes * Updates Android minSdkVersion to 24 * Updates minimum supported SDK version to Flutter 3.32/Dart 3.8 * Deprecates NavigationDisplayOptions.showStopSigns and NavigationDisplayOptions.showTrafficLights * Deprecates Navigator.setOnGpsAvailabilityListener in favor of Navigator.setOnGpsAvailabilityChangeListener BREAKING CHANGE: Most fields in StepInfo are now nullable. Null checks for these properties must be handled.
1 parent a9a1186 commit 0c358cb

File tree

73 files changed

+2601
-801
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2601
-801
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ ServiceDefinitions.json
4848
xcuserdata/
4949
.swiftpm/
5050
.last_build_id
51+
.build/
5152

5253
# Android
5354
local.properties

README.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This repository contains a Flutter plugin that provides a [Google Navigation](ht
1515

1616
| | Android | iOS |
1717
| ------------------------------- | ------------- | --------- |
18-
| **Minimum mobile OS supported** | API level 23+ | iOS 16.0+ |
18+
| **Minimum mobile OS supported** | API level 24+ | iOS 16.0+ |
1919

2020
* A Flutter project
2121
* A Google Cloud project
@@ -43,7 +43,7 @@ Set the `minSdk` in `android/app/build.gradle`:
4343
```groovy
4444
android {
4545
defaultConfig {
46-
minSdk 23
46+
minSdk 24
4747
}
4848
}
4949
```
@@ -199,6 +199,32 @@ This parameter has only an effect on Android.
199199
200200
```
201201

202+
#### Using Map IDs
203+
You can configure your map by providing a `mapId` parameter during map initialization. Map IDs are created in the [Google Cloud Console](https://console.cloud.google.com/google/maps-apis/studio/maps) and allow you to [enable various Google Maps Platform features](https://developers.google.com/maps/documentation/android-sdk/map-ids/mapid-over#features-available), such as cloud-based map styling.
204+
205+
> [!NOTE]
206+
> The `mapId` can only be set once during map initialization and cannot be changed afterwards. Both `GoogleMapsMapView` and `GoogleMapsNavigationView` support the `mapId` parameter.
207+
208+
For `GoogleMapsMapView`:
209+
210+
```dart
211+
GoogleMapsMapView(
212+
mapId: 'YOUR_MAP_ID', // Can only be set during initialization
213+
...
214+
)
215+
```
216+
217+
For `GoogleMapsNavigationView`:
218+
219+
```dart
220+
GoogleMapsNavigationView(
221+
mapId: 'YOUR_MAP_ID', // Can only be set during initialization
222+
...
223+
)
224+
```
225+
226+
For more information about map IDs and how to create them, see the [Google Maps Platform documentation](https://developers.google.com/maps/documentation/get-map-id).
227+
202228
See the [example](./example) directory for a complete navigation sample app.
203229

204230
### Requesting and handling permissions

android/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ android {
7070
}
7171

7272
defaultConfig {
73-
minSdk = 23
73+
minSdk = 24
7474
consumerProguardFiles 'proguard.txt'
7575
}
7676

7777
dependencies {
78-
implementation 'androidx.car.app:app:1.4.0'
79-
implementation 'androidx.car.app:app-projected:1.4.0'
80-
implementation 'com.google.android.libraries.navigation:navigation:6.2.2'
78+
implementation 'androidx.car.app:app:1.7.0'
79+
implementation 'androidx.car.app:app-projected:1.7.0'
80+
implementation 'com.google.android.libraries.navigation:navigation:7.2.0'
8181
testImplementation 'org.jetbrains.kotlin:kotlin-test'
8282
testImplementation 'io.mockk:mockk:1.13.8'
8383
testImplementation 'junit:junit:4.13.2'

android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ object Convert {
8282
options.minZoomPreference?.let { googleMapOptions.minZoomPreference(it.toFloat()) }
8383
options.maxZoomPreference?.let { googleMapOptions.maxZoomPreference(it.toFloat()) }
8484
googleMapOptions.zoomControlsEnabled(options.zoomControlsEnabled)
85+
options.mapId?.let { googleMapOptions.mapId(it) }
8586

8687
return MapOptions(googleMapOptions, options.padding)
8788
}
@@ -310,8 +311,8 @@ object Convert {
310311
*/
311312
fun convertWaypointToDto(waypoint: Waypoint): NavigationWaypointDto {
312313
return NavigationWaypointDto(
313-
waypoint.title,
314-
convertLatLngToDto(waypoint.position),
314+
waypoint.title ?: "",
315+
waypoint.position?.let { convertLatLngToDto(it) },
315316
waypoint.placeId,
316317
waypoint.preferSameSideOfRoad,
317318
waypoint.preferredHeading.takeIf { it != -1 }?.toLong(),
@@ -362,6 +363,7 @@ object Convert {
362363
*/
363364
fun convertDisplayOptionsFromDto(displayOptions: NavigationDisplayOptionsDto): DisplayOptions {
364365
return DisplayOptions().apply {
366+
// Only set if explicitly provided, otherwise SDK defaults are used.
365367
if (displayOptions.showDestinationMarkers != null) {
366368
this.hideDestinationMarkers(!displayOptions.showDestinationMarkers)
367369
}
@@ -474,6 +476,7 @@ object Convert {
474476
Navigator.RouteStatus.OK -> RouteStatusDto.STATUS_OK
475477
Navigator.RouteStatus.QUOTA_CHECK_FAILED -> RouteStatusDto.QUOTA_CHECK_FAILED
476478
Navigator.RouteStatus.WAYPOINT_ERROR -> RouteStatusDto.WAYPOINT_ERROR
479+
Navigator.RouteStatus.DUPLICATE_WAYPOINTS_ERROR -> RouteStatusDto.DUPLICATE_WAYPOINTS_ERROR
477480
}
478481
}
479482

@@ -802,17 +805,17 @@ object Convert {
802805

803806
private fun convertNavInfoStepInfo(stepInfo: StepInfo): StepInfoDto {
804807
return StepInfoDto(
805-
distanceFromPrevStepMeters = stepInfo.distanceFromPrevStepMeters.toLong(),
806-
timeFromPrevStepSeconds = stepInfo.timeFromPrevStepSeconds.toLong(),
808+
distanceFromPrevStepMeters = stepInfo.distanceFromPrevStepMeters?.toLong() ?: 0L,
809+
timeFromPrevStepSeconds = stepInfo.timeFromPrevStepSeconds?.toLong() ?: 0L,
807810
drivingSide = convertDrivingSide(stepInfo.drivingSide),
808811
exitNumber = stepInfo.exitNumber,
809-
fullInstructions = stepInfo.fullInstructionText,
810-
fullRoadName = stepInfo.fullRoadName,
811-
simpleRoadName = stepInfo.simpleRoadName,
812-
roundaboutTurnNumber = stepInfo.roundaboutTurnNumber.toLong(),
813-
stepNumber = stepInfo.stepNumber.toLong(),
812+
fullInstructions = stepInfo.fullInstructionText ?: "",
813+
fullRoadName = stepInfo.fullRoadName ?: "",
814+
simpleRoadName = stepInfo.simpleRoadName ?: "",
815+
roundaboutTurnNumber = stepInfo.roundaboutTurnNumber?.toLong() ?: 0L,
816+
stepNumber = stepInfo.stepNumber?.toLong() ?: 0L,
814817
lanes =
815-
stepInfo.lanes.map { lane ->
818+
stepInfo.lanes?.map { lane ->
816819
LaneDto(
817820
laneDirections =
818821
lane.laneDirections().map { laneDirection ->

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,13 +530,23 @@ abstract class GoogleMapsBaseMapView(
530530
return getMap().isTrafficEnabled
531531
}
532532

533+
fun isBuildingsEnabled(): Boolean {
534+
return getMap().isBuildingsEnabled
535+
}
536+
537+
fun setBuildingsEnabled(enabled: Boolean) {
538+
getMap().isBuildingsEnabled = enabled
539+
}
540+
533541
fun getMyLocation(): Location? {
534542
// Remove this functionality and either guide users to use separate flutter
535543
// library for geolocation or implement separate method under
536544
// [GoogleMapsNavigationSessionManager] to fetch the location
537545
// using the [FusedLocationProviderApi].
538-
@Suppress("DEPRECATION")
539-
return getMap().myLocation
546+
@Suppress("DEPRECATION") val location = getMap().myLocation
547+
// Return null explicitly if location is not available to avoid NullPointerException
548+
// when the platform channel tries to serialize the Location object
549+
return if (location != null && location.provider != null) location else null
540550
}
541551

542552
fun getCameraPosition(): CameraPosition {

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.google.android.gms.maps.model.LatLng
2626
import com.google.android.libraries.mapsplatform.turnbyturn.model.NavInfo
2727
import com.google.android.libraries.navigation.CustomRoutesOptions
2828
import com.google.android.libraries.navigation.DisplayOptions
29+
import com.google.android.libraries.navigation.GpsAvailabilityChangeEvent
2930
import com.google.android.libraries.navigation.NavigationApi
3031
import com.google.android.libraries.navigation.NavigationApi.NavigatorListener
3132
import com.google.android.libraries.navigation.Navigator
@@ -342,10 +343,17 @@ constructor(
342343
remainingTimeOrDistanceChangedListener =
343344
Navigator.RemainingTimeOrDistanceChangedListener {
344345
val timeAndDistance = getNavigator().currentTimeAndDistance
345-
navigationSessionEventApi.onRemainingTimeOrDistanceChanged(
346-
timeAndDistance.seconds.toDouble(),
347-
timeAndDistance.meters.toDouble(),
348-
) {}
346+
// Only send event if we have valid time and distance data
347+
if (
348+
timeAndDistance != null &&
349+
timeAndDistance.seconds != null &&
350+
timeAndDistance.meters != null
351+
) {
352+
navigationSessionEventApi.onRemainingTimeOrDistanceChanged(
353+
timeAndDistance.seconds.toDouble(),
354+
timeAndDistance.meters.toDouble(),
355+
) {}
356+
}
349357
}
350358
}
351359

@@ -495,7 +503,7 @@ constructor(
495503
* @return [TimeAndDistance] object.
496504
*/
497505
fun getCurrentTimeAndDistance(): TimeAndDistance {
498-
return getNavigator().currentTimeAndDistance
506+
return getNavigator().currentTimeAndDistance!!
499507
}
500508

501509
/**
@@ -749,6 +757,17 @@ constructor(
749757
override fun onGpsAvailabilityUpdate(isGpsAvailable: Boolean) {
750758
navigationSessionEventApi.onGpsAvailabilityUpdate(isGpsAvailable) {}
751759
}
760+
761+
override fun onGpsAvailabilityChange(event: GpsAvailabilityChangeEvent?) {
762+
if (event != null) {
763+
navigationSessionEventApi.onGpsAvailabilityChange(
764+
GpsAvailabilityChangeEventDto(
765+
isGpsLost = event.isGpsLost,
766+
isGpsValidForNavigation = event.isGpsValidForNavigation,
767+
)
768+
) {}
769+
}
770+
}
752771
}
753772
getRoadSnappedLocationProvider()?.addLocationListener(roadSnappedLocationListener)
754773
}

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import android.view.View
2222
import com.google.android.gms.maps.CameraUpdateFactory
2323
import com.google.android.libraries.navigation.NavigationView
2424
import com.google.android.libraries.navigation.OnNavigationUiChangedListener
25+
import com.google.android.libraries.navigation.PromptVisibilityChangedListener
2526
import io.flutter.plugin.platform.PlatformView
2627

2728
class GoogleMapsNavigationView
@@ -51,6 +52,8 @@ internal constructor(
5152
null
5253
private var _onNavigationUIEnabledChanged: OnNavigationUiChangedListener? = null
5354

55+
private var _onPromptVisibilityChanged: PromptVisibilityChangedListener? = null
56+
5457
override fun getView(): View {
5558
return _navigationView
5659
}
@@ -110,6 +113,10 @@ internal constructor(
110113
_navigationView.removeOnNavigationUiChangedListener(_onNavigationUIEnabledChanged)
111114
_onNavigationUIEnabledChanged = null
112115
}
116+
if (_onPromptVisibilityChanged != null) {
117+
_navigationView.removePromptVisibilityChangedListener(_onPromptVisibilityChanged)
118+
_onPromptVisibilityChanged = null
119+
}
113120

114121
// When view is disposed, all of these lifecycle functions must be
115122
// called to properly dispose navigation view and prevent leaks.
@@ -171,6 +178,11 @@ internal constructor(
171178
}
172179
_navigationView.addOnNavigationUiChangedListener(_onNavigationUIEnabledChanged)
173180

181+
_onPromptVisibilityChanged = PromptVisibilityChangedListener { promptVisible ->
182+
viewEventApi?.onPromptVisibilityChanged(getViewId().toLong(), promptVisible) {}
183+
}
184+
_navigationView.addPromptVisibilityChangedListener(_onPromptVisibilityChanged)
185+
174186
super.initListeners()
175187
}
176188

@@ -246,6 +258,14 @@ internal constructor(
246258
_isReportIncidentButtonEnabled = enabled
247259
}
248260

261+
fun isIncidentReportingAvailable(): Boolean {
262+
return _navigationView.isIncidentReportingAvailable()
263+
}
264+
265+
fun showReportIncidentsPanel() {
266+
_navigationView.showReportIncidentsPanel()
267+
}
268+
249269
fun isTrafficPromptsEnabled(): Boolean {
250270
return _isTrafficPromptsEnabled
251271
}

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsViewMessageHandler.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,22 @@ class GoogleMapsViewMessageHandler(private val viewRegistry: GoogleMapsViewRegis
368368
getNavigationView(viewId.toInt()).setReportIncidentButtonEnabled(enabled)
369369
}
370370

371+
override fun isIncidentReportingAvailable(viewId: Long): Boolean {
372+
return getNavigationView(viewId.toInt()).isIncidentReportingAvailable()
373+
}
374+
375+
override fun showReportIncidentsPanel(viewId: Long) {
376+
getNavigationView(viewId.toInt()).showReportIncidentsPanel()
377+
}
378+
379+
override fun isBuildingsEnabled(viewId: Long): Boolean {
380+
return getView(viewId.toInt()).isBuildingsEnabled()
381+
}
382+
383+
override fun setBuildingsEnabled(viewId: Long, enabled: Boolean) {
384+
getView(viewId.toInt()).setBuildingsEnabled(enabled)
385+
}
386+
371387
override fun isTrafficPromptsEnabled(viewId: Long): Boolean {
372388
return getNavigationView(viewId.toInt()).isTrafficPromptsEnabled()
373389
}

0 commit comments

Comments
 (0)