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 @@ -35,18 +35,59 @@ import androidx.lifecycle.LifecycleOwner
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMapOptions
import com.google.android.libraries.navigation.NavigationViewForAuto
import com.google.android.libraries.navigation.NavigationView
import com.google.android.libraries.navigation.PromptVisibilityChangedListener

open class AndroidAutoBaseScreen(carContext: CarContext) :
Screen(carContext), SurfaceCallback, NavigationReadyListener {

companion object {
/**
* Map options to use for Android Auto views. Can be set before the Android Auto screen is
* created to customize map appearance.
*/
var mapOptions: AutoMapViewOptions? = null
}

/**
* Provides the map options to use when creating the Android Auto map view.
*
* Override this method in your AndroidAutoBaseScreen subclass to provide custom map options from
* the native layer. This is useful when you want to set map configuration (like mapId) directly
* in native code instead of from Flutter, especially when the Android Auto screen may already be
* open.
*
* The default implementation returns the value from the companion object, which can be set from
* Flutter via GoogleMapsAutoViewController.setAutoMapOptions().
*
* @return AutoMapViewOptions containing map configuration, or null to use defaults
*
* Example:
* ```kotlin
* override fun getAutoMapOptions(): AutoMapViewOptions? {
* return AutoMapViewOptions(
* mapId = "your-map-id",
* mapType = GoogleMap.MAP_TYPE_SATELLITE,
* mapColorScheme = UIUserInterfaceStyle.DARK,
* forceNightMode = NavigationView.FORCE_NIGHT_MODE_AUTO
* )
* }
* ```
*/
public open fun getAutoMapOptions(): AutoMapViewOptions? {
return mapOptions
}

private val VIRTUAL_DISPLAY_NAME = "AndroidAutoNavScreen"
private var mVirtualDisplay: VirtualDisplay? = null
private var mPresentation: Presentation? = null
private var mNavigationView: NavigationViewForAuto? = null
private var mNavigationView: NavigationView? = null
private var mAutoMapView: GoogleMapsAutoMapView? = null
private var mViewRegistry: GoogleMapsViewRegistry? = null
private var mPromptVisibilityListener: PromptVisibilityChangedListener? = null
protected var mIsNavigationReady: Boolean = false
var mGoogleMap: GoogleMap? = null
private var mIsPromptVisible: Boolean = false

init {
initializeSurfaceCallback()
Expand Down Expand Up @@ -101,11 +142,43 @@ open class AndroidAutoBaseScreen(carContext: CarContext) :
mPresentation = Presentation(carContext, virtualDisplay.display)
val presentation = mPresentation ?: return

mNavigationView = NavigationViewForAuto(carContext)
// Get map options from overridable method (can be customized in subclasses)
val autoMapOptions = getAutoMapOptions()
val googleMapOptions =
GoogleMapOptions().apply {
compassEnabled(false) // Always disable compass for Android Auto

// Apply custom map ID if provided
autoMapOptions?.mapId?.let { mapId -> mapId(mapId) }

// Apply map type if provided
autoMapOptions?.mapType?.let { type -> mapType(type) }

// Apply map color scheme if provided
autoMapOptions?.mapColorScheme?.let { colorScheme -> mapColorScheme(colorScheme) }
}

// Create NavigationView with the configured options
mNavigationView = NavigationView(carContext, googleMapOptions)
val navigationView = mNavigationView ?: return
navigationView.onCreate(null)
navigationView.onStart()
navigationView.onResume()

// Apply force night mode if provided (separate from color scheme)
autoMapOptions?.forceNightMode?.let { forceNightMode ->
navigationView.setForceNightMode(forceNightMode)
}

// Configure NavigationView for Android Auto
navigationView.apply {
onCreate(null)
onStart()
onResume()
setHeaderEnabled(false)
setRecenterButtonEnabled(false)
setEtaCardEnabled(false)
setSpeedometerEnabled(false)
setTripProgressBarEnabled(false)
setReportIncidentButtonEnabled(false)
}

presentation.setContentView(navigationView)
presentation.show()
Expand All @@ -116,14 +189,24 @@ open class AndroidAutoBaseScreen(carContext: CarContext) :
if (viewRegistry != null && imageRegistry != null) {
mGoogleMap = googleMap
mViewRegistry = viewRegistry

mAutoMapView =
GoogleMapsAutoMapView(
MapOptions(GoogleMapOptions(), null),
viewRegistry,
imageRegistry,
navigationView,
navigationView,
googleMap,
)

// Set up prompt visibility listener with direct access to NavigationView
mPromptVisibilityListener = PromptVisibilityChangedListener { promptVisible ->
mIsPromptVisible = promptVisible
onPromptVisibilityChanged(promptVisible)
}
navigationView.addPromptVisibilityChangedListener(mPromptVisibilityListener)

sendAutoScreenAvailabilityChangedEvent(true)
invalidate()
}
Expand All @@ -133,6 +216,14 @@ open class AndroidAutoBaseScreen(carContext: CarContext) :
override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
super.onSurfaceDestroyed(surfaceContainer)
sendAutoScreenAvailabilityChangedEvent(false)

// Clean up prompt visibility listener
if (mPromptVisibilityListener != null && mNavigationView != null) {
mNavigationView?.removePromptVisibilityChangedListener(mPromptVisibilityListener)
mPromptVisibilityListener = null
}
mIsPromptVisible = false

mViewRegistry?.unregisterAndroidAutoView()
mNavigationView?.onPause()
mNavigationView?.onStop()
Expand Down Expand Up @@ -166,6 +257,14 @@ open class AndroidAutoBaseScreen(carContext: CarContext) :
) {}
}

// Called when Flutter sends a custom event to native via sendCustomNavigationAutoEvent
// Override this method in your AndroidAutoBaseScreen subclass to handle custom events from
// Flutter
open fun onCustomNavigationAutoEventFromFlutter(event: String, data: Any) {
// Default implementation does nothing
// Subclasses can override to handle custom events
}

private fun sendAutoScreenAvailabilityChangedEvent(isAvailable: Boolean) {
GoogleMapsNavigationPlugin.getInstance()?.autoViewEventApi?.onAutoScreenAvailabilityChanged(
isAvailable
Expand All @@ -175,4 +274,57 @@ open class AndroidAutoBaseScreen(carContext: CarContext) :
override fun onNavigationReady(ready: Boolean) {
mIsNavigationReady = ready
}

/**
* Checks if a traffic prompt is currently visible on the Android Auto screen.
*
* This can be useful to dynamically adjust your UI based on prompt visibility, such as when
* building templates or deciding whether to show custom elements.
*
* @return true if a prompt is currently visible, false otherwise
*
* Example:
* ```kotlin
* override fun onGetTemplate(): Template {
* val builder = NavigationTemplate.Builder()
*
* // Only show custom actions if prompt is not visible
* if (!isPromptVisible()) {
* builder.setActionStrip(myCustomActionStrip)
* }
*
* return builder.build()
* }
* ```
*/
fun isPromptVisible(): Boolean {
return mIsPromptVisible
}

/**
* Called when traffic prompt visibility changes on the Android Auto screen.
*
* Override this method to add custom behavior when prompts appear or disappear, such as
* hiding/showing your custom UI elements to avoid overlapping with system prompts.
*
* @param promptVisible true if the prompt is now visible, false if it's hidden
*
* Example:
* ```kotlin
* override fun onPromptVisibilityChanged(promptVisible: Boolean) {
* super.onPromptVisibilityChanged(promptVisible)
* if (promptVisible) {
* // Hide your custom buttons or UI elements
* } else {
* // Show your custom buttons or UI elements
* }
* }
* ```
*/
open fun onPromptVisibilityChanged(promptVisible: Boolean) {
// Send event to Flutter by default
GoogleMapsNavigationPlugin.getInstance()?.autoViewEventApi?.onPromptVisibilityChanged(
promptVisible
) {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2024 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.
*/

package com.google.maps.flutter.navigation

/**
* Options for configuring Android Auto map views. Contains only settings relevant for Android Auto
* views.
*/
public data class AutoMapViewOptions(
/** The initial camera position for the map view. */
val cameraPosition: CameraPositionDto? = null,

/** Cloud-based map ID for custom styling. */
val mapId: String? = null,

/** The type of map to display. */
val mapType: Int? = null,

/** The color scheme for the map. */
val mapColorScheme: Int? = null,

/** Forces night mode regardless of system settings. */
val forceNightMode: Int? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@
package com.google.maps.flutter.navigation

import android.view.View
import android.view.ViewGroup
import com.google.android.gms.maps.GoogleMap
import com.google.android.libraries.navigation.NavigationViewForAuto
import com.google.android.libraries.navigation.NavigationView

class GoogleMapsAutoMapView
internal constructor(
mapOptions: MapOptions,
viewRegistry: GoogleMapsViewRegistry,
imageRegistry: ImageRegistry,
private val mapView: NavigationViewForAuto,
private val navigationView: NavigationView,
private val viewGroup: ViewGroup,
map: GoogleMap,
) : GoogleMapsBaseMapView(null, mapOptions, null, imageRegistry) {
private var _isTrafficPromptsEnabled: Boolean = true
private var _isTrafficIncidentCardsEnabled: Boolean = true
private var _isReportIncidentButtonEnabled: Boolean = true
private var _forceNightMode: Int = 0

override fun getView(): View {
return mapView
return viewGroup
}

init {
Expand All @@ -40,6 +47,42 @@ internal constructor(
mapReady()
}

override fun setTrafficPromptsEnabled(enabled: Boolean) {
navigationView.setTrafficPromptsEnabled(enabled)
_isTrafficPromptsEnabled = enabled
}

override fun isTrafficPromptsEnabled(): Boolean {
return _isTrafficPromptsEnabled
}

override fun setTrafficIncidentCardsEnabled(enabled: Boolean) {
navigationView.setTrafficIncidentCardsEnabled(enabled)
_isTrafficIncidentCardsEnabled = enabled
}

override fun isTrafficIncidentCardsEnabled(): Boolean {
return _isTrafficIncidentCardsEnabled
}

override fun setReportIncidentButtonEnabled(enabled: Boolean) {
navigationView.setReportIncidentButtonEnabled(enabled)
_isReportIncidentButtonEnabled = enabled
}

override fun isReportIncidentButtonEnabled(): Boolean {
return _isReportIncidentButtonEnabled
}

override fun getForceNightMode(): Int {
return _forceNightMode
}

override fun setForceNightMode(forceNightMode: Int) {
navigationView.setForceNightMode(forceNightMode)
_forceNightMode = forceNightMode
}

// Handled by AndroidAutoBaseScreen.
override fun onStart(): Boolean {
return super.onStart()
Expand Down
Loading
Loading