/* * Garmin HUD Companion Application * Copyright (C) 2022 Piotr Dec / ztsh.eu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package eu.ztsh.garmin.mapbox import android.content.res.Configuration import android.content.res.Resources import android.view.View import androidx.appcompat.app.AppCompatActivity import com.mapbox.geojson.Point import com.mapbox.maps.ImageHolder import com.mapbox.maps.plugin.LocationPuck2D import com.mapbox.maps.plugin.animation.camera import com.mapbox.maps.plugin.locationcomponent.location import com.mapbox.navigation.base.options.NavigationOptions import com.mapbox.navigation.core.MapboxNavigation import com.mapbox.navigation.core.directions.session.RoutesObserver import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver import com.mapbox.navigation.core.trip.session.LocationObserver import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.core.trip.session.VoiceInstructionsObserver import com.mapbox.navigation.ui.maps.camera.NavigationCamera import com.mapbox.navigation.ui.maps.camera.data.MapboxNavigationViewportDataSource import com.mapbox.navigation.ui.maps.camera.lifecycle.NavigationBasicGesturesHandler import com.mapbox.navigation.ui.maps.camera.state.NavigationCameraState import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider import eu.ztsh.garmin.MainActivity import eu.ztsh.garmin.UI import eu.ztsh.garmin.mock.ReplayResources class MapControl( val context: AppCompatActivity, val ui: UI, private val resources: Resources ) : MapboxNavigationObserver { /** * Used to execute camera transitions based on the data generated by the [viewportDataSource]. * This includes transitions from route overview to route following and continuously updating the camera as the location changes. */ lateinit var navigationCamera: NavigationCamera /** * Produces the camera frames based on the location and routing data for the [navigationCamera] to execute. */ lateinit var viewportDataSource: MapboxNavigationViewportDataSource /** * [NavigationLocationProvider] is a utility class that helps to provide location updates generated by the Navigation SDK * to the Maps SDK in order to update the user location indicator on the map. */ val navigationLocationProvider = NavigationLocationProvider() val navigationStatusControl = NavigationStatusControl() val tripControl = TripControl(this, TripMode.GPS) // Observers private lateinit var routeControl: RouteControl private lateinit var voiceControl: VoiceControl private lateinit var routesObserver: RoutesObserver private lateinit var locationObserver: LocationObserver private lateinit var routeProgressObserver: RouteProgressObserver private lateinit var voiceInstructionsObserver: VoiceInstructionsObserver private val searchControl = SearchControl(this, ui) fun init() { viewportDataSource = MapboxNavigationViewportDataSource(ui.mapView.mapboxMap) if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { viewportDataSource.overviewPadding = UI.landscapeOverviewPadding } else { viewportDataSource.overviewPadding = UI.overviewPadding } if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { viewportDataSource.followingPadding = UI.landscapeFollowingPadding } else { viewportDataSource.followingPadding = UI.followingPadding } navigationCamera = NavigationCamera( ui.mapView.mapboxMap, ui.mapView.camera, viewportDataSource ) // set the animations lifecycle listener to ensure the NavigationCamera stops // automatically following the user location when the map is interacted with ui.mapView.camera.addCameraAnimationsLifecycleListener( NavigationBasicGesturesHandler(navigationCamera) ) navigationCamera.registerNavigationCameraStateChangeObserver { navigationCameraState -> // shows/hide the recenter button depending on the camera state when (navigationCameraState) { NavigationCameraState.TRANSITION_TO_FOLLOWING, NavigationCameraState.FOLLOWING -> ui.recenter.visibility = View.INVISIBLE NavigationCameraState.TRANSITION_TO_OVERVIEW, NavigationCameraState.OVERVIEW, NavigationCameraState.IDLE -> ui.recenter.visibility = View.VISIBLE } } routeControl = RouteControl(this, ui, context) voiceControl = VoiceControl(ui, context) routesObserver = routeControl.routesObserver locationObserver = LocationObserver(this) routeProgressObserver = routeControl.routeProgressObserver voiceInstructionsObserver = voiceControl.voiceInstructionsObserver } fun routeToPoint(point: Point) { routeControl.findRoute(point) } fun initNavigation() { MapboxNavigationApp.setup( NavigationOptions.Builder(context) .build() ) // initialize location puck ui.mapView.location.apply { setLocationProvider(navigationLocationProvider) this.locationPuck = LocationPuck2D( bearingImage = ImageHolder.Companion.from( com.mapbox.navigation.ui.maps.R.drawable.mapbox_navigation_puck_icon ) ) puckBearingEnabled = true enabled = true } tripControl.initLocation() } fun mapboxNavigation(): MapboxNavigation { return (context as MainActivity).mapboxNavigation } override fun onAttached(mapboxNavigation: MapboxNavigation) { mapboxNavigation.registerRoutesObserver(routesObserver) mapboxNavigation.registerLocationObserver(locationObserver) mapboxNavigation.registerRouteProgressObserver(routeProgressObserver) mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver) navigationStatusControl.registerObserver(searchControl) tripControl.onAttached(mapboxNavigation) } override fun onDetached(mapboxNavigation: MapboxNavigation) { mapboxNavigation.unregisterRoutesObserver(routesObserver) mapboxNavigation.unregisterLocationObserver(locationObserver) mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver) mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver) navigationStatusControl.unregisterObserver(searchControl) tripControl.onDetached(mapboxNavigation) } fun onDestroy() { routeControl.cancel() voiceControl.cancel() } companion object { const val TAG = "MAPCTRL" } }