From db180a4a1878f7fd9946e7e9e5e4d3bae764fa45 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Sun, 4 Aug 2024 01:04:50 +0200 Subject: [PATCH 1/5] build: Dependencies --- app/build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 8eb3c08..e08ff12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,6 +40,7 @@ android { } ext { mapboxVersion = '3.2.0' + searchApiVersion = '2.3.0' } dependencies { @@ -48,6 +49,12 @@ dependencies { implementation "com.mapbox.navigationcore:voice:$mapboxVersion" implementation "com.mapbox.navigationcore:tripdata:$mapboxVersion" implementation "com.mapbox.navigationcore:ui-components:$mapboxVersion" + + implementation "com.mapbox.search:autofill:$searchApiVersion" + implementation "com.mapbox.search:place-autocomplete:$searchApiVersion" + implementation "com.mapbox.search:mapbox-search-android:$searchApiVersion" + implementation "com.mapbox.search:mapbox-search-android-ui:$searchApiVersion" + implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' From ca7d4187261e358e920a2eb704a9874c9ba5a383 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Sun, 4 Aug 2024 01:24:44 +0200 Subject: [PATCH 2/5] feat: XMLs, UI --- app/src/main/res/drawable/card_background.xml | 8 +++ app/src/main/res/layout/activity_main.xml | 49 +++++++++++++++++++ app/src/main/res/values/dimensions.xml | 4 ++ app/src/main/res/values/strings.xml | 5 ++ 4 files changed, 66 insertions(+) create mode 100644 app/src/main/res/drawable/card_background.xml create mode 100644 app/src/main/res/values/dimensions.xml diff --git a/app/src/main/res/drawable/card_background.xml b/app/src/main/res/drawable/card_background.xml new file mode 100644 index 0000000..e32ecf5 --- /dev/null +++ b/app/src/main/res/drawable/card_background.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index cb6c22d..fe89ff7 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,6 +4,22 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + 8dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c4d613..8a23bc0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,9 @@ Garmin pk.eyJ1IjoibWFwcy16dHNoIiwiYSI6ImNsbDl4YXU4cjA3eW8zcXMzbXdjYjNsN3oifQ.kbDCjthamXvXX_pAdsq3hQ + + Search for places + Unable to locate selected coordinate + Error happened during request + From 65cdeb17d0248b18846160a00083f7d3d7aa8032 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 7 Aug 2024 19:57:38 +0200 Subject: [PATCH 3/5] feat: Search basics --- app/build.gradle | 8 +- .../main/java/eu/ztsh/garmin/MainActivity.kt | 1 + app/src/main/java/eu/ztsh/garmin/UI.kt | 3 + .../java/eu/ztsh/garmin/mapbox/MapControl.kt | 5 + .../garmin/mapbox/NavigationStateListener.kt | 12 + .../eu/ztsh/garmin/mapbox/RouteControl.kt | 8 +- .../eu/ztsh/garmin/mapbox/SearchControl.kt | 270 ++++++++++++++++++ app/src/main/res/layout/activity_main.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 304 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt create mode 100644 app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt diff --git a/app/build.gradle b/app/build.gradle index e08ff12..3a9e254 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,20 +40,26 @@ android { } ext { mapboxVersion = '3.2.0' - searchApiVersion = '2.3.0' + searchApiVersion = '2.3.1' + searchNativeVersion = '2.2.1' } dependencies { + + implementation 'com.mapbox.maps:android:11.5.1' + implementation "com.mapbox.navigationcore:navigation:$mapboxVersion" implementation "com.mapbox.navigationcore:ui-maps:$mapboxVersion" implementation "com.mapbox.navigationcore:voice:$mapboxVersion" implementation "com.mapbox.navigationcore:tripdata:$mapboxVersion" implementation "com.mapbox.navigationcore:ui-components:$mapboxVersion" + implementation "com.mapbox.search:base:$searchApiVersion" implementation "com.mapbox.search:autofill:$searchApiVersion" implementation "com.mapbox.search:place-autocomplete:$searchApiVersion" implementation "com.mapbox.search:mapbox-search-android:$searchApiVersion" implementation "com.mapbox.search:mapbox-search-android-ui:$searchApiVersion" + implementation "com.mapbox.search:mapbox-search-android-native:$searchNativeVersion" implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.appcompat:appcompat:1.7.0' diff --git a/app/src/main/java/eu/ztsh/garmin/MainActivity.kt b/app/src/main/java/eu/ztsh/garmin/MainActivity.kt index bb84358..9c4e337 100644 --- a/app/src/main/java/eu/ztsh/garmin/MainActivity.kt +++ b/app/src/main/java/eu/ztsh/garmin/MainActivity.kt @@ -27,6 +27,7 @@ import eu.ztsh.garmin.mapbox.MapControl import eu.ztsh.garmin.util.PermissionsHelper import java.lang.ref.WeakReference + @SuppressLint("MissingPermission") class MainActivity : AppCompatActivity() { diff --git a/app/src/main/java/eu/ztsh/garmin/UI.kt b/app/src/main/java/eu/ztsh/garmin/UI.kt index c27faff..c16bae5 100644 --- a/app/src/main/java/eu/ztsh/garmin/UI.kt +++ b/app/src/main/java/eu/ztsh/garmin/UI.kt @@ -56,4 +56,7 @@ class UI(b: ActivityMainBinding) { val routeOverview = b.routeOverview val stop = b.stop + val searchResultsView = b.searchResults + val searchPlaceView = b.searchPlaces + val queryEditText = b.query } \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt index 5ae5688..5c5429b 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt @@ -58,6 +58,8 @@ class MapControl( private lateinit var locationObserver: LocationObserver private lateinit var routeProgressObserver: RouteProgressObserver private lateinit var voiceInstructionsObserver: VoiceInstructionsObserver + private val searchControl = SearchControl(this, ui) + private val navigationStateListener = NavigationStateListener() fun init() { viewportDataSource = MapboxNavigationViewportDataSource(ui.mapView.mapboxMap) @@ -133,6 +135,7 @@ class MapControl( mapboxNavigation.registerLocationObserver(locationObserver) mapboxNavigation.registerRouteProgressObserver(routeProgressObserver) mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver) + mapboxNavigation.registerNavigationSessionStateObserver(navigationStateListener) replay.onAttached(mapboxNavigation) } @@ -142,6 +145,8 @@ class MapControl( mapboxNavigation.unregisterLocationObserver(locationObserver) mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver) mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver) + mapboxNavigation.unregisterNavigationSessionStateObserver(navigationStateListener) + replay.onDetached(mapboxNavigation) } diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt new file mode 100644 index 0000000..e4c5b67 --- /dev/null +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt @@ -0,0 +1,12 @@ +package eu.ztsh.garmin.mapbox + +import com.mapbox.navigation.core.trip.session.NavigationSessionState +import com.mapbox.navigation.core.trip.session.NavigationSessionStateObserver + +class NavigationStateListener: NavigationSessionStateObserver { + + override fun onNavigationSessionStateChanged(navigationSession: NavigationSessionState) { +// TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt index a59a899..ecdb76b 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt @@ -116,10 +116,10 @@ class RouteControl(private val mapControl: MapControl, ui: UI, private val conte routeLineView.initializeLayers(it) // add long click listener that search for a route to the clicked destination - ui.mapView.gestures.addOnMapLongClickListener { point -> - findRoute(point) - true - } +// ui.mapView.gestures.addOnMapLongClickListener { point -> +// findRoute(point) +// true +// } } // initialize view interactions diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt new file mode 100644 index 0000000..94a2b74 --- /dev/null +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt @@ -0,0 +1,270 @@ +package eu.ztsh.garmin.mapbox + +import android.content.Context +import android.content.res.Resources +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import androidx.annotation.StringRes +import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.mapbox.geojson.Point +import com.mapbox.maps.CameraOptions +import com.mapbox.maps.EdgeInsets +import com.mapbox.maps.MapView +import com.mapbox.maps.plugin.annotation.annotations +import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions +import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager +import com.mapbox.maps.plugin.gestures.gestures +import com.mapbox.search.autocomplete.PlaceAutocomplete +import com.mapbox.search.autocomplete.PlaceAutocompleteOptions +import com.mapbox.search.autocomplete.PlaceAutocompleteSuggestion +import com.mapbox.search.autocomplete.PlaceAutocompleteType +import com.mapbox.search.ui.adapter.autocomplete.PlaceAutocompleteUiAdapter +import com.mapbox.search.ui.view.CommonSearchViewConfiguration +import com.mapbox.search.ui.view.SearchResultsView +import com.mapbox.search.ui.view.place.SearchPlace +import eu.ztsh.garmin.R +import eu.ztsh.garmin.UI +import kotlinx.coroutines.launch + +class SearchControl(val mapControl: MapControl, val ui: UI) { + + // Set your Access Token here if it's not already set in some other way + // MapboxOptions.accessToken = "" + private var placeAutocomplete: PlaceAutocomplete = PlaceAutocomplete.create() + + private var placeAutocompleteUiAdapter: PlaceAutocompleteUiAdapter + + private val mapMarkersManager: MapMarkersManager = MapMarkersManager(ui.mapView) + + private var ignoreNextQueryUpdate = false + + init { + + ui.searchResultsView.initialize( + SearchResultsView.Configuration( + commonConfiguration = CommonSearchViewConfiguration() + ) + ) + placeAutocompleteUiAdapter = PlaceAutocompleteUiAdapter( + view = ui.searchResultsView, + placeAutocomplete = placeAutocomplete + ) + placeAutocompleteUiAdapter.addSearchListener(object : PlaceAutocompleteUiAdapter.SearchListener { + + override fun onSuggestionsShown(suggestions: List) { + // Nothing to do + } + + override fun onSuggestionSelected(suggestion: PlaceAutocompleteSuggestion) { + openPlaceCard(suggestion) + } + + override fun onPopulateQueryClick(suggestion: PlaceAutocompleteSuggestion) { + ui.queryEditText.setText(suggestion.name) + } + + override fun onError(e: Exception) { + // Nothing to do + } + }) + + ui.searchPlaceView.apply { + initialize(CommonSearchViewConfiguration()) + + isFavoriteButtonVisible = false + + addOnCloseClickListener { + hide() + closePlaceCard() + } + + addOnNavigateClickListener { searchPlace -> + showToast(R.string.not_implemented_yet) +// startActivity(geoIntent(searchPlace.coordinate)) + } + + addOnShareClickListener { searchPlace -> + showToast(R.string.not_implemented_yet) +// startActivity(shareIntent(searchPlace)) + } + } + + ui.queryEditText.addTextChangedListener(object : TextWatcher { + + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) { + if (ignoreNextQueryUpdate) { + ignoreNextQueryUpdate = false + } else { + closePlaceCard() + } + + mapControl.context.apply { + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + placeAutocompleteUiAdapter.search(text.toString()) + ui.searchResultsView.isVisible = text.isNotEmpty() + } + } + } + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // Nothing to do + } + + override fun afterTextChanged(s: Editable) { + // Nothing to do + } + }) + + ui.mapView.gestures.addOnMapLongClickListener { + reverseGeocoding(it) + return@addOnMapLongClickListener true + } + } + + private fun openPlaceCard(suggestion: PlaceAutocompleteSuggestion) { + ignoreNextQueryUpdate = true + ui.queryEditText.setText("") + + mapControl.context.apply { + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + placeAutocomplete.select(suggestion).onValue { result -> + mapMarkersManager.showMarker(result.coordinate) + ui.searchPlaceView.open(SearchPlace.createFromPlaceAutocompleteResult(result)) + ui.queryEditText.hideKeyboard() + ui.searchResultsView.isVisible = false + }.onError { error -> + Log.d(LOG_TAG, "Suggestion selection error", error) + showToast(R.string.place_autocomplete_selection_error) + } + + } + } + } + } + + private fun closePlaceCard() { + ui.searchPlaceView.hide() + mapMarkersManager.clearMarkers() + } + + private fun reverseGeocoding(point: Point) { + val types: List = when (ui.mapView.mapboxMap.cameraState.zoom) { + in 0.0..4.0 -> REGION_LEVEL_TYPES + in 4.0..6.0 -> DISTRICT_LEVEL_TYPES + in 6.0..12.0 -> LOCALITY_LEVEL_TYPES + else -> ALL_TYPES + } + + mapControl.context.lifecycleScope.launch { + mapControl.context.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + val response = placeAutocomplete.reverse(point, PlaceAutocompleteOptions(types = types)) + response.onValue { suggestions -> + if (suggestions.isEmpty()) { + showToast(R.string.place_autocomplete_reverse_geocoding_error_message) + } else { + openPlaceCard(suggestions.first()) + } + }.onError { error -> + Log.d(LOG_TAG, "Reverse geocoding error", error) + showToast(R.string.place_autocomplete_reverse_geocoding_error_message) + } + } + } + } + + private fun showToast(@StringRes resId: Int): Unit = + Toast.makeText(mapControl.context, resId, Toast.LENGTH_LONG).show() + + private fun View.hideKeyboard() = + (mapControl.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) + .hideSoftInputFromWindow(windowToken, 0) + + private class MapMarkersManager(mapView: MapView) { + + private val mapboxMap = mapView.mapboxMap + private val circleAnnotationManager = mapView.annotations.createCircleAnnotationManager(null) + private val markers = mutableMapOf() + + fun clearMarkers() { + markers.clear() + circleAnnotationManager.deleteAll() + } + + fun showMarker(coordinate: Point) { + clearMarkers() + + val circleAnnotationOptions: CircleAnnotationOptions = CircleAnnotationOptions() + .withPoint(coordinate) + .withCircleRadius(8.0) + .withCircleColor("#ee4e8b") + .withCircleStrokeWidth(2.0) + .withCircleStrokeColor("#ffffff") + + val annotation = circleAnnotationManager.create(circleAnnotationOptions) + markers[annotation.id] = coordinate + + CameraOptions.Builder() + .center(coordinate) + .padding(MARKERS_INSETS_OPEN_CARD) + .zoom(14.0) + .build().also { + mapboxMap.setCamera(it) + } + } + } + + private companion object { + + const val PERMISSIONS_REQUEST_LOCATION = 0 + + const val LOG_TAG = "AutocompleteUiActivity" + + val MARKERS_EDGE_OFFSET = dpToPx(64).toDouble() + val PLACE_CARD_HEIGHT = dpToPx(300).toDouble() + val MARKERS_TOP_OFFSET = dpToPx(88).toDouble() + + val MARKERS_INSETS_OPEN_CARD = EdgeInsets( + MARKERS_TOP_OFFSET, MARKERS_EDGE_OFFSET, PLACE_CARD_HEIGHT, MARKERS_EDGE_OFFSET + ) + + val REGION_LEVEL_TYPES = listOf( + PlaceAutocompleteType.AdministrativeUnit.Country, + PlaceAutocompleteType.AdministrativeUnit.Region + ) + + val DISTRICT_LEVEL_TYPES = REGION_LEVEL_TYPES + listOf( + PlaceAutocompleteType.AdministrativeUnit.Postcode, + PlaceAutocompleteType.AdministrativeUnit.District + ) + + val LOCALITY_LEVEL_TYPES = DISTRICT_LEVEL_TYPES + listOf( + PlaceAutocompleteType.AdministrativeUnit.Place, + PlaceAutocompleteType.AdministrativeUnit.Locality + ) + + private val ALL_TYPES = listOf( + PlaceAutocompleteType.Poi, + PlaceAutocompleteType.AdministrativeUnit.Country, + PlaceAutocompleteType.AdministrativeUnit.Region, + PlaceAutocompleteType.AdministrativeUnit.Postcode, + PlaceAutocompleteType.AdministrativeUnit.District, + PlaceAutocompleteType.AdministrativeUnit.Place, + PlaceAutocompleteType.AdministrativeUnit.Locality, + PlaceAutocompleteType.AdministrativeUnit.Neighborhood, + PlaceAutocompleteType.AdministrativeUnit.Street, + PlaceAutocompleteType.AdministrativeUnit.Address, + ) + + private fun dpToPx(dp: Int): Int = (dp * Resources.getSystem().displayMetrics.density).toInt() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fe89ff7..bfea266 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,6 +11,7 @@ android:layout_margin="16dp" android:autofillHints="@null" android:background="@drawable/card_background" + android:textColor="@color/black" android:elevation="4dp" android:hint="@string/place_autocomplete_query_hint" android:inputType="text" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a23bc0..774ae0a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,5 +5,6 @@ Search for places Unable to locate selected coordinate Error happened during request + Not implemented yet From ffbd7c46b8046cbb9a27d9928d9dbd9787a63646 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 9 Aug 2024 01:25:35 +0200 Subject: [PATCH 4/5] fix: Custom navigation events --- .../java/eu/ztsh/garmin/mapbox/MapControl.kt | 14 ++++-- .../garmin/mapbox/NavigationStateListener.kt | 12 ----- .../eu/ztsh/garmin/mapbox/NavigationStatus.kt | 49 +++++++++++++++++++ .../eu/ztsh/garmin/mapbox/RouteControl.kt | 25 +++++++--- .../eu/ztsh/garmin/mapbox/SearchControl.kt | 24 +++++++-- 5 files changed, 97 insertions(+), 27 deletions(-) delete mode 100644 app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt create mode 100644 app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStatus.kt diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt index 5c5429b..f3f4082 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/MapControl.kt @@ -4,6 +4,7 @@ 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 @@ -48,6 +49,8 @@ class MapControl( */ val navigationLocationProvider = NavigationLocationProvider() + val navigationStatusControl = NavigationStatusControl() + val replay = ReplayResources(this) // Observers @@ -59,7 +62,6 @@ class MapControl( private lateinit var routeProgressObserver: RouteProgressObserver private lateinit var voiceInstructionsObserver: VoiceInstructionsObserver private val searchControl = SearchControl(this, ui) - private val navigationStateListener = NavigationStateListener() fun init() { viewportDataSource = MapboxNavigationViewportDataSource(ui.mapView.mapboxMap) @@ -105,6 +107,10 @@ class MapControl( voiceInstructionsObserver = voiceControl.voiceInstructionsObserver } + fun routeToPoint(point: Point) { + routeControl.findRoute(point) + } + fun initNavigation() { MapboxNavigationApp.setup( NavigationOptions.Builder(context) @@ -135,7 +141,8 @@ class MapControl( mapboxNavigation.registerLocationObserver(locationObserver) mapboxNavigation.registerRouteProgressObserver(routeProgressObserver) mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver) - mapboxNavigation.registerNavigationSessionStateObserver(navigationStateListener) + + navigationStatusControl.registerObserver(searchControl) replay.onAttached(mapboxNavigation) } @@ -145,7 +152,8 @@ class MapControl( mapboxNavigation.unregisterLocationObserver(locationObserver) mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver) mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver) - mapboxNavigation.unregisterNavigationSessionStateObserver(navigationStateListener) + + navigationStatusControl.unregisterObserver(searchControl) replay.onDetached(mapboxNavigation) } diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt deleted file mode 100644 index e4c5b67..0000000 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStateListener.kt +++ /dev/null @@ -1,12 +0,0 @@ -package eu.ztsh.garmin.mapbox - -import com.mapbox.navigation.core.trip.session.NavigationSessionState -import com.mapbox.navigation.core.trip.session.NavigationSessionStateObserver - -class NavigationStateListener: NavigationSessionStateObserver { - - override fun onNavigationSessionStateChanged(navigationSession: NavigationSessionState) { -// TODO("Not yet implemented") - } - -} \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStatus.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStatus.kt new file mode 100644 index 0000000..468a697 --- /dev/null +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/NavigationStatus.kt @@ -0,0 +1,49 @@ +package eu.ztsh.garmin.mapbox + +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import java.util.concurrent.CopyOnWriteArraySet +import java.util.concurrent.atomic.AtomicReference + +class NavigationStatusControl { + + private val stateObservers = CopyOnWriteArraySet() + private val current = AtomicReference(NavigationStatus.IDLE) + + fun registerObserver(observer: NavigationStatusObserver) { + stateObservers.add(observer) + observer.onNavigationStatusChanged(current.get()) + } + + fun unregisterObserver(observer: NavigationStatusObserver) { + stateObservers.remove(observer) + } + + fun sendEvent(status: NavigationStatus) { + current.set(status) + stateObservers.forEach { + it.onNavigationStatusChanged(status) + } + if (status == NavigationStatus.FINISHED || status == NavigationStatus.CANCELED) { + // TODO: lifecyclescope? + runBlocking { + delay(1000) + sendEvent(NavigationStatus.IDLE) + } + } + } + +} + +enum class NavigationStatus { + IDLE, + STARTED, + FINISHED, + CANCELED +} + +fun interface NavigationStatusObserver { + + fun onNavigationStatusChanged(navigationStatus: NavigationStatus) + +} \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt index ecdb76b..b0e755e 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt @@ -3,10 +3,10 @@ package eu.ztsh.garmin.mapbox import android.content.Context import android.view.View import android.widget.Toast +import androidx.lifecycle.lifecycleScope import com.mapbox.api.directions.v5.models.Bearing import com.mapbox.api.directions.v5.models.RouteOptions import com.mapbox.geojson.Point -import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.navigation.base.TimeFormat import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions import com.mapbox.navigation.base.extensions.applyLanguageAndVoiceUnitOptions @@ -37,6 +37,10 @@ import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineApiOptions import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineViewOptions import eu.ztsh.garmin.Garmin import eu.ztsh.garmin.UI +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking class RouteControl(private val mapControl: MapControl, ui: UI, private val context: Context) { @@ -115,11 +119,6 @@ class RouteControl(private val mapControl: MapControl, ui: UI, private val conte // Ensure that the route line related layers are present before the route arrow routeLineView.initializeLayers(it) - // add long click listener that search for a route to the clicked destination -// ui.mapView.gestures.addOnMapLongClickListener { point -> -// findRoute(point) -// true -// } } // initialize view interactions @@ -219,7 +218,7 @@ class RouteControl(private val mapControl: MapControl, ui: UI, private val conte } } - private fun findRoute(destination: Point) { + fun findRoute(destination: Point) { val originLocation = mapControl.navigationLocationProvider.lastLocation ?: return val originPoint = Point.fromLngLat(originLocation.longitude, originLocation.latitude) @@ -284,6 +283,15 @@ class RouteControl(private val mapControl: MapControl, ui: UI, private val conte // start simulation mapControl.replay.startSimulation(routes.first().directionsRoute) + + mapControl.context.apply { + lifecycleScope.launch { + async { + delay(5000) + mapControl.navigationCamera.requestNavigationCameraToFollowing() + } + } + } } private fun clearRouteAndStopNavigation() { @@ -298,6 +306,9 @@ class RouteControl(private val mapControl: MapControl, ui: UI, private val conte mapControl.ui.maneuverView.visibility = View.INVISIBLE mapControl.ui.routeOverview.visibility = View.INVISIBLE mapControl.ui.tripProgressCard.visibility = View.INVISIBLE + + // post custom event + mapControl.navigationStatusControl.sendEvent(NavigationStatus.CANCELED) } fun cancel() { diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt index 94a2b74..ef867f2 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt @@ -21,6 +21,7 @@ import com.mapbox.maps.plugin.annotation.annotations import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager import com.mapbox.maps.plugin.gestures.gestures +import com.mapbox.navigation.core.trip.session.NavigationSessionState import com.mapbox.search.autocomplete.PlaceAutocomplete import com.mapbox.search.autocomplete.PlaceAutocompleteOptions import com.mapbox.search.autocomplete.PlaceAutocompleteSuggestion @@ -33,7 +34,7 @@ import eu.ztsh.garmin.R import eu.ztsh.garmin.UI import kotlinx.coroutines.launch -class SearchControl(val mapControl: MapControl, val ui: UI) { +class SearchControl(val mapControl: MapControl, val ui: UI): NavigationStatusObserver { // Set your Access Token here if it's not already set in some other way // MapboxOptions.accessToken = "" @@ -86,13 +87,12 @@ class SearchControl(val mapControl: MapControl, val ui: UI) { } addOnNavigateClickListener { searchPlace -> - showToast(R.string.not_implemented_yet) -// startActivity(geoIntent(searchPlace.coordinate)) + mapControl.routeToPoint(searchPlace.coordinate) + mapControl.navigationStatusControl.sendEvent(NavigationStatus.STARTED) } - addOnShareClickListener { searchPlace -> + addOnShareClickListener { _ -> showToast(R.string.not_implemented_yet) -// startActivity(shareIntent(searchPlace)) } } @@ -182,6 +182,20 @@ class SearchControl(val mapControl: MapControl, val ui: UI) { } } + override fun onNavigationStatusChanged(navigationStatus: NavigationStatus) { + when (navigationStatus) { + NavigationStatus.IDLE -> { + ui.queryEditText.visibility = View.VISIBLE + } + NavigationStatus.STARTED -> { + mapControl.navigationCamera.requestNavigationCameraToOverview() + closePlaceCard() + ui.queryEditText.visibility = View.GONE + } + else -> {} + } + } + private fun showToast(@StringRes resId: Int): Unit = Toast.makeText(mapControl.context, resId, Toast.LENGTH_LONG).show() From 4d9761c30036c0098ff7d5f78f54f23618409b65 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 9 Aug 2024 01:34:00 +0200 Subject: [PATCH 5/5] fix: CR fixes --- app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt | 1 - app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt | 3 --- 2 files changed, 4 deletions(-) diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt index b0e755e..1538318 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/RouteControl.kt @@ -40,7 +40,6 @@ import eu.ztsh.garmin.UI import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking class RouteControl(private val mapControl: MapControl, ui: UI, private val context: Context) { diff --git a/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt b/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt index ef867f2..a8f8fd9 100644 --- a/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt +++ b/app/src/main/java/eu/ztsh/garmin/mapbox/SearchControl.kt @@ -21,7 +21,6 @@ import com.mapbox.maps.plugin.annotation.annotations import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager import com.mapbox.maps.plugin.gestures.gestures -import com.mapbox.navigation.core.trip.session.NavigationSessionState import com.mapbox.search.autocomplete.PlaceAutocomplete import com.mapbox.search.autocomplete.PlaceAutocompleteOptions import com.mapbox.search.autocomplete.PlaceAutocompleteSuggestion @@ -239,8 +238,6 @@ class SearchControl(val mapControl: MapControl, val ui: UI): NavigationStatusObs private companion object { - const val PERMISSIONS_REQUEST_LOCATION = 0 - const val LOG_TAG = "AutocompleteUiActivity" val MARKERS_EDGE_OFFSET = dpToPx(64).toDouble()