diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index c1f5b58..ee2aeea 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -157,6 +157,7 @@ class _LocationsOverviewScreenState extends State _updateLocaleToSettings(); _updateBackgroundListeners(); _showUpdateDialogIfRequired(); + _initLiveLocationUpdate(); locationFetchers.fetchPreviewLocations(); taskService.checkup(logService); @@ -241,10 +242,10 @@ class _LocationsOverviewScreenState extends State void _setLocationFromSettings() async { final settings = context.read(); - final position = settings.getLastMapLocation(); + final rawPosition = settings.getLastMapLocation(); final currentLocation = context.read(); - if (position == null) { + if (rawPosition == null) { return; } @@ -252,23 +253,24 @@ class _LocationsOverviewScreenState extends State locationStatus = LocationStatus.stale; }); - currentLocation.updateCurrentPosition( - Position( - latitude: position.latitude, - longitude: position.longitude, - // Always use a high accuracy, since we are using a cached location - accuracy: max(100, position.accuracy), - timestamp: DateTime.now(), - altitude: 0, - heading: 0, - speed: 0, - speedAccuracy: 0, - ), + final position = Position( + latitude: rawPosition.latitude, + longitude: rawPosition.longitude, + accuracy: rawPosition.accuracy, + timestamp: DateTime.now(), + altitude: 0, + heading: 0, + speed: 0, + speedAccuracy: 0, ); + + await _animateToPosition(position); + currentLocation.updateCurrentPosition(position); } List mergeLocationsIfRequired( - final List locations,) { + final List locations, + ) { if (locations.isEmpty) { return locations; } @@ -305,7 +307,7 @@ class _LocationsOverviewScreenState extends State notificationText: l10n.backgroundLocationFetch_text, notificationTitle: l10n.backgroundLocationFetch_title, notificationIcon: - const AndroidResource(name: "ic_quick_actions_share_now"), + const AndroidResource(name: "ic_quick_actions_share_now"), ), ); } else if (isPlatformApple()) { @@ -344,8 +346,6 @@ class _LocationsOverviewScreenState extends State final settings = context.read(); final taskService = context.read(); - _initLiveLocationUpdate(); - if (settings.useRealtimeUpdates && ((await taskService.hasRunningTasks()) || (await taskService.hasScheduledTasks()))) { @@ -359,7 +359,9 @@ class _LocationsOverviewScreenState extends State } } - void _checkViewAlarms(final Position position,) async { + void _checkViewAlarms( + final Position position, + ) async { final l10n = AppLocalizations.of(context); final viewService = context.read(); final userLocation = await LocationPointService.fromPosition(position); @@ -382,11 +384,6 @@ class _LocationsOverviewScreenState extends State return; } - if (settings.useRealtimeUpdates) { - // Live location updates are handled by the background locator already - return; - } - _positionStream = Geolocator.getPositionStream( locationSettings: _getLocationSettings(), ); @@ -427,8 +424,7 @@ class _LocationsOverviewScreenState extends State _positionStream = null; } - Future _importUniLink(final String url) => - showPlatformModalSheet( + Future _importUniLink(final String url) => showPlatformModalSheet( context: context, material: MaterialModalSheetData( isScrollControlled: true, @@ -465,19 +461,18 @@ class _LocationsOverviewScreenState extends State showPlatformDialog( context: context, - builder: (_) => - PlatformAlertDialog( - title: Text(l10n.uniLinksOpenError), - content: Text(error.message ?? l10n.unknownError), - actions: [ - PlatformDialogAction( - child: Text(l10n.closeNeutralAction), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], + builder: (_) => PlatformAlertDialog( + title: Text(l10n.uniLinksOpenError), + content: Text(error.message ?? l10n.unknownError), + actions: [ + PlatformDialogAction( + child: Text(l10n.closeNeutralAction), + onPressed: () { + Navigator.of(context).pop(); + }, ), + ], + ), ); } } @@ -485,7 +480,7 @@ class _LocationsOverviewScreenState extends State void _handleViewAlarmChecker() { _viewsAlarmCheckerTimer = Timer.periodic( const Duration(minutes: 1), - (_) { + (_) { final viewService = context.read(); if (viewService.viewsWithAlarms.isEmpty) { @@ -514,10 +509,9 @@ class _LocationsOverviewScreenState extends State Navigator.of(context).push( NativePageRoute( context: context, - builder: (_) => - ViewDetailsScreen( - view: viewService.getViewById(data["taskViewID"]), - ), + builder: (_) => ViewDetailsScreen( + view: viewService.getViewById(data["taskViewID"]), + ), ), ); break; @@ -539,9 +533,7 @@ class _LocationsOverviewScreenState extends State void _updateLocaleToSettings() { final settingsService = context.read(); - settingsService.localeName = AppLocalizations - .of(context) - .localeName; + settingsService.localeName = AppLocalizations.of(context).localeName; settingsService.save(); } @@ -558,46 +550,45 @@ class _LocationsOverviewScreenState extends State material: MaterialDialogData( barrierColor: Colors.black, ), - builder: (context) => - PlatformAlertDialog( - title: Text(l10n.updateAvailable_android_title), - content: Text(l10n.updateAvailable_android_description), - actions: [ - PlatformDialogAction( - onPressed: () { - Navigator.of(context).pop(); - }, - material: (context, _) => - MaterialDialogActionData( - icon: const Icon(Icons.watch_later_rounded)), - child: Text(l10n.updateAvailable_android_remindLater), - ), - PlatformDialogAction( - onPressed: () { - appUpdateService.doNotShowDialogueAgain(); + builder: (context) => PlatformAlertDialog( + title: Text(l10n.updateAvailable_android_title), + content: Text(l10n.updateAvailable_android_description), + actions: [ + PlatformDialogAction( + onPressed: () { + Navigator.of(context).pop(); + }, + material: (context, _) => MaterialDialogActionData( + icon: const Icon(Icons.watch_later_rounded)), + child: Text(l10n.updateAvailable_android_remindLater), + ), + PlatformDialogAction( + onPressed: () { + appUpdateService.doNotShowDialogueAgain(); - Navigator.of(context).pop(); - }, - material: (context, _) => - MaterialDialogActionData(icon: const Icon(Icons.block)), - child: Text(l10n.updateAvailable_android_ignore), - ), - PlatformDialogAction( - onPressed: appUpdateService.openStoreForUpdate, - material: (context, _) => - MaterialDialogActionData( - icon: const Icon(Icons.download)), - child: Text(l10n.updateAvailable_android_download), - ), - ], + Navigator.of(context).pop(); + }, + material: (context, _) => + MaterialDialogActionData(icon: const Icon(Icons.block)), + child: Text(l10n.updateAvailable_android_ignore), ), + PlatformDialogAction( + onPressed: appUpdateService.openStoreForUpdate, + material: (context, _) => + MaterialDialogActionData(icon: const Icon(Icons.download)), + child: Text(l10n.updateAvailable_android_download), + ), + ], + ), ); appUpdateService.setHasShownDialogue(); } } - Future _animateToPosition(final Position position,) async { + Future _animateToPosition( + final Position position, + ) async { if (flutterMapController != null) { final zoom = max(15, flutterMapController!.zoom).toDouble(); @@ -660,8 +651,6 @@ class _LocationsOverviewScreenState extends State return; } - _initLiveLocationUpdate(); - FlutterLogs.logInfo( LOG_TAG, "LocationOverviewScreen", @@ -723,9 +712,7 @@ class _LocationsOverviewScreenState extends State return CurrentLocationLayer( positionStream: - context - .read() - .locationMarkerStream, + context.read().locationMarkerStream, followOnLocationUpdate: FollowOnLocationUpdate.never, style: LocationMarkerStyle( marker: DefaultLocationMarker( @@ -743,22 +730,21 @@ class _LocationsOverviewScreenState extends State final locationFetchers = context.read(); final Iterable<(TaskView, LocationPointService)> circleLocations = - selectedViewID == null - ? locationFetchers.fetchers - .where((fetcher) => fetcher.sortedLocations.isNotEmpty) - .map((fetcher) => (fetcher.view, fetcher.sortedLocations.last)) - : viewService.views - .map( - (view) => - mergeLocationsIfRequired( - locationFetchers - .getLocations(view) - .whereNot((location) => location == visibleLocation) - .toList(), - ), - ) - .expand((element) => element) - .map((location) => (selectedView!, location)); + selectedViewID == null + ? locationFetchers.fetchers + .where((fetcher) => fetcher.sortedLocations.isNotEmpty) + .map((fetcher) => (fetcher.view, fetcher.sortedLocations.last)) + : viewService.views + .map( + (view) => mergeLocationsIfRequired( + locationFetchers + .getLocations(view) + .whereNot((location) => location == visibleLocation) + .toList(), + ), + ) + .expand((element) => element) + .map((location) => (selectedView!, location)); if (settings.getMapProvider() == MapProvider.apple) { return apple_maps.AppleMap( @@ -789,30 +775,29 @@ class _LocationsOverviewScreenState extends State (view) => selectedViewID == null || view.id == selectedViewID) .map( (view) => - mergeLocationsIfRequired(locationFetchers.getLocations(view)) - .map( - (location) => - apple_maps.Circle( - circleId: apple_maps.CircleId(location.id), - center: apple_maps.LatLng( - location.latitude, - location.longitude, - ), - radius: location.accuracy, - fillColor: view.color.withOpacity(0.2), - strokeColor: view.color, - strokeWidth: location.accuracy < 10 ? 1 : 3), - ) - .toList(), - ) + mergeLocationsIfRequired(locationFetchers.getLocations(view)) + .map( + (location) => apple_maps.Circle( + circleId: apple_maps.CircleId(location.id), + center: apple_maps.LatLng( + location.latitude, + location.longitude, + ), + radius: location.accuracy, + fillColor: view.color.withOpacity(0.2), + strokeColor: view.color, + strokeWidth: location.accuracy < 10 ? 1 : 3), + ) + .toList(), + ) .expand((element) => element) .toSet(), polylines: Set.from( locationFetchers.fetchers .where((fetcher) => - selectedViewID == null || fetcher.view.id == selectedViewID) + selectedViewID == null || fetcher.view.id == selectedViewID) .map( - (fetcher) { + (fetcher) { final view = fetcher.view; return apple_maps.Polyline( @@ -830,15 +815,14 @@ class _LocationsOverviewScreenState extends State }, // TODO points: mergeLocationsIfRequired( - locationFetchers.getLocations(view)) + locationFetchers.getLocations(view)) .reversed .map( - (location) => - apple_maps.LatLng( + (location) => apple_maps.LatLng( location.latitude, location.longitude, ), - ) + ) .toList(), ); }, @@ -861,18 +845,18 @@ class _LocationsOverviewScreenState extends State CircleLayer( circles: circleLocations .map((data) { - final view = data.$1; - final location = data.$2; - - return CircleMarker( - radius: location.accuracy, - useRadiusInMeter: true, - point: LatLng(location.latitude, location.longitude), - borderStrokeWidth: 1, - color: view.color.withOpacity(.1 * colorOpacityMultiplier), - borderColor: view.color.withOpacity(colorOpacityMultiplier), - ); - }) + final view = data.$1; + final location = data.$2; + + return CircleMarker( + radius: location.accuracy, + useRadiusInMeter: true, + point: LatLng(location.latitude, location.longitude), + borderStrokeWidth: 1, + color: view.color.withOpacity(.1 * colorOpacityMultiplier), + borderColor: view.color.withOpacity(colorOpacityMultiplier), + ); + }) .toList() .cast(), ), @@ -896,9 +880,9 @@ class _LocationsOverviewScreenState extends State polylines: List.from( locationFetchers.fetchers .where((fetcher) => - selectedViewID == null || fetcher.view.id == selectedViewID) + selectedViewID == null || fetcher.view.id == selectedViewID) .map( - (fetcher) { + (fetcher) { final view = fetcher.view; final locations = mergeLocationsIfRequired( locationFetchers.getLocations(view), @@ -909,16 +893,16 @@ class _LocationsOverviewScreenState extends State strokeWidth: 10, strokeJoin: StrokeJoin.round, gradientColors: locations.length <= - LOCATION_POLYLINE_OPAQUE_AMOUNT_THRESHOLD + LOCATION_POLYLINE_OPAQUE_AMOUNT_THRESHOLD ? null : List.generate( - 9, (index) => view.color.withOpacity(0.9)) + - [view.color.withOpacity(.3)], + 9, (index) => view.color.withOpacity(0.9)) + + [view.color.withOpacity(.3)], points: locations.reversed .map( (location) => - LatLng(location.latitude, location.longitude), - ) + LatLng(location.latitude, location.longitude), + ) .toList(), ); }, @@ -933,7 +917,7 @@ class _LocationsOverviewScreenState extends State popupDisplayOptions: PopupDisplayOptions( builder: (context, marker) { final view = viewService.views.firstWhere( - (view) => Key(view.id) == marker.key, + (view) => Key(view.id) == marker.key, ); return ViewLocationPopup( @@ -951,14 +935,10 @@ class _LocationsOverviewScreenState extends State ), markers: viewService.views .where((view) => - (selectedViewID == null || view.id == selectedViewID) && - locationFetchers - .getLocations(view) - .isNotEmpty) + (selectedViewID == null || view.id == selectedViewID) && + locationFetchers.getLocations(view).isNotEmpty) .map((view) { - final latestLocation = locationFetchers - .getLocations(view) - .last; + final latestLocation = locationFetchers.getLocations(view).last; return Marker( key: Key(view.id), @@ -967,19 +947,18 @@ class _LocationsOverviewScreenState extends State latestLocation.longitude, ), anchorPos: AnchorPos.align(AnchorAlign.top), - builder: (context) => - Icon( - Icons.location_on, - size: 40, - color: view.color, - shadows: const [ - Shadow( - blurRadius: 10, - color: Colors.black, - offset: Offset(0, 0), - ), - ], + builder: (context) => Icon( + Icons.location_on, + size: 40, + color: view.color, + shadows: const [ + Shadow( + blurRadius: 10, + color: Colors.black, + offset: Offset(0, 0), ), + ], + ), ); }).toList(), ), @@ -994,11 +973,10 @@ class _LocationsOverviewScreenState extends State return Stack( children: locationFetchers.fetchers .where((fetcher) => - (selectedViewID == null || fetcher.view.id == selectedViewID) && - fetcher.sortedLocations.isNotEmpty) + (selectedViewID == null || fetcher.view.id == selectedViewID) && + fetcher.sortedLocations.isNotEmpty) .map( - (fetcher) => - OutOfBoundMarker( + (fetcher) => OutOfBoundMarker( lastViewLocation: fetcher.sortedLocations.last, onTap: () { showViewLocations(fetcher.view); @@ -1008,12 +986,13 @@ class _LocationsOverviewScreenState extends State appleMapController: appleMapController, flutterMapController: flutterMapController, ), - ) + ) .toList(), ); } - void showViewLocations(final TaskView view, { + void showViewLocations( + final TaskView view, { final bool jumpToLatestLocation = true, }) async { final locationFetchers = context.read(); @@ -1055,7 +1034,8 @@ class _LocationsOverviewScreenState extends State } } - Widget buildViewTile(final TaskView? view, { + Widget buildViewTile( + final TaskView? view, { final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, }) { final l10n = AppLocalizations.of(context); @@ -1110,15 +1090,14 @@ class _LocationsOverviewScreenState extends State showCupertinoModalPopup( context: context, barrierDismissible: true, - builder: (cupertino) => - CupertinoActionSheet( - cancelButton: CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); - }, - child: Text(l10n.cancelLabel), - ), - actions: [ + builder: (cupertino) => CupertinoActionSheet( + cancelButton: CupertinoActionSheetAction( + onPressed: () { + Navigator.pop(context); + }, + child: Text(l10n.cancelLabel), + ), + actions: [ CupertinoActionSheetAction( child: buildViewTile( null, @@ -1133,34 +1112,32 @@ class _LocationsOverviewScreenState extends State }, ) ] + - viewService.views - .map( - (view) => - CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); - showViewLocations(view); - }, - child: buildViewTile( - view, - mainAxisAlignment: MainAxisAlignment - .center, - ), - ), + viewService.views + .map( + (view) => CupertinoActionSheetAction( + onPressed: () { + Navigator.pop(context); + showViewLocations(view); + }, + child: buildViewTile( + view, + mainAxisAlignment: MainAxisAlignment.center, + ), + ), ) - .toList(), - ), + .toList(), + ), ); }, child: selectedViewID == null ? Icon( - Icons.location_on_rounded, - color: settings.getPrimaryColor(context), - ) + Icons.location_on_rounded, + color: settings.getPrimaryColor(context), + ) : Icon( - Icons.circle_rounded, - color: selectedView!.color, - ), + Icons.circle_rounded, + color: selectedView!.color, + ), ), ), ), @@ -1181,93 +1158,88 @@ class _LocationsOverviewScreenState extends State vertical: SMALL_SPACE, ), child: PlatformWidget( - material: (context, _) => - DropdownButton( - isDense: true, - value: selectedViewID, - onChanged: (selection) { - if (selection == null) { - setState(() { - showFAB = true; - selectedViewID = null; - visibleLocation = null; - }); - return; - } - - final view = viewService.views.firstWhere( - (view) => view.id == selection, - ); - - showViewLocations(view); - }, - underline: Container(), - alignment: Alignment.center, - isExpanded: true, - items: [ - DropdownMenuItem( - value: null, - child: buildViewTile(null), - ), - for (final view in viewService.views) ...[ - DropdownMenuItem( - value: view.id, - child: buildViewTile(view), - ), - ], - ], + material: (context, _) => DropdownButton( + isDense: true, + value: selectedViewID, + onChanged: (selection) { + if (selection == null) { + setState(() { + showFAB = true; + selectedViewID = null; + visibleLocation = null; + }); + return; + } + + final view = viewService.views.firstWhere( + (view) => view.id == selection, + ); + + showViewLocations(view); + }, + underline: Container(), + alignment: Alignment.center, + isExpanded: true, + items: [ + DropdownMenuItem( + value: null, + child: buildViewTile(null), ), - cupertino: (context, _) => - CupertinoButton( - onPressed: () { - showCupertinoModalPopup( - context: context, - barrierDismissible: true, - builder: (cupertino) => - CupertinoActionSheet( - cancelButton: CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); - }, - child: Text(l10n.cancelLabel), + for (final view in viewService.views) ...[ + DropdownMenuItem( + value: view.id, + child: buildViewTile(view), + ), + ], + ], + ), + cupertino: (context, _) => CupertinoButton( + onPressed: () { + showCupertinoModalPopup( + context: context, + barrierDismissible: true, + builder: (cupertino) => CupertinoActionSheet( + cancelButton: CupertinoActionSheetAction( + onPressed: () { + Navigator.pop(context); + }, + child: Text(l10n.cancelLabel), + ), + actions: [ + CupertinoActionSheetAction( + child: buildViewTile( + null, + mainAxisAlignment: MainAxisAlignment.center, ), - actions: [ - CupertinoActionSheetAction( - child: buildViewTile( - null, - mainAxisAlignment: MainAxisAlignment - .center, - ), + onPressed: () { + Navigator.pop(context); + setState(() { + selectedViewID = null; + visibleLocation = null; + }); + }, + ) + ] + + viewService.views + .map( + (view) => CupertinoActionSheetAction( onPressed: () { Navigator.pop(context); - setState(() { - selectedViewID = null; - visibleLocation = null; - }); + showViewLocations(view); }, - ) - ] + - viewService.views - .map( - (view) => - CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); - showViewLocations(view); - }, - child: buildViewTile( - view, - mainAxisAlignment: - MainAxisAlignment.center, - ), - ), - ) - .toList(), - ), - ); - }, - child: buildViewTile(selectedView), - ), + child: buildViewTile( + view, + mainAxisAlignment: + MainAxisAlignment.center, + ), + ), + ) + .toList(), + ), + ); + }, + child: buildViewTile(selectedView), + ), ), ), ), @@ -1322,7 +1294,7 @@ class _LocationsOverviewScreenState extends State final settings = context.read(); final link = - await (task as Task).publisher.generateLink(settings.getServerHost()); + await (task as Task).publisher.generateLink(settings.getServerHost()); // Copy to clipboard await Clipboard.setData(ClipboardData(text: link)); @@ -1352,7 +1324,7 @@ class _LocationsOverviewScreenState extends State AnimatedScale( scale: showDetailedLocations ? 1 : 0, duration: - showDetailedLocations ? 1200.milliseconds : 100.milliseconds, + showDetailedLocations ? 1200.milliseconds : 100.milliseconds, curve: showDetailedLocations ? Curves.elasticOut : Curves.easeIn, child: Tooltip( message: disableShowDetailedLocations @@ -1375,7 +1347,7 @@ class _LocationsOverviewScreenState extends State onPressed: () { setState(() { disableShowDetailedLocations = - !disableShowDetailedLocations; + !disableShowDetailedLocations; }); }, ), @@ -1472,7 +1444,7 @@ class _LocationsOverviewScreenState extends State onPressed: importLocation, icon: const Icon(Icons.download_rounded), label: - Text(l10n.sharesOverviewScreen_importTask_action_import), + Text(l10n.sharesOverviewScreen_importTask_action_import), backgroundColor: background, foregroundColor: foreground, ), @@ -1554,62 +1526,58 @@ class _LocationsOverviewScreenState extends State showCupertinoModalPopup( context: context, barrierDismissible: true, - builder: (cupertino) => - CupertinoActionSheet( - cancelButton: CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), - child: Text(l10n.cancelLabel), + builder: (cupertino) => CupertinoActionSheet( + cancelButton: CupertinoActionSheetAction( + onPressed: () => Navigator.pop(context), + child: Text(l10n.cancelLabel), + ), + actions: [ + CupertinoActionSheetAction( + onPressed: withPopNavigation(createNewQuickLocationShare)( + context), + child: CupertinoListTile( + leading: const Icon(Icons.share_location_rounded), + title: Text(l10n.shareLocation_title), ), - actions: [ - CupertinoActionSheetAction( - onPressed: withPopNavigation( - createNewQuickLocationShare)( - context), - child: CupertinoListTile( - leading: const Icon(Icons.share_location_rounded), - title: Text(l10n.shareLocation_title), - ), - ), - CupertinoActionSheetAction( - onPressed: withPopNavigation(importLocation)(context), - child: CupertinoListTile( - leading: + ), + CupertinoActionSheetAction( + onPressed: withPopNavigation(importLocation)(context), + child: CupertinoListTile( + leading: const Icon(CupertinoIcons.square_arrow_down_fill), - title: Text( - l10n - .sharesOverviewScreen_importTask_action_import), - ), - ), - CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); + title: Text( + l10n.sharesOverviewScreen_importTask_action_import), + ), + ), + CupertinoActionSheetAction( + onPressed: () { + Navigator.pop(context); - Navigator.push( - context, - MaterialWithModalsPageRoute( - builder: ( - context) => const SharesOverviewScreen(), - ), - ); - }, - child: CupertinoListTile( - leading: const Icon(CupertinoIcons.list_bullet), - title: Text(l10n.sharesOverviewScreen_title), + Navigator.push( + context, + MaterialWithModalsPageRoute( + builder: (context) => const SharesOverviewScreen(), ), - ), - CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); + ); + }, + child: CupertinoListTile( + leading: const Icon(CupertinoIcons.list_bullet), + title: Text(l10n.sharesOverviewScreen_title), + ), + ), + CupertinoActionSheetAction( + onPressed: () { + Navigator.pop(context); - showSettings(context); - }, - child: CupertinoListTile( - leading: Icon(context.platformIcons.settings), - title: Text(l10n.settingsScreen_title), - ), - ), - ], + showSettings(context); + }, + child: CupertinoListTile( + leading: Icon(context.platformIcons.settings), + title: Text(l10n.settingsScreen_title), + ), ), + ], + ), ); }, ),