Commit 182c6e24 authored by CLOUARD Regis's avatar CLOUARD Regis
Browse files

Merge branch 'Add_track_animation' into 'master'

Add track animation

See merge request !58
parents cff84270 8a6a8f45
......@@ -7,15 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## 90 [3.1.0] - 2021-08-01
### Fixed
- NFC for iOS
- Disable insecure http url for the multimedia pages (tourist mode).
### Added
- Back button on QR code view
- New statistics for course result
- Speed in km/h on the iage of the result track legend
- Back button on the QR code page
- New statistics for course (speed, distance overcost out of straight line)
- Speed in km/h in the track legend
- Animation of the track in real-time
## 89 [3.0.1] - 2021-06-21
......@@ -25,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## 85 [3.0.0] - 2021-06-08
### changed
### Changed
- Complete rewrite of the Android code in Flutter (new common Android & iOS).
......
......@@ -20,7 +20,7 @@
android:name="io.flutter.app.FlutterApplication"
android:label="Vikazimut"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
android:usesCleartextTraffic="false">
<activity
android:name="fr.ensicaen.vikazimut.MainActivity"
android:launchMode="singleTop"
......
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath 'com.android.tools.build:gradle:4.2.2'
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
......
......@@ -88,9 +88,7 @@ class _WebWidgetState extends State<WebWidget> {
WebView(
initialUrl: widget._url,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
onWebViewCreated: (WebViewController webViewController) => _controller.complete(webViewController),
navigationDelegate: (NavigationRequest request) {
if (_end) {
return NavigationDecision.prevent;
......
......@@ -11,7 +11,7 @@ abstract class AbstractRouteLayer implements Layer {
AbstractRouteLayer(MapView mapView) : _mapView = mapView;
void drawRoute(List<GeodesicPoint> route, GeodesicPoint? lastLocation) {
void drawRoute(List<GeodesicPoint> route, [GeodesicPoint? lastLocation]) {
_route = route;
_lastLocation = lastLocation;
_mapView.invalidate();
......
import 'package:flutter/material.dart';
import 'package:vikazimut/map/geodesic_point.dart';
import 'package:vikazimut/map/view/map_view.dart';
import 'abstract_route_layer.dart';
class AnimatedRouteLayer extends AbstractRouteLayer {
static const double STROKE_WIDTH = 7.0;
late final Paint _paint;
AnimatedRouteLayer(MapView mapView) : super(mapView) {
_paint = new Paint()
..color = Colors.red
..strokeWidth = STROKE_WIDTH
..isAntiAlias = true;
}
@override
void drawPolyLine(Canvas canvas, List<GeodesicPoint> route, GeodesicPoint? lastLocation) {
if (route.length == 0) return;
GeodesicPoint location1 = route[0];
Offset pt1 = convertGeodesicPointToMapPoint(location1);
for (int i = 1; i < route.length; i++) {
GeodesicPoint location2 = route[i];
Offset pt2 = convertGeodesicPointToMapPoint(location2);
canvas.drawLine(pt1, pt2, _paint);
pt1 = pt2;
}
GeodesicPoint lastLocation = route.last;
Offset pt2 = convertGeodesicPointToMapPoint(lastLocation);
canvas.drawCircle(pt2, 30, _paint);
}
}
......@@ -25,7 +25,9 @@ class FlatRouteLayer extends AbstractRouteLayer {
canvas.drawLine(pt1, pt2, _paint);
pt1 = pt2;
}
Offset pt2 = convertGeodesicPointToMapPoint(lastLocation!);
canvas.drawLine(pt1, pt2, _paint);
if (lastLocation != null) {
Offset pt2 = convertGeodesicPointToMapPoint(lastLocation);
canvas.drawLine(pt1, pt2, _paint);
}
}
}
......@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:vikazimut/utils/translations.dart';
import '../result.dart';
import '../track/sport_route_trace_view.dart';
import '../track/sport_track_view.dart';
import 'abstract_global_result_presenter.dart';
class SportGlobalResultPresenter extends AbstractGlobalResultPresenter {
......
......@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:vikazimut/utils/translations.dart';
import '../result.dart';
import '../track/walk_route_trace_view.dart';
import '../track/walk_track_view.dart';
import 'abstract_global_result_presenter.dart';
class WalkGlobalResultPresenter extends AbstractGlobalResultPresenter {
......
import 'dart:io';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:vikazimut/map/geodesic_point.dart';
import 'package:vikazimut/map/orienteering_map.dart';
import 'package:vikazimut/map/view/abstract_route_layer.dart';
import 'package:vikazimut/map/view/map_view.dart';
import 'package:vikazimut/utils/translations.dart';
import '../result.dart';
abstract class AbstractRouteTraceView extends StatelessWidget {
final OrienteeringMap _map;
final String _gpxRoute;
AbstractRouteTraceView(OrienteeringMap map, gpxRoute)
: _map = map,
_gpxRoute = gpxRoute;
AbstractRouteLayer createRouteOverlay(MapView mapView);
PreferredSizeWidget? buildAppBarLegend();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_map.getName()),
bottom: buildAppBarLegend(),
),
body: Container(
width: double.infinity,
height: double.infinity,
color: Colors.white,
child: FutureBuilder<ui.Image>(
future: MapView.loadImage(File(_map.getDrawableName())),
builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
if (snapshot.hasData) {
var mapView = MapView(
image: snapshot.data!,
geoPosition: _map.getBoundingBox(),
size: (context.findRenderObject() as RenderBox).size,
);
SchedulerBinding.instance!.addPostFrameCallback((_) => _addRouteOnMapViewAsALayer(_gpxRoute, mapView));
return mapView;
} else if (snapshot.hasError) {
return Text('${Translations.getString("error_prefix")} ${snapshot.error}');
} else {
return Container();
}
},
),
),
);
}
void _addRouteOnMapViewAsALayer(String gpxRoute, MapView mapView) {
AbstractRouteLayer routeOverlay = createRouteOverlay(mapView);
mapView.addLayer(routeOverlay);
List<GeodesicPoint> route = Result.getGpsRouteAsWaypoints(gpxRoute);
routeOverlay.drawRoute(route, null);
}
}
This diff is collapsed.
......@@ -4,7 +4,7 @@ import 'package:vikazimut/map/view/abstract_route_layer.dart';
import 'package:vikazimut/map/view/colored_route_layer.dart';
import 'package:vikazimut/map/view/map_view.dart';
import 'abstract_route_trace_view.dart';
import 'abstract_track_view.dart';
class SportRouteTraceView extends AbstractRouteTraceView {
SportRouteTraceView(OrienteeringMap map, String gpxRoute) : super(map, gpxRoute);
......
import 'package:flutter/animation.dart';
import 'package:vikazimut/map/geodesic_point.dart';
import 'package:vikazimut/map/view/animated_route_layer.dart';
import 'package:vikazimut/map/view/map_view.dart';
import 'abstract_track_view.dart';
class TrackAnimation {
AnimationController? _controller;
Function()? _action;
void execute(AbstractRouteTraceViewState parent, MapView mapView, List<GeodesicPoint> track) async {
var animatedRouteLayer = AnimatedRouteLayer(mapView);
mapView.addLayer(animatedRouteLayer);
_controller = AnimationController(
vsync: parent,
duration: Duration(seconds: 10),
);
var minTime = getMinTimeInS(track);
var maxTime = getMaxTimeInS(track);
Animation sizeAnimation = IntTween(begin: minTime, end: maxTime).animate(_controller!);
_controller!.addListener(() {
var subTrack = getSubTrack(track, sizeAnimation.value);
animatedRouteLayer.drawRoute(subTrack);
});
_controller!.forward().whenComplete(() {
_controller?.dispose();
_controller = null;
mapView.removeLayer(animatedRouteLayer);
if (_action != null) {
_action!();
}
});
}
void then(dynamic Function() action) {
_action = action;
}
void dispose() {
_controller?.dispose();
}
var _currentLegIndex = 0;
List<GeodesicPoint> getSubTrack(List<GeodesicPoint> track, int currentTime) {
// TODO Tests
// TODO question garde t-on le temps réel ?
for (int i = _currentLegIndex; i < track.length; i++) {
if (track[i].getTimeInMillis() < currentTime) {
_currentLegIndex++;
} else {
break;
}
}
if (_currentLegIndex > 0) {
var t2 = track[_currentLegIndex].getTimeInMillis();
var t1 = track[_currentLegIndex - 1].getTimeInMillis();
double x = (currentTime - t1) / (t2 - t1);
var latitude = x * track[_currentLegIndex].getLatitude() + (1.0 - x) * track[_currentLegIndex - 1].getLatitude();
var longitude = x * track[_currentLegIndex].getLongitude() + (1.0 - x) * track[_currentLegIndex - 1].getLongitude();
var list = track.sublist(0, _currentLegIndex - 1);
list.add(new GeodesicPoint(latitude, longitude));
return list;
} else {
return track.sublist(0, 0);
}
}
int getMinTimeInS(List<GeodesicPoint> track) {
return track[0].getTimeInMillis();
}
int getMaxTimeInS(List<GeodesicPoint> track) {
return track.last.getTimeInMillis();
}
}
......@@ -4,7 +4,7 @@ import 'package:vikazimut/map/view/abstract_route_layer.dart';
import 'package:vikazimut/map/view/flat_route_layer.dart';
import 'package:vikazimut/map/view/map_view.dart';
import 'abstract_route_trace_view.dart';
import 'abstract_track_view.dart';
class WalkRouteTraceView extends AbstractRouteTraceView {
WalkRouteTraceView(OrienteeringMap map, String gpxRoute) : super(map, gpxRoute);
......
......@@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.0.2+90
version: 3.1.0+90
environment:
sdk: ">=2.12.0 <3.0.0"
......@@ -23,7 +23,7 @@ environment:
dependencies:
flutter:
sdk: flutter
mockito: ^5.0.9
mockito: ^5.0.11
provider: ^5.0.0
......@@ -35,7 +35,7 @@ dependencies:
sprintf: ^6.0.0
intl: ^0.17.0
webview_flutter: ^2.0.8
webview_flutter: ^2.0.10
xml: ^5.1.1
http: ^0.13.3
url_launcher: ^6.0.4
......@@ -53,10 +53,10 @@ dependencies:
badges: ^2.0.1
location: ^4.3.0
geolocator: ^7.1.0
geolocator: ^7.3.0
flutter_compass: ^0.6.1
qr_code_scanner: ^0.5.1
qr_code_scanner: ^0.5.2
nfc_manager: ^3.1.0
audioplayers: ^0.19.0
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment