diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/RoutingExample.dart | 148 | ||||
| -rw-r--r-- | lib/events/NextStopLoadedEvent.dart | 1 | ||||
| -rw-r--r-- | lib/events/StopCompletedEvent.dart | 1 | ||||
| -rw-r--r-- | lib/pages/navigation_page.dart | 180 |
4 files changed, 275 insertions, 55 deletions
diff --git a/lib/RoutingExample.dart b/lib/RoutingExample.dart index 0ec3e7e..02c097a 100644 --- a/lib/RoutingExample.dart +++ b/lib/RoutingExample.dart @@ -17,6 +17,8 @@ import 'package:here_sdk/routing.dart'; import 'package:image/image.dart' as image; import 'package:here_sdk/routing.dart' as here; import 'package:training_planner/events/MapPanningEvent.dart'; +import 'package:training_planner/events/NextStopLoadedEvent.dart'; +import 'package:training_planner/events/StopCompletedEvent.dart'; import 'package:training_planner/pages/navigation_page.dart'; import 'route.dart' as DHLRoute; @@ -25,12 +27,45 @@ import 'main.dart'; // A callback to notify the hosting widget. typedef ShowDialogFunction = void Function(String title, String message); +class DestinationPin { + final String text; + final GeoCoordinates? coords; + WidgetPin? pin; + + DestinationPin({this.text = '', this.coords}); +} + +class ActiveTask { + final int firstParcelNumber; + final String deliveryTimeBlock; + final int lastParcelNumber; + final String fullAddress; + final bool needsSignature; + final bool notAtNeighbors; + + ActiveTask( + this.firstParcelNumber, + this.deliveryTimeBlock, + this.lastParcelNumber, + this.fullAddress, + this.needsSignature, + this.notAtNeighbors); +} + class RoutingExample { Timer? timer; bool isLookingAround = false; double currentZoom = 20; HereMapController hereMapController; + StreamSubscription? stopCompletedEvent; + List<MapPolyline> _routeSections = []; + List<MapPolyline> _pathSections = []; + List<DestinationPin> _parcelNumberPins = []; + List<GeoCoordinates> _destinationCoords = []; + List<ActiveTask> allTasks = []; + late ActiveTask activeTask; + int routeSectionCursor = 0; late DHLRoute.Route _route; @@ -40,6 +75,8 @@ class RoutingExample { RoutingExample(HereMapController _hereMapController) : hereMapController = _hereMapController { + activeTask = ActiveTask(0, "", 0, "", false, false); + try { _routingEngine = RoutingEngine(); } on InstantiationException { @@ -65,6 +102,21 @@ class RoutingExample { isLookingAround = true; eventBus.fire(MapPanningEvent(true)); }); + + stopCompletedEvent = eventBus.on<StopCompletedEvent>().listen((e) { + routeSectionCursor += 1; + if (routeSectionCursor >= allTasks.length) { + routeSectionCursor = allTasks.length - 1; + } + activeTask = allTasks[routeSectionCursor]; + updateHighlightedRouteSections(); + eventBus.fire(NextStopLoadedEvent()); + }); + } + + void destroy() { + stopCompletedEvent?.cancel(); + timer?.cancel(); } void _updateLocation(Position value) { @@ -129,9 +181,20 @@ class RoutingExample { } void showAnchoredMapViewPin(GeoCoordinates coords, String text) { - var widgetPin = hereMapController.pinWidget( - _createWidget(text, Color.fromARGB(200, 0, 144, 138)), coords); - widgetPin?.anchor = Anchor2D.withHorizontalAndVertical(0.5, 0.5); + _parcelNumberPins.add(DestinationPin(text: text, coords: coords)); + } + + DHLRoute.Task _findTaskWithLowestSequenceNumberInGroup( + DHLRoute.Route route, List<String> groupPids) { + List<DHLRoute.Task> tasksFound = []; + + for (final item in route.tasks!) { + if (groupPids.contains(item.pid)) tasksFound.add(item); + } + + tasksFound.sort((e1, e2) => int.parse(e1.deliverySequenceNumber!) + .compareTo(int.parse(e2.deliverySequenceNumber!))); + return tasksFound.first; } Future<void> addRoute(DHLRoute.Route route) async { @@ -145,23 +208,54 @@ class RoutingExample { List<Waypoint> waypoints = [Waypoint.withDefaults(routeStartCoords)]; - GeoCoordinates previousCoords = routeStartCoords; + bool isFirst = true; for (final item in route.tasks!) { var destinationGeoCoordinates = GeoCoordinates( double.parse(item.addressLatitude!), double.parse(item.addressLongitude!)); - if (item.groupFirst == 'false') continue; + if (item.isIntervention != 'true' && + item.groupSize != null && + item.groupPids != null && + int.parse(item.groupSize!) > 1) { + var firstTaskInGroup = + _findTaskWithLowestSequenceNumberInGroup(route, item.groupPids!); + if (firstTaskInGroup != item) { + continue; + } + } waypoints.add(Waypoint.withDefaults(destinationGeoCoordinates)); - showAnchoredMapViewPin( - destinationGeoCoordinates, item.deliverySequenceNumber.toString()); - previousCoords = destinationGeoCoordinates; + _parcelNumberPins.add(DestinationPin( + text: item.deliverySequenceNumber.toString(), + coords: destinationGeoCoordinates)); + _destinationCoords.add(destinationGeoCoordinates); + debugPrint(item.deliverySequenceNumber); + + int sequenceNumber = int.parse(item.deliverySequenceNumber!); + int groupLastSequenceNumber = int.parse(item.deliverySequenceNumber!); + if (item.groupSize != null) { + groupLastSequenceNumber += int.parse(item.groupSize!) - 1; + } + var groupedTask = ActiveTask( + sequenceNumber, + item.timeframe!, + groupLastSequenceNumber, + item.fullAddressForNavigation!, + item.indicationSignatureRequired == true, + item.indicationNotAtNeighbours == true); + + if (isFirst) { + activeTask = groupedTask; + isFirst = false; + } + allTasks.add(groupedTask); } PedestrianOptions f = PedestrianOptions.withDefaults(); f.routeOptions.alternatives = 0; + f.routeOptions.enableTrafficOptimization = false; f.routeOptions.optimizationMode = OptimizationMode.fastest; _routingEngine.calculatePedestrianRoute(waypoints, f, @@ -181,6 +275,8 @@ class RoutingExample { var error = routingError.toString(); } }); + + eventBus.fire(NextStopLoadedEvent()); } _showLineToHouse(Section section, GeoCoordinates houseCoords) { @@ -192,7 +288,7 @@ class RoutingExample { MapPolyline(walkLine, 8, Color.fromARGB(160, 255, 20, 20)); hereMapController.mapScene.addMapPolyline(walkPathPolyline); - //_routeSections.add(walkPathPolyline); + _pathSections.add(walkPathPolyline); } _showRouteOnMap(here.Route route) { @@ -211,13 +307,43 @@ class RoutingExample { } void updateHighlightedRouteSections() { + // Show the next 20 parcel pins, to let the delivery driver decide on possible detours. + int maxPins = 20; + for (int i = 0; i < _parcelNumberPins.length; i++) { + DestinationPin pin = _parcelNumberPins.elementAt(i); + + if (i > routeSectionCursor && i < routeSectionCursor + maxPins) { + if (pin.pin != null) continue; + var widgetPin = hereMapController.pinWidget( + _createWidget(pin.text, Color.fromARGB(200, 0, 144, 138)), + _destinationCoords[i]); + widgetPin?.anchor = Anchor2D.withHorizontalAndVertical(0.5, 0.5); + pin.pin = widgetPin; + } else { + pin.pin?.unpin(); + pin.pin = null; + } + + // Highlight current destination. + if (i == routeSectionCursor) { + var widgetPin = hereMapController.pinWidget( + _createWidget(pin.text, ui.Color.fromARGB(199, 143, 8, 31)), + _destinationCoords[i]); + widgetPin?.anchor = Anchor2D.withHorizontalAndVertical(0.5, 0.5); + pin.pin = widgetPin; + } + } + + // Show the next 5 sections as to not clutter the screen. + int maxSections = 5; for (int i = 0; i < _routeSections.length; i++) { MapPolyline section = _routeSections.elementAt(i); - int maxSections = 5; + MapPolyline path = _pathSections.elementAt(i); // previous section if (i == routeSectionCursor - 1) { section.lineColor = Color.fromARGB(160, 168, 113, 108); + path.lineColor = Color.fromARGB(0, 255, 255, 255); } // current and next 5 sections else if (i >= routeSectionCursor && @@ -228,8 +354,10 @@ class RoutingExample { 0, 144, 138); + path.lineColor = Color.fromARGB(160, 255, 0, 0); } else { section.lineColor = Color.fromARGB(0, 255, 255, 255); + path.lineColor = Color.fromARGB(0, 255, 255, 255); } } } diff --git a/lib/events/NextStopLoadedEvent.dart b/lib/events/NextStopLoadedEvent.dart new file mode 100644 index 0000000..c4fd59d --- /dev/null +++ b/lib/events/NextStopLoadedEvent.dart @@ -0,0 +1 @@ +class NextStopLoadedEvent {} diff --git a/lib/events/StopCompletedEvent.dart b/lib/events/StopCompletedEvent.dart new file mode 100644 index 0000000..8aa283b --- /dev/null +++ b/lib/events/StopCompletedEvent.dart @@ -0,0 +1 @@ +class StopCompletedEvent {} diff --git a/lib/pages/navigation_page.dart b/lib/pages/navigation_page.dart index 531395e..d1c15b9 100644 --- a/lib/pages/navigation_page.dart +++ b/lib/pages/navigation_page.dart @@ -8,6 +8,8 @@ import 'package:in_date_utils/in_date_utils.dart' as DateUtilities; import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:training_planner/RoutingExample.dart'; import 'package:training_planner/events/MapPanningEvent.dart'; +import 'package:training_planner/events/NextStopLoadedEvent.dart'; +import 'package:training_planner/events/StopCompletedEvent.dart'; import 'package:training_planner/main.dart'; import 'package:training_planner/shift.dart'; import 'package:training_planner/style/style.dart'; @@ -28,8 +30,9 @@ class NavigationPage extends StatefulWidget { class _NavigationPageState extends State<NavigationPage> { RoutingExample? _routingExample; - StreamSubscription? panGestureEvent; + StreamSubscription? taskLoadedEvent; + ActiveTask? activeTask; Future<bool> _handleLocationPermission() async { bool serviceEnabled; @@ -69,6 +72,10 @@ class _NavigationPageState extends State<NavigationPage> { panGestureEvent = eventBus.on<MapPanningEvent>().listen((event) { changeIsLookingAround(event.isPanning); }); + + taskLoadedEvent = eventBus.on<NextStopLoadedEvent>().listen((event) { + setState(() {}); + }); } void changeIsLookingAround(bool val) { @@ -86,52 +93,135 @@ class _NavigationPageState extends State<NavigationPage> { } void _mockStopComplete() { - _routingExample?.routeSectionCursor++; - _routingExample?.updateHighlightedRouteSections(); + eventBus.fire(StopCompletedEvent()); } @override Widget build(BuildContext context) { return Scaffold( - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - FloatingActionButton( - onPressed: () => _mockStopComplete(), - child: Icon(Icons.check_circle), - ), - Visibility( - visible: _routingExample == null - ? false - : _routingExample!.isLookingAround, - child: FloatingActionButton( - backgroundColor: Colors.green, - child: const Icon(Icons.center_focus_strong), - onPressed: () => { - changeIsLookingAround(false), - _routingExample?.flyTo(_routingExample!.lastPosition) - }, + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + FloatingActionButton( + onPressed: () => _mockStopComplete(), + child: Icon(Icons.check_circle), ), + Visibility( + visible: _routingExample == null + ? false + : _routingExample!.isLookingAround, + child: FloatingActionButton( + backgroundColor: Colors.green, + child: const Icon(Icons.center_focus_strong), + onPressed: () => { + changeIsLookingAround(false), + _routingExample?.flyTo(_routingExample!.lastPosition) + }, + ), + ), + Padding(padding: EdgeInsets.all(5)), + FloatingActionButton( + onPressed: () => _zoomOut(), + child: Icon(Icons.zoom_out), + ), + Padding(padding: EdgeInsets.all(2)), + FloatingActionButton( + onPressed: () => _zoomIn(), + child: Icon(Icons.zoom_in), + ) + ], + ), + ), + body: Column( + children: [ + _createNextDropInfoWidget(), + Container( + decoration: BoxDecoration(color: Colors.black), + height: 2, ), - Padding(padding: EdgeInsets.all(5)), - FloatingActionButton( - onPressed: () => _zoomOut(), - child: Icon(Icons.zoom_out), - ), - Padding(padding: EdgeInsets.all(2)), - FloatingActionButton( - onPressed: () => _zoomIn(), - child: Icon(Icons.zoom_in), - ) + Expanded(child: HereMap(onMapCreated: _onMapCreated)), ], - ), - ), - body: Stack( + )); + } + + Widget _createNextDropInfoWidget() { + if (_routingExample == null) return Padding(padding: EdgeInsets.all(0)); + + return Container( + decoration: BoxDecoration(color: Colors.white), + height: 80, + child: Column( children: [ - HereMap(onMapCreated: _onMapCreated), + SizedBox( + height: 10, + ), + Container( + height: 50, + padding: EdgeInsets.only(left: 10, right: 10), + child: Row( + children: [ + RichText( + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: <TextSpan>[ + TextSpan( + text: '[' + + _routingExample!.activeTask.firstParcelNumber + .toString() + + ' - ' + + _routingExample!.activeTask.lastParcelNumber + .toString() + + '', + style: TextStyle( + color: Color.fromARGB(255, 0, 0, 0), + fontSize: 25, + ), + ), + TextSpan( + text: ' ' + + (_routingExample!.activeTask.lastParcelNumber - + _routingExample! + .activeTask.firstParcelNumber + + 1) + .toString(), + style: TextStyle( + color: Color.fromARGB(150, 0, 0, 0), + fontSize: 12, + ), + ), + TextSpan( + text: ']', + style: TextStyle( + color: Color.fromARGB(255, 0, 0, 0), + fontSize: 25, + ), + ), + ], + ), + ), + Padding(padding: EdgeInsets.all(5)), + Expanded( + child: Text( + _routingExample!.activeTask.fullAddress, + style: TextStyle( + color: Color.fromARGB(255, 0, 0, 0), + fontSize: 18, + ), + ), + ), + ], + ), + ), + Container( + height: 20, + padding: EdgeInsets.only(left: 10, right: 10), + child: Row(children: [ + Text(_routingExample!.activeTask.deliveryTimeBlock) + ]), + ), ], ), ); @@ -142,9 +232,9 @@ class _NavigationPageState extends State<NavigationPage> { (MapError? error) { if (error == null) { _routingExample = RoutingExample(hereMapController); - routeProvider - .getRoute(0) - .then((value) => _routingExample?.addRoute(value)); + routeProvider.getRoute(0).then((value) { + _routingExample?.addRoute(value); + }); } else { print("Map scene not loaded. MapError: " + error.toString()); } @@ -154,11 +244,11 @@ class _NavigationPageState extends State<NavigationPage> { @override void dispose() { // Free HERE SDK resources before the application shuts down. - SDKNativeEngine.sharedInstance?.dispose(); - SdkContext.release(); + //SDKNativeEngine.sharedInstance?.dispose(); + //SdkContext.release(); panGestureEvent?.cancel(); - - _routingExample?.timer?.cancel(); + taskLoadedEvent?.cancel(); + _routingExample?.destroy(); super.dispose(); } } |
