diff --git a/car_app/lib/converter/appwrite_converters/point.dart b/car_app/lib/converter/appwrite_converters/point.dart index 728e2fea61dbd7d7a7f8d11af02e649003748d64..72600987bf19f2e75ad816ddede23893cc652b05 100644 --- a/car_app/lib/converter/appwrite_converters/point.dart +++ b/car_app/lib/converter/appwrite_converters/point.dart @@ -1,6 +1,6 @@ -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/appwrite/point.dart'; -extension PointMapConverter on Point { +extension PointMapConverter on AppwritePoint { static const String idFieldName = "id"; static const String longitudeFieldName = "longitude"; static const String latitudeFieldName = "latitude"; @@ -11,8 +11,8 @@ extension PointMapConverter on Point { latitudeFieldName: latitude, }; - static Point fromMap(Map<String, dynamic> map) { - return Point( + static AppwritePoint fromMap(Map<String, dynamic> map) { + return AppwritePoint( id: map[idFieldName], latitude: map[latitudeFieldName], longitude: map[longitudeFieldName], diff --git a/car_app/lib/converter/appwrite_converters/route.dart b/car_app/lib/converter/appwrite_converters/route.dart index 19c8fa8e241da8b21836133bc68536be2992767f..65e2e7e90abc36cd142dda4bfdfce6cbc80d0289 100644 --- a/car_app/lib/converter/appwrite_converters/route.dart +++ b/car_app/lib/converter/appwrite_converters/route.dart @@ -1,8 +1,7 @@ -import 'package:car_app/converter/appwrite_converters/point.dart'; -import 'package:car_app/model/point.dart'; -import 'package:car_app/model/route.dart'; +import 'package:car_app/model/appwrite/route.dart'; +import 'package:car_app/model/interface/point.dart'; -extension RouteMapConverter on Route { +extension RouteMapConverter on AppwriteRoute { static const String idFieldName = "id"; static const String teamIdFieldName = "teamId"; static const String pointsFieldName = "points"; @@ -10,12 +9,11 @@ extension RouteMapConverter on Route { static const String endTimeFieldName = "endTime"; static const String userIdFieldName = "userId"; - Map<String, dynamic> toMap(String userId, {Iterable<Point>? points}) { - points ??= this.points; + Map<String, dynamic> toMap(String userId, Iterable<String>? pointIds) { return { idFieldName: id, - pointsFieldName: points.map((e) => e.id).toList(), + pointsFieldName: points, teamIdFieldName: carId, startTimeFieldName: startTime.toIso8601String(), endTimeFieldName: endTime.toIso8601String(), @@ -23,10 +21,8 @@ extension RouteMapConverter on Route { }; } - static Route fromMap(Map<String, dynamic> map, {Iterable<Point>? points}) { - points ??= List<Point>.from((List<Map<String, dynamic>>.from(map[pointsFieldName])).map((x) => PointMapConverter.fromMap(x))); - - return Route( + static AppwriteRoute<T> fromMap<T extends Point>(Map<String, dynamic> map, Iterable<T> points) { + return AppwriteRoute( id: map[idFieldName], carId: map[teamIdFieldName], points: points, diff --git a/car_app/lib/converter/converter.dart b/car_app/lib/converter/converter.dart index cf1446c2e07ed6a98b970a6e8b3221e2bf65c20f..3a8dbb4b4905e818dfa44a260f8a16044b2cb940 100644 --- a/car_app/lib/converter/converter.dart +++ b/car_app/lib/converter/converter.dart @@ -1,3 +1,3 @@ -abstract class Converter <T1, T2> { +abstract interface class Converter <T1, T2> { T2 convert(T1 t); } diff --git a/car_app/lib/converter/location_converters.dart/location_point_point_converter.dart b/car_app/lib/converter/location_converters.dart/location_point_point_converter.dart new file mode 100644 index 0000000000000000000000000000000000000000..0addce41f2cac291ddc4114074e14f5e6ab05a54 --- /dev/null +++ b/car_app/lib/converter/location_converters.dart/location_point_point_converter.dart @@ -0,0 +1,15 @@ +import 'package:car_app/converter/converter.dart'; +import 'package:car_app/factory/interface/point.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/location/point.dart'; + +class LocationPointToPointConverter<T extends Point> implements Converter<LocationPoint, T> { + final PointFactory<T> pointFactory; + + LocationPointToPointConverter({required this.pointFactory}); + + @override + T convert(LocationPoint t) { + return pointFactory.createInstance(t.latitude, t.longitude); + } +} \ No newline at end of file diff --git a/car_app/lib/converter/location_converters.dart/point.dart b/car_app/lib/converter/location_converters.dart/point.dart index 66dc1a5e3f4e721eeacb217832dfac7b90af0215..7127cd88b2fc6d7fc79b7dc7257752ef3bf5cad4 100644 --- a/car_app/lib/converter/location_converters.dart/point.dart +++ b/car_app/lib/converter/location_converters.dart/point.dart @@ -1,6 +1,6 @@ -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/location/point.dart'; -extension PointMapConverter on Point { +extension PointMapConverter on LocationPoint { static const String longitudeFieldName = "longitude"; static const String latitudeFieldName = "latitude"; @@ -9,8 +9,8 @@ extension PointMapConverter on Point { latitudeFieldName: latitude, }; - static Point fromMap(Map<String, dynamic> map) { - return Point( + static LocationPoint fromMap(Map<String, dynamic> map) { + return LocationPoint( latitude: map[latitudeFieldName], longitude: map[longitudeFieldName], ); diff --git a/car_app/lib/converter/location_converters.dart/position.dart b/car_app/lib/converter/location_converters.dart/position.dart new file mode 100644 index 0000000000000000000000000000000000000000..af3010ac62180e25577c9db354dcead943c073fa --- /dev/null +++ b/car_app/lib/converter/location_converters.dart/position.dart @@ -0,0 +1,11 @@ +import 'package:car_app/model/location/point.dart'; +import 'package:geolocator/geolocator.dart'; + +extension LocationPointConverter on Position { + LocationPoint toLocationPoint() { + return LocationPoint( + latitude: latitude, + longitude: longitude + ); + } +} diff --git a/car_app/lib/converter/point_latlng_converter.dart b/car_app/lib/converter/point_latlng_converter.dart index 183ade021283c66f6ac17165f66cb59a9b9b9319..2ad50b273a0bb2f8be9de59bbf9d3bf1562799d7 100644 --- a/car_app/lib/converter/point_latlng_converter.dart +++ b/car_app/lib/converter/point_latlng_converter.dart @@ -1,8 +1,8 @@ import 'package:car_app/converter/converter.dart'; -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/interface/point.dart'; import 'package:latlong2/latlong.dart'; -class PointToLatLngConverter extends Converter<Point, LatLng> { +class PointToLatLngConverter implements Converter<Point, LatLng> { @override LatLng convert(Point t) { return LatLng(t.latitude.toDouble(), t.longitude.toDouble()); diff --git a/car_app/lib/converter/position_point_converter.dart b/car_app/lib/converter/position_point_converter.dart deleted file mode 100644 index 700d17de3919bb521f3c98693ddfcb96ce54ee50..0000000000000000000000000000000000000000 --- a/car_app/lib/converter/position_point_converter.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:car_app/converter/converter.dart'; -import 'package:car_app/model/point.dart'; -import 'package:geolocator/geolocator.dart'; - -class PositionToPointConverter extends Converter<Position, Point> { - @override - Point convert(Position t) { - return Point(latitude: t.latitude, longitude: t.longitude); - } -} \ No newline at end of file diff --git a/car_app/lib/converter/storage_converters/point.dart b/car_app/lib/converter/storage_converters/appwrite/point.dart similarity index 53% rename from car_app/lib/converter/storage_converters/point.dart rename to car_app/lib/converter/storage_converters/appwrite/point.dart index 66dc1a5e3f4e721eeacb217832dfac7b90af0215..72600987bf19f2e75ad816ddede23893cc652b05 100644 --- a/car_app/lib/converter/storage_converters/point.dart +++ b/car_app/lib/converter/storage_converters/appwrite/point.dart @@ -1,16 +1,19 @@ -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/appwrite/point.dart'; -extension PointMapConverter on Point { +extension PointMapConverter on AppwritePoint { + static const String idFieldName = "id"; static const String longitudeFieldName = "longitude"; static const String latitudeFieldName = "latitude"; Map<String, dynamic> toMap() => { + idFieldName: id, longitudeFieldName: longitude, latitudeFieldName: latitude, }; - static Point fromMap(Map<String, dynamic> map) { - return Point( + static AppwritePoint fromMap(Map<String, dynamic> map) { + return AppwritePoint( + id: map[idFieldName], latitude: map[latitudeFieldName], longitude: map[longitudeFieldName], ); diff --git a/car_app/lib/converter/storage_converters/route.dart b/car_app/lib/converter/storage_converters/appwrite/route.dart similarity index 51% rename from car_app/lib/converter/storage_converters/route.dart rename to car_app/lib/converter/storage_converters/appwrite/route.dart index 5102a2ee32d00404ca04432f4c8f247f6e91282c..5e1853ff29e5b61f6ee319306e8aa442d2f29db8 100644 --- a/car_app/lib/converter/storage_converters/route.dart +++ b/car_app/lib/converter/storage_converters/appwrite/route.dart @@ -1,35 +1,29 @@ -import 'package:car_app/converter/storage_converters/point.dart'; -import 'package:car_app/model/point.dart'; -import 'package:car_app/model/route.dart'; +import 'package:car_app/model/appwrite/route.dart'; +import 'package:car_app/model/interface/point.dart'; -extension RouteMapConverter on Route { +extension RouteMapConverter on AppwriteRoute { static const String idFieldName = "id"; static const String teamIdFieldName = "teamId"; static const String pointsFieldName = "points"; static const String startTimeFieldName = "startTime"; static const String endTimeFieldName = "endTime"; - Map<String, dynamic> toMap() { - List<Map<String, dynamic>> jsonPoints = []; - - for (var point in points) { - jsonPoints.add(point.toMap()); - } - + Map<String, dynamic> toMap(Iterable<String> pointIds) { return { idFieldName: id, - pointsFieldName: jsonPoints, + pointsFieldName: pointIds, teamIdFieldName: carId, startTimeFieldName: startTime.toIso8601String(), endTimeFieldName: endTime.toIso8601String(), }; } - static Route fromMap(Map<String, dynamic> map) { - return Route( + static AppwriteRoute<T> fromMap<T extends Point>(Map<String, dynamic> map, Iterable<T> points) { + // List<Point>.from((List<Map<String, dynamic>>.from(map[pointsFieldName])).map((x) => PointMapConverter.fromMap(x))) + return AppwriteRoute( id: map[idFieldName], carId: map[teamIdFieldName], - points: List<Point>.from((List<Map<String, dynamic>>.from(map[pointsFieldName])).map((x) => PointMapConverter.fromMap(x))), + points: points, startTime: DateTime.parse(map[startTimeFieldName]), endTime: DateTime.parse(map[endTimeFieldName]), ); diff --git a/car_app/lib/factory/appwrite/point.dart b/car_app/lib/factory/appwrite/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..dfd1a84021f4acd5e68d8e7bfada4cff62f0bf03 --- /dev/null +++ b/car_app/lib/factory/appwrite/point.dart @@ -0,0 +1,14 @@ +import 'package:appwrite/appwrite.dart'; +import 'package:car_app/factory/interface/point.dart'; +import 'package:car_app/model/appwrite/point.dart'; + +class AppwritePointFactory implements PointFactory<AppwritePoint> { + @override + AppwritePoint createInstance(num latitude, num longitude) { + return AppwritePoint( + id: ID.unique(), + latitude: latitude, + longitude: longitude + ); + } +} diff --git a/car_app/lib/factory/appwrite/route.dart b/car_app/lib/factory/appwrite/route.dart new file mode 100644 index 0000000000000000000000000000000000000000..896c4058c01e97777d8caf048b185d953c5f8f1f --- /dev/null +++ b/car_app/lib/factory/appwrite/route.dart @@ -0,0 +1,17 @@ +import 'package:appwrite/appwrite.dart'; +import 'package:car_app/factory/interface/route.dart'; +import 'package:car_app/model/appwrite/route.dart'; +import 'package:car_app/model/interface/point.dart'; + +class AppwriteRouteFactory<T extends Point> implements RouteFactory<AppwriteRoute<T>, T> { + @override + AppwriteRoute<T> createInstance(String? carId, Iterable<T> points, DateTime startTime, DateTime endTime) { + return AppwriteRoute( + id: ID.unique(), + carId: carId, + points: points, + startTime: startTime, + endTime: endTime + ); + } +} diff --git a/car_app/lib/factory/interface/point.dart b/car_app/lib/factory/interface/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..6f07b147fbf6fd46bf9f0849ef605c2afab2021f --- /dev/null +++ b/car_app/lib/factory/interface/point.dart @@ -0,0 +1,5 @@ +import 'package:car_app/model/interface/point.dart'; + +abstract interface class PointFactory<T extends Point> { + T createInstance(num latitude, num longitude); +} diff --git a/car_app/lib/factory/interface/route.dart b/car_app/lib/factory/interface/route.dart new file mode 100644 index 0000000000000000000000000000000000000000..be99202da726a1cff062e65f3212edb6f91b55ee --- /dev/null +++ b/car_app/lib/factory/interface/route.dart @@ -0,0 +1,6 @@ +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/interface/route.dart'; + +abstract interface class RouteFactory<T extends Route<T1>, T1 extends Point> { + T createInstance(String? carId, Iterable<T1> points, DateTime startTime, DateTime endTime); +} diff --git a/car_app/lib/main.dart b/car_app/lib/main.dart index a8d3d4265ecfec0baa2ebdb510502204c2c45779..bc7f3800bf57b1de98cc77b264550c187eead232 100644 --- a/car_app/lib/main.dart +++ b/car_app/lib/main.dart @@ -1,16 +1,30 @@ +import 'dart:convert'; + +import 'package:car_app/converter/location_converters.dart/location_point_point_converter.dart'; +import 'package:car_app/exception/key_not_found.dart'; +import 'package:car_app/factory/appwrite/point.dart'; +import 'package:car_app/factory/appwrite/route.dart'; +import 'package:car_app/factory/interface/route.dart' as route_factory; +import 'package:car_app/model/appwrite/point.dart'; +import 'package:car_app/model/appwrite/route.dart'; import 'package:car_app/service/appwrite/appwrite_authentication.dart'; import 'package:car_app/service/appwrite/appwrite_calendar_event.dart'; import 'package:car_app/service/appwrite/appwrite_car.dart'; +import 'package:car_app/service/appwrite/appwrite_point.dart'; import 'package:car_app/service/appwrite/appwrite_route.dart'; import 'package:car_app/service/cached_username_password_authentication.dart'; import 'package:car_app/service/chached_route.dart'; +import 'package:car_app/service/geolocator_location.dart'; import 'package:car_app/service/interface/authentication.dart'; import 'package:car_app/service/interface/calendar_event.dart'; import 'package:car_app/service/interface/car.dart'; +import 'package:car_app/service/interface/location.dart'; import 'package:car_app/service/interface/logging.dart'; import 'package:car_app/service/interface/route.dart'; import 'package:car_app/service/logging/logger/logger.dart'; -import 'package:car_app/service/storage/route_storage.dart'; +import 'package:car_app/service/storage/hive_storage.dart'; +import 'package:car_app/service/storage/model_storage/appwrite/point_storage.dart'; +import 'package:car_app/service/storage/model_storage/appwrite/route_storage.dart'; import 'package:car_app/service/storage/secure_storage.dart'; import 'package:car_app/view/calendar.dart'; import 'package:car_app/view/startup.dart'; @@ -20,55 +34,113 @@ import 'package:car_app/viewmodel/authentication.dart'; import 'package:car_app/viewmodel/calendar.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:path/path.dart' as path; import 'package:car_app/viewmodel/tracking.dart'; +import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; +const String hiveEncryptionKeyFieldName = "hiveEncryptionKey"; void main() async { WidgetsFlutterBinding.ensureInitialized(); var applicationDocumentsDirectory = await getApplicationDocumentsDirectory(); + var hiveEncryptionKey = await getOrMakeHiveEncryptionKey(); + runApp(App( - applicationDocumentsPath: applicationDocumentsDirectory.path + applicationDocumentsPath: applicationDocumentsDirectory.path, + hiveEncryptionKey: hiveEncryptionKey, )); } +Future<List<int>> getOrMakeHiveEncryptionKey() async { + var secureStorage = SecureStorageService(); + + List<int> hiveEncryptionKey; + try { + var decodedHiveKey = await secureStorage.get(hiveEncryptionKeyFieldName); + hiveEncryptionKey = base64Decode(decodedHiveKey); + } on KeyNotFoundException catch (_) { + hiveEncryptionKey = Hive.generateSecureKey(); + await secureStorage.add(hiveEncryptionKeyFieldName, base64Encode(hiveEncryptionKey)); + } + + return hiveEncryptionKey; +} + class App extends StatelessWidget { final String applicationDocumentsPath; + final List<int> hiveEncryptionKey; - const App({super.key, required this.applicationDocumentsPath}); + const App({ + super.key, + required this.applicationDocumentsPath, + required this.hiveEncryptionKey, + }); @override Widget build(BuildContext context) { + // We must create the services here to ensure types + + // Factories + var appwriteRouteFactory = AppwriteRouteFactory<AppwritePoint>(); + var appwritePointFactory = AppwritePointFactory(); + + // Services + var secureStorageService = SecureStorageService(); + var loggingService = Logger(filePath: applicationDocumentsPath, isRelease: kReleaseMode); + var authenticationService = CachedUsernamePasswordAuthenticationService( + authenticationService: AppwriteAuthentication(), + storageService: secureStorageService, + ); + + var appwritePointService = AppwritePointService(); + var appwritePointStorageService = AppwritePointStorageService( + storageService: getCachingHiveStorageService("point", applicationDocumentsPath), + ); + var appwriteRouteService = CachedRouteService( + routeService: AppwriteRouteService<AppwritePoint>( + authenticationService: authenticationService, + pointService: appwritePointService, + ), + storageService: AppwriteRouteStorageService( + storageService: getCachingHiveStorageService("route", applicationDocumentsPath), + pointStorageService: appwritePointStorageService, + ) + ); + var appwriteCarService = AppwriteCarService( + authenticationService: authenticationService, + ); + var appwriteCalendarEventService = AppwriteCalendarEventService(); + var locationService = GeolocatorLocationService<AppwritePoint>( + locationPointToPointConverter: LocationPointToPointConverter( + pointFactory: appwritePointFactory, + ), + ); + return MultiProvider( providers: [ - // Providers - Provider<LoggingService>(create: (context) => Logger(filePath: applicationDocumentsPath, isRelease: kReleaseMode)), - Provider<AuthenticationService>(create: (context) => CachedUsernamePasswordAuthenticationService( - authenticationService: AppwriteAuthentication(), - storageService: SecureStorageService(), - )), - Provider<RouteService>(create: (context) => CachedRouteService( - routeService: AppwriteRouteService( - authenticationService: Provider.of<AuthenticationService>(context, listen: false) - ), - storageService: RouteStorageService( - storageService: SecureStorageService(), - ) - )), - Provider<CarService>(create: (context) => AppwriteCarService( - authenticationService: Provider.of(context, listen: false), - )), - Provider<CalendarEventService>(create: (context) => AppwriteCalendarEventService()), + // Factories + Provider<route_factory.RouteFactory<AppwriteRoute<AppwritePoint>, AppwritePoint>>(create: (context) => appwriteRouteFactory), + + // Service providers + Provider<LoggingService>(create: (context) => loggingService), + Provider<AuthenticationService>(create: (context) => authenticationService), + Provider<RouteService<AppwriteRoute<AppwritePoint>, AppwritePoint>>(create: (context) => appwriteRouteService), + Provider<CarService>(create: (context) => appwriteCarService), + Provider<CalendarEventService>(create: (context) => appwriteCalendarEventService), + Provider<LocationService<AppwritePoint>>(create: (context) => locationService), // ChangeNotifierProviders // currently no better (auto) injection method found, best case would be something similar to https://git.420joos.dev/ost/seproj/studiapp/-/blob/master/StudiApp/StudiApp/MauiProgram.cs - ChangeNotifierProvider(create: (context) => TrackingViewModel( + ChangeNotifierProvider<TrackingViewModel>(create: (context) => TrackingViewModel<AppwriteRoute<AppwritePoint>, AppwritePoint>( authenticationService: Provider.of(context, listen: false), routeService: Provider.of(context, listen: false), carService: Provider.of(context, listen: false), loggingService: Provider.of(context, listen: false), + locationService: Provider.of(context, listen: false), + routeFactory: Provider.of(context, listen: false), )), ChangeNotifierProvider(create: (context) => AuthenticationViewModel( authenticationService: Provider.of(context, listen: false), @@ -96,4 +168,13 @@ class App extends StatelessWidget { ), ); } + + HiveStorageService getCachingHiveStorageService(String boxName, String applicationDocumentsDirectory) { + return HiveStorageService( + dbName: "cache", + dbPath: path.join(applicationDocumentsDirectory, "db"), + boxName: boxName, + encryptionKey: hiveEncryptionKey, + ); + } } diff --git a/car_app/lib/model/appwrite/base_model.dart b/car_app/lib/model/appwrite/base_model.dart new file mode 100644 index 0000000000000000000000000000000000000000..bcc4eb39c1f32a1e097d315fb6f87bd83cd101e9 --- /dev/null +++ b/car_app/lib/model/appwrite/base_model.dart @@ -0,0 +1,8 @@ +import 'package:car_app/model/id_model/id_model.dart'; + +abstract class AppwriteBaseModel implements IdModel<String> { + @override + final String id; + + AppwriteBaseModel({required this.id}); +} diff --git a/car_app/lib/model/appwrite/point.dart b/car_app/lib/model/appwrite/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..044ff17d0aea60f157f170239b28ac19577b422c --- /dev/null +++ b/car_app/lib/model/appwrite/point.dart @@ -0,0 +1,12 @@ +import 'package:car_app/model/appwrite/base_model.dart'; +import 'package:car_app/model/id_model/point.dart'; + +class AppwritePoint extends AppwriteBaseModel implements PointIdModel<String> { + @override + final num latitude; + + @override + final num longitude; + + AppwritePoint({required super.id, required this.latitude, required this.longitude}); +} diff --git a/car_app/lib/model/appwrite/route.dart b/car_app/lib/model/appwrite/route.dart new file mode 100644 index 0000000000000000000000000000000000000000..bf3413a32abab6abfbb37211593aa964734b45a3 --- /dev/null +++ b/car_app/lib/model/appwrite/route.dart @@ -0,0 +1,19 @@ +import 'package:car_app/model/appwrite/base_model.dart'; +import 'package:car_app/model/id_model/route.dart'; +import 'package:car_app/model/interface/point.dart'; + +class AppwriteRoute<T extends Point> extends AppwriteBaseModel implements RouteIdModel<String, T> { + @override + final String? carId; + + @override + final Iterable<T> points; + + @override + final DateTime startTime; + + @override + final DateTime endTime; + + AppwriteRoute({required super.id, required this.carId, required this.points, required this.startTime, required this.endTime}); +} diff --git a/car_app/lib/model/id_model/id_model.dart b/car_app/lib/model/id_model/id_model.dart new file mode 100644 index 0000000000000000000000000000000000000000..7ef45e709baa91407817b698e26456112e7ca952 --- /dev/null +++ b/car_app/lib/model/id_model/id_model.dart @@ -0,0 +1,3 @@ +abstract interface class IdModel<T> { + T get id; +} diff --git a/car_app/lib/model/id_model/point.dart b/car_app/lib/model/id_model/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..f34b315fa05d8e67091209e7b9a1fe0c9038f032 --- /dev/null +++ b/car_app/lib/model/id_model/point.dart @@ -0,0 +1,4 @@ +import 'package:car_app/model/id_model/id_model.dart'; +import 'package:car_app/model/interface/point.dart'; + +abstract interface class PointIdModel<T> implements IdModel<T>, Point {} diff --git a/car_app/lib/model/id_model/route.dart b/car_app/lib/model/id_model/route.dart new file mode 100644 index 0000000000000000000000000000000000000000..cc78bf8302939a4e45eae7450daa554cf74af0cd --- /dev/null +++ b/car_app/lib/model/id_model/route.dart @@ -0,0 +1,5 @@ +import 'package:car_app/model/id_model/id_model.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/interface/route.dart'; + +abstract interface class RouteIdModel<T, T1 extends Point> implements IdModel<T>, Route<T1> {} diff --git a/car_app/lib/model/interface/README.md b/car_app/lib/model/interface/README.md new file mode 100644 index 0000000000000000000000000000000000000000..de4155ebfa227ba6656c8dca9e589a32766ddec4 --- /dev/null +++ b/car_app/lib/model/interface/README.md @@ -0,0 +1 @@ +All derivations of these models should override == and hashcode diff --git a/car_app/lib/model/interface/point.dart b/car_app/lib/model/interface/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..b54623cb779db9e92fca38d168c59aca30375e73 --- /dev/null +++ b/car_app/lib/model/interface/point.dart @@ -0,0 +1,4 @@ +abstract interface class Point { + num get longitude; + num get latitude; +} diff --git a/car_app/lib/model/interface/route.dart b/car_app/lib/model/interface/route.dart new file mode 100644 index 0000000000000000000000000000000000000000..199e0c8a936ec948dff7e65c6365559fb7dcb625 --- /dev/null +++ b/car_app/lib/model/interface/route.dart @@ -0,0 +1,8 @@ +import 'package:car_app/model/interface/point.dart'; + +abstract interface class Route<T extends Point> { + String? get carId; + Iterable<T> get points; + DateTime get startTime; + DateTime get endTime; +} diff --git a/car_app/lib/model/location/point.dart b/car_app/lib/model/location/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..3f6b0d8165476e689b4e4b559f3c5d0e35039b9f --- /dev/null +++ b/car_app/lib/model/location/point.dart @@ -0,0 +1,11 @@ +import 'package:car_app/model/interface/point.dart'; + +class LocationPoint implements Point { + @override + final num latitude; + + @override + final num longitude; + + LocationPoint({required this.latitude, required this.longitude}); +} diff --git a/car_app/lib/model/point.dart b/car_app/lib/model/point.dart deleted file mode 100644 index 8392b224b186b547773f3d02104999703b286046..0000000000000000000000000000000000000000 --- a/car_app/lib/model/point.dart +++ /dev/null @@ -1,7 +0,0 @@ -class Point { - final String? id; - final num longitude; - final num latitude; - - Point({this.id, required this.longitude, required this.latitude}); -} \ No newline at end of file diff --git a/car_app/lib/model/route.dart b/car_app/lib/model/route.dart deleted file mode 100644 index 1aa6db5872730841a8ce4b31ac2e79cece9dc7bd..0000000000000000000000000000000000000000 --- a/car_app/lib/model/route.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:car_app/model/point.dart'; - -class Route { - final String? id; - final String? carId; - final Iterable<Point> points; - final DateTime startTime; - final DateTime endTime; - - Route({this.id, this.carId, required this.points, required this.startTime, required this.endTime}); -} diff --git a/car_app/lib/service/appwrite/appwrite_point.dart b/car_app/lib/service/appwrite/appwrite_point.dart index 9b6d26f92a0b24cecce71ab4ebebd461a0873f10..8671ade820a5fa449a708695a617325bcc452e63 100644 --- a/car_app/lib/service/appwrite/appwrite_point.dart +++ b/car_app/lib/service/appwrite/appwrite_point.dart @@ -1,11 +1,13 @@ import 'package:car_app/converter/appwrite_converters/point.dart'; -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/appwrite/point.dart'; import 'package:car_app/service/appwrite/appwrite_api_function.dart'; +import 'package:car_app/service/interface/point.dart'; -class AppwritePointService extends AppwriteApiFunctionService { +class AppwritePointService extends AppwriteApiFunctionService implements PointService<AppwritePoint> { static const String path = "/point"; - Future<Point> add(Point point) async { + @override + Future<AppwritePoint> add(AppwritePoint point) async { var action = "add point"; var execution = await executeApiFunction(action, "POST", path, body: point.toMap()); diff --git a/car_app/lib/service/appwrite/appwrite_route.dart b/car_app/lib/service/appwrite/appwrite_route.dart index 7f847b1973651b5b093cdf73dc1cd9b5873e35f4..b5a9eda60273322795d17f91ce906eca9e1a999e 100644 --- a/car_app/lib/service/appwrite/appwrite_route.dart +++ b/car_app/lib/service/appwrite/appwrite_route.dart @@ -1,28 +1,29 @@ import 'package:car_app/converter/appwrite_converters/route.dart'; -import 'package:car_app/model/route.dart'; +import 'package:car_app/model/appwrite/route.dart'; +import 'package:car_app/model/id_model/point.dart'; import 'package:car_app/service/appwrite/appwrite_api_function.dart'; -import 'package:car_app/service/appwrite/appwrite_point.dart'; import 'package:car_app/service/interface/authentication.dart'; +import 'package:car_app/service/interface/point.dart'; import 'package:car_app/service/interface/route.dart'; -class AppwriteRouteService extends AppwriteApiFunctionService implements RouteService { +class AppwriteRouteService<T extends PointIdModel> extends AppwriteApiFunctionService implements RouteService<AppwriteRoute<T>, T> { static const String path = "/route"; - final AppwritePointService pointService = AppwritePointService(); + final PointService<T> pointService; final AuthenticationService authenticationService; - AppwriteRouteService({required this.authenticationService}); + AppwriteRouteService({required this.pointService, required this.authenticationService}); @override - Future<Route> add(Route route) async { + Future<AppwriteRoute<T>> add(AppwriteRoute<T> route) async { var points = await Stream.fromFutures(route.points.map((e) => pointService.add(e))).toList(); var action = "add route"; var user = await authenticationService.user; - var execution = await executeApiFunction(action, "POST", path, body: route.toMap(user.id, points: points)); + var execution = await executeApiFunction(action, "POST", path, body: route.toMap(user.id, points.map((e) => e.id))); throwIfExecutionBodyIsEmpty(execution, action); - return instanceFromExecution(execution, (m) => RouteMapConverter.fromMap(m, points: points), action); + return instanceFromExecution(execution, (m) => RouteMapConverter.fromMap(m, points), action); } } diff --git a/car_app/lib/service/chached_route.dart b/car_app/lib/service/chached_route.dart index 44cea9cd17291a070ead3cbce71048d0f5f486fd..0fbc025292067489b160d59ddb6b9391ad2f8073 100644 --- a/car_app/lib/service/chached_route.dart +++ b/car_app/lib/service/chached_route.dart @@ -1,38 +1,37 @@ import 'package:car_app/exception/key_not_found.dart'; import 'package:car_app/exception/server.dart'; -import 'package:car_app/model/route.dart'; -import 'package:car_app/service/interface/key_value_storage.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/interface/route.dart'; import 'package:car_app/service/interface/route.dart'; +import 'package:car_app/service/storage/model_storage/route_storage.dart'; -class CachedRouteService implements RouteService { - final RouteService routeService; - final KeyValueStorageService<String, List<Route>> storageService; - final String storageKey = "routes"; +class CachedRouteService<T extends Route<T1>, T1 extends Point> implements RouteService<T, T1> { + final RouteService<T, T1> routeService; + final RouteStorageService<T, T1> storageService; CachedRouteService({required this.routeService, required this.storageService}); @override - Future<Route> add(Route route) async { + Future<T> add(T route) async { var isSynchronized = await _synchronize(); if(isSynchronized) { try { return routeService.add(route); } on ServerException catch (_) { - await _addRouteToStorage(route); + await storageService.add(route); return route; } } - await _addRouteToStorage(route); + await storageService.add(route); return route; } Future<bool> _synchronize() async { - List<Route> unsuccessfulRoutes = []; + List<T> unsuccessfulRoutes = []; try { - var routes = await storageService.get(storageKey); - for (var route in routes) { + await for (var route in storageService.getAll()) { try { await routeService.add(route); return true; @@ -43,17 +42,10 @@ class CachedRouteService implements RouteService { } on KeyNotFoundException catch (_) {} // No routes stored yet - storageService.add(storageKey, unsuccessfulRoutes); - return false; - } - - Future<void> _addRouteToStorage(Route route) async { - List<Route> routes = []; - try { - routes = await storageService.get(storageKey); - } on KeyNotFoundException catch (_) {} // Ignore exception, as no routes are stored yet + for (var route in unsuccessfulRoutes) { + await storageService.add(route); + } - routes.add(route); - return storageService.add(storageKey, routes); + return false; } } diff --git a/car_app/lib/service/location.dart b/car_app/lib/service/geolocator_location.dart similarity index 82% rename from car_app/lib/service/location.dart rename to car_app/lib/service/geolocator_location.dart index ef4af0e4507ffca3e459904522327c8da188d073..f00a37a33b2d245042f008d0d40eca161dcb7157 100644 --- a/car_app/lib/service/location.dart +++ b/car_app/lib/service/geolocator_location.dart @@ -3,9 +3,11 @@ import 'dart:ui'; import 'dart:io' show Platform; import 'package:car_app/converter/location_converters.dart/point.dart'; -import 'package:car_app/converter/position_point_converter.dart'; +import 'package:car_app/converter/location_converters.dart/position.dart'; import 'package:car_app/exception/permission.dart'; -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/location/point.dart'; +import 'package:car_app/service/interface/location.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:flutter_background_service_android/flutter_background_service_android.dart'; import 'package:geolocator/geolocator.dart'; @@ -13,24 +15,21 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:car_app/converter/converter.dart'; -enum Mode { - foreground, - background, -} - -class LocationService { - static final LocationService _locationService = LocationService._privateConstructor(); +class GeolocatorLocationService<T extends Point> implements LocationService<T> { final FlutterBackgroundService _backgroundService = FlutterBackgroundService(); - final StreamController<Point> _locationStream = StreamController.broadcast(); + final StreamController<T> _locationStream = StreamController.broadcast(); + final Converter<LocationPoint, T> locationPointToPointConverter; late StreamSubscription _backgroundServiceSubscription; - factory LocationService() => _locationService; - - Stream<Point> get locationUpdate => _locationStream.stream; + @override + Stream<T> get locationUpdate => _locationStream.stream; - LocationService._privateConstructor(); + GeolocatorLocationService({ + required this.locationPointToPointConverter, + }); + @override Future<void> start() async { var permissionStatus = await Permission.locationWhenInUse.request(); if (permissionStatus != PermissionStatus.granted) { @@ -46,15 +45,15 @@ class LocationService { ); _backgroundServiceSubscription = _backgroundService.on('update').listen((event) { if (event != null && event.containsKey('point')) { - var point = PointMapConverter.fromMap(event['point']); + var position = PointMapConverter.fromMap(event['point']); + var point = locationPointToPointConverter.convert(position); _locationStream.add(point); } }); _backgroundService.startService(); } - static void _onServiceStart(ServiceInstance service) async { - final Converter<Position, Point> converter = PositionToPointConverter(); + static void _onServiceStart<T extends Point>(ServiceInstance service) async { final StreamController<Position> positionUpdate = StreamController<Position>.broadcast(); late Stream<Position> positionStream; final List<Position> backgroundStore = []; @@ -62,9 +61,8 @@ class LocationService { StreamSubscription<Position>? backgroundSubscription; void sendUpdate(Position position) { - var point = converter.convert(position); - service.invoke('update',{ - 'point': point.toMap(), + service.invoke('update', { + 'point': position.toLocationPoint().toMap() }); } @@ -134,11 +132,13 @@ class LocationService { }); } + @override Future<void> stop() async { _backgroundService.invoke('stop'); _backgroundServiceSubscription.cancel(); } + @override void setMode(Mode mode) { if (mode == Mode.foreground) { _backgroundService.invoke('setAsForeground'); diff --git a/car_app/lib/service/interface/key_value_storage.dart b/car_app/lib/service/interface/key_value_storage.dart index 53655c0076225bddd50618751cee80c94e52cfc2..be17d94cb92335a17a2bd66fd92a05460a39a0aa 100644 --- a/car_app/lib/service/interface/key_value_storage.dart +++ b/car_app/lib/service/interface/key_value_storage.dart @@ -2,4 +2,5 @@ abstract interface class KeyValueStorageService<T, T1> { Future<void> add(T key, T1 value); Future<T1> remove(T key); Future<T1> get(T key); + Stream<MapEntry<T, T1>> getAll(); } diff --git a/car_app/lib/service/interface/location.dart b/car_app/lib/service/interface/location.dart new file mode 100644 index 0000000000000000000000000000000000000000..61ca6112b94d75cf317b4036d608a77e730d62f0 --- /dev/null +++ b/car_app/lib/service/interface/location.dart @@ -0,0 +1,13 @@ +import 'package:car_app/model/interface/point.dart'; + +enum Mode { + foreground, + background, +} + +abstract interface class LocationService<T extends Point> { + Stream<T> get locationUpdate; + Future<void> start(); + Future<void> stop(); + void setMode(Mode mode); +} diff --git a/car_app/lib/service/interface/point.dart b/car_app/lib/service/interface/point.dart new file mode 100644 index 0000000000000000000000000000000000000000..463178662d88cef53daeab7a1b92f218c0f4ca19 --- /dev/null +++ b/car_app/lib/service/interface/point.dart @@ -0,0 +1,5 @@ +import 'package:car_app/model/interface/point.dart'; + +abstract interface class PointService<T extends Point> { + Future<T> add(T point); +} diff --git a/car_app/lib/service/interface/route.dart b/car_app/lib/service/interface/route.dart index 29cf4879543e66abcd3afd00c663f424bcf7f61f..e3965c99a909ec4ab4da638f2bf403b3a28b0d09 100644 --- a/car_app/lib/service/interface/route.dart +++ b/car_app/lib/service/interface/route.dart @@ -1,6 +1,7 @@ -import 'package:car_app/model/route.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/interface/route.dart'; -abstract interface class RouteService { - Future<Route> add(Route route); +abstract interface class RouteService<T extends Route<T1>, T1 extends Point> { + Future<T> add(T route); // Future<List<RouteInformation>> getAll(); } diff --git a/car_app/lib/service/storage/hive_storage.dart b/car_app/lib/service/storage/hive_storage.dart new file mode 100644 index 0000000000000000000000000000000000000000..7b555a478b6feab675f72574136a1ad4f05280ad --- /dev/null +++ b/car_app/lib/service/storage/hive_storage.dart @@ -0,0 +1,68 @@ +import 'package:car_app/exception/key_not_found.dart'; +import 'package:car_app/service/interface/key_value_storage.dart'; +import 'package:hive/hive.dart'; + +class HiveStorageService implements KeyValueStorageService<String, Map<String, dynamic>> { + final String dbPath; + final String dbName; + final String boxName; + final List<int> encryptionKey; + + HiveStorageService({ + required this.dbName, + required this.dbPath, + required this.boxName, + required this.encryptionKey, + }); + + @override + Future<void> add(String key, Map<String, dynamic> value) async { + var box = await getBox(); + box.put(key, value); + } + + @override + Future<Map<String, dynamic>> get(String key) async { + var box = await getBox(); + var value = await box.get(key); + + if(value == null) { + throw KeyNotFoundException(key); + } + return castHiveResult(value); + } + + @override + Future<Map<String, dynamic>> remove(String key) async { + var box = await getBox(); + var value = await get(key); + + await box.delete(key); + return value; + } + + Future<CollectionBox<Map<dynamic, dynamic>>> getBox() async { + var collection = await BoxCollection.open( + dbName, + {boxName}, + path: dbPath, + key: HiveAesCipher(encryptionKey) + ); + + return await collection.openBox<Map<dynamic, dynamic>>(boxName); + } + + Map<String, dynamic> castHiveResult(Map<dynamic, dynamic> map) { + return Map<String, dynamic>.from(map); + } + + @override + Stream<MapEntry<String, Map<String, dynamic>>> getAll() async* { + var box = await getBox(); + var keys = await box.getAllKeys(); + + for(var key in keys) { + yield MapEntry(key, await get(key)); + } + } +} diff --git a/car_app/lib/service/storage/model_storage/appwrite/point_storage.dart b/car_app/lib/service/storage/model_storage/appwrite/point_storage.dart new file mode 100644 index 0000000000000000000000000000000000000000..ae5328e603c87f7366e4d0c15b7f80e866dd803c --- /dev/null +++ b/car_app/lib/service/storage/model_storage/appwrite/point_storage.dart @@ -0,0 +1,17 @@ +import 'package:car_app/converter/storage_converters/appwrite/point.dart'; +import 'package:car_app/model/appwrite/point.dart'; +import 'package:car_app/service/storage/model_storage/point_storage.dart'; + +class AppwritePointStorageService extends PointStorageService<AppwritePoint> { + AppwritePointStorageService({required super.storageService}); + + @override + AppwritePoint pointFromMap(Map<String, dynamic> map) { + return PointMapConverter.fromMap(map); + } + + @override + Map<String, dynamic> pointToMap(AppwritePoint model) { + return model.toMap(); + } +} diff --git a/car_app/lib/service/storage/model_storage/appwrite/route_storage.dart b/car_app/lib/service/storage/model_storage/appwrite/route_storage.dart new file mode 100644 index 0000000000000000000000000000000000000000..bc173f78260aa11947d81d0d06e9b538a2d74fe1 --- /dev/null +++ b/car_app/lib/service/storage/model_storage/appwrite/route_storage.dart @@ -0,0 +1,21 @@ +import 'package:car_app/converter/storage_converters/appwrite/route.dart'; +import 'package:car_app/model/appwrite/route.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/service/storage/model_storage/route_storage.dart'; + +class AppwriteRouteStorageService<T extends Point> extends RouteStorageService<AppwriteRoute<T>, T> { + @override + final String pointIdsFieldName = RouteMapConverter.pointsFieldName; + + AppwriteRouteStorageService({required super.storageService, required super.pointStorageService}); + + @override + AppwriteRoute<T> routeFromMapWithPoints(Map<String, dynamic> map, Iterable<T> points) { + return RouteMapConverter.fromMap<T>(map, points); + } + + @override + Map<String, dynamic> routeToMapWithPointIds(AppwriteRoute<T> route, Iterable<String> pointIds) { + return route.toMap(pointIds); + } +} diff --git a/car_app/lib/service/storage/model_storage/model_storage.dart b/car_app/lib/service/storage/model_storage/model_storage.dart new file mode 100644 index 0000000000000000000000000000000000000000..ea47e4abcc19125dd82349a6fbe9cebc659eece1 --- /dev/null +++ b/car_app/lib/service/storage/model_storage/model_storage.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +import 'package:car_app/service/interface/key_value_storage.dart'; +import 'package:uuid/uuid.dart'; + +abstract class ModelStorageService<T> { + final KeyValueStorageService<String, Map<String, dynamic>> storageService; + final uuid = const Uuid(); + + ModelStorageService({required this.storageService}); + + Future<String> add(T model); + Future<T> get(String key); + Future<T> remove(String key); + Stream<T> getAll(); + + String generateId() { + return uuid.v4(); + } +} diff --git a/car_app/lib/service/storage/model_storage/point_storage.dart b/car_app/lib/service/storage/model_storage/point_storage.dart new file mode 100644 index 0000000000000000000000000000000000000000..b24d8a7b44cdcff14a28ab0dc174ae6f37ee5b1a --- /dev/null +++ b/car_app/lib/service/storage/model_storage/point_storage.dart @@ -0,0 +1,34 @@ +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/service/storage/model_storage/model_storage.dart'; + +abstract class PointStorageService<T extends Point> extends ModelStorageService<T> { + PointStorageService({required super.storageService}); + + Map<String, dynamic> pointToMap(T model); + T pointFromMap(Map<String, dynamic> map); + + @override + Future<String> add(T model) async { + var id = generateId(); + await storageService.add(id, pointToMap(model)); + + return id; + } + + @override + Future<T> get(String key) async { + Map<String, dynamic> map = await storageService.get(key); + return pointFromMap(map); + } + + @override + Future<T> remove(String key) async { + var map = await storageService.remove(key); + return pointFromMap(map); + } + + @override + Stream<T> getAll() { + return storageService.getAll().map((e) => pointFromMap(e.value)); + } +} diff --git a/car_app/lib/service/storage/model_storage/route_storage.dart b/car_app/lib/service/storage/model_storage/route_storage.dart new file mode 100644 index 0000000000000000000000000000000000000000..e4efa5ef9ce3889c6f64302c99ac2627aeb205e0 --- /dev/null +++ b/car_app/lib/service/storage/model_storage/route_storage.dart @@ -0,0 +1,65 @@ +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/interface/route.dart'; +import 'package:car_app/service/storage/model_storage/model_storage.dart'; +import 'package:car_app/service/storage/model_storage/point_storage.dart'; + +abstract class RouteStorageService<T extends Route<T1>, T1 extends Point> extends ModelStorageService<T> { + final PointStorageService<T1> pointStorageService; + + String get pointIdsFieldName; + + RouteStorageService({required super.storageService, required this.pointStorageService}); + + T routeFromMapWithPoints(Map<String, dynamic> map, Iterable<T1> points); + Map<String, dynamic> routeToMapWithPointIds(T route, Iterable<String> pointIds); + + @override + Future<String> add(T model) async { + var pointIds = <String>[]; + for (var point in model.points) { + pointIds.add(await pointStorageService.add(point)); + } + + var id = generateId(); + await storageService.add(id, routeToMapWithPointIds(model, pointIds)); + return id; + } + + @override + Future<T> get(String key) async { + return routeFromMap(await storageService.get(key)); + } + + @override + Stream<T> getAll() { + return storageService.getAll().asyncMap((e) async => await routeFromMap(e.value)); + } + + @override + Future<T> remove(String key) async { + var map = await storageService.get(key); + var item = await routeFromMap(map); + var pointIds = map[pointIdsFieldName] as List<String>; + + var futures = <Future<T1>>[]; + for (var pointId in pointIds) { + futures.add(pointStorageService.remove(pointId)); + } + + await Future.wait(futures); + await storageService.remove(key); + + return item; + } + + Future<T> routeFromMap(Map<String, dynamic> map) async { + var pointIds = map[pointIdsFieldName] as List<String>; + + var points = <T1>[]; + for (var pointId in pointIds) { + points.add(await pointStorageService.get(pointId)); + } + + return routeFromMapWithPoints(map, points); + } +} diff --git a/car_app/lib/service/storage/route_storage.dart b/car_app/lib/service/storage/route_storage.dart deleted file mode 100644 index 19eb7c4ffe6c7a8e7b96a2bffa4edf3ff3c70d47..0000000000000000000000000000000000000000 --- a/car_app/lib/service/storage/route_storage.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:convert'; - -import 'package:car_app/converter/storage_converters/route.dart'; -import 'package:car_app/model/route.dart'; -import 'package:car_app/service/interface/key_value_storage.dart'; - -class RouteStorageService implements KeyValueStorageService<String, List<Route>> { - final KeyValueStorageService<String, String> storageService; - - RouteStorageService({required this.storageService}); - - @override - Future<void> add(String key, List<Route> value) { - var json = jsonEncode(value.map((e) => e.toMap()).toList()); - return storageService.add(key, json); - } - - @override - Future<List<Route>> get(String key) async { - var json = await storageService.get(key); - return _getRoutesFromJson(json); - } - - @override - Future<List<Route>> remove(String key) async { - var json = await storageService.remove(key); - return _getRoutesFromJson(json); - } - - List<Route> _getRoutesFromJson(String json) { - var routeMaps = List<Map<String, dynamic>>.from(jsonDecode(json)); - return routeMaps.map((e) => RouteMapConverter.fromMap(e)).toList(); - } -} diff --git a/car_app/lib/service/storage/secure_storage.dart b/car_app/lib/service/storage/secure_storage.dart index 4b2e0e1e8d340082df3b46baec725cbc76a96e96..3c93e42ac116c5a42dcffcd2a29563003a8eaf5b 100644 --- a/car_app/lib/service/storage/secure_storage.dart +++ b/car_app/lib/service/storage/secure_storage.dart @@ -26,4 +26,12 @@ class SecureStorageService implements KeyValueStorageService<String, String> { await _storage.delete(key: key); return value; } + + @override + Stream<MapEntry<String, String>> getAll() async* { + var map = await _storage.readAll(); + for (var entry in map.entries) { + yield MapEntry(entry.key, entry.value); + } + } } diff --git a/car_app/lib/view/tracking.dart b/car_app/lib/view/tracking.dart index d71a1d5e63546e1a369fa9cb5fb4663796395909..b2bd7df2c19222cfcd629ca21dc365411c51e0db 100644 --- a/car_app/lib/view/tracking.dart +++ b/car_app/lib/view/tracking.dart @@ -1,6 +1,6 @@ import 'package:car_app/converter/point_latlng_converter.dart'; import 'package:car_app/exception/server.dart'; -import 'package:car_app/model/point.dart'; +import 'package:car_app/model/interface/point.dart'; import 'package:car_app/view/defaults.dart'; import 'package:car_app/view/routes_titles.dart'; import 'package:car_app/viewmodel/tracking.dart'; diff --git a/car_app/lib/viewmodel/tracking.dart b/car_app/lib/viewmodel/tracking.dart index b6067dce55e8ea8530ba78a39209f54a8d7ae1c6..fffa7eba04c936c6c80524c74260487a2238e6b8 100644 --- a/car_app/lib/viewmodel/tracking.dart +++ b/car_app/lib/viewmodel/tracking.dart @@ -1,13 +1,14 @@ import 'dart:async'; import 'package:car_app/exception/permission.dart'; -import 'package:car_app/model/point.dart'; -import 'package:car_app/model/route.dart'; +import 'package:car_app/factory/interface/route.dart'; +import 'package:car_app/model/interface/point.dart'; +import 'package:car_app/model/interface/route.dart'; import 'package:car_app/service/interface/authentication.dart'; import 'package:car_app/service/interface/car.dart'; +import 'package:car_app/service/interface/location.dart'; import 'package:car_app/service/interface/logging.dart'; import 'package:car_app/service/interface/route.dart'; -import 'package:car_app/service/location.dart'; import 'package:flutter/foundation.dart'; enum TrackingState { @@ -34,13 +35,14 @@ extension TrackingButtonText on TrackingState { } } -class TrackingViewModel extends ChangeNotifier { +class TrackingViewModel<T extends Route<T1>, T1 extends Point> extends ChangeNotifier { final AuthenticationService authenticationService; - final RouteService routeService; + final RouteService<T, T1> routeService; final CarService carService; - final LocationService _locationService = LocationService(); + final LocationService<T1> locationService; final LoggingService loggingService; - final List<Point> _points = []; + final RouteFactory<T, T1> routeFactory; + final List<T1> _points = []; late StreamSubscription <Point> _locationSubscription; late DateTime _trackingStartTime; @@ -50,14 +52,21 @@ class TrackingViewModel extends ChangeNotifier { TrackingState _trackingState = TrackingState.stopped; String get trackingButtonText => _trackingState.text; - TrackingViewModel({required this.authenticationService, required this.routeService, required this.carService, required this.loggingService}); + TrackingViewModel({ + required this.authenticationService, + required this.locationService, + required this.routeService, + required this.carService, + required this.loggingService, + required this.routeFactory, + }); void locationServiceToForeground() { - _locationService.setMode(Mode.foreground); + locationService.setMode(Mode.foreground); } void locationServiceToBackground() { - _locationService.setMode(Mode.background); + locationService.setMode(Mode.background); } void setCarId(String carId) { @@ -93,12 +102,12 @@ class TrackingViewModel extends ChangeNotifier { _setTrackingState(TrackingState.starting); - _locationSubscription = _locationService.locationUpdate.listen((p) { + _locationSubscription = locationService.locationUpdate.listen((p) { onUpdate(p); _points.add(p); }); - _locationService.start().then((_) { + locationService.start().then((_) { _setTrackingState(TrackingState.started); }) .catchError(test: (e) => e is PermissionException, (e, s) { @@ -111,9 +120,10 @@ class TrackingViewModel extends ChangeNotifier { void _stopTracking() { _setTrackingState(TrackingState.stopping); _locationSubscription.cancel(); - _locationService.stop().then((_) async { + locationService.stop().then((_) async { _setTrackingState(TrackingState.stopped); - await routeService.add(Route(points: _points, carId: _carId, startTime: _trackingStartTime, endTime: DateTime.now())); + var route = routeFactory.createInstance(_carId, _points, _trackingStartTime, DateTime.now()); + await routeService.add(route); }); } diff --git a/car_app/pubspec.lock b/car_app/pubspec.lock index c55cdd4b86f853e8152148849b8f9b39e38e8c50..c5ce58eae4899b5ab2cc3c7e1b231997a30082e9 100644 --- a/car_app/pubspec.lock +++ b/car_app/pubspec.lock @@ -384,6 +384,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" html: dependency: transitive description: @@ -553,7 +561,7 @@ packages: source: hosted version: "2.0.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" @@ -886,13 +894,13 @@ packages: source: hosted version: "3.1.1" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.4.0" vector_math: dependency: transitive description: diff --git a/car_app/pubspec.yaml b/car_app/pubspec.yaml index ac4f2f2f6563179570c8186617384e11e7966f86..ed42bcb6a7ffbb9f38b6b71f3078bb6bffcf941f 100644 --- a/car_app/pubspec.yaml +++ b/car_app/pubspec.yaml @@ -45,6 +45,9 @@ dependencies: path_provider: ^2.1.2 table_calendar: ^3.1.1 intl: ^0.19.0 + hive: ^2.2.3 + uuid: ^4.4.0 + path: ^1.9.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.