From b91dad6532bbd31ae5cb7cc746bf1908d53f308c Mon Sep 17 00:00:00 2001 From: cp-ishita-g <154948719+cp-ishita-g@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:19:19 +0530 Subject: [PATCH] Fix steady location issue (#106) * fix journey break issue * add 10 meter check to update location * add grace period and check time interval * journey ui updates * add cache for last five location storage and remove location table db * show steady duration in steady location in journey * add log in firebase for journey * change algorithm for journey tracking * set location on home screen to fix location updates on creating new acc after delete with deleting app * fix lint * revert day change call and update create_at and update_at * update ios deploy --- app/assets/locales/app_en.arb | 4 +- app/ios/Runner/AppDelegate.swift | 44 +- .../domain/extenstions/date_formatter.dart | 5 +- app/lib/main.dart | 29 +- .../ui/flow/home/home_screen_viewmodel.dart | 14 +- .../flow/journey/components/journey_map.dart | 16 +- .../detail/user_journey_detail_screen.dart | 123 ++--- .../user_journey_detail_view_model.dart | 49 ++ .../timeline/journey_timeline_screen.dart | 81 +-- .../timeline/journey_timeline_view_model.dart | 76 +++ app/pubspec.lock | 24 +- data/.flutter-plugins | 96 ++-- data/.flutter-plugins-dependencies | 2 +- data/lib/api/auth/api_user_service.dart | 4 + .../location/journey/api_journey_service.dart | 77 ++- data/lib/api/location/journey/journey.dart | 28 +- .../api/location/journey/journey.freezed.dart | 46 +- data/lib/api/location/journey/journey.g.dart | 4 - data/lib/api/location/location.dart | 12 + data/lib/api/location/location_table.dart | 33 -- .../api/location/location_table.freezed.dart | 201 -------- data/lib/api/location/location_table.g.dart | 21 - data/lib/log/logger.dart | 19 +- data/lib/repository/journey_repository.dart | 464 +++++++----------- data/lib/service/auth_service.dart | 2 +- data/lib/service/location_service.dart | 28 +- .../storage/database/location_database.dart | 28 -- .../storage/database/location_table_dao.dart | 49 -- data/lib/storage/location_caches.dart | 60 +++ data/pubspec.yaml | 1 + 30 files changed, 643 insertions(+), 997 deletions(-) delete mode 100644 data/lib/api/location/location_table.dart delete mode 100644 data/lib/api/location/location_table.freezed.dart delete mode 100644 data/lib/api/location/location_table.g.dart delete mode 100644 data/lib/storage/database/location_database.dart delete mode 100644 data/lib/storage/database/location_table_dao.dart create mode 100644 data/lib/storage/location_caches.dart diff --git a/app/assets/locales/app_en.arb b/app/assets/locales/app_en.arb index 1bad6d1b..413a55d8 100644 --- a/app/assets/locales/app_en.arb +++ b/app/assets/locales/app_en.arb @@ -280,5 +280,7 @@ "journey_timeline_empty_screen_text": "No location history found!", "journey_timeline_Since_text": "Since {date}", "journey_timeline_today_text": "Today {date}", - "journey_timeline_date_picker_select_text": "Select" + "journey_timeline_date_picker_select_text": "Select", + + "journey_detail_getting_address_text": "Getting the address..." } \ No newline at end of file diff --git a/app/ios/Runner/AppDelegate.swift b/app/ios/Runner/AppDelegate.swift index fdf835e4..ad1f3efe 100644 --- a/app/ios/Runner/AppDelegate.swift +++ b/app/ios/Runner/AppDelegate.swift @@ -7,13 +7,11 @@ import CoreLocation @main @objc class AppDelegate: FlutterAppDelegate { - + var geofencePlugin: GeofenceService? var locationManager: CLLocationManager? var previousLocation: CLLocation? - - let minimumDistance: Double = 10.0 // 10 meters - + override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? @@ -23,15 +21,16 @@ import CoreLocation if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate } - + setUpLocation() - + let key = Bundle.main.object(forInfoDictionaryKey: "ApiMapKey") GMSServices.provideAPIKey(key as! String) - + geofencePluginRegistration() - + GeneratedPluginRegistrant.register(with: self) + setUpFlutterMethodChannelForInvokeLocation() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } @@ -46,9 +45,9 @@ extension AppDelegate { private func geofencePluginRegistration() { let controller: FlutterViewController = window?.rootViewController as! FlutterViewController let geofenceChannel = FlutterMethodChannel(name: "geofence_plugin", binaryMessenger: controller.binaryMessenger) - + geofencePlugin = GeofenceService(channel: geofenceChannel) - + geofenceChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in guard let self = self else { return } if call.method == "startMonitoring" { @@ -79,18 +78,35 @@ extension AppDelegate: CLLocationManagerDelegate { private func setUpLocation() { locationManager = CLLocationManager() locationManager?.delegate = self + locationManager?.distanceFilter = 10 + locationManager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters locationManager?.allowsBackgroundLocationUpdates = true locationManager?.pausesLocationUpdatesAutomatically = false locationManager?.startMonitoringSignificantLocationChanges() } + private func setUpFlutterMethodChannelForInvokeLocation() { + let controller = window?.rootViewController as! FlutterViewController + let locationChannel = FlutterMethodChannel(name: "com.yourspace/set_up_location", binaryMessenger: controller.binaryMessenger) + + locationChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in + guard let self = self else { return } + if call.method == "setUpLocation" { + self.setUpLocation() + result(nil) + } else { + result(FlutterMethodNotImplemented) + } + } + } + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let currentLocation = locations.last else { return } - if let previousLocation = previousLocation { - let distance = currentLocation.distance(from: previousLocation) - - if distance < minimumDistance { + if let lastLocation = previousLocation { + let distance = currentLocation.distance(from: lastLocation) + + if distance < 10 { return } } diff --git a/app/lib/domain/extenstions/date_formatter.dart b/app/lib/domain/extenstions/date_formatter.dart index 63594284..41374e56 100644 --- a/app/lib/domain/extenstions/date_formatter.dart +++ b/app/lib/domain/extenstions/date_formatter.dart @@ -20,7 +20,6 @@ enum DateFormatType { relativeDate, relativeDateWeekday, serverIso, - dayTime, shortDayMonth } @@ -71,8 +70,6 @@ extension DateFormatter on DateTime { return _formattedPastTime(context); case DateFormatType.serverIso: return DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(toUtc()); - case DateFormatType.dayTime: - return DateFormat('dd MMMM hh:mm a').format(this); case DateFormatType.shortDayMonth: return DateFormat('d MMMM').format(this); } @@ -118,7 +115,7 @@ extension DateFormatter on DateTime { } else if (isSameDay(yesterday)) { return context.l10n.common_yesterday; } else if (year == today.year) { - return DateFormat('${includeWeekday ? 'EEEE, ' : ''}d MMMM').format(this); + return DateFormat('${includeWeekday ? 'EEEE, ' : ''}d MMM').format(this); } else { return DateFormat('${includeWeekday ? 'EEEE, ' : ''}d MMM yyyy') .format(this); diff --git a/app/lib/main.dart b/app/lib/main.dart index a0d0df1e..1a60d8d3 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:battery_plus/battery_plus.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:data/api/location/journey/api_journey_service.dart'; import 'package:data/api/location/location.dart'; import 'package:data/log/logger.dart'; import 'package:data/repository/journey_repository.dart'; @@ -32,6 +33,7 @@ import 'domain/fcm/notification_handler.dart'; const platform = MethodChannel('com.yourspace/location'); late final LocationService locationService; late final JourneyRepository journeyRepository; +late final ApiJourneyService journeyService; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -78,7 +80,8 @@ void updateCurrentUserState(RemoteMessage message, NetworkService networkService Future _initContainer() async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); locationService = LocationService(FirebaseFirestore.instance); - journeyRepository = JourneyRepository(FirebaseFirestore.instance); + journeyService = ApiJourneyService(FirebaseFirestore.instance); + journeyRepository = JourneyRepository(journeyService); final prefs = await SharedPreferences.getInstance(); @@ -138,7 +141,6 @@ StreamSubscription? positionSubscription; Timer? _timer; Position? _position; Position? _previousPosition; -LocationData? _iOSPreviousPosition; int? _batteryLevel; @pragma('vm:entry-point') @@ -157,7 +159,8 @@ Future onStart(ServiceInstance service) async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); locationService = LocationService(FirebaseFirestore.instance); - journeyRepository = JourneyRepository(FirebaseFirestore.instance); + journeyService = ApiJourneyService(FirebaseFirestore.instance); + journeyRepository = JourneyRepository(journeyService); final batteryService = BatteryService(FirebaseFirestore.instance); final userId = await _getUserIdFromPreferences(); final battery = Battery(); @@ -194,23 +197,14 @@ void _startLocationUpdates() { Future _updateUserLocationWithIOS(LocationData locationPosition) async { final userId = await _getUserIdFromPreferences(); if (userId != null) { - final isSame = _iOSPreviousPosition?.latitude == locationPosition.latitude && - _iOSPreviousPosition?.longitude == locationPosition.longitude; - - if (isSame) return; - _iOSPreviousPosition = locationPosition; - try { - final userState = await journeyRepository.getUserState(userId, locationPosition); - await locationService.saveCurrentLocation( userId, LatLng(locationPosition.latitude, locationPosition.longitude), - DateTime.now().millisecondsSinceEpoch, - userState, + DateTime.now().millisecondsSinceEpoch ); - await journeyRepository.saveUserJourney(userState, userId, locationPosition); + await journeyRepository.saveLocationJourney(locationPosition, userId); } catch (error, stack) { logger.e( 'Error while updating user location and journey from native iOS location data', @@ -225,6 +219,7 @@ void _updateUserLocation( String userId, Position? position, ) async { + if (Platform.isIOS) return; final isSame = _previousPosition?.latitude == position?.latitude && _previousPosition?.longitude == position?.longitude; @@ -237,17 +232,13 @@ void _updateUserLocation( longitude: position.longitude, timestamp: position.timestamp, ); - - final userState = await journeyRepository.getUserState(userId, locationData); - await locationService.saveCurrentLocation( userId, LatLng(position.latitude, position.longitude), DateTime.now().millisecondsSinceEpoch, - userState, ); - await journeyRepository.saveUserJourney(userState, userId, locationData); + await journeyRepository.saveLocationJourney(locationData, userId); } catch (error, stack) { logger.e( 'Main: error while getting ot update user location and journey', diff --git a/app/lib/ui/flow/home/home_screen_viewmodel.dart b/app/lib/ui/flow/home/home_screen_viewmodel.dart index 92be051c..c37d3465 100644 --- a/app/lib/ui/flow/home/home_screen_viewmodel.dart +++ b/app/lib/ui/flow/home/home_screen_viewmodel.dart @@ -6,11 +6,14 @@ import 'package:data/log/logger.dart'; import 'package:data/service/permission_service.dart'; import 'package:data/service/space_service.dart'; import 'package:data/storage/app_preferences.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'home_screen_viewmodel.freezed.dart'; +const platform = MethodChannel('com.yourspace/set_up_location'); + final homeViewStateProvider = StateNotifierProvider.autoDispose( (ref) => HomeViewNotifier( @@ -32,6 +35,7 @@ class HomeViewNotifier extends StateNotifier { final ApiUser? _currentUser; final ApiUserService userService; final ApiSession? _userSession; + static const platform = MethodChannel('com.yourspace/set_up_location'); HomeViewNotifier( this.spaceService, @@ -47,13 +51,21 @@ class HomeViewNotifier extends StateNotifier { if (_currentUser == null && _userSession == null) return; listenUserSession(_currentUser!.id, _userSession!.id); + _setupLocationOnIOS(); } String? get currentSpaceId => _currentSpaceIdController.state; + Future _setupLocationOnIOS() async { + try { + await platform.invokeMethod('setUpLocation'); + } on PlatformException catch (e) { + logger.e('HomeViewNotifier: error while invoke update location in iOS', error: e); + } + } + void listenSpaceMember() async { if (state.loading) return; - try { state = state.copyWith(loading: true); spaceService.streamAllSpace().listen((spaces) { diff --git a/app/lib/ui/flow/journey/components/journey_map.dart b/app/lib/ui/flow/journey/components/journey_map.dart index 6b323690..f6091142 100644 --- a/app/lib/ui/flow/journey/components/journey_map.dart +++ b/app/lib/ui/flow/journey/components/journey_map.dart @@ -131,7 +131,7 @@ class _JourneyMapState extends State { final longDistanceLatLng = _getLongDistanceCoordinate(); final centerLatLng = _getCenterCoordinate(_fromLatLng, longDistanceLatLng); - final zoom = _calculateZoomLevel(journey.route_distance ?? 0); + final zoom = _calculateZoomLevel(_getDistanceString(journey)); return (polyline, centerLatLng, zoom); } @@ -197,4 +197,18 @@ class _JourneyMapState extends State { } }); } + + double _getDistanceString(ApiLocationJourney location) { + final steadyLocation = location.toPositionFromSteadyJourney(); + final movingLocation = location.toPositionFromMovingJourney(); + + final routeDistance = steadyLocation.distanceTo(movingLocation); + + if (routeDistance < 1000) { + return routeDistance.round().ceilToDouble(); + } else { + final distanceInKm = routeDistance / 1000; + return distanceInKm.round().ceilToDouble(); + } + } } diff --git a/app/lib/ui/flow/journey/detail/user_journey_detail_screen.dart b/app/lib/ui/flow/journey/detail/user_journey_detail_screen.dart index 3a02567e..a6c57f68 100644 --- a/app/lib/ui/flow/journey/detail/user_journey_detail_screen.dart +++ b/app/lib/ui/flow/journey/detail/user_journey_detail_screen.dart @@ -1,14 +1,12 @@ -import 'dart:ui' as ui; - import 'package:data/api/location/journey/journey.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geocoding/geocoding.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:style/extenstions/context_extenstions.dart'; import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_dart.dart'; +import 'package:yourspace_flutter/domain/extenstions/context_extenstions.dart'; import 'package:yourspace_flutter/domain/extenstions/date_formatter.dart'; import 'package:yourspace_flutter/domain/extenstions/lat_lng_extenstion.dart'; import 'package:yourspace_flutter/ui/components/app_page.dart'; @@ -32,14 +30,14 @@ class UserJourneyDetailScreen extends ConsumerStatefulWidget { class _UserJourneyDetailScreenState extends ConsumerState { final List _markers = []; + late UserJourneyDetailViewModel notifier; @override void initState() { super.initState(); runPostFrame(() { - ref - .read(userJourneyDetailStateProvider.notifier) - .loadData(widget.journey); + notifier = ref.read(userJourneyDetailStateProvider.notifier); + notifier.loadData(widget.journey); }); } @@ -77,9 +75,8 @@ class _UserJourneyDetailScreenState } Widget _journeyInfo(UserJourneyDetailState state) { - final distance = _getDistanceString(state.journey!.route_distance ?? 0.0); - final duration = - _getRouteDurationString(state.journey!.route_duration ?? 0); + final distance = notifier.getDistanceString(state.journey!); + final duration = notifier.getRouteDurationString(state.journey!); return Padding( padding: EdgeInsets.only( @@ -145,7 +142,7 @@ class _UserJourneyDetailScreenState Widget _placeInfo(List placeMark, int? createdAt) { final formattedTime = _getDateAndTime(createdAt ?? 0); - final address = placeMark.getFormattedAddress(); + final address = placeMark.isNotEmpty ? placeMark.getFormattedAddress() : context.l10n.journey_detail_getting_address_text; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -167,62 +164,10 @@ class _UserJourneyDetailScreenState ); } - String _formattedAddress( - List? fromPlaces, - List? toPlaces, - ) { - if (fromPlaces == null || fromPlaces.isEmpty) return ''; - final fromPlace = fromPlaces.first; - final toPlace = toPlaces?.first; - - final fromCity = fromPlace.locality ?? ''; - final toCity = toPlace?.locality ?? ''; - - final fromArea = fromPlace.subLocality ?? ''; - final toArea = toPlace?.subLocality ?? ''; - - final fromState = fromPlace.administrativeArea ?? ''; - final toState = toPlace?.administrativeArea ?? ''; - - if (toPlace == null) { - return "$fromArea, $fromCity"; - } else if (fromArea == toArea) { - return "$fromArea, $fromCity"; - } else if (fromCity == toCity) { - return "$fromArea to $toArea, $fromCity"; - } else if (fromState == toState) { - return "$fromArea, $fromCity to $toArea, $toCity"; - } else { - return "$fromCity, $fromState to $toCity, $toState"; - } - } - - String _getDistanceString(double distance) { - if (distance < 1000) { - return '${distance.round()} m'; - } else { - final distanceInKm = distance / 1000; - return '${distanceInKm.round()} km'; - } - } - - String _getRouteDurationString(int durationInMilliseconds) { - final duration = Duration(milliseconds: durationInMilliseconds); - final hours = duration.inHours; - final minutes = duration.inMinutes % 60; - final seconds = duration.inSeconds % 60; - if (hours > 0) { - return '$hours hr $minutes mins'; - } else if (minutes > 0) { - return '$minutes mins'; - } else { - return '$seconds sec'; - } - } - String _getDateAndTime(int createdAt) { DateTime createdAtDate = DateTime.fromMillisecondsSinceEpoch(createdAt); - return createdAtDate.format(context, DateFormatType.dayTime); + final startTime = createdAtDate.format(context, DateFormatType.time); + return '${createdAtDate.format(context, DateFormatType.dayMonthFull)} $startTime'; } void _observeError() { @@ -249,10 +194,40 @@ class _UserJourneyDetailScreenState }); } + String _formattedAddress( + List? fromPlaces, + List? toPlaces, + ) { + if (fromPlaces == null || fromPlaces.isEmpty) return ''; + final fromPlace = fromPlaces.first; + final toPlace = toPlaces?.first; + + final fromCity = fromPlace.locality ?? ''; + final toCity = toPlace?.locality ?? ''; + + final fromArea = fromPlace.subLocality ?? ''; + final toArea = toPlace?.subLocality ?? ''; + + final fromState = fromPlace.administrativeArea ?? ''; + final toState = toPlace?.administrativeArea ?? ''; + + if (toPlace == null) { + return "$fromArea, $fromCity"; + } else if (fromArea == toArea) { + return "$fromArea, $fromCity"; + } else if (fromCity == toCity) { + return "$fromArea to $toArea, $fromCity"; + } else if (fromState == toState) { + return "$fromArea, $fromCity to $toArea, $toCity"; + } else { + return "$fromCity, $fromState to $toCity, $toState"; + } + } + Future> _buildMarkers(LatLng fromLatLng, LatLng toLatLng) async { - final fromIcon = await _createCustomIcon( + final fromIcon = await notifier.createCustomIcon( 'assets/images/ic_start_location_detail_icon.png'); - final toIcon = await _createCustomIcon( + final toIcon = await notifier.createCustomIcon( 'assets/images/ic_end_location_detail_icon.png'); final List markers = [ @@ -274,20 +249,4 @@ class _UserJourneyDetailScreenState return markers; } - - Future _createCustomIcon(String assetPath) async { - final data = await rootBundle.load(assetPath); - final codec = await ui.instantiateImageCodec( - data.buffer.asUint8List(), - targetWidth: 200, - targetHeight: 200, - ); - final frameInfo = await codec.getNextFrame(); - - final byteData = - await frameInfo.image.toByteData(format: ui.ImageByteFormat.png); - final resizedBytes = byteData!.buffer.asUint8List(); - - return BitmapDescriptor.fromBytes(resizedBytes); - } } diff --git a/app/lib/ui/flow/journey/detail/user_journey_detail_view_model.dart b/app/lib/ui/flow/journey/detail/user_journey_detail_view_model.dart index c56ed729..8ff8c7e8 100644 --- a/app/lib/ui/flow/journey/detail/user_journey_detail_view_model.dart +++ b/app/lib/ui/flow/journey/detail/user_journey_detail_view_model.dart @@ -1,6 +1,8 @@ +import 'dart:ui' as ui; import 'package:data/api/location/journey/api_journey_service.dart'; import 'package:data/api/location/journey/journey.dart'; import 'package:data/log/logger.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:geocoding/geocoding.dart'; @@ -54,6 +56,53 @@ class UserJourneyDetailViewModel extends StateNotifier { ); } } + + String getDistanceString(ApiLocationJourney location) { + final steadyLocation = location.toPositionFromSteadyJourney(); + final movingLocation = location.toPositionFromMovingJourney(); + + final routeDistance = steadyLocation.distanceTo(movingLocation); + + if (routeDistance < 1000) { + return '${routeDistance.round()} m'; + } else { + final distanceInKm = routeDistance / 1000; + return '${distanceInKm.round()} km'; + } + } + + String getRouteDurationString(ApiLocationJourney journey) { + final routeDurationMillis = journey.update_at! - journey.created_at!; + final routeDuration = Duration(milliseconds: routeDurationMillis); + + final hours = routeDuration.inHours; + final minutes = routeDuration.inMinutes % 60; + final seconds = routeDuration.inSeconds % 60; + + if (hours > 0) { + return "$hours hr $minutes mins"; + } else if (minutes > 0) { + return "$minutes mins"; + } else { + return "$seconds sec"; + } + } + + Future createCustomIcon(String assetPath) async { + final data = await rootBundle.load(assetPath); + final codec = await ui.instantiateImageCodec( + data.buffer.asUint8List(), + targetWidth: 200, + targetHeight: 200, + ); + final frameInfo = await codec.getNextFrame(); + + final byteData = + await frameInfo.image.toByteData(format: ui.ImageByteFormat.png); + final resizedBytes = byteData!.buffer.asUint8List(); + + return BitmapDescriptor.fromBytes(resizedBytes); + } } @freezed diff --git a/app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart b/app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart index 207d6727..6fe638ef 100644 --- a/app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart +++ b/app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart @@ -1,9 +1,6 @@ -import 'dart:ui' as ui; - import 'package:data/api/auth/auth_models.dart'; import 'package:data/api/location/journey/journey.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:geocoding/geocoding.dart'; @@ -15,7 +12,6 @@ import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_dart.dart'; import 'package:yourspace_flutter/domain/extenstions/context_extenstions.dart'; import 'package:yourspace_flutter/domain/extenstions/date_formatter.dart'; -import 'package:yourspace_flutter/domain/extenstions/lat_lng_extenstion.dart'; import 'package:yourspace_flutter/ui/components/app_page.dart'; import 'package:yourspace_flutter/ui/flow/journey/timeline/journey_timeline_view_model.dart'; @@ -164,9 +160,10 @@ class _JourneyTimelineScreenState extends ConsumerState { String? spaceId, ) { final location = LatLng(journey.from_latitude, journey.from_longitude); + final steadyDuration = notifier.getSteadyDuration(journey.created_at!, journey.update_at!); final formattedTime = (isFirstItem) ? _getFormattedLocationTimeForFirstItem(journey.created_at!) - : _getFormattedLocationTime(journey.created_at!); + : _getFormattedTimeForSteadyLocation(journey.created_at!, steadyDuration); return Padding( padding: EdgeInsets.only(top: isFirstItem ? 16 : 0), @@ -209,7 +206,7 @@ class _JourneyTimelineScreenState extends ConsumerState { ) { final time = _getFormattedJourneyTime( journey.created_at ?? 0, journey.update_at ?? 0); - final distance = _getDistanceString(journey.route_distance ?? 0.0); + final distance = notifier.getDistanceString(journey); final fromLatLng = LatLng(journey.from_latitude, journey.from_longitude); final toLatLng = LatLng(journey.to_latitude ?? 0.0, journey.to_longitude ?? 0.0); @@ -271,7 +268,7 @@ class _JourneyTimelineScreenState extends ConsumerState { } return FutureBuilder( - future: _getAddress(latLng), + future: notifier.getAddress(latLng), builder: (_, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return _placeInfo( @@ -285,6 +282,8 @@ class _JourneyTimelineScreenState extends ConsumerState { context.l10n.journey_timeline_unknown_address_text; _addressCache[latLng] = address; return _placeInfo(address, formattedTime); + } else if (snapshot.hasError) { + return _placeInfo("Request timeout", formattedTime); } else { return _placeInfo( context.l10n.journey_timeline_unknown_address_text, @@ -378,17 +377,11 @@ class _JourneyTimelineScreenState extends ConsumerState { final time = createdAtDate.format(context, DateFormatType.time); return context.l10n.journey_timeline_Since_text(time); } else { - final dayTime = createdAtDate.format(context, DateFormatType.dayTime); + final dayTime = createdAtDate.format(context, DateFormatType.dayMonthFull); return context.l10n.journey_timeline_Since_text(dayTime); } } - Future _getAddress(LatLng latLng) async { - await Future.delayed(const Duration(seconds: 2)); - final address = await latLng.getAddressFromLocation(); - return address; - } - String _getFormattedJourneyTime(int startAt, int endAt) { DateTime startAtDate = DateTime.fromMillisecondsSinceEpoch(startAt); DateTime endAtDate = DateTime.fromMillisecondsSinceEpoch(endAt); @@ -413,16 +406,19 @@ class _JourneyTimelineScreenState extends ConsumerState { final time = createdAtDate.format(context, DateFormatType.time); return context.l10n.journey_timeline_today_text(time); } else { - return '${createdAtDate.format(context, DateFormatType.relativeDate)} $startTime'; + return '${createdAtDate.format(context, DateFormatType.dayMonthFull)} $startTime'; } } - String _getDistanceString(double distance) { - if (distance < 1000) { - return '${distance.round()} m'; + String _getFormattedTimeForSteadyLocation(int createdAt, String steadyDuration) { + DateTime createdAtDate = DateTime.fromMillisecondsSinceEpoch(createdAt); + final startTime = createdAtDate.format(context, DateFormatType.time); + + if (createdAtDate.isToday) { + final time = createdAtDate.format(context, DateFormatType.time); + return '${context.l10n.journey_timeline_today_text(time)} for $steadyDuration'; } else { - final distanceInKm = distance / 1000; - return '${distanceInKm.round()} km'; + return '${createdAtDate.format(context, DateFormatType.dayMonthFull)} $startTime for $steadyDuration'; } } @@ -433,37 +429,14 @@ class _JourneyTimelineScreenState extends ConsumerState { final toPlaceMarks = await placemarkFromCoordinates(toLatLng.latitude, toLatLng.longitude); - return _formattedAddress(fromPlaceMarks.first, toPlaceMarks.first); - } - - String _formattedAddress(Placemark fromPlace, Placemark? toPlace) { - final fromCity = fromPlace.locality ?? ''; - final toCity = toPlace?.locality ?? ''; - - final fromArea = fromPlace.subLocality ?? ''; - final toArea = toPlace?.subLocality ?? ''; - - final fromState = fromPlace.administrativeArea ?? ''; - final toState = toPlace?.administrativeArea ?? ''; - - if (toPlace == null) { - return "$fromArea, $fromCity"; - } else if (fromArea == toArea) { - return "$fromArea, $fromCity"; - } else if (fromCity == toCity) { - return "$fromArea to $toArea, $fromCity"; - } else if (fromState == toState) { - return "$fromArea, $fromCity to $toArea, $toCity"; - } else { - return "$fromCity, $fromState to $toCity, $toState"; - } + return notifier.formattedAddress(fromPlaceMarks.first, toPlaceMarks.first); } Future> _buildMarkers(LatLng fromLatLng, LatLng toLatLng) async { final fromIcon = - await _createCustomIcon('assets/images/ic_feed_location_icon.png'); + await notifier.createCustomIcon('assets/images/ic_feed_location_icon.png'); final toIcon = - await _createCustomIcon('assets/images/ic_distance_icon.png'); + await notifier.createCustomIcon('assets/images/ic_distance_icon.png'); final List markers = [ Marker( @@ -484,22 +457,6 @@ class _JourneyTimelineScreenState extends ConsumerState { return markers; } - Future _createCustomIcon(String assetPath) async { - final data = await rootBundle.load(assetPath); - final codec = await ui.instantiateImageCodec( - data.buffer.asUint8List(), - targetWidth: 38, - targetHeight: 54, - ); - final frameInfo = await codec.getNextFrame(); - - final byteData = - await frameInfo.image.toByteData(format: ui.ImageByteFormat.png); - final resizedBytes = byteData!.buffer.asUint8List(); - - return BitmapDescriptor.fromBytes(resizedBytes); - } - String _onSelectDatePickerDate(int? timestamp) { if (timestamp == null) return ''; final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); diff --git a/app/lib/ui/flow/journey/timeline/journey_timeline_view_model.dart b/app/lib/ui/flow/journey/timeline/journey_timeline_view_model.dart index 6361148e..20b8cf65 100644 --- a/app/lib/ui/flow/journey/timeline/journey_timeline_view_model.dart +++ b/app/lib/ui/flow/journey/timeline/journey_timeline_view_model.dart @@ -1,10 +1,15 @@ +import 'dart:ui' as ui; import 'package:data/api/auth/auth_models.dart'; import 'package:data/api/location/journey/api_journey_service.dart'; import 'package:data/api/location/journey/journey.dart'; import 'package:data/log/logger.dart'; import 'package:data/storage/app_preferences.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:yourspace_flutter/domain/extenstions/lat_lng_extenstion.dart'; part 'journey_timeline_view_model.freezed.dart'; @@ -35,6 +40,7 @@ class JourneyTimelineViewModel extends StateNotifier { spaceId: _currentSpaceId.state, ); _loadJourney(); + journeyService.uploadLogFileToFirebase(); } void _loadJourney({bool loadMore = false}) async { @@ -105,6 +111,76 @@ class JourneyTimelineViewModel extends StateNotifier { ); _loadJourney(); } + + String getSteadyDuration(int createdAt, int updatedAt) { + Duration duration = DateTime.fromMillisecondsSinceEpoch(updatedAt) + .difference(DateTime.fromMillisecondsSinceEpoch(createdAt)); + + if (duration.inHours > 0) { + return '${duration.inHours}h ${duration.inMinutes.remainder(60)}min'; + } else { + return '${duration.inMinutes} min'; + } + } + + String getDistanceString(ApiLocationJourney location) { + final steadyLocation = location.toPositionFromSteadyJourney(); + final movingLocation = location.toPositionFromMovingJourney(); + + final routeDistance = steadyLocation.distanceTo(movingLocation); + + if (routeDistance < 1000) { + return '${routeDistance.round()} m'; + } else { + final distanceInKm = routeDistance / 1000; + return '${distanceInKm.round()} km'; + } + } + + Future getAddress(LatLng latLng) async { + await Future.delayed(const Duration(seconds: 2)); + final address = await latLng.getAddressFromLocation(); + return address; + } + + String formattedAddress(Placemark fromPlace, Placemark? toPlace) { + final fromCity = fromPlace.locality ?? ''; + final toCity = toPlace?.locality ?? ''; + + final fromArea = fromPlace.subLocality ?? ''; + final toArea = toPlace?.subLocality ?? ''; + + final fromState = fromPlace.administrativeArea ?? ''; + final toState = toPlace?.administrativeArea ?? ''; + + if (toPlace == null) { + return "$fromArea, $fromCity"; + } else if (fromArea == toArea) { + return "$fromArea, $fromCity"; + } else if (fromCity == toCity) { + return "$fromArea -> $toArea, $fromCity"; + } else if (fromState == toState) { + return "$fromArea, $fromCity -> $toArea, $toCity"; + } else { + return "$fromCity, $fromState -> $toCity, $toState"; + } + } + + Future createCustomIcon(String assetPath) async { + final data = await rootBundle.load(assetPath); + final codec = await ui.instantiateImageCodec( + data.buffer.asUint8List(), + targetWidth: 38, + targetHeight: 54, + ); + final frameInfo = await codec.getNextFrame(); + + final byteData = + await frameInfo.image.toByteData(format: ui.ImageByteFormat.png); + final resizedBytes = byteData!.buffer.asUint8List(); + + return BitmapDescriptor.fromBytes(resizedBytes); + } } @freezed diff --git a/app/pubspec.lock b/app/pubspec.lock index 680b6693..b88e9945 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -1168,18 +1168,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -1224,18 +1224,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: @@ -1700,10 +1700,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" time: dependency: transitive description: @@ -1860,10 +1860,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" watcher: dependency: transitive description: diff --git a/data/.flutter-plugins b/data/.flutter-plugins index 7849ff3d..98e90265 100644 --- a/data/.flutter-plugins +++ b/data/.flutter-plugins @@ -1,47 +1,51 @@ # This is a generated file; do not edit or check into version control. -cloud_firestore=/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/ -cloud_firestore_web=/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore_web-3.12.5/ -cloud_functions=/home/kaushik/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/ -cloud_functions_web=/home/kaushik/.pub-cache/hosted/pub.dev/cloud_functions_web-4.9.6/ -connectivity_plus=/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/ -device_info_plus=/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/ -firebase_auth=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/ -firebase_auth_web=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth_web-5.12.0/ -firebase_core=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/ -firebase_core_web=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core_web-2.17.1/ -firebase_messaging=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/ -firebase_messaging_web=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.8.7/ -firebase_storage=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/ -firebase_storage_web=/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage_web-3.9.7/ -flutter_background_service=/home/kaushik/.pub-cache/hosted/pub.dev/flutter_background_service-5.0.6/ -flutter_background_service_android=/home/kaushik/.pub-cache/hosted/pub.dev/flutter_background_service_android-6.2.3/ -flutter_background_service_ios=/home/kaushik/.pub-cache/hosted/pub.dev/flutter_background_service_ios-5.0.1/ -flutter_plugin_android_lifecycle=/home/kaushik/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.21/ -flutter_timezone=/home/kaushik/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/ -geolocator=/home/kaushik/.pub-cache/hosted/pub.dev/geolocator-12.0.0/ -geolocator_android=/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_android-4.6.0/ -geolocator_apple=/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.7/ -geolocator_web=/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_web-4.0.0/ -geolocator_windows=/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.3/ -google_maps_flutter=/home/kaushik/.pub-cache/hosted/pub.dev/google_maps_flutter-2.2.8/ -google_maps_flutter_android=/home/kaushik/.pub-cache/hosted/pub.dev/google_maps_flutter_android-2.12.1/ -google_maps_flutter_ios=/home/kaushik/.pub-cache/hosted/pub.dev/google_maps_flutter_ios-2.10.0/ -google_sign_in=/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in-6.2.1/ -google_sign_in_android=/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.24/ -google_sign_in_ios=/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.6/ -google_sign_in_web=/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4/ -package_info_plus=/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/ -path_provider_linux=/home/kaushik/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ -path_provider_windows=/home/kaushik/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/ -permission_handler=/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler-11.3.1/ -permission_handler_android=/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.7/ -permission_handler_apple=/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/ -permission_handler_html=/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.1/ -permission_handler_windows=/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/ -shared_preferences=/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences-2.2.3/ -shared_preferences_android=/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.3/ -shared_preferences_foundation=/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/ -shared_preferences_linux=/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/ -shared_preferences_web=/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/ -shared_preferences_windows=/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/ -sqflite=/home/kaushik/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/ +cloud_firestore=/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/ +cloud_firestore_web=/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore_web-3.12.5/ +cloud_functions=/Users/ishita/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/ +cloud_functions_web=/Users/ishita/.pub-cache/hosted/pub.dev/cloud_functions_web-4.9.6/ +connectivity_plus=/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/ +device_info_plus=/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/ +firebase_auth=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/ +firebase_auth_web=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth_web-5.12.0/ +firebase_core=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/ +firebase_core_web=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core_web-2.17.5/ +firebase_messaging=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/ +firebase_messaging_web=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.8.7/ +firebase_storage=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/ +firebase_storage_web=/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage_web-3.9.7/ +flutter_background_service=/Users/ishita/.pub-cache/hosted/pub.dev/flutter_background_service-5.0.6/ +flutter_background_service_android=/Users/ishita/.pub-cache/hosted/pub.dev/flutter_background_service_android-6.2.3/ +flutter_background_service_ios=/Users/ishita/.pub-cache/hosted/pub.dev/flutter_background_service_ios-5.0.1/ +flutter_plugin_android_lifecycle=/Users/ishita/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.21/ +flutter_timezone=/Users/ishita/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/ +geolocator=/Users/ishita/.pub-cache/hosted/pub.dev/geolocator-12.0.0/ +geolocator_android=/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_android-4.6.0/ +geolocator_apple=/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.7/ +geolocator_web=/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_web-4.0.0/ +geolocator_windows=/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.3/ +google_maps_flutter=/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter-2.7.0/ +google_maps_flutter_android=/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter_android-2.12.1/ +google_maps_flutter_ios=/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter_ios-2.10.0/ +google_maps_flutter_web=/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter_web-0.5.8/ +google_sign_in=/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in-6.2.1/ +google_sign_in_android=/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.23/ +google_sign_in_ios=/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.6/ +google_sign_in_web=/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.3+3/ +package_info_plus=/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/ +path_provider=/Users/ishita/.pub-cache/hosted/pub.dev/path_provider-2.1.4/ +path_provider_android=/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_android-2.2.10/ +path_provider_foundation=/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/ +path_provider_linux=/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ +path_provider_windows=/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/ +permission_handler=/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler-11.3.1/ +permission_handler_android=/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.7/ +permission_handler_apple=/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/ +permission_handler_html=/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.1/ +permission_handler_windows=/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/ +shared_preferences=/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences-2.2.3/ +shared_preferences_android=/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.2/ +shared_preferences_foundation=/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/ +shared_preferences_linux=/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/ +shared_preferences_web=/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/ +shared_preferences_windows=/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/ +sqflite=/Users/ishita/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/ diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies index c7d36c22..c7a07288 100644 --- a/data/.flutter-plugins-dependencies +++ b/data/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_background_service_ios","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_background_service_ios-5.0.1/","native_build":true,"dependencies":[]},{"name":"flutter_timezone","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","native_build":true,"dependencies":[]},{"name":"geolocator_apple","path":"/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.7/","native_build":true,"dependencies":[]},{"name":"google_maps_flutter_ios","path":"/home/kaushik/.pub-cache/hosted/pub.dev/google_maps_flutter_ios-2.10.0/","native_build":true,"dependencies":[]},{"name":"google_sign_in_ios","path":"/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/home/kaushik/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_background_service_android","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_background_service_android-6.2.3/","native_build":true,"dependencies":[]},{"name":"flutter_plugin_android_lifecycle","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.21/","native_build":true,"dependencies":[]},{"name":"flutter_timezone","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","native_build":true,"dependencies":[]},{"name":"geolocator_android","path":"/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_android-4.6.0/","native_build":true,"dependencies":[]},{"name":"google_maps_flutter_android","path":"/home/kaushik/.pub-cache/hosted/pub.dev/google_maps_flutter_android-2.12.1/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"google_sign_in_android","path":"/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.24/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.7/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.3/","native_build":true,"dependencies":[]},{"name":"sqflite","path":"/home/kaushik/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","native_build":true,"dependencies":[]},{"name":"geolocator_apple","path":"/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.7/","native_build":true,"dependencies":[]},{"name":"google_sign_in_ios","path":"/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/home/kaushik/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","native_build":false,"dependencies":[]},{"name":"device_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kaushik/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"geolocator_windows","path":"/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.3/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kaushik/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]},{"name":"permission_handler_windows","path":"/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[]},{"name":"shared_preferences_windows","path":"/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_firestore_web-3.12.5/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/cloud_functions_web-4.9.6/","dependencies":["firebase_core_web"]},{"name":"connectivity_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","dependencies":[]},{"name":"device_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_auth_web-5.12.0/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_core_web-2.17.1/","dependencies":[]},{"name":"firebase_messaging_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.8.7/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/firebase_storage_web-3.9.7/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/home/kaushik/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","dependencies":[]},{"name":"geolocator_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/geolocator_web-4.0.0/","dependencies":[]},{"name":"google_sign_in_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4/","dependencies":[]},{"name":"package_info_plus","path":"/home/kaushik/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","dependencies":[]},{"name":"permission_handler_html","path":"/home/kaushik/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/home/kaushik/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"connectivity_plus","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_background_service","dependencies":["flutter_background_service_android","flutter_background_service_ios"]},{"name":"flutter_background_service_android","dependencies":[]},{"name":"flutter_background_service_ios","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_timezone","dependencies":[]},{"name":"geolocator","dependencies":["geolocator_android","geolocator_apple","geolocator_web","geolocator_windows"]},{"name":"geolocator_android","dependencies":[]},{"name":"geolocator_apple","dependencies":[]},{"name":"geolocator_web","dependencies":[]},{"name":"geolocator_windows","dependencies":[]},{"name":"google_maps_flutter","dependencies":["google_maps_flutter_android","google_maps_flutter_ios"]},{"name":"google_maps_flutter_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"google_maps_flutter_ios","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sqflite","dependencies":[]}],"date_created":"2024-09-19 15:08:48.059948","version":"3.22.2"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_background_service_ios","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_background_service_ios-5.0.1/","native_build":true,"dependencies":[]},{"name":"flutter_timezone","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","native_build":true,"dependencies":[]},{"name":"geolocator_apple","path":"/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.7/","native_build":true,"dependencies":[]},{"name":"google_maps_flutter_ios","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter_ios-2.10.0/","native_build":true,"dependencies":[]},{"name":"google_sign_in_ios","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/ishita/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_background_service_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_background_service_android-6.2.3/","native_build":true,"dependencies":[]},{"name":"flutter_plugin_android_lifecycle","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.21/","native_build":true,"dependencies":[]},{"name":"flutter_timezone","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","native_build":true,"dependencies":[]},{"name":"geolocator_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_android-4.6.0/","native_build":true,"dependencies":[]},{"name":"google_maps_flutter_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter_android-2.12.1/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"google_sign_in_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.23/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_android-2.2.10/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.7/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.2/","native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/ishita/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_functions-4.7.6/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_messaging-14.9.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","native_build":true,"dependencies":[]},{"name":"geolocator_apple","path":"/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.7/","native_build":true,"dependencies":[]},{"name":"google_sign_in_ios","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/ishita/.pub-cache/hosted/pub.dev/sqflite-2.3.3+1/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","native_build":false,"dependencies":[]},{"name":"device_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore-4.17.5/","native_build":true,"dependencies":["firebase_core"]},{"name":"connectivity_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","native_build":true,"dependencies":[]},{"name":"device_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth-4.20.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core-2.32.0/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage-11.7.7/","native_build":true,"dependencies":["firebase_core"]},{"name":"geolocator_windows","path":"/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.3/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/ishita/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]},{"name":"permission_handler_windows","path":"/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_firestore_web-3.12.5/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/cloud_functions_web-4.9.6/","dependencies":["firebase_core_web"]},{"name":"connectivity_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/connectivity_plus-6.0.5/","dependencies":[]},{"name":"device_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_auth_web-5.12.0/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_core_web-2.17.5/","dependencies":[]},{"name":"firebase_messaging_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.8.7/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/firebase_storage_web-3.9.7/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/Users/ishita/.pub-cache/hosted/pub.dev/flutter_timezone-1.0.8/","dependencies":[]},{"name":"geolocator_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/geolocator_web-4.0.0/","dependencies":[]},{"name":"google_maps_flutter_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_maps_flutter_web-0.5.8/","dependencies":[]},{"name":"google_sign_in_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.3+3/","dependencies":[]},{"name":"package_info_plus","path":"/Users/ishita/.pub-cache/hosted/pub.dev/package_info_plus-8.0.0/","dependencies":[]},{"name":"permission_handler_html","path":"/Users/ishita/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/ishita/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"connectivity_plus","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_background_service","dependencies":["flutter_background_service_android","flutter_background_service_ios"]},{"name":"flutter_background_service_android","dependencies":[]},{"name":"flutter_background_service_ios","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_timezone","dependencies":[]},{"name":"geolocator","dependencies":["geolocator_android","geolocator_apple","geolocator_web","geolocator_windows"]},{"name":"geolocator_android","dependencies":[]},{"name":"geolocator_apple","dependencies":[]},{"name":"geolocator_web","dependencies":[]},{"name":"geolocator_windows","dependencies":[]},{"name":"google_maps_flutter","dependencies":["google_maps_flutter_android","google_maps_flutter_ios","google_maps_flutter_web"]},{"name":"google_maps_flutter_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"google_maps_flutter_ios","dependencies":[]},{"name":"google_maps_flutter_web","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sqflite","dependencies":[]}],"date_created":"2024-09-26 09:43:26.235466","version":"3.24.1","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/data/lib/api/auth/api_user_service.dart b/data/lib/api/auth/api_user_service.dart index dddbac55..358ad91d 100644 --- a/data/lib/api/auth/api_user_service.dart +++ b/data/lib/api/auth/api_user_service.dart @@ -1,6 +1,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:data/api/network/client.dart'; import 'package:data/service/device_service.dart'; +import 'package:data/storage/location_caches.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -42,6 +43,8 @@ class ApiUserService { this.locationManager, ); + final LocationCache _locationCache = LocationCache(); + CollectionReference get _userRef => _db.collection("users").withConverter( fromFirestore: ApiUser.fromFireStore, @@ -227,5 +230,6 @@ class ApiUserService { userSessionJsonNotifier.state = null; onBoardNotifier.state = false; currentUserSpaceId.state = null; + _locationCache.clear(); } } diff --git a/data/lib/api/location/journey/api_journey_service.dart b/data/lib/api/location/journey/api_journey_service.dart index 1f736bec..282bc2a8 100644 --- a/data/lib/api/location/journey/api_journey_service.dart +++ b/data/lib/api/location/journey/api_journey_service.dart @@ -1,10 +1,12 @@ +import 'dart:io'; + import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:data/utils/location_converters.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:path_provider/path_provider.dart'; import '../../../log/logger.dart'; -import '../../../storage/database/location_table_dao.dart'; import '../../network/client.dart'; import 'journey.dart'; @@ -15,8 +17,6 @@ final journeyServiceProvider = Provider((ref) => ApiJourneyService( class ApiJourneyService { late FirebaseFirestore _db; - final LocationTableDao locationTableDao = LocationTableDao(); - ApiJourneyService(FirebaseFirestore fireStore) { _db = fireStore; } @@ -26,29 +26,14 @@ class ApiJourneyService { CollectionReference _journeyRef(String userId) => _userRef.doc(userId).collection("user_journeys"); - Future getLastJourneyLocation(String userId) async { - final querySnapshot = await _journeyRef(userId) - .where('user_id', isEqualTo: userId) - .orderBy('created_at', descending: true) - .limit(1) - .get(); - - final doc = querySnapshot.docs.firstOrNull; - if (doc != null) { - return ApiLocationJourney.fromJson(doc.data() as Map); - } - return null; - } - - Future saveCurrentJourney({ + Future saveCurrentJourney({ required String userId, required double fromLatitude, required double fromLongitude, double? toLatitude, double? toLongitude, LatLng? toLatLng, - double? routeDistance, - int? routeDuration, + List? routes, int? created_at, int? updated_at, }) async { @@ -66,14 +51,12 @@ class ApiJourneyService { from_longitude: fromLatLng.longitude, to_latitude: toLatLng?.latitude, to_longitude: toLatLng?.longitude, - route_distance: routeDistance, - route_duration: routeDuration, - routes: [], + routes: routes ?? [], created_at: created_at ?? DateTime.now().millisecondsSinceEpoch, - update_at: updated_at ?? created_at ?? DateTime.now().millisecondsSinceEpoch, + update_at: updated_at ?? DateTime.now().millisecondsSinceEpoch, ); await docRef.set(journey.toJson()); - await _updateLocationJourneyInDatabase(userId, journey); + return docRef.id; } Future updateLastLocationJourney( @@ -81,7 +64,6 @@ class ApiJourneyService { ApiLocationJourney journey, ) async { try { - await _updateLocationJourneyInDatabase(userId, journey); await _journeyRef(userId).doc(journey.id).set(journey.toJson()); } catch (error) { logger.e( @@ -91,24 +73,18 @@ class ApiJourneyService { } } - Future _updateLocationJourneyInDatabase( - String userId, - ApiLocationJourney journey, - ) async { - try { - final locationData = await locationTableDao.getLocationData(userId); - if (locationData != null) { - final updatedLocationData = locationData.copyWith( - lastLocationJourney: LocationConverters.journeyToString(journey), - ); - await locationTableDao.updateLocationTable(updatedLocationData); - } - } catch (error) { - logger.e( - 'ApiJourneyService: Error while updating journey in database', - error: error, - ); + Future getLastJourneyLocation(String userId) async { + final querySnapshot = await _journeyRef(userId) + .where('user_id', isEqualTo: userId) + .orderBy('created_at', descending: true) + .limit(1) + .get(); + + final doc = querySnapshot.docs.firstOrNull; + if (doc != null) { + return ApiLocationJourney.fromJson(doc.data() as Map); } + return null; } Future> getJourneyHistory( @@ -194,4 +170,17 @@ class ApiJourneyService { } return null; } + + Future uploadLogFileToFirebase() async { + try { + final directory = await getApplicationDocumentsDirectory(); + final logFile = File('${directory.path}/app.log'); + final storage = FirebaseStorage.instance; + final fileName = 'LOG_${DateTime.now().millisecondsSinceEpoch}.log'; + final logFileRef = storage.ref().child('logs/$fileName'); + await logFileRef.putFile(logFile); + } catch (e) { + logger.e('Error uploading log file: $e'); + } + } } diff --git a/data/lib/api/location/journey/journey.dart b/data/lib/api/location/journey/journey.dart index 5db86e44..d86e54b2 100644 --- a/data/lib/api/location/journey/journey.dart +++ b/data/lib/api/location/journey/journey.dart @@ -1,4 +1,5 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:data/api/location/location.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'journey.freezed.dart'; @@ -15,8 +16,6 @@ class ApiLocationJourney with _$ApiLocationJourney { required double from_longitude, double? to_latitude, double? to_longitude, - double? route_distance, - int? route_duration, @Default([]) List routes, int? created_at, int? update_at, @@ -35,6 +34,31 @@ class ApiLocationJourney with _$ApiLocationJourney { bool isSteadyLocation() { return to_latitude == null && to_longitude == null; } + + LocationData toPositionFromSteadyJourney() { + return LocationData(latitude: from_latitude, longitude: from_longitude, timestamp: DateTime.now()); + } + + LocationData toPositionFromMovingJourney() { + return LocationData(latitude: to_latitude ?? 0, longitude: to_longitude ?? 0, timestamp: DateTime.now()); + } + + JourneyRoute toRouteFromSteadyJourney() { + return JourneyRoute(latitude: from_latitude, longitude: from_longitude); + } + + static ApiLocationJourney fromPosition(LocationData pos, String userId, String newJourneyId) { + return ApiLocationJourney( + id: newJourneyId, + user_id: userId, + from_latitude: pos.latitude, + from_longitude: pos.longitude, + to_latitude: pos.latitude, + to_longitude: pos.longitude, + created_at: pos.timestamp.millisecondsSinceEpoch, + update_at: pos.timestamp.millisecondsSinceEpoch, + ); + } } @freezed diff --git a/data/lib/api/location/journey/journey.freezed.dart b/data/lib/api/location/journey/journey.freezed.dart index 647a0342..a4c39d80 100644 --- a/data/lib/api/location/journey/journey.freezed.dart +++ b/data/lib/api/location/journey/journey.freezed.dart @@ -26,8 +26,6 @@ mixin _$ApiLocationJourney { double get from_longitude => throw _privateConstructorUsedError; double? get to_latitude => throw _privateConstructorUsedError; double? get to_longitude => throw _privateConstructorUsedError; - double? get route_distance => throw _privateConstructorUsedError; - int? get route_duration => throw _privateConstructorUsedError; List get routes => throw _privateConstructorUsedError; int? get created_at => throw _privateConstructorUsedError; int? get update_at => throw _privateConstructorUsedError; @@ -51,8 +49,6 @@ abstract class $ApiLocationJourneyCopyWith<$Res> { double from_longitude, double? to_latitude, double? to_longitude, - double? route_distance, - int? route_duration, List routes, int? created_at, int? update_at}); @@ -77,8 +73,6 @@ class _$ApiLocationJourneyCopyWithImpl<$Res, $Val extends ApiLocationJourney> Object? from_longitude = null, Object? to_latitude = freezed, Object? to_longitude = freezed, - Object? route_distance = freezed, - Object? route_duration = freezed, Object? routes = null, Object? created_at = freezed, Object? update_at = freezed, @@ -108,14 +102,6 @@ class _$ApiLocationJourneyCopyWithImpl<$Res, $Val extends ApiLocationJourney> ? _value.to_longitude : to_longitude // ignore: cast_nullable_to_non_nullable as double?, - route_distance: freezed == route_distance - ? _value.route_distance - : route_distance // ignore: cast_nullable_to_non_nullable - as double?, - route_duration: freezed == route_duration - ? _value.route_duration - : route_duration // ignore: cast_nullable_to_non_nullable - as int?, routes: null == routes ? _value.routes : routes // ignore: cast_nullable_to_non_nullable @@ -147,8 +133,6 @@ abstract class _$$LocationJourneyImplCopyWith<$Res> double from_longitude, double? to_latitude, double? to_longitude, - double? route_distance, - int? route_duration, List routes, int? created_at, int? update_at}); @@ -171,8 +155,6 @@ class __$$LocationJourneyImplCopyWithImpl<$Res> Object? from_longitude = null, Object? to_latitude = freezed, Object? to_longitude = freezed, - Object? route_distance = freezed, - Object? route_duration = freezed, Object? routes = null, Object? created_at = freezed, Object? update_at = freezed, @@ -202,14 +184,6 @@ class __$$LocationJourneyImplCopyWithImpl<$Res> ? _value.to_longitude : to_longitude // ignore: cast_nullable_to_non_nullable as double?, - route_distance: freezed == route_distance - ? _value.route_distance - : route_distance // ignore: cast_nullable_to_non_nullable - as double?, - route_duration: freezed == route_duration - ? _value.route_duration - : route_duration // ignore: cast_nullable_to_non_nullable - as int?, routes: null == routes ? _value._routes : routes // ignore: cast_nullable_to_non_nullable @@ -236,8 +210,6 @@ class _$LocationJourneyImpl extends _LocationJourney { required this.from_longitude, this.to_latitude, this.to_longitude, - this.route_distance, - this.route_duration, final List routes = const [], this.created_at, this.update_at}) @@ -259,10 +231,6 @@ class _$LocationJourneyImpl extends _LocationJourney { final double? to_latitude; @override final double? to_longitude; - @override - final double? route_distance; - @override - final int? route_duration; final List _routes; @override @JsonKey() @@ -279,7 +247,7 @@ class _$LocationJourneyImpl extends _LocationJourney { @override String toString() { - return 'ApiLocationJourney(id: $id, user_id: $user_id, from_latitude: $from_latitude, from_longitude: $from_longitude, to_latitude: $to_latitude, to_longitude: $to_longitude, route_distance: $route_distance, route_duration: $route_duration, routes: $routes, created_at: $created_at, update_at: $update_at)'; + return 'ApiLocationJourney(id: $id, user_id: $user_id, from_latitude: $from_latitude, from_longitude: $from_longitude, to_latitude: $to_latitude, to_longitude: $to_longitude, routes: $routes, created_at: $created_at, update_at: $update_at)'; } @override @@ -297,10 +265,6 @@ class _$LocationJourneyImpl extends _LocationJourney { other.to_latitude == to_latitude) && (identical(other.to_longitude, to_longitude) || other.to_longitude == to_longitude) && - (identical(other.route_distance, route_distance) || - other.route_distance == route_distance) && - (identical(other.route_duration, route_duration) || - other.route_duration == route_duration) && const DeepCollectionEquality().equals(other._routes, _routes) && (identical(other.created_at, created_at) || other.created_at == created_at) && @@ -318,8 +282,6 @@ class _$LocationJourneyImpl extends _LocationJourney { from_longitude, to_latitude, to_longitude, - route_distance, - route_duration, const DeepCollectionEquality().hash(_routes), created_at, update_at); @@ -347,8 +309,6 @@ abstract class _LocationJourney extends ApiLocationJourney { required final double from_longitude, final double? to_latitude, final double? to_longitude, - final double? route_distance, - final int? route_duration, final List routes, final int? created_at, final int? update_at}) = _$LocationJourneyImpl; @@ -370,10 +330,6 @@ abstract class _LocationJourney extends ApiLocationJourney { @override double? get to_longitude; @override - double? get route_distance; - @override - int? get route_duration; - @override List get routes; @override int? get created_at; diff --git a/data/lib/api/location/journey/journey.g.dart b/data/lib/api/location/journey/journey.g.dart index ea16c303..d55dab58 100644 --- a/data/lib/api/location/journey/journey.g.dart +++ b/data/lib/api/location/journey/journey.g.dart @@ -15,8 +15,6 @@ _$LocationJourneyImpl _$$LocationJourneyImplFromJson( from_longitude: (json['from_longitude'] as num).toDouble(), to_latitude: (json['to_latitude'] as num?)?.toDouble(), to_longitude: (json['to_longitude'] as num?)?.toDouble(), - route_distance: (json['route_distance'] as num?)?.toDouble(), - route_duration: (json['route_duration'] as num?)?.toInt(), routes: (json['routes'] as List?) ?.map((e) => JourneyRoute.fromJson(e as Map)) .toList() ?? @@ -34,8 +32,6 @@ Map _$$LocationJourneyImplToJson( 'from_longitude': instance.from_longitude, 'to_latitude': instance.to_latitude, 'to_longitude': instance.to_longitude, - 'route_distance': instance.route_distance, - 'route_duration': instance.route_duration, 'routes': instance.routes.map((e) => e.toJson()).toList(), 'created_at': instance.created_at, 'update_at': instance.update_at, diff --git a/data/lib/api/location/location.dart b/data/lib/api/location/location.dart index c3234b04..31fdc114 100644 --- a/data/lib/api/location/location.dart +++ b/data/lib/api/location/location.dart @@ -1,7 +1,9 @@ //ignore_for_file: constant_identifier_names import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:data/api/location/journey/journey.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:geolocator/geolocator.dart'; part 'location.freezed.dart'; part 'location.g.dart'; @@ -39,10 +41,20 @@ class LocationData { final double latitude; final double longitude; final DateTime timestamp; + final JourneyRoute? routes; LocationData({ required this.latitude, required this.longitude, required this.timestamp, + this.routes, }); + + JourneyRoute toRoute() { + return JourneyRoute(latitude: latitude, longitude: longitude); + } + + double distanceTo(LocationData other) { + return Geolocator.distanceBetween(latitude, longitude, other.latitude, other.longitude); + } } \ No newline at end of file diff --git a/data/lib/api/location/location_table.dart b/data/lib/api/location/location_table.dart deleted file mode 100644 index 5cc683d1..00000000 --- a/data/lib/api/location/location_table.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'location_table.freezed.dart'; -part 'location_table.g.dart'; - -@freezed -class LocationTable with _$LocationTable { - const LocationTable._(); - const factory LocationTable({ - required String userId, - String? lastFiveMinutesLocations, - String? lastLocationJourney, - }) = _LocationTable; - - factory LocationTable.fromJson(Map json) => - _$LocationTableFromJson(json); - - Map toMap() { - return { - 'userId': userId, - 'lastFiveMinutesLocations': lastFiveMinutesLocations, - 'lastLocationJourney': lastLocationJourney, - }; - } - - factory LocationTable.fromMap(Map map) { - return LocationTable( - userId: map['userId'], - lastFiveMinutesLocations: map['lastFiveMinutesLocations'], - lastLocationJourney: map['lastLocationJourney'], - ); - } -} diff --git a/data/lib/api/location/location_table.freezed.dart b/data/lib/api/location/location_table.freezed.dart deleted file mode 100644 index 2a821e23..00000000 --- a/data/lib/api/location/location_table.freezed.dart +++ /dev/null @@ -1,201 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'location_table.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -LocationTable _$LocationTableFromJson(Map json) { - return _LocationTable.fromJson(json); -} - -/// @nodoc -mixin _$LocationTable { - String get userId => throw _privateConstructorUsedError; - String? get lastFiveMinutesLocations => throw _privateConstructorUsedError; - String? get lastLocationJourney => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $LocationTableCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $LocationTableCopyWith<$Res> { - factory $LocationTableCopyWith( - LocationTable value, $Res Function(LocationTable) then) = - _$LocationTableCopyWithImpl<$Res, LocationTable>; - @useResult - $Res call( - {String userId, - String? lastFiveMinutesLocations, - String? lastLocationJourney}); -} - -/// @nodoc -class _$LocationTableCopyWithImpl<$Res, $Val extends LocationTable> - implements $LocationTableCopyWith<$Res> { - _$LocationTableCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? userId = null, - Object? lastFiveMinutesLocations = freezed, - Object? lastLocationJourney = freezed, - }) { - return _then(_value.copyWith( - userId: null == userId - ? _value.userId - : userId // ignore: cast_nullable_to_non_nullable - as String, - lastFiveMinutesLocations: freezed == lastFiveMinutesLocations - ? _value.lastFiveMinutesLocations - : lastFiveMinutesLocations // ignore: cast_nullable_to_non_nullable - as String?, - lastLocationJourney: freezed == lastLocationJourney - ? _value.lastLocationJourney - : lastLocationJourney // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$LocationTableImplCopyWith<$Res> - implements $LocationTableCopyWith<$Res> { - factory _$$LocationTableImplCopyWith( - _$LocationTableImpl value, $Res Function(_$LocationTableImpl) then) = - __$$LocationTableImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String userId, - String? lastFiveMinutesLocations, - String? lastLocationJourney}); -} - -/// @nodoc -class __$$LocationTableImplCopyWithImpl<$Res> - extends _$LocationTableCopyWithImpl<$Res, _$LocationTableImpl> - implements _$$LocationTableImplCopyWith<$Res> { - __$$LocationTableImplCopyWithImpl( - _$LocationTableImpl _value, $Res Function(_$LocationTableImpl) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? userId = null, - Object? lastFiveMinutesLocations = freezed, - Object? lastLocationJourney = freezed, - }) { - return _then(_$LocationTableImpl( - userId: null == userId - ? _value.userId - : userId // ignore: cast_nullable_to_non_nullable - as String, - lastFiveMinutesLocations: freezed == lastFiveMinutesLocations - ? _value.lastFiveMinutesLocations - : lastFiveMinutesLocations // ignore: cast_nullable_to_non_nullable - as String?, - lastLocationJourney: freezed == lastLocationJourney - ? _value.lastLocationJourney - : lastLocationJourney // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$LocationTableImpl extends _LocationTable { - const _$LocationTableImpl( - {required this.userId, - this.lastFiveMinutesLocations, - this.lastLocationJourney}) - : super._(); - - factory _$LocationTableImpl.fromJson(Map json) => - _$$LocationTableImplFromJson(json); - - @override - final String userId; - @override - final String? lastFiveMinutesLocations; - @override - final String? lastLocationJourney; - - @override - String toString() { - return 'LocationTable(userId: $userId, lastFiveMinutesLocations: $lastFiveMinutesLocations, lastLocationJourney: $lastLocationJourney)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$LocationTableImpl && - (identical(other.userId, userId) || other.userId == userId) && - (identical( - other.lastFiveMinutesLocations, lastFiveMinutesLocations) || - other.lastFiveMinutesLocations == lastFiveMinutesLocations) && - (identical(other.lastLocationJourney, lastLocationJourney) || - other.lastLocationJourney == lastLocationJourney)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, userId, lastFiveMinutesLocations, lastLocationJourney); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$LocationTableImplCopyWith<_$LocationTableImpl> get copyWith => - __$$LocationTableImplCopyWithImpl<_$LocationTableImpl>(this, _$identity); - - @override - Map toJson() { - return _$$LocationTableImplToJson( - this, - ); - } -} - -abstract class _LocationTable extends LocationTable { - const factory _LocationTable( - {required final String userId, - final String? lastFiveMinutesLocations, - final String? lastLocationJourney}) = _$LocationTableImpl; - const _LocationTable._() : super._(); - - factory _LocationTable.fromJson(Map json) = - _$LocationTableImpl.fromJson; - - @override - String get userId; - @override - String? get lastFiveMinutesLocations; - @override - String? get lastLocationJourney; - @override - @JsonKey(ignore: true) - _$$LocationTableImplCopyWith<_$LocationTableImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/data/lib/api/location/location_table.g.dart b/data/lib/api/location/location_table.g.dart deleted file mode 100644 index ffad871b..00000000 --- a/data/lib/api/location/location_table.g.dart +++ /dev/null @@ -1,21 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'location_table.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$LocationTableImpl _$$LocationTableImplFromJson(Map json) => - _$LocationTableImpl( - userId: json['userId'] as String, - lastFiveMinutesLocations: json['lastFiveMinutesLocations'] as String?, - lastLocationJourney: json['lastLocationJourney'] as String?, - ); - -Map _$$LocationTableImplToJson(_$LocationTableImpl instance) => - { - 'userId': instance.userId, - 'lastFiveMinutesLocations': instance.lastFiveMinutesLocations, - 'lastLocationJourney': instance.lastLocationJourney, - }; diff --git a/data/lib/log/logger.dart b/data/lib/log/logger.dart index 8dbe65c1..82026038 100644 --- a/data/lib/log/logger.dart +++ b/data/lib/log/logger.dart @@ -1,9 +1,10 @@ +import 'dart:io'; + import 'package:logger/logger.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path_provider/path_provider.dart'; import 'log_format.dart'; - - final logger = setupLogger(); const logFileName = 'app.log'; @@ -20,10 +21,22 @@ Logger setupLogger() { final logger = Logger( filter: null, printer: AppLogPrinter(), - output: null + output: AppFileOutput(), ); logStart(); return logger; } + +class AppFileOutput extends LogOutput { + @override + void output(OutputEvent event) async { + final directory = await getApplicationDocumentsDirectory(); + final logFile = File('${directory.path}/$logFileName'); + + for (var line in event.lines) { + await logFile.writeAsString('$line\n', mode: FileMode.append); + } + } +} diff --git a/data/lib/repository/journey_repository.dart b/data/lib/repository/journey_repository.dart index 13e6ab28..a475aedc 100644 --- a/data/lib/repository/journey_repository.dart +++ b/data/lib/repository/journey_repository.dart @@ -1,347 +1,219 @@ -//ignore_for_file: constant_identifier_names +// ignore_for_file: constant_identifier_names -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:data/api/location/location_table.dart'; -import 'package:data/storage/database/location_table_dao.dart'; +import 'package:data/api/location/journey/journey.dart'; +import 'package:data/api/location/location.dart'; +import 'package:data/log/logger.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geolocator/geolocator.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:uuid/uuid.dart'; import '../api/location/journey/api_journey_service.dart'; -import '../api/location/journey/journey.dart'; -import '../api/location/location.dart'; -import '../log/logger.dart'; -import '../service/location_service.dart'; -import '../utils/location_converters.dart'; +import '../storage/location_caches.dart'; -const MIN_TIME_DIFFERENCE = 5 * 60 * 1000; -const MIN_DISTANCE = 100.0; +const MIN_DISTANCE = 150.0; // 150 meters +const MIN_TIME_DIFFERENCE = 5 * 60 * 1000; // 5 minutes -class JourneyRepository { - late LocationService _locationService; - late ApiJourneyService _journeyService; +final journeyRepositoryProvider = Provider((ref) => JourneyRepository( + ref.read(journeyServiceProvider), +)); - final LocationTableDao _locationTableDao = LocationTableDao(); +class JourneyRepository { + final ApiJourneyService journeyService; + final LocationCache locationCache = LocationCache(); - JourneyRepository(FirebaseFirestore fireStore) { - _locationService = LocationService(fireStore); - _journeyService = ApiJourneyService(fireStore); - } + JourneyRepository(this.journeyService); - Future getUserState(String userId, LocationData locationPosition) async { + Future saveLocationJourney(LocationData extractedLocation, String userId) async { try { - var locationData = await _getLocationData(userId); - if (locationData != null) { - _checkAndUpdateLastFiveMinLocation( - userId, - locationData, - locationPosition, - ); - } else { - final latLng = - LatLng(locationPosition.latitude, locationPosition.longitude); - final locations = [ - ApiLocation( - id: const Uuid().v4(), - user_id: userId, - latitude: latLng.latitude, - longitude: latLng.longitude, - created_at: DateTime.now().millisecondsSinceEpoch, - ), - ]; - final tableData = LocationTable( - userId: userId, - lastFiveMinutesLocations: - LocationConverters.locationListToString(locations), - ); - await _locationTableDao.insertLocationTable(tableData); + var lastKnownJourney = await getLastKnownLocation(userId, extractedLocation); + + bool isDayChanged = this.isDayChanged(extractedLocation, lastKnownJourney); + + if (isDayChanged) { + await saveJourneyOnDayChanged(userId, lastKnownJourney); + return; } - locationData = await _getLocationData(userId); - final userState = _getCurrentUserState(locationData, locationPosition); - return userState; + + await checkAndSaveLastFiveLocations(extractedLocation, userId); + await checkAndSaveLocationJourney(userId, extractedLocation, lastKnownJourney); } catch (error, stack) { logger.e( - 'JourneyRepository: Error while getting user state', - error: error, - stackTrace: stack, + 'Journey Repository: Error while save journey', + error: error, stackTrace: stack ); - return USER_STATE_STEADY; } } - int _getCurrentUserState( - LocationTable? locationData, LocationData locationPosition) { - if (locationData != null) { - final lastFiveMinLocation = _getLastFiveMinuteLocations(locationData); - if (lastFiveMinLocation!.isMoving(locationPosition)) return USER_STATE_MOVING; - } - return USER_STATE_STEADY; - } + bool isDayChanged(LocationData extractedLocation, ApiLocationJourney lastKnownJourney) { + var lastKnownDate = DateTime.fromMillisecondsSinceEpoch(lastKnownJourney.update_at!); + var extractedDate = DateTime.fromMillisecondsSinceEpoch(extractedLocation.timestamp.millisecondsSinceEpoch); - void _checkAndUpdateLastFiveMinLocation( - String userId, - LocationTable locationData, - LocationData locationPosition, - ) async { - final locations = _getLastFiveMinuteLocations(locationData); - - if (locations == null || locations.isEmpty) { - final fiveMinLocation = - await _locationService.getLastFiveMinLocation(userId).first; - await _updateLocationData(locationData, fiveMinLocation); - } else { - final latest = (locations.length == 1) - ? locations.first - : locations.reduce((current, next) => - current.created_at! > next.created_at! ? current : next); - - if (latest.created_at! < DateTime.now().millisecondsSinceEpoch - 60000) { - final latLng = - LatLng(locationPosition.latitude, locationPosition.longitude); - final updated = List.from(locations); - updated.removeWhere((loc) => - locationPosition.timestamp.millisecondsSinceEpoch - - loc.created_at! > - MIN_TIME_DIFFERENCE); - updated.add(ApiLocation( - id: const Uuid().v4(), - user_id: userId, - latitude: latLng.latitude, - longitude: latLng.longitude, - created_at: DateTime.now().millisecondsSinceEpoch, - )); - await _updateLocationData(locationData, updated); - } - } + return lastKnownDate.day != extractedDate.day; } - List? _getLastFiveMinuteLocations(LocationTable locationData) { - return locationData.lastFiveMinutesLocations != null - ? LocationConverters.locationListFromString( - locationData.lastFiveMinutesLocations!) - : []; - } + Future saveJourneyOnDayChanged(String userId, ApiLocationJourney lastKnownJourney) async { + String newJourneyId = await journeyService.saveCurrentJourney( + userId: userId, + fromLatitude: lastKnownJourney.from_latitude, + fromLongitude: lastKnownJourney.from_longitude, + toLatitude: lastKnownJourney.to_latitude ?? 0, + toLongitude: lastKnownJourney.to_longitude ?? 0, + ); - Future _updateLocationData( - LocationTable locationData, - List locations, - ) async { - final updatedData = locationData.copyWith( - lastFiveMinutesLocations: - LocationConverters.locationListToString(locations), + var newJourney = lastKnownJourney.copyWith( + id: newJourneyId, + created_at: DateTime.now().millisecondsSinceEpoch, + update_at: DateTime.now().millisecondsSinceEpoch, ); - await _locationTableDao.updateLocationTable(updatedData); + + locationCache.putLastJourney(newJourney, userId); } - Future saveUserJourney(int userSate, String userId, LocationData locationPosition) async { - final locationData = await _getLocationData(userId); - final lastJourney = await _getLastJourneyLocation(userId, locationData); + Future getLastKnownLocation(String userId, LocationData extractedLocation) async { + var lastKnownJourney = locationCache.getLastJourney(userId); - try { - if (lastJourney == null) { - await _journeyService.saveCurrentJourney( + if (lastKnownJourney != null) { + return lastKnownJourney; + } else { + lastKnownJourney = await journeyService.getLastJourneyLocation(userId); + + if (lastKnownJourney != null) { + locationCache.putLastJourney(lastKnownJourney, userId); + return lastKnownJourney; + } else { + String newJourneyId = await journeyService.saveCurrentJourney( userId: userId, - fromLatitude: locationPosition.latitude, - fromLongitude: locationPosition.longitude, + fromLatitude: extractedLocation.latitude, + fromLongitude: extractedLocation.longitude, + created_at: extractedLocation.timestamp.millisecondsSinceEpoch, ); - } else if (userSate == USER_STATE_MOVING) { - await _saveJourneyForMovingUser(userId, lastJourney, locationPosition); - } else if (userSate == USER_STATE_STEADY) { - await _saveJourneyForSteadyUser(userId, lastJourney, locationPosition); + var locationJourney = ApiLocationJourney.fromPosition(extractedLocation, userId, newJourneyId); + locationCache.putLastJourney(locationJourney, userId); + logger.i('get last known location - save current journey: $locationJourney', time: DateTime.now()); + return locationJourney; } - } catch (error, stack) { - logger.e( - 'JourneyRepository: Error while saving user journey', - error: error, - stackTrace: stack, - ); } } - Future _getLocationData(String userId) async { - return await _locationTableDao.getLocationData(userId); - } + Future checkAndSaveLocationJourney(String userId, LocationData extractedLocation, ApiLocationJourney lastKnownJourney) async { + var locations = locationCache.getLastFiveLocations(userId); + var geometricMedian = locations.isNotEmpty ? geometricMedianCalculation(locations) : null; + + double distance = lastKnownJourney.isSteadyLocation() + ? distanceBetween(geometricMedian ?? extractedLocation, + lastKnownJourney.toPositionFromSteadyJourney()) + : distanceBetween(geometricMedian ?? extractedLocation, + lastKnownJourney.toPositionFromMovingJourney()); + + int timeDifference = geometricMedian?.timestamp.millisecondsSinceEpoch ?? + extractedLocation.timestamp.millisecondsSinceEpoch - + lastKnownJourney.update_at!; - Future _getLastJourneyLocation( - String userId, - LocationTable? locationData, - ) async { - if (locationData != null && locationData.lastLocationJourney != null) { - return LocationConverters.journeyFromString( - locationData.lastLocationJourney!); + if (lastKnownJourney.isSteadyLocation()) { + if (distance > MIN_DISTANCE) { + await saveJourneyWhenUserStartsMoving(userId, extractedLocation, lastKnownJourney); + } } else { - final lastJourneyLocation = - await _journeyService.getLastJourneyLocation(userId); - if (lastJourneyLocation != null) { - final updatedLocationData = locationData?.copyWith( - lastLocationJourney: - LocationConverters.journeyToString(lastJourneyLocation), - ); - if (updatedLocationData != null) { - await _locationTableDao.updateLocationTable(updatedLocationData); - } + if (distance > MIN_DISTANCE) { + await updateJourneyForContinuedMovingUser(userId, extractedLocation, lastKnownJourney); + } else if (distance < MIN_DISTANCE && timeDifference > MIN_TIME_DIFFERENCE) { + await saveJourneyOnJourneyStopped(userId, extractedLocation, lastKnownJourney); } - return lastJourneyLocation; } } - Future _saveJourneyForMovingUser( - String userId, - ApiLocationJourney lastJourney, - LocationData locationPosition, - ) async { - final extractedLocation = - LatLng(locationPosition.latitude, locationPosition.longitude); - final movingDistance = _distanceBetween( - extractedLocation, - LatLng(lastJourney.to_latitude ?? 0.0, lastJourney.to_longitude ?? 0.0), + Future saveJourneyWhenUserStartsMoving(String userId, LocationData extractedLocation, ApiLocationJourney lastKnownJourney) async { + String newJourneyId = await journeyService.saveCurrentJourney( + userId: userId, + fromLatitude: lastKnownJourney.from_latitude, + fromLongitude: lastKnownJourney.from_longitude, + toLatitude: extractedLocation.latitude, + toLongitude: extractedLocation.longitude, ); - final steadyDistance = _distanceBetween( - extractedLocation, - LatLng(lastJourney.from_latitude, lastJourney.from_longitude), + + var journey = ApiLocationJourney( + id: newJourneyId, + user_id: userId, + from_latitude: lastKnownJourney.from_latitude, + from_longitude: lastKnownJourney.from_longitude, + to_latitude: extractedLocation.latitude, + to_longitude: extractedLocation.longitude, + routes: [lastKnownJourney.toRouteFromSteadyJourney(), extractedLocation.toRoute()], ); - if (lastJourney.isSteadyLocation()) { - await _journeyService.saveCurrentJourney( - userId: userId, - fromLatitude: lastJourney.from_latitude, - fromLongitude: lastJourney.from_longitude, - toLatitude: locationPosition.latitude, - toLongitude: locationPosition.longitude, - routeDistance: steadyDistance, - routeDuration: locationPosition.timestamp.millisecondsSinceEpoch - - lastJourney.update_at!, - ); - } else { - final updatedRoutes = List.from(lastJourney.routes) - ..add(JourneyRoute( - latitude: extractedLocation.latitude, - longitude: extractedLocation.longitude, - )); - - await _journeyService.updateLastLocationJourney( - userId, - lastJourney.copyWith( - to_latitude: extractedLocation.latitude, - to_longitude: extractedLocation.longitude, - route_distance: (lastJourney.route_distance ?? 0.0) + movingDistance, - route_duration: locationPosition.timestamp.millisecondsSinceEpoch - - lastJourney.created_at!, - routes: updatedRoutes, - update_at: DateTime.now().millisecondsSinceEpoch, - ), - ); - } + locationCache.putLastJourney(journey, userId); + logger.i('save journey when user start moving $journey', time: DateTime.now()); } - Future _saveJourneyForSteadyUser( - String userId, - ApiLocationJourney lastJourney, - LocationData locationPosition, - ) async { - final extractedLocation = - LatLng(locationPosition.latitude, locationPosition.longitude); - final lastLatLng = (lastJourney.isSteadyLocation()) - ? LatLng(lastJourney.from_latitude, lastJourney.from_longitude) - : LatLng(lastJourney.to_latitude!, lastJourney.to_longitude!); - final distance = _distanceBetween(extractedLocation, lastLatLng); - final timeDifference = locationPosition.timestamp.millisecondsSinceEpoch - - lastJourney.created_at!; - - if (timeDifference > MIN_TIME_DIFFERENCE && distance > MIN_DISTANCE) { - if (lastJourney.isSteadyLocation()) { - await _journeyService.updateLastLocationJourney( - userId = userId, - lastJourney.copyWith( - update_at: DateTime.now().millisecondsSinceEpoch), - ); - } else { - await _journeyService.saveCurrentJourney( - userId: userId, - fromLatitude: lastJourney.to_latitude!, - fromLongitude: lastJourney.to_longitude!, - created_at: lastJourney.update_at, - ); - } - await _journeyService.saveCurrentJourney( - userId: userId, - fromLatitude: lastJourney.to_latitude ?? lastJourney.from_latitude, - fromLongitude: lastJourney.to_longitude ?? lastJourney.from_longitude, - toLatitude: locationPosition.latitude, - toLongitude: locationPosition.longitude, - routeDistance: distance, - routeDuration: locationPosition.timestamp.millisecondsSinceEpoch - - lastJourney.update_at!, - created_at: lastJourney.update_at, - updated_at: DateTime.now().millisecondsSinceEpoch, - ); - } else if (timeDifference < MIN_TIME_DIFFERENCE && - distance > MIN_DISTANCE) { - final updatedRoutes = List.from(lastJourney.routes) - ..add(JourneyRoute( - latitude: extractedLocation.latitude, - longitude: extractedLocation.longitude, - )); - - await _journeyService.updateLastLocationJourney( - userId, - lastJourney.copyWith( - to_latitude: extractedLocation.latitude, - to_longitude: extractedLocation.longitude, - route_distance: distance, - routes: updatedRoutes, - route_duration: locationPosition.timestamp.millisecondsSinceEpoch - - lastJourney.created_at!, - update_at: DateTime.now().millisecondsSinceEpoch, - )); - } else if (timeDifference > MIN_TIME_DIFFERENCE && - distance < MIN_DISTANCE) { - if (lastJourney.isSteadyLocation()) { - await _journeyService.updateLastLocationJourney( - userId = userId, - lastJourney.copyWith( - update_at: DateTime.now().millisecondsSinceEpoch), - ); - } else { - await _journeyService.saveCurrentJourney( - userId: userId, - fromLatitude: locationPosition.latitude, - fromLongitude: locationPosition.longitude, - created_at: DateTime.now().millisecondsSinceEpoch, - ); - } - } else if (timeDifference < MIN_TIME_DIFFERENCE && - distance < MIN_DISTANCE) { - await _journeyService.updateLastLocationJourney( - userId = userId, - lastJourney.copyWith(update_at: DateTime.now().millisecondsSinceEpoch), - ); - } + Future updateJourneyForContinuedMovingUser(String userId, LocationData extractedLocation, ApiLocationJourney lastKnownJourney) async { + var journey = ApiLocationJourney( + id: lastKnownJourney.id, + user_id: userId, + from_latitude: lastKnownJourney.from_latitude, + from_longitude: lastKnownJourney.from_longitude, + to_latitude: extractedLocation.latitude, + to_longitude: extractedLocation.longitude, + routes: [...lastKnownJourney.routes, extractedLocation.toRoute()], + created_at: lastKnownJourney.created_at, + update_at: DateTime.now().millisecondsSinceEpoch, + ); + await journeyService.updateLastLocationJourney(userId, journey); + locationCache.putLastJourney(journey, userId); + logger.i('update journey for continued moving user: $journey', time: DateTime.now()); } - double _distanceBetween(LatLng startLocation, LatLng endLocation) { + Future saveJourneyOnJourneyStopped(String userId, LocationData extractedLocation, ApiLocationJourney lastKnownJourney) async { + var movingJourney = ApiLocationJourney( + id: lastKnownJourney.id, + user_id: userId, + from_latitude: lastKnownJourney.from_latitude, + from_longitude: lastKnownJourney.from_longitude, + to_latitude: extractedLocation.latitude, + to_longitude: extractedLocation.longitude, + routes: [...lastKnownJourney.routes, extractedLocation.toRoute()], + created_at: lastKnownJourney.created_at, + update_at: lastKnownJourney.update_at, + ); + + journeyService.updateLastLocationJourney(userId, movingJourney); + + var newJourneyId = await journeyService.saveCurrentJourney( + userId: userId, + fromLatitude: extractedLocation.latitude, + fromLongitude: extractedLocation.longitude, + created_at: lastKnownJourney.update_at, + ); + + var steadyJourney = ApiLocationJourney( + id: newJourneyId, + user_id: userId, + from_latitude: extractedLocation.latitude, + from_longitude: extractedLocation.longitude, + ); + + locationCache.putLastJourney(steadyJourney, userId); + logger.i('save journey on journey stopped: $steadyJourney', time: DateTime.now()); + } + + double distanceBetween(LocationData loc1, LocationData loc2) { return Geolocator.distanceBetween( - startLocation.latitude, - startLocation.longitude, - endLocation.latitude, - endLocation.longitude, + loc1.latitude, + loc1.longitude, + loc2.latitude, + loc2.longitude, ); } -} -extension ApiLocationListExtensions on List { - bool isMoving(LocationData locationPosition) { - return any((location) { - final newLocation = - LatLng(locationPosition.latitude, locationPosition.longitude); - final lastLocation = LatLng(location.latitude, location.longitude); - final distance = Geolocator.distanceBetween( - lastLocation.latitude, - lastLocation.longitude, - newLocation.latitude, - newLocation.longitude, - ); - return distance > MIN_DISTANCE; + Future checkAndSaveLastFiveLocations(LocationData extractedLocation, String userId) async { + var lastFiveLocations = locationCache.getLastFiveLocations(userId); + lastFiveLocations.add(extractedLocation); + locationCache.putLastFiveLocations(lastFiveLocations, userId); + } + + LocationData geometricMedianCalculation(List locations) { + return locations.reduce((a, b) { + var totalDistanceA = locations.map((loc) => distanceBetween(a, loc)).reduce((a, b) => a + b); + var totalDistanceB = locations.map((loc) => distanceBetween(b, loc)).reduce((a, b) => a + b); + return totalDistanceA < totalDistanceB ? a : b; }); } } diff --git a/data/lib/service/auth_service.dart b/data/lib/service/auth_service.dart index 0604a69a..a9b9f208 100644 --- a/data/lib/service/auth_service.dart +++ b/data/lib/service/auth_service.dart @@ -62,8 +62,8 @@ class AuthService { } Future deleteAccount({String? currentUserId}) async { - await userService.deleteUser(currentUserId ?? _currentUser?.id ?? ''); userService.clearPreference(); + await userService.deleteUser(currentUserId ?? _currentUser?.id ?? ''); } Future getUserNetworkStatus( diff --git a/data/lib/service/location_service.dart b/data/lib/service/location_service.dart index 3b771acf..4cb21317 100644 --- a/data/lib/service/location_service.dart +++ b/data/lib/service/location_service.dart @@ -47,7 +47,6 @@ class LocationService { String userId, LatLng latLng, int recodedAt, - int? userState, ) async { final docRef = _locationRef(userId).doc(); @@ -56,33 +55,8 @@ class LocationService { user_id: userId, latitude: latLng.latitude, longitude: latLng.longitude, - created_at: recodedAt, - user_state: userState); + created_at: recodedAt); await docRef.set(location); } - - Stream> getLastFiveMinLocation(String userId) { - final currentTime = DateTime.now().millisecondsSinceEpoch; - - return Stream.fromIterable(List.generate(5, (i) => i)) - .asyncMap((i) async { - final startTime = currentTime - (i + 1) * 60000; - final endTime = startTime - 60000; - final querySnapshot = await _locationRef(userId) - .where('user_id', isEqualTo: userId) - .where('created_at', isGreaterThanOrEqualTo: endTime) - .where('created_at', isLessThan: startTime) - .orderBy('created_at', descending: true) - .limit(1) - .get(); - - return querySnapshot.docs.isNotEmpty - ? querySnapshot.docs.first.data() as ApiLocation - : null; - }) - .where((location) => location != null) - .toList() - .asStream(); - } } diff --git a/data/lib/storage/database/location_database.dart b/data/lib/storage/database/location_database.dart deleted file mode 100644 index fb625e8a..00000000 --- a/data/lib/storage/database/location_database.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:path/path.dart'; -import 'package:sqflite/sqflite.dart'; - -class LocationDatabaseHelper { - static const tableName = 'location_table_database'; - Database? _database; - - Future get database async { - if (_database != null) { - return _database!; - } - _database = await initDataBase(); - return _database!; - } - - Future initDataBase() async { - String path = join(await getDatabasesPath(), 'database'); - return await openDatabase(path, version: 1, onCreate: (db, version) { - return db.execute(''' - CREATE TABLE $tableName( - userId TEXT PRIMARY KEY, - lastFiveMinutesLocations TEXT, - lastLocationJourney TEXT - ) - '''); - }); - } -} diff --git a/data/lib/storage/database/location_table_dao.dart b/data/lib/storage/database/location_table_dao.dart deleted file mode 100644 index 0ec3d4a3..00000000 --- a/data/lib/storage/database/location_table_dao.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:data/api/location/location_table.dart'; -import 'package:data/storage/database/location_database.dart'; -import 'package:sqflite/sqflite.dart'; - -class LocationTableDao { - final LocationDatabaseHelper _locationDatabase = LocationDatabaseHelper(); - - Future insertLocationTable(LocationTable locationTable) async { - final db = await _locationDatabase.database; - await db.insert( - LocationDatabaseHelper.tableName, - locationTable.toMap(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - } - - Future getLocationData(String userId) async { - final db = await _locationDatabase.database; - final data = await db.query( - LocationDatabaseHelper.tableName, - where: 'userId = ?', - whereArgs: [userId], - ); - if (data.isNotEmpty) { - return LocationTable.fromJson(data.first); - } else { - return null; - } - } - - Future updateLocationTable(LocationTable locationTable) async { - final db = await _locationDatabase.database; - await db.update( - LocationDatabaseHelper.tableName, - locationTable.toJson(), - where: 'userId = ?', - whereArgs: [locationTable.userId], - ); - } - - Future deleteLocationTable(String userId) async { - final db = await _locationDatabase.database; - await db.delete( - LocationDatabaseHelper.tableName, - where: 'userId = ?', - whereArgs: [userId], - ); - } -} diff --git a/data/lib/storage/location_caches.dart b/data/lib/storage/location_caches.dart new file mode 100644 index 00000000..c7d9dcfc --- /dev/null +++ b/data/lib/storage/location_caches.dart @@ -0,0 +1,60 @@ +import 'dart:collection'; + +import 'package:data/api/location/journey/journey.dart'; +import 'package:data/api/location/location.dart'; + +class LocationCache { + final Cache _lastJourneyCache; + final Cache> _lastFiveLocationCache; + + LocationCache({int cacheSize = 5}) + : _lastJourneyCache = Cache(cacheSize), + _lastFiveLocationCache = Cache>(cacheSize); + + void putLastJourney(ApiLocationJourney journey, String userId) { + _lastJourneyCache.put(userId, journey); + } + + ApiLocationJourney? getLastJourney(String userId) { + return _lastJourneyCache.get(userId); + } + + void putLastFiveLocations(List locations, String userId) { + _lastFiveLocationCache.put(userId, locations); + } + + List getLastFiveLocations(String userId) { + return _lastFiveLocationCache.get(userId) ?? []; + } + + void clear() { + _lastJourneyCache.clear(); + _lastFiveLocationCache.clear(); + } +} + +class Cache { + final int capacity; + final LinkedHashMap _cache = LinkedHashMap(); + + Cache(this.capacity); + + void put(K key, V value) { + if (_cache.length >= capacity) { + _cache.remove(_cache.keys.first); + } + _cache[key] = value; + } + + V? get(K key) { + if (!_cache.containsKey(key)) return null; + + final value = _cache.remove(key); + _cache[key] = value as V; + return value; + } + + void clear() { + _cache.clear(); + } +} \ No newline at end of file diff --git a/data/pubspec.yaml b/data/pubspec.yaml index c6ba69d7..fd7c17cf 100644 --- a/data/pubspec.yaml +++ b/data/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: flutter_background_service: ^5.0.6 sqflite: ^2.3.3+1 path: ^1.8.3 + path_provider: ^2.0.11 google_maps_flutter: ^2.2.8 connectivity_plus: ^6.0.5