Skip to content

Commit

Permalink
Feature/on map tap callback (#292)
Browse files Browse the repository at this point in the history
* Fix Clear Embedded Route

* rename android package

* fix linting issues partial

* tap on map now gives users an event back

* fixed IsSilent not being used in native

* make onMapTapCallback optional from settings

* fixed alternative routes on iOS

---------

Co-authored-by: Emmanuel Oche <[email protected]>
  • Loading branch information
marcotrumpet and eopeter authored Nov 14, 2023
1 parent 5b46370 commit 83325fd
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class FlutterMapboxNavigationPlugin : FlutterPlugin, MethodCallHandler,
var showAlternateRoutes: Boolean = true
var longPressDestinationEnabled: Boolean = true
var allowsUTurnsAtWayPoints: Boolean = false
var enableOnMapTapCallback: Boolean = false
var navigationMode = DirectionsCriteria.PROFILE_DRIVING_TRAFFIC
var simulateRoute = false
var enableFreeDriveMode = false
Expand Down Expand Up @@ -145,6 +146,11 @@ class FlutterMapboxNavigationPlugin : FlutterPlugin, MethodCallHandler,
allowsUTurnsAtWayPoints = allowsUTurns
}

val onMapTap = arguments?.get("enableOnMapTapCallback") as? Boolean
if (onMapTap != null) {
enableOnMapTapCallback = onMapTap
}

val language = arguments?.get("language") as? String
if (language != null) {
navigationLanguage = language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ open class TurnByTurn(
val point = item.value as HashMap<*, *>
val latitude = point["Latitude"] as Double
val longitude = point["Longitude"] as Double
this.addedWaypoints.add(Waypoint(Point.fromLngLat(longitude, latitude)))
val isSilent = point["IsSilent"] as Boolean
this.addedWaypoints.add(Waypoint(Point.fromLngLat(longitude, latitude),isSilent))
}
this.getRoute(this.context)
result.success(true)
Expand Down Expand Up @@ -311,6 +312,11 @@ open class TurnByTurn(
if (longPress != null) {
this.longPressDestinationEnabled = longPress
}

val onMapTap = arguments["enableOnMapTapCallback"] as? Boolean
if (onMapTap != null) {
this.enableOnMapTapCallback = onMapTap
}
}

open fun registerObservers() {
Expand Down Expand Up @@ -380,6 +386,7 @@ open class TurnByTurn(
private var voiceInstructionsEnabled = true
private var bannerInstructionsEnabled = true
private var longPressDestinationEnabled = true
private var enableOnMapTapCallback = false
private var animateBuildRoute = true
private var isOptimized = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import android.content.Intent
import android.content.IntentFilter
import android.location.Location
import android.os.Bundle

import org.json.JSONObject
import androidx.appcompat.app.AppCompatActivity
import com.eopeter.fluttermapboxnavigation.FlutterMapboxNavigationPlugin
import com.eopeter.fluttermapboxnavigation.R
Expand All @@ -24,6 +26,7 @@ import com.mapbox.geojson.Point
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.maps.plugin.gestures.OnMapLongClickListener
import com.mapbox.maps.plugin.gestures.OnMapClickListener
import com.mapbox.maps.plugin.gestures.gestures
import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
import com.mapbox.navigation.base.extensions.applyLanguageAndVoiceUnitOptions
Expand Down Expand Up @@ -85,6 +88,7 @@ class NavigationActivity : AppCompatActivity() {
binding = NavigationActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.navigationView.addListener(navigationStateListener)
binding.navigationView.registerMapObserver(onMapClick)
accessToken =
PluginUtilities.getResourceFromContext(this.applicationContext, "mapbox_access_token")

Expand All @@ -103,6 +107,9 @@ class NavigationActivity : AppCompatActivity() {
}
}

if (FlutterMapboxNavigationPlugin.enableOnMapTapCallback) {
binding.navigationView.registerMapObserver(onMapClick)
}
val act = this
// Add custom view binders
binding.navigationView.customizeViewBinders {
Expand Down Expand Up @@ -182,6 +189,9 @@ class NavigationActivity : AppCompatActivity() {
if (FlutterMapboxNavigationPlugin.longPressDestinationEnabled) {
binding.navigationView.unregisterMapObserver(onMapLongClick)
}
if (FlutterMapboxNavigationPlugin.enableOnMapTapCallback) {
binding.navigationView.unregisterMapObserver(onMapClick)
}
binding.navigationView.removeListener(navigationStateListener)

MapboxNavigationApp.current()?.unregisterBannerInstructionsObserver(this.bannerInstructionObserver)
Expand Down Expand Up @@ -426,4 +436,27 @@ class NavigationActivity : AppCompatActivity() {
return false
}
}

/**
* Notifies with attach and detach events on [MapView]
*/
private val onMapClick = object : MapViewObserver(), OnMapClickListener {

override fun onAttached(mapView: MapView) {
mapView.gestures.addOnMapClickListener(this)
}

override fun onDetached(mapView: MapView) {
mapView.gestures.removeOnMapClickListener(this)
}

override fun onMapClick(point: Point): Boolean {
var waypoint = mapOf<String, String>(
Pair("latitude", point.latitude().toString()),
Pair("longitude", point.longitude().toString())
)
sendEvent(MapBoxEvents.ON_MAP_TAP, JSONObject(waypoint).toString())
return false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ enum class MapBoxEvents(val value: String) {
ON_ARRIVAL("on_arrival"),
FAILED_TO_REROUTE("failed_to_reroute"),
REROUTE_ALONG("reroute_along"),
ON_MAP_TAP("on_map_tap")
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ import android.content.Context
import android.view.View
import com.eopeter.fluttermapboxnavigation.TurnByTurn
import com.eopeter.fluttermapboxnavigation.databinding.NavigationActivityBinding
import com.eopeter.fluttermapboxnavigation.models.MapBoxEvents
import com.eopeter.fluttermapboxnavigation.utilities.PluginUtilities
import com.mapbox.geojson.Point
import com.mapbox.maps.MapView
import com.mapbox.maps.plugin.gestures.OnMapClickListener
import com.mapbox.maps.plugin.gestures.gestures
import com.mapbox.navigation.dropin.map.MapViewObserver
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.platform.PlatformView
import org.json.JSONObject

class EmbeddedNavigationMapView(
context: Context,
Expand Down Expand Up @@ -38,13 +46,44 @@ class EmbeddedNavigationMapView(
enableMapLongClickIntercept = false;
}
}

if((this.arguments?.get("enableOnMapTapCallback") as Boolean)) {
this.binding.navigationView.registerMapObserver(onMapClick)
}
}

override fun getView(): View {
return binding.root
}

override fun dispose() {
if((this.arguments?.get("enableOnMapTapCallback") as Boolean)) {
this.binding.navigationView.unregisterMapObserver(onMapClick)
}
unregisterObservers()
}

/**
* Notifies with attach and detach events on [MapView]
*/
private val onMapClick = object : MapViewObserver(), OnMapClickListener {

override fun onAttached(mapView: MapView) {
mapView.gestures.addOnMapClickListener(this)
}

override fun onDetached(mapView: MapView) {
mapView.gestures.removeOnMapClickListener(this)
}

override fun onMapClick(point: Point): Boolean {
var waypoint = mapOf<String, String>(
Pair("latitude", point.latitude().toString()),
Pair("longitude", point.longitude().toString())
)
PluginUtilities.sendEvent(MapBoxEvents.ON_MAP_TAP, JSONObject(waypoint).toString())
return false
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class PluginUtilities {

fun sendEvent(event: MapBoxEvents, data: String = "") {
val jsonString =
if (MapBoxEvents.MILESTONE_EVENT == event || event == MapBoxEvents.USER_OFF_ROUTE || event == MapBoxEvents.ROUTE_BUILT) "{" +
if (MapBoxEvents.MILESTONE_EVENT == event || event == MapBoxEvents.USER_OFF_ROUTE || event == MapBoxEvents.ROUTE_BUILT || event == MapBoxEvents.ON_MAP_TAP) "{" +
" \"eventType\": \"${event.value}\"," +
" \"data\": $data" +
"}" else "{" +
Expand Down
35 changes: 32 additions & 3 deletions ios/Classes/EmbeddedNavigationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,13 @@ public class FlutterMapboxNavigationView : NavigationFactory, FlutterPlatformVie
gesture.delegate = self
navigationMapView?.addGestureRecognizer(gesture)
}


if _enableOnMapTapCallback {
let onTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
onTapGesture.numberOfTapsRequired = 1
onTapGesture.delegate = self
navigationMapView?.addGestureRecognizer(onTapGesture)
}
}

func clearRoute(arguments: NSDictionary?, result: @escaping FlutterResult)
Expand Down Expand Up @@ -230,6 +236,7 @@ public class FlutterMapboxNavigationView : NavigationFactory, FlutterPlatformVie

routeOptions.distanceMeasurementSystem = _voiceUnits == "imperial" ? .imperial : .metric
routeOptions.locale = Locale(identifier: _language)
routeOptions.includesAlternativeRoutes = _alternatives
self.routeOptions = routeOptions

// Generate the route object and draw it on the map
Expand Down Expand Up @@ -411,16 +418,38 @@ extension FlutterMapboxNavigationView : NavigationMapViewDelegate {
}

extension FlutterMapboxNavigationView : UIGestureRecognizerDelegate {

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}

@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
guard gesture.state == .ended else { return }
let location = navigationMapView.mapView.mapboxMap.coordinate(for: gesture.location(in: navigationMapView.mapView))
requestRoute(destination: location)
}

@objc func handleTap(_ gesture: UITapGestureRecognizer) {
guard gesture.state == .ended else {return}
let location = navigationMapView.mapView.mapboxMap.coordinate(for: gesture.location(in: navigationMapView.mapView))
let waypoint: Encodable = [
"latitude" : location.latitude,
"longitude" : location.longitude,
]
do {
let encodedData = try JSONEncoder().encode(waypoint)
let jsonString = String(data: encodedData,
encoding: .utf8)

if (jsonString?.isEmpty ?? true) {
return
}

sendEvent(eventType: .on_map_tap,data: jsonString!)
} catch {
return
}
}

func requestRoute(destination: CLLocationCoordinate2D) {
isEmbeddedNavigation = true
Expand Down
6 changes: 6 additions & 0 deletions ios/Classes/NavigationFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ public class NavigationFactory : NSObject, FlutterStreamHandler
var _bearing: Double = 0.0
var _animateBuildRoute = true
var _longPressDestinationEnabled = true
var _alternatives = true
var _shouldReRoute = true
var _showReportFeedbackButton = true
var _showEndOfRouteFeedback = true
var _enableOnMapTapCallback = false
var navigationDirections: Directions?

func addWayPoints(arguments: NSDictionary?, result: @escaping FlutterResult)
Expand Down Expand Up @@ -88,6 +90,8 @@ public class NavigationFactory : NSObject, FlutterStreamHandler

parseFlutterArguments(arguments: arguments)

_options?.includesAlternativeRoutes = _alternatives

if(_wayPoints.count > 3 && arguments?["mode"] == nil)
{
_navigationMode = "driving"
Expand Down Expand Up @@ -213,13 +217,15 @@ public class NavigationFactory : NSObject, FlutterStreamHandler
_navigationMode = arguments?["mode"] as? String ?? "drivingWithTraffic"
_showReportFeedbackButton = arguments?["showReportFeedbackButton"] as? Bool ?? _showReportFeedbackButton
_showEndOfRouteFeedback = arguments?["showEndOfRouteFeedback"] as? Bool ?? _showEndOfRouteFeedback
_enableOnMapTapCallback = arguments?["enableOnMapTapCallback"] as? Bool ?? _enableOnMapTapCallback
_mapStyleUrlDay = arguments?["mapStyleUrlDay"] as? String
_mapStyleUrlNight = arguments?["mapStyleUrlNight"] as? String
_zoom = arguments?["zoom"] as? Double ?? _zoom
_bearing = arguments?["bearing"] as? Double ?? _bearing
_tilt = arguments?["tilt"] as? Double ?? _tilt
_animateBuildRoute = arguments?["animateBuildRoute"] as? Bool ?? _animateBuildRoute
_longPressDestinationEnabled = arguments?["longPressDestinationEnabled"] as? Bool ?? _longPressDestinationEnabled
_alternatives = arguments?["alternatives"] as? Bool ?? _alternatives
}


Expand Down
1 change: 1 addition & 0 deletions ios/Classes/models/EventType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ enum MapBoxEventType: String, Codable
case on_arrival
case failed_to_reroute
case reroute_along
case on_map_tap
}
1 change: 1 addition & 0 deletions lib/src/embedded/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class MapBoxNavigationViewController {
'Name': wayPoint.name,
'Latitude': wayPoint.latitude,
'Longitude': wayPoint.longitude,
'IsSilent': wayPoint.isSilent,
};
pointList.add(pointMap);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/src/models/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ enum MapBoxEvent {
banner_instruction,
on_arrival,
failed_to_reroute,
reroute_along
reroute_along,
on_map_tap
}
6 changes: 6 additions & 0 deletions lib/src/models/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class MapBoxOptions {
this.animateBuildRoute,
this.showReportFeedbackButton = true,
this.showEndOfRouteFeedback = true,
this.enableOnMapTapCallback = false,
});

MapBoxOptions.from(MapBoxOptions option) {
Expand Down Expand Up @@ -163,6 +164,10 @@ class MapBoxOptions {
/// In iOS this will show/hide the end of route page when navigation is done. Default to True.
bool? showEndOfRouteFeedback;

/// Gives you the ability to receive back a waypoint corresponding
/// to where you tap on the map.
bool? enableOnMapTapCallback;

Map<String, dynamic> toMap() {
final optionsMap = <String, dynamic>{};
void addIfNonNull(String fieldName, dynamic value) {
Expand Down Expand Up @@ -219,6 +224,7 @@ class MapBoxOptions {

addIfNonNull('showReportFeedbackButton', showReportFeedbackButton);
addIfNonNull('showEndOfRouteFeedback', showEndOfRouteFeedback);
addIfNonNull('enableOnMapTapCallback', enableOnMapTapCallback);

return optionsMap;
}
Expand Down
5 changes: 5 additions & 0 deletions lib/src/models/route_event.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';

import 'package:flutter_mapbox_navigation/flutter_mapbox_navigation.dart';

Expand Down Expand Up @@ -26,6 +27,10 @@ class RouteEvent {
(dataJson as String).isNotEmpty) {
data =
MapBoxFeedback.fromJson(jsonDecode(dataJson) as Map<String, dynamic>);
} else if (eventType == MapBoxEvent.on_map_tap) {
final json =
Platform.isAndroid ? dataJson : jsonDecode(dataJson as String);
data = WayPoint.fromJson(json as Map<String, dynamic>);
} else {
data = jsonEncode(dataJson);
}
Expand Down
8 changes: 6 additions & 2 deletions lib/src/models/way_point.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ class WayPoint {
/// create [WayPoint] from a json
WayPoint.fromJson(Map<String, dynamic> json) {
name = json['name'] as String?;
latitude = json['latitude'] as double?;
longitude = json['longitude'] as double?;
latitude = (json['latitude'] is String)
? double.tryParse(json['latitude'] as String)
: json['latitude'] as double?;
longitude = (json['longitude'] is String)
? double.tryParse(json['longitude'] as String)
: json['longitude'] as double?;

if (json['isSilent'] == null) {
isSilent = false;
Expand Down

0 comments on commit 83325fd

Please sign in to comment.