From 7f64d4b3d9bd75887b51978b4193402787205626 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:05:54 +0200 Subject: [PATCH 01/19] feat: Add LocationHistoryUpdater.dart --- lib/app_wrappers/LocationHistoryUpdater.dart | 46 +++++++++++++ lib/main.dart | 14 +++- .../location_history_service/constants.dart | 4 ++ .../location_history_service/index.dart | 1 + .../location_history.dart | 65 +++++++++++++++++++ 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 lib/app_wrappers/LocationHistoryUpdater.dart create mode 100644 lib/services/location_history_service/constants.dart create mode 100644 lib/services/location_history_service/index.dart create mode 100644 lib/services/location_history_service/location_history.dart diff --git a/lib/app_wrappers/LocationHistoryUpdater.dart b/lib/app_wrappers/LocationHistoryUpdater.dart new file mode 100644 index 0000000..6ceae64 --- /dev/null +++ b/lib/app_wrappers/LocationHistoryUpdater.dart @@ -0,0 +1,46 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/location_history_service/index.dart'; +import 'package:locus/services/location_point_service.dart'; +import 'package:provider/provider.dart'; + +/// Makes sure that the [LocationHistory] is updated with the current location +/// from the [CurrentLocationService]. +class LocationHistoryUpdater extends StatefulWidget { + const LocationHistoryUpdater({super.key}); + + @override + State createState() => _LocationHistoryUpdaterState(); +} + +class _LocationHistoryUpdaterState extends State { + late final CurrentLocationService _currentLocation; + late final StreamSubscription _subscription; + late final LocationHistory _locationHistory; + + @override + void initState() { + super.initState(); + + _currentLocation = context.read(); + _subscription = _currentLocation.stream.listen((position) async { + final location = await LocationPointService.fromPosition(position); + + _locationHistory.add(location); + }); + } + + @override + void dispose() { + _subscription.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index cb00750..11fbdbc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,12 +7,12 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_logs/flutter_logs.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; -import 'package:locus/api/get-relays-meta.dart'; +import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/screens/locations_overview_screen_widgets/LocationFetchers.dart'; import 'package:locus/services/app_update_service.dart'; import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/location_history_service/index.dart'; import 'package:locus/services/log_service.dart'; -import 'package:locus/services/manager_service/background_fetch.dart'; import 'package:locus/services/settings_service/index.dart'; import 'package:locus/services/task_service/index.dart'; import 'package:locus/services/view_service/index.dart'; @@ -67,12 +67,14 @@ void main() async { SettingsService.restore(), LogService.restore(), AppUpdateService.restore(), + LocationHistory.restore(), ]); final TaskService taskService = futures[0]; final ViewService viewService = futures[1]; final SettingsService settingsService = futures[2]; final LogService logService = futures[3]; final AppUpdateService appUpdateService = futures[4]; + final LocationHistory locationHistory = futures[5]; await logService.deleteOldLogs(); @@ -91,8 +93,14 @@ void main() async { create: (_) => LocationFetchers(viewService.views)), ChangeNotifierProvider( create: (_) => CurrentLocationService()), + ChangeNotifierProvider(create: (_) => locationHistory), ], - child: const App(), + child: const Stack( + children: [ + LocationHistoryUpdater(), + App(), + ], + ), ), ); } diff --git a/lib/services/location_history_service/constants.dart b/lib/services/location_history_service/constants.dart new file mode 100644 index 0000000..c94cbc4 --- /dev/null +++ b/lib/services/location_history_service/constants.dart @@ -0,0 +1,4 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +const storage = FlutterSecureStorage(); +const KEY = "_locus_own_location_history_v1"; diff --git a/lib/services/location_history_service/index.dart b/lib/services/location_history_service/index.dart new file mode 100644 index 0000000..33eb563 --- /dev/null +++ b/lib/services/location_history_service/index.dart @@ -0,0 +1 @@ +export "./location_history.dart"; diff --git a/lib/services/location_history_service/location_history.dart b/lib/services/location_history_service/location_history.dart new file mode 100644 index 0000000..7917991 --- /dev/null +++ b/lib/services/location_history_service/location_history.dart @@ -0,0 +1,65 @@ +import 'package:flutter/cupertino.dart'; + +import "./constants.dart"; +import '../location_point_service.dart'; + +class LocationHistory extends ChangeNotifier { + late final List locations; + + LocationHistory( + final List? locations, + ) : locations = locations ?? []; + + factory LocationHistory.fromJSON(final Map data) => + LocationHistory( + data["locations"] != null + ? List.from( + data["locations"].map( + (location) => LocationPointService.fromJSON(location), + ), + ) + : null, + ); + + static Future restore() async { + final data = await storage.read(key: KEY); + + if (data == null) { + return LocationHistory(null); + } + + return LocationHistory.fromJSON(data as Map); + } + + // To avoid too many crumbled locations, we only save locations that are at + // least one minute apart + bool _canAdd(final LocationPointService location) { + if (locations.isEmpty) { + return true; + } + + return locations.last.createdAt + .difference(location.createdAt) + .inMinutes + .abs() > + 1; + } + + void add(final LocationPointService location) { + if (!_canAdd(location)) { + return; + } + + locations.add(location); + notifyListeners(); + } + + void clear() { + locations.clear(); + notifyListeners(); + } + + void toJSON() => { + "locations": locations.map((location) => location.toJSON()).toList(), + }; +} From e4a0d1366ea12289587a92527243f1ca3cc34de4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:24:19 +0200 Subject: [PATCH 02/19] refactor: Move InitLocationFromSettings.dart into own widget --- .../InitLocationFromSettings.dart | 10 ++++++ lib/constants/values.dart | 2 +- lib/services/current_location_service.dart | 2 ++ pubspec.lock | 32 +++++++------------ 4 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 lib/app_wrappers/InitLocationFromSettings.dart diff --git a/lib/app_wrappers/InitLocationFromSettings.dart b/lib/app_wrappers/InitLocationFromSettings.dart new file mode 100644 index 0000000..3851604 --- /dev/null +++ b/lib/app_wrappers/InitLocationFromSettings.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class InitLocationFromSettings extends StatelessWidget { + const InitLocationFromSettings({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/constants/values.dart b/lib/constants/values.dart index bde1205..c7b6cd6 100644 --- a/lib/constants/values.dart +++ b/lib/constants/values.dart @@ -5,7 +5,7 @@ const TRANSLATION_HELP_URL = "https://github.com/Myzel394/locus"; const DONATION_URL = "https://github.com/Myzel394/locus"; const APK_RELEASES_URL = "https://github.com/Myzel394/locus/releases"; -const BACKGROUND_LOCATION_UPDATES_MINIMUM_DISTANCE_FILTER = 50; +const BACKGROUND_LOCATION_UPDATES_MINIMUM_DISTANCE_FILTER = 30; const LOCATION_FETCH_TIME_LIMIT = Duration(minutes: 5); const LOCATION_INTERVAL = Duration(minutes: 1); diff --git a/lib/services/current_location_service.dart b/lib/services/current_location_service.dart index 065db0b..8e11d71 100644 --- a/lib/services/current_location_service.dart +++ b/lib/services/current_location_service.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:geolocator/geolocator.dart'; +import 'package:locus/screens/LocationsOverviewScreen.dart'; class CurrentLocationService extends ChangeNotifier { final StreamController _positionStreamController = @@ -11,6 +12,7 @@ class CurrentLocationService extends ChangeNotifier { final StreamController _locationMarkerStreamController = StreamController.broadcast(); Position? currentPosition; + LocationStatus locationStatus = LocationStatus.stale; Stream get stream => _positionStreamController.stream; diff --git a/pubspec.lock b/pubspec.lock index d126e0f..6108353 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -253,10 +253,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.17.1" convert: dependency: transitive description: @@ -809,10 +809,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.18.0" io: dependency: transitive description: @@ -937,18 +937,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" material_design_icons_flutter: dependency: "direct main" description: @@ -1350,10 +1350,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: @@ -1398,10 +1398,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.1" timezone: dependency: transitive description: @@ -1634,14 +1634,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" web_socket_channel: dependency: transitive description: From e7cd821acfab1ba6ceaa9cd726e10a694aa1cef6 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:04:50 +0200 Subject: [PATCH 03/19] refactor: Use UniLinksHandler.dart --- lib/app_wrappers/UniLinksHandler.dart | 119 +++++++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 58 ----------- 3 files changed, 121 insertions(+), 58 deletions(-) create mode 100644 lib/app_wrappers/UniLinksHandler.dart diff --git a/lib/app_wrappers/UniLinksHandler.dart b/lib/app_wrappers/UniLinksHandler.dart new file mode 100644 index 0000000..6f84639 --- /dev/null +++ b/lib/app_wrappers/UniLinksHandler.dart @@ -0,0 +1,119 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_logs/flutter_logs.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:locus/screens/ImportTaskSheet.dart'; +import 'package:uni_links/uni_links.dart'; + +// l10n +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../constants/values.dart'; + +class UniLinksHandler extends StatefulWidget { + const UniLinksHandler({super.key}); + + @override + State createState() => _UniLinksHandlerState(); +} + +class _UniLinksHandlerState extends State { + late final StreamSubscription _stream; + + @override + void initState() { + super.initState(); + + FlutterLogs.logInfo( + LOG_TAG, + "Uni Links", + "Initiating uni links...", + ); + _stream = linkStream.listen((final String? link) { + if (link != null) { + _importLink(link); + } + }); + + _initInitialLink(); + } + + @override + void dispose() { + _stream.cancel(); + super.dispose(); + } + + Future _importLink(final String url) { + FlutterLogs.logInfo( + LOG_TAG, + "Uni Links", + "Importing new uni link", + ); + + return showPlatformModalSheet( + context: context, + material: MaterialModalSheetData( + isScrollControlled: true, + isDismissible: true, + backgroundColor: Colors.transparent, + ), + builder: (context) => ImportTaskSheet(initialURL: url), + ); + } + + void _initInitialLink() async { + final l10n = AppLocalizations.of(context); + + FlutterLogs.logInfo( + LOG_TAG, + "Uni Links", + "Checking initial link", + ); + + try { + // Only fired when the app was in background + final initialLink = await getInitialLink(); + + if (initialLink == null) { + FlutterLogs.logInfo( + LOG_TAG, + "Uni Links", + "----> but it is null, so skipping it.", + ); + return; + } + + await _importLink(initialLink); + } on PlatformException catch (error) { + FlutterLogs.logError( + LOG_TAG, + "Uni Links", + "Error initializing uni links: $error", + ); + + 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(); + }, + ), + ], + ), + ); + } + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index 11fbdbc..e0c9690 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter_logs/flutter_logs.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; +import 'package:locus/app_wrappers/UniLinksHandler.dart'; import 'package:locus/screens/locations_overview_screen_widgets/LocationFetchers.dart'; import 'package:locus/services/app_update_service.dart'; import 'package:locus/services/current_location_service.dart'; @@ -98,6 +99,7 @@ void main() async { child: const Stack( children: [ LocationHistoryUpdater(), + UniLinksHandler(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index d41315a..65963e2 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -19,7 +19,6 @@ import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; -import 'package:locus/api/get-relays-meta.dart'; import 'package:locus/constants/spacing.dart'; import 'package:locus/screens/ImportTaskSheet.dart'; import 'package:locus/screens/SettingsScreen.dart'; @@ -54,7 +53,6 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; -import 'package:uni_links/uni_links.dart'; import '../constants/notifications.dart'; import '../constants/values.dart'; @@ -111,7 +109,6 @@ class _LocationsOverviewScreenState extends State LocationPointService? visibleLocation; Position? lastPosition; - StreamSubscription? _uniLinksStream; Timer? _viewsAlarmCheckerTimer; LocationStatus locationStatus = LocationStatus.stale; @@ -154,7 +151,6 @@ class _LocationsOverviewScreenState extends State ..addPostFrameCallback((_) { _setLocationFromSettings(); initQuickActions(context); - _initUniLinks(); _updateLocaleToSettings(); _updateBackgroundListeners(); _showUpdateDialogIfRequired(); @@ -205,7 +201,6 @@ class _LocationsOverviewScreenState extends State flutterMapController?.dispose(); _viewsAlarmCheckerTimer?.cancel(); - _uniLinksStream?.cancel(); mapEventStream.close(); _removeLiveLocationUpdate(); @@ -425,59 +420,6 @@ class _LocationsOverviewScreenState extends State _positionStream = null; } - Future _importUniLink(final String url) => showPlatformModalSheet( - context: context, - material: MaterialModalSheetData( - isScrollControlled: true, - isDismissible: true, - backgroundColor: Colors.transparent, - ), - builder: (context) => ImportTaskSheet(initialURL: url), - ); - - Future _initUniLinks() async { - final l10n = AppLocalizations.of(context); - - FlutterLogs.logInfo(LOG_TAG, "Uni Links", "Initiating uni links..."); - - _uniLinksStream = linkStream.listen((final String? link) { - if (link != null) { - _importUniLink(link); - } - }); - - try { - // Only fired when the app was in background - final initialLink = await getInitialLink(); - - if (initialLink != null) { - await _importUniLink(initialLink); - } - } on PlatformException catch (error) { - FlutterLogs.logError( - LOG_TAG, - "Uni Links", - "Error initializing uni links: $error", - ); - - 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(); - }, - ), - ], - ), - ); - } - } - void _handleViewAlarmChecker() { _viewsAlarmCheckerTimer = Timer.periodic( const Duration(minutes: 1), From 911e3d94df3e4df94adf507f0ee4effaa54db388 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:21:05 +0200 Subject: [PATCH 04/19] refactor: Add UpdateLastLocationToSettings.dart --- .../UpdateLastLocationToSettings.dart | 58 +++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 15 ----- 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 lib/app_wrappers/UpdateLastLocationToSettings.dart diff --git a/lib/app_wrappers/UpdateLastLocationToSettings.dart b/lib/app_wrappers/UpdateLastLocationToSettings.dart new file mode 100644 index 0000000..f60899f --- /dev/null +++ b/lib/app_wrappers/UpdateLastLocationToSettings.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/settings_service/SettingsMapLocation.dart'; +import 'package:locus/services/settings_service/index.dart'; +import 'package:provider/provider.dart'; + +class UpdateLastLocationToSettings extends StatefulWidget { + const UpdateLastLocationToSettings({super.key}); + + @override + State createState() => + _UpdateLastLocationToSettingsState(); +} + +class _UpdateLastLocationToSettingsState + extends State { + late final CurrentLocationService _currentLocation; + + @override + void initState() { + super.initState(); + + _currentLocation = context.read(); + + _currentLocation.addListener(_handleLocationChange); + } + + void _handleLocationChange() async { + final settings = context.read(); + final position = _currentLocation.currentPosition; + + if (position == null) { + return; + } + + settings.setLastMapLocation( + SettingsLastMapLocation( + latitude: position.latitude, + longitude: position.longitude, + accuracy: position.accuracy, + ), + ); + await settings.save(); + } + + @override + void dispose() { + _currentLocation.removeListener(_handleLocationChange); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index e0c9690..146b9a9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/UniLinksHandler.dart'; +import 'package:locus/app_wrappers/UpdateLastLocationToSettings.dart'; import 'package:locus/screens/locations_overview_screen_widgets/LocationFetchers.dart'; import 'package:locus/services/app_update_service.dart'; import 'package:locus/services/current_location_service.dart'; @@ -100,6 +101,7 @@ void main() async { children: [ LocationHistoryUpdater(), UniLinksHandler(), + UpdateLastLocationToSettings(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 65963e2..1cab84a 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -31,7 +31,6 @@ import 'package:locus/screens/locations_overview_screen_widgets/ViewLocationPopu import 'package:locus/services/current_location_service.dart'; import 'package:locus/services/manager_service/background_locator.dart'; import 'package:locus/services/manager_service/helpers.dart'; -import 'package:locus/services/settings_service/SettingsMapLocation.dart'; import 'package:locus/services/settings_service/index.dart'; import 'package:locus/services/task_service/index.dart'; import 'package:locus/services/view_service/index.dart'; @@ -327,19 +326,6 @@ class _LocationsOverviewScreenState extends State ); } - void _updateLocationToSettings(final Position position) async { - final settings = context.read(); - - settings.setLastMapLocation( - SettingsLastMapLocation( - latitude: position.latitude, - longitude: position.longitude, - accuracy: position.accuracy, - ), - ); - await settings.save(); - } - void _updateBackgroundListeners() async { final settings = context.read(); final taskService = context.read(); @@ -391,7 +377,6 @@ class _LocationsOverviewScreenState extends State currentLocation.updateCurrentPosition(position); _checkViewAlarms(position); - _updateLocationToSettings(position); setState(() { lastPosition = position; From 3d581fc2bc86d96446bd38297a9b1b12beb49905 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:34:36 +0200 Subject: [PATCH 05/19] refactor: Add RegisterBackgroundListeners.dart --- .../RegisterBackgroundListeners.dart | 96 +++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 20 ---- .../manager_service/background_locator.dart | 2 +- 4 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 lib/app_wrappers/RegisterBackgroundListeners.dart diff --git a/lib/app_wrappers/RegisterBackgroundListeners.dart b/lib/app_wrappers/RegisterBackgroundListeners.dart new file mode 100644 index 0000000..4fc4594 --- /dev/null +++ b/lib/app_wrappers/RegisterBackgroundListeners.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_logs/flutter_logs.dart'; +import 'package:locus/constants/values.dart'; +import 'package:locus/services/manager_service/background_locator.dart'; +import 'package:locus/services/manager_service/index.dart'; +import 'package:locus/services/settings_service/index.dart'; +import 'package:locus/services/task_service/index.dart'; +import 'package:provider/provider.dart'; + +class RegisterBackgroundListeners extends StatefulWidget { + const RegisterBackgroundListeners({super.key}); + + @override + State createState() => + _RegisterBackgroundListenersState(); +} + +class _RegisterBackgroundListenersState + extends State { + late final SettingsService _settings; + late final TaskService _taskService; + + @override + void initState() { + super.initState(); + + _settings = context.read(); + _taskService = context.read(); + + _settings.addListener(_updateListeners); + _taskService.addListener(_updateListeners); + + _updateListeners(); + } + + @override + void dispose() { + _settings.removeListener(_updateListeners); + _taskService.removeListener(_updateListeners); + + super.dispose(); + } + + void _updateListeners() async { + FlutterLogs.logInfo( + LOG_TAG, + "Register Background Listeners", + "Updating listeners...", + ); + final shouldCheckLocation = (await _taskService.hasRunningTasks()) || + (await _taskService.hasScheduledTasks()); + + if (!shouldCheckLocation) { + FlutterLogs.logInfo(LOG_TAG, "Register Background Listeners", + "---> but no tasks are running or scheduled, so unregistering everything."); + + await removeBackgroundLocator(); + removeBackgroundFetch(); + + return; + } + + FlutterLogs.logInfo( + LOG_TAG, + "Register Background Listeners", + "Registering BackgroundFetch", + ); + + // Always use background fetch as a fallback + await configureBackgroundFetch(); + registerBackgroundFetch(); + + if (_settings.useRealtimeUpdates) { + FlutterLogs.logInfo( + LOG_TAG, + "Register Background Listeners", + "Should use realtime updates; Registering background locator", + ); + + await configureBackgroundLocator(); + await registerBackgroundLocator(context); + } else { + FlutterLogs.logInfo( + LOG_TAG, + "Register Background Listeners", + "Not using realtime updates; Removing background locator", + ); + removeBackgroundLocator(); + } + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index 146b9a9..0f8c833 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter_logs/flutter_logs.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; +import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/UniLinksHandler.dart'; import 'package:locus/app_wrappers/UpdateLastLocationToSettings.dart'; import 'package:locus/screens/locations_overview_screen_widgets/LocationFetchers.dart'; @@ -102,6 +103,7 @@ void main() async { LocationHistoryUpdater(), UniLinksHandler(), UpdateLastLocationToSettings(), + RegisterBackgroundListeners(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 1cab84a..343a958 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -141,8 +141,6 @@ class _LocationsOverviewScreenState extends State locationFetchers.addAll(viewService.views); - settings.addListener(_updateBackgroundListeners); - taskService.addListener(_updateBackgroundListeners); locationFetchers.addLocationUpdatesListener(_rebuild); WidgetsBinding.instance @@ -151,7 +149,6 @@ class _LocationsOverviewScreenState extends State _setLocationFromSettings(); initQuickActions(context); _updateLocaleToSettings(); - _updateBackgroundListeners(); _showUpdateDialogIfRequired(); _initLiveLocationUpdate(); locationFetchers.fetchPreviewLocations(); @@ -326,23 +323,6 @@ class _LocationsOverviewScreenState extends State ); } - void _updateBackgroundListeners() async { - final settings = context.read(); - final taskService = context.read(); - - if (settings.useRealtimeUpdates && - ((await taskService.hasRunningTasks()) || - (await taskService.hasScheduledTasks()))) { - removeBackgroundFetch(); - - await configureBackgroundLocator(); - await initializeBackgroundLocator(context); - } else { - await configureBackgroundFetch(); - registerBackgroundFetch(); - } - } - void _checkViewAlarms( final Position position, ) async { diff --git a/lib/services/manager_service/background_locator.dart b/lib/services/manager_service/background_locator.dart index 5401924..61d0e49 100644 --- a/lib/services/manager_service/background_locator.dart +++ b/lib/services/manager_service/background_locator.dart @@ -68,7 +68,7 @@ Future configureBackgroundLocator() { return BackgroundLocator.initialize(); } -Future initializeBackgroundLocator(final BuildContext context,) { +Future registerBackgroundLocator(final BuildContext context,) { final l10n = AppLocalizations.of(context); FlutterLogs.logInfo( From 6c0b076c9e9059be3a9badf9f6c8fcd3fd8318c0 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:37:55 +0200 Subject: [PATCH 06/19] refactor: Add UpdateLocaleToSettings.dart --- lib/app_wrappers/UpdateLocaleToSettings.dart | 42 ++++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 8 ---- 3 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 lib/app_wrappers/UpdateLocaleToSettings.dart diff --git a/lib/app_wrappers/UpdateLocaleToSettings.dart b/lib/app_wrappers/UpdateLocaleToSettings.dart new file mode 100644 index 0000000..d67a26c --- /dev/null +++ b/lib/app_wrappers/UpdateLocaleToSettings.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:locus/services/settings_service/index.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class UpdateLocaleToSettings extends StatefulWidget { + const UpdateLocaleToSettings({super.key}); + + @override + State createState() => _UpdateLocaleToSettingsState(); +} + +class _UpdateLocaleToSettingsState extends State { + late final SettingsService _settings; + + @override + void initState() { + super.initState(); + + _settings = context.read(); + _settings.addListener(_updateLocale); + } + + @override + void dispose() { + _settings.removeListener(_updateLocale); + super.dispose(); + } + + void _updateLocale() async { + _settings.localeName = AppLocalizations + .of(context) + .localeName; + + await _settings.save(); + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index 0f8c833..a8a33aa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/UniLinksHandler.dart'; import 'package:locus/app_wrappers/UpdateLastLocationToSettings.dart'; +import 'package:locus/app_wrappers/UpdateLocaleToSettings.dart'; import 'package:locus/screens/locations_overview_screen_widgets/LocationFetchers.dart'; import 'package:locus/services/app_update_service.dart'; import 'package:locus/services/current_location_service.dart'; @@ -104,6 +105,7 @@ void main() async { UniLinksHandler(), UpdateLastLocationToSettings(), RegisterBackgroundListeners(), + UpdateLocaleToSettings(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 343a958..06e979f 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -148,7 +148,6 @@ class _LocationsOverviewScreenState extends State ..addPostFrameCallback((_) { _setLocationFromSettings(); initQuickActions(context); - _updateLocaleToSettings(); _showUpdateDialogIfRequired(); _initLiveLocationUpdate(); locationFetchers.fetchPreviewLocations(); @@ -438,13 +437,6 @@ class _LocationsOverviewScreenState extends State }); } - void _updateLocaleToSettings() { - final settingsService = context.read(); - - settingsService.localeName = AppLocalizations.of(context).localeName; - settingsService.save(); - } - void _showUpdateDialogIfRequired() async { final l10n = AppLocalizations.of(context); final appUpdateService = context.read(); From 6a8e6d2c1ed9ca5791bc5e7f9e1d30143a7b2365 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:47:47 +0200 Subject: [PATCH 07/19] refactor: Add HandleNotifications.dart --- lib/app_wrappers/HandleNotifications.dart | 98 +++++++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 49 ------------ 3 files changed, 100 insertions(+), 49 deletions(-) create mode 100644 lib/app_wrappers/HandleNotifications.dart diff --git a/lib/app_wrappers/HandleNotifications.dart b/lib/app_wrappers/HandleNotifications.dart new file mode 100644 index 0000000..b023cc4 --- /dev/null +++ b/lib/app_wrappers/HandleNotifications.dart @@ -0,0 +1,98 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:flutter_logs/flutter_logs.dart'; +import 'package:locus/constants/notifications.dart'; +import 'package:locus/constants/values.dart'; +import 'package:locus/main.dart'; +import 'package:locus/screens/ViewDetailsScreen.dart'; +import 'package:locus/services/view_service/index.dart'; +import 'package:locus/utils/PageRoute.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:provider/provider.dart'; + +class HandleNotifications extends StatefulWidget { + const HandleNotifications({super.key}); + + @override + State createState() => _HandleNotificationsState(); +} + +class _HandleNotificationsState extends State { + late final StreamSubscription _subscription; + + @override + void initState() { + super.initState(); + _subscription = + selectedNotificationsStream.stream.listen(_handleNotification); + } + + @override + void dispose() { + _subscription.cancel(); + + super.dispose(); + } + + void _handleNotification(final NotificationResponse notification) { + FlutterLogs.logInfo( + LOG_TAG, + "Notification", + "Notification received: ${notification.payload}", + ); + + if (notification.payload == null) { + FlutterLogs.logWarn( + LOG_TAG, + "Notification", + "----> but no payload, so ignoring.", + ); + return; + } + + try { + final data = jsonDecode(notification.payload!); + final type = NotificationActionType.values[data["type"]]; + + FlutterLogs.logInfo( + LOG_TAG, + "Notification", + "Type is $type." + ); + + switch (type) { + case NotificationActionType.openTaskView: + final viewService = context.read(); + + Navigator.of(context).push( + NativePageRoute( + context: context, + builder: (_) => + ViewDetailsScreen( + view: viewService.getViewById(data["taskViewID"]), + ), + ), + ); + break; + case NotificationActionType.openPermissionsSettings: + openAppSettings(); + + break; + } + } catch (error) { + FlutterLogs.logError( + LOG_TAG, + "Notification", + "Error handling notification: $error", + ); + } + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index a8a33aa..8cbb640 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_logs/flutter_logs.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; +import 'package:locus/app_wrappers/HandleNotifications.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/UniLinksHandler.dart'; @@ -106,6 +107,7 @@ void main() async { UpdateLastLocationToSettings(), RegisterBackgroundListeners(), UpdateLocaleToSettings(), + HandleNotifications(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 06e979f..3fb1012 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:core'; import 'dart:io'; import 'dart:math'; @@ -29,7 +28,6 @@ import 'package:locus/screens/locations_overview_screen_widgets/OutOfBoundMarker import 'package:locus/screens/locations_overview_screen_widgets/ShareLocationSheet.dart'; import 'package:locus/screens/locations_overview_screen_widgets/ViewLocationPopup.dart'; import 'package:locus/services/current_location_service.dart'; -import 'package:locus/services/manager_service/background_locator.dart'; import 'package:locus/services/manager_service/helpers.dart'; import 'package:locus/services/settings_service/index.dart'; import 'package:locus/services/task_service/index.dart'; @@ -50,22 +48,16 @@ import 'package:locus/widgets/MapActionsContainer.dart'; import 'package:locus/widgets/Paper.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; -import '../constants/notifications.dart'; import '../constants/values.dart'; import '../init_quick_actions.dart'; -import '../main.dart'; import '../services/app_update_service.dart'; import '../services/location_point_service.dart'; import '../services/log_service.dart'; -import '../services/manager_service/background_fetch.dart'; -import '../utils/PageRoute.dart'; import '../utils/color.dart'; import '../utils/platform.dart'; import '../utils/theme.dart'; -import 'ViewDetailsScreen.dart'; import 'locations_overview_screen_widgets/ViewDetailsSheet.dart'; // After this threshold, locations will not be merged together anymore @@ -137,7 +129,6 @@ class _LocationsOverviewScreenState extends State final locationFetchers = context.read(); _handleViewAlarmChecker(); - _handleNotifications(); locationFetchers.addAll(viewService.views); @@ -397,46 +388,6 @@ class _LocationsOverviewScreenState extends State ); } - void _handleNotifications() { - selectedNotificationsStream.stream.listen((notification) { - FlutterLogs.logInfo( - LOG_TAG, - "Notification", - "Notification received: ${notification.payload}", - ); - - try { - final data = jsonDecode(notification.payload ?? "{}"); - final type = NotificationActionType.values[data["type"]]; - - switch (type) { - case NotificationActionType.openTaskView: - final viewService = context.read(); - - Navigator.of(context).push( - NativePageRoute( - context: context, - builder: (_) => ViewDetailsScreen( - view: viewService.getViewById(data["taskViewID"]), - ), - ), - ); - break; - case NotificationActionType.openPermissionsSettings: - openAppSettings(); - - break; - } - } catch (error) { - FlutterLogs.logError( - LOG_TAG, - "Notification", - "Error handling notification: $error", - ); - } - }); - } - void _showUpdateDialogIfRequired() async { final l10n = AppLocalizations.of(context); final appUpdateService = context.read(); From 39d981a5d0a9ab1d8b7bb15cce28d4cf0d9c12bc Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:58:33 +0200 Subject: [PATCH 08/19] refactor: Add CheckViewAlarmsLive.dart --- lib/app_wrappers/CheckViewAlarmsLive.dart | 56 +++++++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 20 -------- 3 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 lib/app_wrappers/CheckViewAlarmsLive.dart diff --git a/lib/app_wrappers/CheckViewAlarmsLive.dart b/lib/app_wrappers/CheckViewAlarmsLive.dart new file mode 100644 index 0000000..3131a6d --- /dev/null +++ b/lib/app_wrappers/CheckViewAlarmsLive.dart @@ -0,0 +1,56 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/location_point_service.dart'; +import 'package:locus/services/manager_service/helpers.dart'; +import 'package:locus/services/view_service/index.dart'; +import 'package:provider/provider.dart'; + +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +/// Checks view alarms while the app is in use +class CheckViewAlarmsLive extends StatefulWidget { + const CheckViewAlarmsLive({super.key}); + + @override + State createState() => _CheckViewAlarmsLiveState(); +} + +class _CheckViewAlarmsLiveState extends State { + late final CurrentLocationService _currentLocation; + late final StreamSubscription _subscription; + + @override + void initState() { + super.initState(); + + _subscription = _currentLocation.stream.listen((position) async { + final l10n = AppLocalizations.of(context); + final viewService = context.read(); + final userLocation = await LocationPointService.fromPosition(position); + + if (!mounted) { + return; + } + + checkViewAlarms( + l10n: l10n, + viewService: viewService, + userLocation: userLocation, + ); + }); + } + + @override + void dispose() { + _subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/main.dart b/lib/main.dart index 8cbb640..ecd16ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_logs/flutter_logs.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; +import 'package:locus/app_wrappers/CheckViewAlarmsLive.dart'; import 'package:locus/app_wrappers/HandleNotifications.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; @@ -108,6 +109,7 @@ void main() async { RegisterBackgroundListeners(), UpdateLocaleToSettings(), HandleNotifications(), + CheckViewAlarmsLive(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 3fb1012..0920fc4 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -313,24 +313,6 @@ class _LocationsOverviewScreenState extends State ); } - void _checkViewAlarms( - final Position position, - ) async { - final l10n = AppLocalizations.of(context); - final viewService = context.read(); - final userLocation = await LocationPointService.fromPosition(position); - - if (!mounted) { - return; - } - - checkViewAlarms( - l10n: l10n, - viewService: viewService, - userLocation: userLocation, - ); - } - void _initLiveLocationUpdate() { if (_positionStream != null) { return; @@ -346,8 +328,6 @@ class _LocationsOverviewScreenState extends State currentLocation.updateCurrentPosition(position); - _checkViewAlarms(position); - setState(() { lastPosition = position; locationStatus = LocationStatus.active; From 0f48c917365b37f0433443146eab4169626dded5 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 22:06:44 +0200 Subject: [PATCH 09/19] refactor: Add ManageQuickActions.dart --- lib/app_wrappers/ManageQuickActions.dart | 133 +++++++++++++++++++++++ lib/init_quick_actions.dart | 80 -------------- lib/main.dart | 6 +- lib/screens/LocationsOverviewScreen.dart | 3 - lib/screens/ShortcutScreen.dart | 2 +- lib/screens/WelcomeScreen.dart | 9 -- 6 files changed, 138 insertions(+), 95 deletions(-) create mode 100644 lib/app_wrappers/ManageQuickActions.dart delete mode 100644 lib/init_quick_actions.dart diff --git a/lib/app_wrappers/ManageQuickActions.dart b/lib/app_wrappers/ManageQuickActions.dart new file mode 100644 index 0000000..2f2be0b --- /dev/null +++ b/lib/app_wrappers/ManageQuickActions.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_logs/flutter_logs.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:locus/constants/values.dart'; +import 'package:locus/screens/ShortcutScreen.dart'; +import 'package:locus/services/settings_service/index.dart'; +import 'package:locus/utils/PageRoute.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; +import 'package:provider/provider.dart'; +import 'package:quick_actions/quick_actions.dart'; + +enum ShortcutType { + createOneHour, + shareNow, + stopAllTasks, +} + +const actions = QuickActions(); + +const SHORTCUT_TYPE_ICON_MAP = { + ShortcutType.createOneHour: Icons.timelapse_rounded, + ShortcutType.shareNow: Icons.location_on, + ShortcutType.stopAllTasks: Icons.stop_circle_rounded, +}; + +class ManageQuickActions extends StatefulWidget { + const ManageQuickActions({super.key}); + + @override + State createState() => _ManageQuickActionsState(); +} + +class _ManageQuickActionsState extends State { + @override + void initState() { + super.initState(); + + final settings = context.read(); + + if (settings.userHasSeenWelcomeScreen) { + _registerActions(); + } else { + _removeActions(); + } + } + + void _registerActions() { + final l10n = AppLocalizations.of(context); + + FlutterLogs.logInfo( + LOG_TAG, + "Quick Actions", + "Initializing quick actions...", + ); + + actions.initialize((type) async { + FlutterLogs.logInfo( + LOG_TAG, "Quick Actions", "Quick action $type triggered."); + + if (isCupertino(context)) { + showCupertinoModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (_) => + ShortcutScreen( + type: ShortcutType.values.firstWhere( + (element) => element.name == type, + ), + ), + ); + } else { + Navigator.push( + context, + NativePageRoute( + context: context, + builder: (_) => + ShortcutScreen( + type: ShortcutType.values.firstWhere( + (element) => element.name == type, + ), + ), + ), + ); + } + }); + + actions.setShortcutItems([ + ShortcutItem( + type: ShortcutType.createOneHour.name, + localizedTitle: l10n.quickActions_createOneHour, + icon: "ic_quick_actions_create_one_hour_task", + ), + ShortcutItem( + type: ShortcutType.shareNow.name, + localizedTitle: l10n.quickActions_shareNow, + icon: "ic_quick_actions_share_now", + ), + ShortcutItem( + type: ShortcutType.stopAllTasks.name, + localizedTitle: l10n.quickActions_stopTasks, + icon: "ic_quick_actions_stop_all_tasks", + ), + ]); + + FlutterLogs.logInfo( + LOG_TAG, + "Quick Actions", + "Quick actions initialized successfully!", + ); + } + + void _removeActions() { + FlutterLogs.logInfo( + LOG_TAG, + "Quick Actions", + "Removing quick actions...", + ); + + actions.clearShortcutItems(); + + FlutterLogs.logInfo( + LOG_TAG, + "Quick Actions", + "Quick actions removed successfully!", + ); + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/init_quick_actions.dart b/lib/init_quick_actions.dart deleted file mode 100644 index 19d9a9e..0000000 --- a/lib/init_quick_actions.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_logs/flutter_logs.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; -import 'package:locus/constants/values.dart'; -import 'package:locus/screens/ShortcutScreen.dart'; -import 'package:locus/utils/PageRoute.dart'; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; -import 'package:quick_actions/quick_actions.dart'; - -enum ShortcutType { - createOneHour, - shareNow, - stopAllTasks, -} - -const actions = QuickActions(); - -const SHORTCUT_TYPE_ICON_MAP = { - ShortcutType.createOneHour: Icons.timelapse_rounded, - ShortcutType.shareNow: Icons.location_on, - ShortcutType.stopAllTasks: Icons.stop_circle_rounded, -}; - -void initQuickActions(final BuildContext context) { - final l10n = AppLocalizations.of(context); - - FlutterLogs.logInfo( - LOG_TAG, "Quick Actions", "Initializing quick actions..."); - - actions.initialize((type) async { - FlutterLogs.logInfo( - LOG_TAG, "Quick Actions", "Quick action $type triggered."); - - if (isCupertino(context)) { - showCupertinoModalBottomSheet( - context: context, - backgroundColor: Colors.transparent, - builder: (_) => ShortcutScreen( - type: ShortcutType.values.firstWhere( - (element) => element.name == type, - ), - ), - ); - } else { - Navigator.push( - context, - NativePageRoute( - context: context, - builder: (_) => ShortcutScreen( - type: ShortcutType.values.firstWhere( - (element) => element.name == type, - ), - ), - ), - ); - } - }); - - actions.setShortcutItems([ - ShortcutItem( - type: ShortcutType.createOneHour.name, - localizedTitle: l10n.quickActions_createOneHour, - icon: "ic_quick_actions_create_one_hour_task", - ), - ShortcutItem( - type: ShortcutType.shareNow.name, - localizedTitle: l10n.quickActions_shareNow, - icon: "ic_quick_actions_share_now", - ), - ShortcutItem( - type: ShortcutType.stopAllTasks.name, - localizedTitle: l10n.quickActions_stopTasks, - icon: "ic_quick_actions_stop_all_tasks", - ), - ]); - - FlutterLogs.logInfo( - LOG_TAG, "Quick Actions", "Quick actions initialized successfully!"); -} diff --git a/lib/main.dart b/lib/main.dart index ecd16ac..61f91be 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:locus/app_wrappers/CheckViewAlarmsLive.dart'; import 'package:locus/app_wrappers/HandleNotifications.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; +import 'package:locus/app_wrappers/ManageQuickActions.dart'; import 'package:locus/app_wrappers/UniLinksHandler.dart'; import 'package:locus/app_wrappers/UpdateLastLocationToSettings.dart'; import 'package:locus/app_wrappers/UpdateLocaleToSettings.dart'; @@ -27,7 +28,7 @@ import 'package:provider/provider.dart'; const storage = FlutterSecureStorage(); final StreamController selectedNotificationsStream = - StreamController.broadcast(); +StreamController.broadcast(); void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -55,7 +56,7 @@ void main() async { ); FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); const initializationSettings = InitializationSettings( android: AndroidInitializationSettings("ic_launcher_foreground"), iOS: DarwinInitializationSettings(), @@ -110,6 +111,7 @@ void main() async { UpdateLocaleToSettings(), HandleNotifications(), CheckViewAlarmsLive(), + ManageQuickActions(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 0920fc4..b37c3b0 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -28,7 +28,6 @@ import 'package:locus/screens/locations_overview_screen_widgets/OutOfBoundMarker import 'package:locus/screens/locations_overview_screen_widgets/ShareLocationSheet.dart'; import 'package:locus/screens/locations_overview_screen_widgets/ViewLocationPopup.dart'; import 'package:locus/services/current_location_service.dart'; -import 'package:locus/services/manager_service/helpers.dart'; import 'package:locus/services/settings_service/index.dart'; import 'package:locus/services/task_service/index.dart'; import 'package:locus/services/view_service/index.dart'; @@ -51,7 +50,6 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:provider/provider.dart'; import '../constants/values.dart'; -import '../init_quick_actions.dart'; import '../services/app_update_service.dart'; import '../services/location_point_service.dart'; import '../services/log_service.dart'; @@ -138,7 +136,6 @@ class _LocationsOverviewScreenState extends State ..addObserver(this) ..addPostFrameCallback((_) { _setLocationFromSettings(); - initQuickActions(context); _showUpdateDialogIfRequired(); _initLiveLocationUpdate(); locationFetchers.fetchPreviewLocations(); diff --git a/lib/screens/ShortcutScreen.dart b/lib/screens/ShortcutScreen.dart index 3fb8ee3..5bf8fe3 100644 --- a/lib/screens/ShortcutScreen.dart +++ b/lib/screens/ShortcutScreen.dart @@ -2,8 +2,8 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:locus/app_wrappers/ManageQuickActions.dart'; import 'package:locus/constants/spacing.dart'; -import 'package:locus/init_quick_actions.dart'; import 'package:locus/services/task_service/index.dart'; import 'package:locus/services/timers_service.dart'; import 'package:locus/utils/location/index.dart'; diff --git a/lib/screens/WelcomeScreen.dart b/lib/screens/WelcomeScreen.dart index 2c57585..ab5c2cc 100644 --- a/lib/screens/WelcomeScreen.dart +++ b/lib/screens/WelcomeScreen.dart @@ -6,7 +6,6 @@ import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_svg/svg.dart'; import 'package:locus/constants/spacing.dart'; -import 'package:locus/init_quick_actions.dart'; import 'package:locus/screens/LocationsOverviewScreen.dart'; import 'package:locus/screens/welcome_screen_widgets/SimpleContinuePage.dart'; import 'package:locus/services/settings_service/index.dart'; @@ -30,14 +29,6 @@ class WelcomeScreen extends StatefulWidget { class _WelcomeScreenState extends State { final PageController _controller = PageController(); - @override - void initState() { - super.initState(); - - // Reset - actions.clearShortcutItems(); - } - @override void dispose() { _controller.dispose(); From c0f85f58e4fbaeb4e6ff0f3d6d68f9e68c0170c0 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 22:16:41 +0200 Subject: [PATCH 10/19] refactor: Add InitCurrentLocationFromSettings.dart --- .../InitCurrentLocationFromSettings.dart | 42 +++++++++++++++++++ lib/main.dart | 6 ++- lib/screens/LocationsOverviewScreen.dart | 21 ++-------- .../settings_service/SettingsMapLocation.dart | 15 +++++++ 4 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 lib/app_wrappers/InitCurrentLocationFromSettings.dart diff --git a/lib/app_wrappers/InitCurrentLocationFromSettings.dart b/lib/app_wrappers/InitCurrentLocationFromSettings.dart new file mode 100644 index 0000000..ad66988 --- /dev/null +++ b/lib/app_wrappers/InitCurrentLocationFromSettings.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/settings_service/SettingsMapLocation.dart'; +import 'package:locus/services/settings_service/index.dart'; +import 'package:provider/provider.dart'; + +class InitCurrentLocationFromSettings extends StatefulWidget { + const InitCurrentLocationFromSettings({super.key}); + + @override + State createState() => + _InitCurrentLocationFromSettingsState(); +} + +class _InitCurrentLocationFromSettingsState + extends State { + late final CurrentLocationService _currentLocation; + + @override + void initState() { + super.initState(); + + _currentLocation = context.read(); + final settings = context.read(); + final lastLocation = settings.getLastMapLocation(); + + if (lastLocation != null) { + _setLocation(lastLocation); + } + } + + void _setLocation(final SettingsLastMapLocation rawLocation) { + final position = rawLocation.asPosition(); + + _currentLocation.updateCurrentPosition(position); + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index 61f91be..187f302 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:locus/App.dart'; import 'package:locus/app_wrappers/CheckViewAlarmsLive.dart'; import 'package:locus/app_wrappers/HandleNotifications.dart'; +import 'package:locus/app_wrappers/InitCurrentLocationFromSettings.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/ManageQuickActions.dart'; @@ -28,7 +29,7 @@ import 'package:provider/provider.dart'; const storage = FlutterSecureStorage(); final StreamController selectedNotificationsStream = -StreamController.broadcast(); + StreamController.broadcast(); void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -56,7 +57,7 @@ void main() async { ); FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); const initializationSettings = InitializationSettings( android: AndroidInitializationSettings("ic_launcher_foreground"), iOS: DarwinInitializationSettings(), @@ -112,6 +113,7 @@ void main() async { HandleNotifications(), CheckViewAlarmsLive(), ManageQuickActions(), + InitCurrentLocationFromSettings(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index b37c3b0..22e055d 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -221,10 +221,9 @@ class _LocationsOverviewScreenState extends State void _setLocationFromSettings() async { final settings = context.read(); - final rawPosition = settings.getLastMapLocation(); - final currentLocation = context.read(); + final rawLocation = settings.getLastMapLocation(); - if (rawPosition == null) { + if (rawLocation == null) { return; } @@ -232,21 +231,7 @@ class _LocationsOverviewScreenState extends State locationStatus = LocationStatus.stale; }); - final position = Position( - latitude: rawPosition.latitude, - longitude: rawPosition.longitude, - accuracy: rawPosition.accuracy, - altitudeAccuracy: 0.0, - headingAccuracy: 0.0, - timestamp: DateTime.now(), - altitude: 0, - heading: 0, - speed: 0, - speedAccuracy: 0, - ); - - await _animateToPosition(position); - currentLocation.updateCurrentPosition(position); + await _animateToPosition(rawLocation.asPosition()); } List mergeLocationsIfRequired( diff --git a/lib/services/settings_service/SettingsMapLocation.dart b/lib/services/settings_service/SettingsMapLocation.dart index 528b262..f6641b7 100644 --- a/lib/services/settings_service/SettingsMapLocation.dart +++ b/lib/services/settings_service/SettingsMapLocation.dart @@ -1,3 +1,4 @@ +import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; class SettingsLastMapLocation { @@ -26,4 +27,18 @@ class SettingsLastMapLocation { }; LatLng toLatLng() => LatLng(latitude, longitude); + + Position asPosition() => + Position( + latitude: latitude, + longitude: longitude, + accuracy: accuracy, + altitudeAccuracy: 0.0, + headingAccuracy: 0.0, + timestamp: DateTime.now(), + altitude: 0, + heading: 0, + speed: 0, + speedAccuracy: 0, + ); } From 7113c5d3659e44baeb9d8a4c6a18657d15c75bb9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 11 Oct 2023 22:21:40 +0200 Subject: [PATCH 11/19] refactor: Add ShowUpdateDialog.dart --- lib/app_wrappers/ShowUpdateDialog.dart | 76 ++++++++++++++++++++++++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 54 ----------------- 3 files changed, 78 insertions(+), 54 deletions(-) create mode 100644 lib/app_wrappers/ShowUpdateDialog.dart diff --git a/lib/app_wrappers/ShowUpdateDialog.dart b/lib/app_wrappers/ShowUpdateDialog.dart new file mode 100644 index 0000000..f5905b3 --- /dev/null +++ b/lib/app_wrappers/ShowUpdateDialog.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:locus/services/app_update_service.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; + +class ShowUpdateDialog extends StatefulWidget { + const ShowUpdateDialog({super.key}); + + @override + State createState() => _ShowUpdateDialogState(); +} + +class _ShowUpdateDialogState extends State { + late final AppUpdateService _appUpdateService; + + @override + void initState() { + super.initState(); + + _appUpdateService = context.read(); + } + + void _showDialogIfRequired() async { + final l10n = AppLocalizations.of(context); + + if (_appUpdateService.shouldShowDialogue() && + !_appUpdateService.hasShownDialogue && + mounted) { + await showPlatformDialog( + context: context, + barrierDismissible: false, + 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(); + + 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(); + } + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/main.dart b/lib/main.dart index 187f302..92a195a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,6 +13,7 @@ import 'package:locus/app_wrappers/InitCurrentLocationFromSettings.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/ManageQuickActions.dart'; +import 'package:locus/app_wrappers/ShowUpdateDialog.dart'; import 'package:locus/app_wrappers/UniLinksHandler.dart'; import 'package:locus/app_wrappers/UpdateLastLocationToSettings.dart'; import 'package:locus/app_wrappers/UpdateLocaleToSettings.dart'; @@ -114,6 +115,7 @@ void main() async { CheckViewAlarmsLive(), ManageQuickActions(), InitCurrentLocationFromSettings(), + ShowUpdateDialog(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 22e055d..68bf7ea 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -123,7 +123,6 @@ class _LocationsOverviewScreenState extends State final viewService = context.read(); final logService = context.read(); final settings = context.read(); - final appUpdateService = context.read(); final locationFetchers = context.read(); _handleViewAlarmChecker(); @@ -136,13 +135,11 @@ class _LocationsOverviewScreenState extends State ..addObserver(this) ..addPostFrameCallback((_) { _setLocationFromSettings(); - _showUpdateDialogIfRequired(); _initLiveLocationUpdate(); locationFetchers.fetchPreviewLocations(); taskService.checkup(logService); - appUpdateService.addListener(_rebuild); viewService.addListener(_handleViewServiceChange); Geolocator.checkPermission().then((status) { @@ -178,7 +175,6 @@ class _LocationsOverviewScreenState extends State @override dispose() { - final appUpdateService = context.read(); final locationFetchers = context.read(); flutterMapController?.dispose(); @@ -190,7 +186,6 @@ class _LocationsOverviewScreenState extends State WidgetsBinding.instance.removeObserver(this); - appUpdateService.removeListener(_rebuild); locationFetchers.removeLocationUpdatesListener(_rebuild); super.dispose(); @@ -350,55 +345,6 @@ class _LocationsOverviewScreenState extends State ); } - void _showUpdateDialogIfRequired() async { - final l10n = AppLocalizations.of(context); - final appUpdateService = context.read(); - - if (appUpdateService.shouldShowDialogue() && - !appUpdateService.hasShownDialogue && - mounted) { - await showPlatformDialog( - context: context, - barrierDismissible: false, - 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(); - - 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 { From efb029eee458289a10e735875a1fa58af9e694dc Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:24:04 +0200 Subject: [PATCH 12/19] refactor: Add PublishTaskPositionsOnUpdate.dart --- .../PublishTaskPositionsOnUpdate.dart | 58 +++++++++++++++++++ .../RegisterBackgroundListeners.dart | 4 ++ lib/main.dart | 2 + lib/screens/LocationsOverviewScreen.dart | 16 ----- 4 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 lib/app_wrappers/PublishTaskPositionsOnUpdate.dart diff --git a/lib/app_wrappers/PublishTaskPositionsOnUpdate.dart b/lib/app_wrappers/PublishTaskPositionsOnUpdate.dart new file mode 100644 index 0000000..efec59f --- /dev/null +++ b/lib/app_wrappers/PublishTaskPositionsOnUpdate.dart @@ -0,0 +1,58 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/location_point_service.dart'; +import 'package:locus/services/task_service/index.dart'; +import 'package:provider/provider.dart'; + +class PublishTaskPositionsOnUpdate extends StatefulWidget { + const PublishTaskPositionsOnUpdate({super.key}); + + @override + State createState() => + _PublishTaskPositionsOnUpdateState(); +} + +class _PublishTaskPositionsOnUpdateState + extends State { + late final CurrentLocationService _currentLocation; + late final StreamSubscription _stream; + + @override + void initState() { + super.initState(); + + _currentLocation = context.read(); + + _stream = _currentLocation.stream.listen((position) async { + final taskService = context.read(); + + final runningTasks = await taskService.getRunningTasks().toList(); + + if (runningTasks.isEmpty) { + return; + } + + final locationData = await LocationPointService.fromPosition(position); + + for (final task in runningTasks) { + await task.publisher.publishOutstandingPositions(); + await task.publisher.publishLocation( + locationData.copyWithDifferentId(), + ); + } + }); + } + + @override + void dispose() { + _stream.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/app_wrappers/RegisterBackgroundListeners.dart b/lib/app_wrappers/RegisterBackgroundListeners.dart index 4fc4594..92c0b41 100644 --- a/lib/app_wrappers/RegisterBackgroundListeners.dart +++ b/lib/app_wrappers/RegisterBackgroundListeners.dart @@ -42,6 +42,10 @@ class _RegisterBackgroundListenersState } void _updateListeners() async { + if (!_settings.userHasSeenWelcomeScreen) { + return; + } + FlutterLogs.logInfo( LOG_TAG, "Register Background Listeners", diff --git a/lib/main.dart b/lib/main.dart index 92a195a..da3d09e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:locus/app_wrappers/CheckViewAlarmsLive.dart'; import 'package:locus/app_wrappers/HandleNotifications.dart'; import 'package:locus/app_wrappers/InitCurrentLocationFromSettings.dart'; import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; +import 'package:locus/app_wrappers/PublishTaskPositionsOnUpdate.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/ManageQuickActions.dart'; import 'package:locus/app_wrappers/ShowUpdateDialog.dart'; @@ -116,6 +117,7 @@ void main() async { ManageQuickActions(), InitCurrentLocationFromSettings(), ShowUpdateDialog(), + PublishTaskPositionsOnUpdate(), App(), ], ), diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 68bf7ea..4bccc0f 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -300,7 +300,6 @@ class _LocationsOverviewScreenState extends State ); _positionStream!.listen((position) async { - final taskService = context.read(); final currentLocation = context.read(); currentLocation.updateCurrentPosition(position); @@ -309,21 +308,6 @@ class _LocationsOverviewScreenState extends State lastPosition = position; locationStatus = LocationStatus.active; }); - - final runningTasks = await taskService.getRunningTasks().toList(); - - if (runningTasks.isEmpty) { - return; - } - - final locationData = await LocationPointService.fromPosition(position); - - for (final task in runningTasks) { - await task.publisher.publishOutstandingPositions(); - await task.publisher.publishLocation( - locationData.copyWithDifferentId(), - ); - } }); } From 1feb7adc30c28cb9974a0c3dd029d753ddc5f338 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 12 Oct 2023 22:31:52 +0200 Subject: [PATCH 13/19] refactor: Rename to UpdateLocationHistory.dart --- ...pdater.dart => UpdateLocationHistory.dart} | 15 ++++------ lib/main.dart | 8 +++--- .../location_history.dart | 28 +++++++++++-------- 3 files changed, 26 insertions(+), 25 deletions(-) rename lib/app_wrappers/{LocationHistoryUpdater.dart => UpdateLocationHistory.dart} (61%) diff --git a/lib/app_wrappers/LocationHistoryUpdater.dart b/lib/app_wrappers/UpdateLocationHistory.dart similarity index 61% rename from lib/app_wrappers/LocationHistoryUpdater.dart rename to lib/app_wrappers/UpdateLocationHistory.dart index 6ceae64..4a40069 100644 --- a/lib/app_wrappers/LocationHistoryUpdater.dart +++ b/lib/app_wrappers/UpdateLocationHistory.dart @@ -3,19 +3,18 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:locus/services/current_location_service.dart'; import 'package:locus/services/location_history_service/index.dart'; -import 'package:locus/services/location_point_service.dart'; import 'package:provider/provider.dart'; /// Makes sure that the [LocationHistory] is updated with the current location /// from the [CurrentLocationService]. -class LocationHistoryUpdater extends StatefulWidget { - const LocationHistoryUpdater({super.key}); +class UpdateLocationHistory extends StatefulWidget { + const UpdateLocationHistory({super.key}); @override - State createState() => _LocationHistoryUpdaterState(); + State createState() => _UpdateLocationHistoryState(); } -class _LocationHistoryUpdaterState extends State { +class _UpdateLocationHistoryState extends State { late final CurrentLocationService _currentLocation; late final StreamSubscription _subscription; late final LocationHistory _locationHistory; @@ -25,11 +24,7 @@ class _LocationHistoryUpdaterState extends State { super.initState(); _currentLocation = context.read(); - _subscription = _currentLocation.stream.listen((position) async { - final location = await LocationPointService.fromPosition(position); - - _locationHistory.add(location); - }); + _subscription = _currentLocation.stream.listen(_locationHistory.add); } @override diff --git a/lib/main.dart b/lib/main.dart index da3d09e..4730c1d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,7 +10,7 @@ import 'package:locus/App.dart'; import 'package:locus/app_wrappers/CheckViewAlarmsLive.dart'; import 'package:locus/app_wrappers/HandleNotifications.dart'; import 'package:locus/app_wrappers/InitCurrentLocationFromSettings.dart'; -import 'package:locus/app_wrappers/LocationHistoryUpdater.dart'; +import 'package:locus/app_wrappers/UpdateLocationHistory.dart'; import 'package:locus/app_wrappers/PublishTaskPositionsOnUpdate.dart'; import 'package:locus/app_wrappers/RegisterBackgroundListeners.dart'; import 'package:locus/app_wrappers/ManageQuickActions.dart'; @@ -31,7 +31,7 @@ import 'package:provider/provider.dart'; const storage = FlutterSecureStorage(); final StreamController selectedNotificationsStream = - StreamController.broadcast(); +StreamController.broadcast(); void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -59,7 +59,7 @@ void main() async { ); FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); const initializationSettings = InitializationSettings( android: AndroidInitializationSettings("ic_launcher_foreground"), iOS: DarwinInitializationSettings(), @@ -107,7 +107,7 @@ void main() async { ], child: const Stack( children: [ - LocationHistoryUpdater(), + UpdateLocationHistory(), UniLinksHandler(), UpdateLastLocationToSettings(), RegisterBackgroundListeners(), diff --git a/lib/services/location_history_service/location_history.dart b/lib/services/location_history_service/location_history.dart index 7917991..06e5276 100644 --- a/lib/services/location_history_service/location_history.dart +++ b/lib/services/location_history_service/location_history.dart @@ -1,21 +1,23 @@ import 'package:flutter/cupertino.dart'; +import 'package:geolocator/geolocator.dart'; import "./constants.dart"; import '../location_point_service.dart'; class LocationHistory extends ChangeNotifier { - late final List locations; + late final List locations; LocationHistory( - final List? locations, + final List? locations, ) : locations = locations ?? []; factory LocationHistory.fromJSON(final Map data) => LocationHistory( data["locations"] != null - ? List.from( + ? List.from( data["locations"].map( - (location) => LocationPointService.fromJSON(location), + (location) => + Position.fromMap(location as Map), ), ) : null, @@ -33,24 +35,28 @@ class LocationHistory extends ChangeNotifier { // To avoid too many crumbled locations, we only save locations that are at // least one minute apart - bool _canAdd(final LocationPointService location) { + bool _canAdd(final Position position) { + if (position.timestamp == null) { + return false; + } + if (locations.isEmpty) { return true; } - return locations.last.createdAt - .difference(location.createdAt) + return locations.last.timestamp! + .difference(position.timestamp!) .inMinutes .abs() > 1; } - void add(final LocationPointService location) { - if (!_canAdd(location)) { + void add(final Position position) { + if (!_canAdd(position)) { return; } - locations.add(location); + locations.add(position); notifyListeners(); } @@ -60,6 +66,6 @@ class LocationHistory extends ChangeNotifier { } void toJSON() => { - "locations": locations.map((location) => location.toJSON()).toList(), + "locations": locations.map((location) => location.toJson()).toList(), }; } From 10503af5e3223f144f017c45c4343109590fa472 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 12 Oct 2023 22:43:10 +0200 Subject: [PATCH 14/19] feat: Update location history on background task --- lib/main.dart | 6 +-- .../location_history.dart | 11 ++++- lib/services/manager_service/task.dart | 48 ++++++++++++++++++- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 4730c1d..1dccb72 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,7 +31,7 @@ import 'package:provider/provider.dart'; const storage = FlutterSecureStorage(); final StreamController selectedNotificationsStream = -StreamController.broadcast(); + StreamController.broadcast(); void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -59,7 +59,7 @@ void main() async { ); FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); const initializationSettings = InitializationSettings( android: AndroidInitializationSettings("ic_launcher_foreground"), iOS: DarwinInitializationSettings(), @@ -105,7 +105,7 @@ void main() async { create: (_) => CurrentLocationService()), ChangeNotifierProvider(create: (_) => locationHistory), ], - child: const Stack( + child: const Column( children: [ UpdateLocationHistory(), UniLinksHandler(), diff --git a/lib/services/location_history_service/location_history.dart b/lib/services/location_history_service/location_history.dart index 06e5276..119ddba 100644 --- a/lib/services/location_history_service/location_history.dart +++ b/lib/services/location_history_service/location_history.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/cupertino.dart'; import 'package:geolocator/geolocator.dart'; @@ -65,7 +67,14 @@ class LocationHistory extends ChangeNotifier { notifyListeners(); } - void toJSON() => { + Map toJSON() => { "locations": locations.map((location) => location.toJson()).toList(), }; + + Future save() async { + await storage.write( + key: KEY, + value: jsonEncode(toJSON()), + ); + } } diff --git a/lib/services/manager_service/task.dart b/lib/services/manager_service/task.dart index aa5445b..0e85adf 100644 --- a/lib/services/manager_service/task.dart +++ b/lib/services/manager_service/task.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_logs/flutter_logs.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:locus/constants/notifications.dart'; import 'package:locus/constants/values.dart'; +import 'package:locus/services/location_history_service/index.dart'; import 'package:locus/services/location_point_service.dart'; import 'package:locus/services/manager_service/helpers.dart'; import 'package:locus/services/settings_service/index.dart'; @@ -40,6 +42,38 @@ void _showPermissionMissingNotification({ ); } +void _updateLocation(final Position position) async { + FlutterLogs.logInfo( + LOG_TAG, + "Headless Task", + "Updating Location History; Restoring...", + ); + + final locationHistory = await LocationHistory.restore(); + + FlutterLogs.logInfo( + LOG_TAG, + "Headless Task", + "Updating Location History; Adding position.", + ); + + locationHistory.add(position); + + FlutterLogs.logInfo( + LOG_TAG, + "Headless Task", + "Updating Location History; Saving...", + ); + + await locationHistory.save(); + + FlutterLogs.logInfo( + LOG_TAG, + "Headless Task", + "Updating Location History; Done!", + ); +} + Future runBackgroundTask({ final LocationPointService? locationData, final bool force = false, @@ -75,6 +109,18 @@ Future runBackgroundTask({ return; } + final location = locationData ?? await getLocationData(); + + try { + _updateLocation(location.asPosition()); + } catch (error) { + FlutterLogs.logError( + LOG_TAG, + "Headless Task", + "Error while updating location history: $error", + ); + } + if (!force) { FlutterLogs.logInfo( LOG_TAG, @@ -108,8 +154,6 @@ Future runBackgroundTask({ "Executing headless task now.", ); - final location = locationData ?? await getLocationData(); - FlutterLogs.logInfo( LOG_TAG, "Headless Task", From 39191c5ca468c5d8766db418bc273159e50c31fc Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 12 Oct 2023 23:40:19 +0200 Subject: [PATCH 15/19] fix: Fix widget positions --- lib/App.dart | 271 ++++++++++-------- lib/app_wrappers/CheckViewAlarmsLive.dart | 4 +- .../InitCurrentLocationFromSettings.dart | 13 +- lib/app_wrappers/ManageQuickActions.dart | 36 +-- .../RegisterBackgroundListeners.dart | 8 +- lib/app_wrappers/UniLinksHandler.dart | 27 +- lib/app_wrappers/UpdateLocationHistory.dart | 7 +- lib/main.dart | 17 +- .../location_history.dart | 2 +- .../manager_service/background_locator.dart | 6 +- 10 files changed, 206 insertions(+), 185 deletions(-) diff --git a/lib/App.dart b/lib/App.dart index e540f0d..da50e91 100644 --- a/lib/App.dart +++ b/lib/App.dart @@ -13,6 +13,17 @@ import 'package:locus/widgets/DismissKeyboard.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:provider/provider.dart'; +import 'app_wrappers/CheckViewAlarmsLive.dart'; +import 'app_wrappers/HandleNotifications.dart'; +import 'app_wrappers/InitCurrentLocationFromSettings.dart'; +import 'app_wrappers/ManageQuickActions.dart'; +import 'app_wrappers/PublishTaskPositionsOnUpdate.dart'; +import 'app_wrappers/RegisterBackgroundListeners.dart'; +import 'app_wrappers/ShowUpdateDialog.dart'; +import 'app_wrappers/UniLinksHandler.dart'; +import 'app_wrappers/UpdateLastLocationToSettings.dart'; +import 'app_wrappers/UpdateLocaleToSettings.dart'; +import 'app_wrappers/UpdateLocationHistory.dart'; import 'constants/themes.dart'; ColorScheme createColorScheme( @@ -51,83 +62,115 @@ class App extends StatelessWidget { child: DynamicColorBuilder( builder: (ColorScheme? lightColorScheme, ColorScheme? darkColorScheme) => - PlatformApp( - title: 'Locus', - material: (_, __) => MaterialAppData( - theme: (() { - if (lightColorScheme != null) { + Expanded( + child: PlatformApp( + title: 'Locus', + material: (_, __) => MaterialAppData( + theme: (() { + if (lightColorScheme != null) { + return LIGHT_THEME_MATERIAL.copyWith( + colorScheme: settings.primaryColor == null + ? lightColorScheme + : createColorScheme( + lightColorScheme, + settings.primaryColor!, + Brightness.light, + ), + primaryColor: + settings.primaryColor ?? lightColorScheme.primary, + ); + } + return LIGHT_THEME_MATERIAL.copyWith( colorScheme: settings.primaryColor == null - ? lightColorScheme + ? null : createColorScheme( - lightColorScheme, + lightColorScheme ?? + ColorScheme.fromSwatch( + primarySwatch: + createMaterialColor(settings.primaryColor!), + ), settings.primaryColor!, Brightness.light, ), - primaryColor: - settings.primaryColor ?? lightColorScheme.primary, + primaryColor: settings.primaryColor, ); - } - - return LIGHT_THEME_MATERIAL.copyWith( - colorScheme: settings.primaryColor == null - ? null - : createColorScheme( - lightColorScheme ?? - ColorScheme.fromSwatch( - primarySwatch: - createMaterialColor(settings.primaryColor!), - ), - settings.primaryColor!, - Brightness.light, - ), - primaryColor: settings.primaryColor, - ); - })(), - darkTheme: (() { - if (settings.getAndroidTheme() == AndroidTheme.miui) { - return DARK_THEME_MATERIAL_MIUI.copyWith( - colorScheme: settings.primaryColor == null - ? null - : createColorScheme( - const ColorScheme.dark(), - settings.primaryColor!, - Brightness.dark, + })(), + darkTheme: (() { + if (settings.getAndroidTheme() == AndroidTheme.miui) { + return DARK_THEME_MATERIAL_MIUI.copyWith( + colorScheme: settings.primaryColor == null + ? null + : createColorScheme( + const ColorScheme.dark(), + settings.primaryColor!, + Brightness.dark, + ), + primaryColor: settings.primaryColor, + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: + settings.primaryColor ?? MIUI_PRIMARY_COLOR, + foregroundColor: Colors.white, + splashFactory: NoSplash.splashFactory, + textStyle: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, ), - primaryColor: settings.primaryColor, - elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - backgroundColor: - settings.primaryColor ?? MIUI_PRIMARY_COLOR, - foregroundColor: Colors.white, - splashFactory: NoSplash.splashFactory, - textStyle: const TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.w700, ), ), - ), - ); - } + ); + } + + if (darkColorScheme != null) { + return DARK_THEME_MATERIAL.copyWith( + colorScheme: settings.primaryColor == null + ? darkColorScheme + : createColorScheme( + darkColorScheme, + settings.primaryColor!, + Brightness.dark, + ), + primaryColor: + settings.primaryColor ?? darkColorScheme.primary, + scaffoldBackgroundColor: HSLColor.fromColor( + settings.primaryColor ?? darkColorScheme.background) + .withLightness(0.08) + .toColor(), + dialogBackgroundColor: settings.primaryColor == null + ? darkColorScheme.background + : HSLColor.fromColor(settings.primaryColor!) + .withLightness(0.15) + .toColor(), + inputDecorationTheme: + DARK_THEME_MATERIAL.inputDecorationTheme.copyWith( + fillColor: settings.primaryColor == null + ? null + : HSLColor.fromColor(settings.primaryColor!) + .withLightness(0.3) + .withSaturation(.5) + .toColor(), + ), + ); + } - if (darkColorScheme != null) { return DARK_THEME_MATERIAL.copyWith( colorScheme: settings.primaryColor == null - ? darkColorScheme + ? null : createColorScheme( - darkColorScheme, + const ColorScheme.dark(), settings.primaryColor!, Brightness.dark, ), - primaryColor: - settings.primaryColor ?? darkColorScheme.primary, - scaffoldBackgroundColor: HSLColor.fromColor( - settings.primaryColor ?? darkColorScheme.background) - .withLightness(0.08) - .toColor(), + primaryColor: settings.primaryColor, + scaffoldBackgroundColor: settings.primaryColor == null + ? null + : HSLColor.fromColor(settings.primaryColor!) + .withLightness(0.08) + .toColor(), dialogBackgroundColor: settings.primaryColor == null - ? darkColorScheme.background + ? null : HSLColor.fromColor(settings.primaryColor!) .withLightness(0.15) .toColor(), @@ -141,74 +184,60 @@ class App extends StatelessWidget { .toColor(), ), ); - } + })(), + themeMode: ThemeMode.system, + ), + cupertino: (_, __) => CupertinoAppData( + theme: settings.primaryColor == null + ? LIGHT_THEME_CUPERTINO + : LIGHT_THEME_CUPERTINO.copyWith( + primaryColor: settings.primaryColor, + ), + ), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + builder: (context, child) => Stack( + children: [ + const UpdateLocationHistory(), + const UniLinksHandler(), + const UpdateLastLocationToSettings(), + const RegisterBackgroundListeners(), + const UpdateLocaleToSettings(), + const HandleNotifications(), + const CheckViewAlarmsLive(), + const ManageQuickActions(), + const InitCurrentLocationFromSettings(), + const ShowUpdateDialog(), + const PublishTaskPositionsOnUpdate(), + if (child != null) child, + ], + ), + onGenerateRoute: (routeSettings) { + final screen = (() { + if (settings.getRequireBiometricAuthenticationOnStart()) { + return const BiometricsRequiredStartupScreen(); + } - return DARK_THEME_MATERIAL.copyWith( - colorScheme: settings.primaryColor == null - ? null - : createColorScheme( - const ColorScheme.dark(), - settings.primaryColor!, - Brightness.dark, - ), - primaryColor: settings.primaryColor, - scaffoldBackgroundColor: settings.primaryColor == null - ? null - : HSLColor.fromColor(settings.primaryColor!) - .withLightness(0.08) - .toColor(), - dialogBackgroundColor: settings.primaryColor == null - ? null - : HSLColor.fromColor(settings.primaryColor!) - .withLightness(0.15) - .toColor(), - inputDecorationTheme: - DARK_THEME_MATERIAL.inputDecorationTheme.copyWith( - fillColor: settings.primaryColor == null - ? null - : HSLColor.fromColor(settings.primaryColor!) - .withLightness(0.3) - .withSaturation(.5) - .toColor(), - ), - ); - })(), - themeMode: ThemeMode.system, - ), - cupertino: (_, __) => CupertinoAppData( - theme: settings.primaryColor == null - ? LIGHT_THEME_CUPERTINO - : LIGHT_THEME_CUPERTINO.copyWith( - primaryColor: settings.primaryColor, - ), - ), - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - onGenerateRoute: (routeSettings) { - final screen = (() { - if (settings.getRequireBiometricAuthenticationOnStart()) { - return const BiometricsRequiredStartupScreen(); - } + if (!settings.userHasSeenWelcomeScreen) { + return const WelcomeScreen(); + } - if (!settings.userHasSeenWelcomeScreen) { - return const WelcomeScreen(); - } + return const LocationsOverviewScreen(); + })(); - return const LocationsOverviewScreen(); - })(); + if (isCupertino(context)) { + return MaterialWithModalsPageRoute( + builder: (_) => screen, + settings: routeSettings, + ); + } - if (isCupertino(context)) { - return MaterialWithModalsPageRoute( + return NativePageRoute( builder: (_) => screen, - settings: routeSettings, + context: context, ); - } - - return NativePageRoute( - builder: (_) => screen, - context: context, - ); - }, + }, + ), ), ), ); diff --git a/lib/app_wrappers/CheckViewAlarmsLive.dart b/lib/app_wrappers/CheckViewAlarmsLive.dart index 3131a6d..8a1e55b 100644 --- a/lib/app_wrappers/CheckViewAlarmsLive.dart +++ b/lib/app_wrappers/CheckViewAlarmsLive.dart @@ -19,14 +19,14 @@ class CheckViewAlarmsLive extends StatefulWidget { } class _CheckViewAlarmsLiveState extends State { - late final CurrentLocationService _currentLocation; late final StreamSubscription _subscription; @override void initState() { super.initState(); - _subscription = _currentLocation.stream.listen((position) async { + final currentLocation = context.read(); + _subscription = currentLocation.stream.listen((position) async { final l10n = AppLocalizations.of(context); final viewService = context.read(); final userLocation = await LocationPointService.fromPosition(position); diff --git a/lib/app_wrappers/InitCurrentLocationFromSettings.dart b/lib/app_wrappers/InitCurrentLocationFromSettings.dart index ad66988..7f1770e 100644 --- a/lib/app_wrappers/InitCurrentLocationFromSettings.dart +++ b/lib/app_wrappers/InitCurrentLocationFromSettings.dart @@ -21,12 +21,15 @@ class _InitCurrentLocationFromSettingsState super.initState(); _currentLocation = context.read(); - final settings = context.read(); - final lastLocation = settings.getLastMapLocation(); - if (lastLocation != null) { - _setLocation(lastLocation); - } + WidgetsBinding.instance.addPostFrameCallback((_) { + final settings = context.read(); + final lastLocation = settings.getLastMapLocation(); + + if (lastLocation != null) { + _setLocation(lastLocation); + } + }); } void _setLocation(final SettingsLastMapLocation rawLocation) { diff --git a/lib/app_wrappers/ManageQuickActions.dart b/lib/app_wrappers/ManageQuickActions.dart index 2f2be0b..9e37152 100644 --- a/lib/app_wrappers/ManageQuickActions.dart +++ b/lib/app_wrappers/ManageQuickActions.dart @@ -36,13 +36,15 @@ class _ManageQuickActionsState extends State { void initState() { super.initState(); - final settings = context.read(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final settings = context.read(); - if (settings.userHasSeenWelcomeScreen) { - _registerActions(); - } else { - _removeActions(); - } + if (settings.userHasSeenWelcomeScreen) { + _registerActions(); + } else { + _removeActions(); + } + }); } void _registerActions() { @@ -62,24 +64,22 @@ class _ManageQuickActionsState extends State { showCupertinoModalBottomSheet( context: context, backgroundColor: Colors.transparent, - builder: (_) => - ShortcutScreen( - type: ShortcutType.values.firstWhere( - (element) => element.name == type, - ), - ), + builder: (_) => ShortcutScreen( + type: ShortcutType.values.firstWhere( + (element) => element.name == type, + ), + ), ); } else { Navigator.push( context, NativePageRoute( context: context, - builder: (_) => - ShortcutScreen( - type: ShortcutType.values.firstWhere( - (element) => element.name == type, - ), - ), + builder: (_) => ShortcutScreen( + type: ShortcutType.values.firstWhere( + (element) => element.name == type, + ), + ), ), ); } diff --git a/lib/app_wrappers/RegisterBackgroundListeners.dart b/lib/app_wrappers/RegisterBackgroundListeners.dart index 92c0b41..e8a5aab 100644 --- a/lib/app_wrappers/RegisterBackgroundListeners.dart +++ b/lib/app_wrappers/RegisterBackgroundListeners.dart @@ -27,10 +27,12 @@ class _RegisterBackgroundListenersState _settings = context.read(); _taskService = context.read(); - _settings.addListener(_updateListeners); - _taskService.addListener(_updateListeners); + WidgetsBinding.instance.addPostFrameCallback((_) { + _settings.addListener(_updateListeners); + _taskService.addListener(_updateListeners); - _updateListeners(); + _updateListeners(); + }); } @override diff --git a/lib/app_wrappers/UniLinksHandler.dart b/lib/app_wrappers/UniLinksHandler.dart index 6f84639..8022c19 100644 --- a/lib/app_wrappers/UniLinksHandler.dart +++ b/lib/app_wrappers/UniLinksHandler.dart @@ -37,7 +37,9 @@ class _UniLinksHandlerState extends State { } }); - _initInitialLink(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _initInitialLink(); + }); } @override @@ -96,18 +98,19 @@ class _UniLinksHandlerState 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(); + }, + ), + ], ), - ], - ), ); } } diff --git a/lib/app_wrappers/UpdateLocationHistory.dart b/lib/app_wrappers/UpdateLocationHistory.dart index 4a40069..67211fa 100644 --- a/lib/app_wrappers/UpdateLocationHistory.dart +++ b/lib/app_wrappers/UpdateLocationHistory.dart @@ -15,16 +15,15 @@ class UpdateLocationHistory extends StatefulWidget { } class _UpdateLocationHistoryState extends State { - late final CurrentLocationService _currentLocation; late final StreamSubscription _subscription; - late final LocationHistory _locationHistory; @override void initState() { super.initState(); - _currentLocation = context.read(); - _subscription = _currentLocation.stream.listen(_locationHistory.add); + final currentLocation = context.read(); + final locationHistory = context.read(); + _subscription = currentLocation.stream.listen(locationHistory.add); } @override diff --git a/lib/main.dart b/lib/main.dart index 1dccb72..156c6f8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -105,22 +105,7 @@ void main() async { create: (_) => CurrentLocationService()), ChangeNotifierProvider(create: (_) => locationHistory), ], - child: const Column( - children: [ - UpdateLocationHistory(), - UniLinksHandler(), - UpdateLastLocationToSettings(), - RegisterBackgroundListeners(), - UpdateLocaleToSettings(), - HandleNotifications(), - CheckViewAlarmsLive(), - ManageQuickActions(), - InitCurrentLocationFromSettings(), - ShowUpdateDialog(), - PublishTaskPositionsOnUpdate(), - App(), - ], - ), + child: const App(), ), ); } diff --git a/lib/services/location_history_service/location_history.dart b/lib/services/location_history_service/location_history.dart index 119ddba..d143512 100644 --- a/lib/services/location_history_service/location_history.dart +++ b/lib/services/location_history_service/location_history.dart @@ -32,7 +32,7 @@ class LocationHistory extends ChangeNotifier { return LocationHistory(null); } - return LocationHistory.fromJSON(data as Map); + return LocationHistory.fromJSON(jsonDecode(data) as Map); } // To avoid too many crumbled locations, we only save locations that are at diff --git a/lib/services/manager_service/background_locator.dart b/lib/services/manager_service/background_locator.dart index 61d0e49..6a885c7 100644 --- a/lib/services/manager_service/background_locator.dart +++ b/lib/services/manager_service/background_locator.dart @@ -68,7 +68,7 @@ Future configureBackgroundLocator() { return BackgroundLocator.initialize(); } -Future registerBackgroundLocator(final BuildContext context,) { +Future registerBackgroundLocator(final BuildContext context) { final l10n = AppLocalizations.of(context); FlutterLogs.logInfo( @@ -83,7 +83,7 @@ Future registerBackgroundLocator(final BuildContext context,) { androidSettings: AndroidSettings( accuracy: LocationAccuracy.HIGH, distanceFilter: - BACKGROUND_LOCATION_UPDATES_MINIMUM_DISTANCE_FILTER.toDouble(), + BACKGROUND_LOCATION_UPDATES_MINIMUM_DISTANCE_FILTER.toDouble(), client: isGMSFlavor ? LocationClient.google : LocationClient.android, androidNotificationSettings: AndroidNotificationSettings( notificationTitle: l10n.backgroundLocator_title, @@ -95,7 +95,7 @@ Future registerBackgroundLocator(final BuildContext context,) { ), iosSettings: IOSSettings( distanceFilter: - BACKGROUND_LOCATION_UPDATES_MINIMUM_DISTANCE_FILTER.toDouble(), + BACKGROUND_LOCATION_UPDATES_MINIMUM_DISTANCE_FILTER.toDouble(), accuracy: LocationAccuracy.HIGH, showsBackgroundLocationIndicator: true, stopWithTerminate: false, From 3d7d16152fd24c1f32b67e6a8d9aaf0bf42ef1c9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:35:26 +0200 Subject: [PATCH 16/19] feat: Check for airplane mode (closes #129) --- lib/services/manager_service/task.dart | 20 +++++++++++++++++++- pubspec.lock | 16 ++++++++++++++++ pubspec.yaml | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/services/manager_service/task.dart b/lib/services/manager_service/task.dart index 0e85adf..eaac08d 100644 --- a/lib/services/manager_service/task.dart +++ b/lib/services/manager_service/task.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:airplane_mode_checker/airplane_mode_checker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @@ -30,7 +31,7 @@ void _showPermissionMissingNotification({ AndroidChannelIDs.appIssues.name, l10n.androidNotificationChannel_appIssues_name, channelDescription: - l10n.androidNotificationChannel_appIssues_description, + l10n.androidNotificationChannel_appIssues_description, onlyAlertOnce: true, importance: Importance.max, priority: Priority.max, @@ -78,6 +79,23 @@ Future runBackgroundTask({ final LocationPointService? locationData, final bool force = false, }) async { + FlutterLogs.logInfo( + LOG_TAG, + "Headless Task", + "Checking Airplane mode", + ); + + final status = await AirplaneModeChecker.checkAirplaneMode(); + + if (status == AirplaneModeStatus.on) { + FlutterLogs.logInfo( + LOG_TAG, + "Headless Task", + "----> Airplane mode is on. Skipping headless task.", + ); + return; + } + FlutterLogs.logInfo( LOG_TAG, "Headless Task", diff --git a/pubspec.lock b/pubspec.lock index 6108353..16deb8f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.1" + airplane_mode_checker: + dependency: "direct main" + description: + name: airplane_mode_checker + sha256: "16d7de1125056d6f7a9c5c1dc0056afe78c36a9e8fc39768ef9f9c21b07c0d5e" + url: "https://pub.dev" + source: hosted + version: "2.0.0" analyzer: dependency: transitive description: @@ -645,6 +653,14 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c" + url: "https://pub.dev" + source: hosted + version: "8.2.2" frontend_server_client: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index af7f3a3..7bff767 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,7 @@ dependencies: collection: ^1.17.1 background_locator_2: ^2.0.6 queue: ^3.1.0+2 + airplane_mode_checker: ^2.0.0 # Uncomment this for publishing FLOSS variant # Taken from https://github.com/Zverik/every_door/blob/aaf8d2fdeac483041bcac2c7c79ef760b99dff2b/pubspec.yaml#L55 From e7ef54d16719ee0ae6f77021a14b0a2c6e6f5a3d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 13 Oct 2023 21:45:20 +0200 Subject: [PATCH 17/19] fix: Properly show own location on map --- lib/screens/LocationsOverviewScreen.dart | 21 +++++++ .../location_history.dart | 60 +++++++++++-------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 4bccc0f..6516a8d 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -28,6 +28,7 @@ import 'package:locus/screens/locations_overview_screen_widgets/OutOfBoundMarker import 'package:locus/screens/locations_overview_screen_widgets/ShareLocationSheet.dart'; import 'package:locus/screens/locations_overview_screen_widgets/ViewLocationPopup.dart'; import 'package:locus/services/current_location_service.dart'; +import 'package:locus/services/location_history_service/index.dart'; import 'package:locus/services/settings_service/index.dart'; import 'package:locus/services/task_service/index.dart'; import 'package:locus/services/view_service/index.dart'; @@ -488,6 +489,12 @@ class _LocationsOverviewScreenState extends State ) .expand((element) => element) .map((location) => (selectedView!, location)); + final ownLocations = context.watch().previewLocations.map( + (location) => LatLng( + location.latitude, + location.longitude, + ), + ); if (settings.getMapProvider() == MapProvider.apple) { return apple_maps.AppleMap( @@ -653,6 +660,20 @@ class _LocationsOverviewScreenState extends State ), ), _buildUserMarkerLayer(), + if (ownLocations.isNotEmpty) + PolylineLayer( + polylines: [ + Polyline( + strokeWidth: 10, + strokeJoin: StrokeJoin.round, + gradientColors: ownLocations + .mapIndexed((index, _) => Colors.cyanAccent + .withOpacity(index / ownLocations.length)) + .toList(), + points: ownLocations.toList(), + ), + ], + ), PopupMarkerLayer( options: PopupMarkerLayerOptions( markerTapBehavior: MarkerTapBehavior.togglePopupAndHideRest(), diff --git a/lib/services/location_history_service/location_history.dart b/lib/services/location_history_service/location_history.dart index d143512..f5f5ac9 100644 --- a/lib/services/location_history_service/location_history.dart +++ b/lib/services/location_history_service/location_history.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; import 'package:geolocator/geolocator.dart'; @@ -9,19 +10,30 @@ import '../location_point_service.dart'; class LocationHistory extends ChangeNotifier { late final List locations; - LocationHistory( - final List? locations, - ) : locations = locations ?? []; + LocationHistory(final List? locations,) + : locations = locations ?? []; + + // Locations used for the user preview. Only shows the locations in the last + // hour + List get previewLocations { + final minDate = DateTime.now().subtract(const Duration(hours: 1)); + + return locations + .where((location) => + location.timestamp != null && location.timestamp!.isAfter(minDate)) + .sorted((a, b) => a.timestamp!.compareTo(b.timestamp!)) + .toList(); + } factory LocationHistory.fromJSON(final Map data) => LocationHistory( data["locations"] != null ? List.from( - data["locations"].map( - (location) => - Position.fromMap(location as Map), - ), - ) + data["locations"].map( + (location) => + Position.fromMap(location as Map), + ), + ) : null, ); @@ -38,27 +50,26 @@ class LocationHistory extends ChangeNotifier { // To avoid too many crumbled locations, we only save locations that are at // least one minute apart bool _canAdd(final Position position) { - if (position.timestamp == null) { - return false; - } - - if (locations.isEmpty) { - return true; - } - - return locations.last.timestamp! - .difference(position.timestamp!) - .inMinutes - .abs() > - 1; + return position.timestamp != null; } void add(final Position position) { - if (!_canAdd(position)) { - return; + final lastLocation = locations.lastOrNull; + + if (lastLocation != null && + lastLocation.timestamp!.difference(position.timestamp!).abs() <= + const Duration(minutes: 1)) { + // Replace oldest one with new one + locations.removeLast(); } locations.add(position); + + final strippedLocations = locations.take(60).toList(); + + locations.clear(); + locations.addAll(strippedLocations); + notifyListeners(); } @@ -67,7 +78,8 @@ class LocationHistory extends ChangeNotifier { notifyListeners(); } - Map toJSON() => { + Map toJSON() => + { "locations": locations.map((location) => location.toJson()).toList(), }; From 83595f4d92c1123cd9cf4bc42ced625f4e25c8b6 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 15 Oct 2023 16:46:43 +0200 Subject: [PATCH 18/19] fix: Only init live location after permission check --- lib/screens/LocationsOverviewScreen.dart | 514 ++++++++++++----------- lib/services/manager_service/task.dart | 4 +- pubspec.lock | 34 +- 3 files changed, 292 insertions(+), 260 deletions(-) diff --git a/lib/screens/LocationsOverviewScreen.dart b/lib/screens/LocationsOverviewScreen.dart index 6516a8d..cd92680 100644 --- a/lib/screens/LocationsOverviewScreen.dart +++ b/lib/screens/LocationsOverviewScreen.dart @@ -136,7 +136,6 @@ class _LocationsOverviewScreenState extends State ..addObserver(this) ..addPostFrameCallback((_) { _setLocationFromSettings(); - _initLiveLocationUpdate(); locationFetchers.fetchPreviewLocations(); taskService.checkup(logService); @@ -146,6 +145,8 @@ class _LocationsOverviewScreenState extends State Geolocator.checkPermission().then((status) { if ({LocationPermission.always, LocationPermission.whileInUse} .contains(status)) { + _initLiveLocationUpdate(); + updateCurrentPosition( askPermissions: false, showErrorMessage: false, @@ -231,8 +232,7 @@ class _LocationsOverviewScreenState extends State } List mergeLocationsIfRequired( - final List locations, - ) { + final List locations,) { if (locations.isEmpty) { return locations; } @@ -269,7 +269,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()) { @@ -320,7 +320,7 @@ class _LocationsOverviewScreenState extends State void _handleViewAlarmChecker() { _viewsAlarmCheckerTimer = Timer.periodic( const Duration(minutes: 1), - (_) { + (_) { final viewService = context.read(); if (viewService.viewsWithAlarms.isEmpty) { @@ -330,9 +330,7 @@ class _LocationsOverviewScreenState extends State ); } - Future _animateToPosition( - final Position position, - ) async { + Future _animateToPosition(final Position position,) async { if (flutterMapController != null) { final zoom = max(15, flutterMapController!.zoom).toDouble(); @@ -456,7 +454,9 @@ class _LocationsOverviewScreenState extends State return CurrentLocationLayer( positionStream: - context.read().locationMarkerStream, + context + .read() + .locationMarkerStream, followOnLocationUpdate: FollowOnLocationUpdate.never, style: LocationMarkerStyle( marker: DefaultLocationMarker( @@ -474,27 +474,32 @@ 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)); - final ownLocations = context.watch().previewLocations.map( - (location) => LatLng( + 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)); + final ownLocations = context + .watch() + .previewLocations + .map( + (location) => + LatLng( location.latitude, location.longitude, ), - ); + ); if (settings.getMapProvider() == MapProvider.apple) { return apple_maps.AppleMap( @@ -525,29 +530,30 @@ 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( @@ -565,14 +571,15 @@ 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(), ); }, @@ -595,18 +602,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(), ), @@ -630,9 +637,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), @@ -643,16 +650,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(), ); }, @@ -667,7 +674,8 @@ class _LocationsOverviewScreenState extends State strokeWidth: 10, strokeJoin: StrokeJoin.round, gradientColors: ownLocations - .mapIndexed((index, _) => Colors.cyanAccent + .mapIndexed((index, _) => + Colors.cyanAccent .withOpacity(index / ownLocations.length)) .toList(), points: ownLocations.toList(), @@ -681,7 +689,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( @@ -699,10 +707,14 @@ 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), @@ -711,18 +723,19 @@ 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(), ), @@ -737,10 +750,11 @@ 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); @@ -750,13 +764,12 @@ 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(); @@ -798,8 +811,7 @@ class _LocationsOverviewScreenState extends State } } - Widget buildViewTile( - final TaskView? view, { + Widget buildViewTile(final TaskView? view, { final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, }) { final l10n = AppLocalizations.of(context); @@ -854,14 +866,15 @@ 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, @@ -876,32 +889,34 @@ 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, + ), ), ), ), @@ -922,88 +937,93 @@ 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), - ), - ], - ], - ), - cupertino: (context, _) => CupertinoButton( - onPressed: () { - showCupertinoModalPopup( - context: context, - barrierDismissible: true, - builder: (cupertino) => CupertinoActionSheet( - cancelButton: CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); - }, - child: Text(l10n.cancelLabel), + 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), ), - actions: [ - CupertinoActionSheetAction( - child: buildViewTile( - null, - mainAxisAlignment: MainAxisAlignment.center, + 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), ), - onPressed: () { - Navigator.pop(context); - setState(() { - selectedViewID = null; - visibleLocation = null; - }); - }, - ) - ] + - viewService.views - .map( - (view) => CupertinoActionSheetAction( + actions: [ + CupertinoActionSheetAction( + child: buildViewTile( + null, + mainAxisAlignment: MainAxisAlignment + .center, + ), onPressed: () { Navigator.pop(context); - showViewLocations(view); + setState(() { + selectedViewID = null; + visibleLocation = null; + }); }, - child: buildViewTile( - view, - mainAxisAlignment: - MainAxisAlignment.center, - ), - ), - ) - .toList(), - ), - ); - }, - child: buildViewTile(selectedView), - ), + ) + ] + + viewService.views + .map( + (view) => + CupertinoActionSheetAction( + onPressed: () { + Navigator.pop(context); + showViewLocations(view); + }, + child: buildViewTile( + view, + mainAxisAlignment: + MainAxisAlignment.center, + ), + ), + ) + .toList(), + ), + ); + }, + child: buildViewTile(selectedView), + ), ), ), ), @@ -1058,7 +1078,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)); @@ -1088,7 +1108,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 @@ -1111,7 +1131,7 @@ class _LocationsOverviewScreenState extends State onPressed: () { setState(() { disableShowDetailedLocations = - !disableShowDetailedLocations; + !disableShowDetailedLocations; }); }, ), @@ -1208,7 +1228,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, ), @@ -1290,58 +1310,62 @@ class _LocationsOverviewScreenState extends State showCupertinoModalPopup( context: context, barrierDismissible: true, - 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), + builder: (cupertino) => + CupertinoActionSheet( + cancelButton: CupertinoActionSheetAction( + onPressed: () => Navigator.pop(context), + child: Text(l10n.cancelLabel), ), - ), - CupertinoActionSheetAction( - onPressed: withPopNavigation(importLocation)(context), - child: CupertinoListTile( - leading: + 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: 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(), + Navigator.push( + context, + MaterialWithModalsPageRoute( + builder: ( + context) => const SharesOverviewScreen(), + ), + ); + }, + child: CupertinoListTile( + leading: const Icon(CupertinoIcons.list_bullet), + title: Text(l10n.sharesOverviewScreen_title), ), - ); - }, - child: CupertinoListTile( - leading: const Icon(CupertinoIcons.list_bullet), - title: Text(l10n.sharesOverviewScreen_title), - ), - ), - CupertinoActionSheetAction( - onPressed: () { - Navigator.pop(context); + ), + 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), + ), + ), + ], ), - ], - ), ); }, ), diff --git a/lib/services/manager_service/task.dart b/lib/services/manager_service/task.dart index eaac08d..7627920 100644 --- a/lib/services/manager_service/task.dart +++ b/lib/services/manager_service/task.dart @@ -31,7 +31,7 @@ void _showPermissionMissingNotification({ AndroidChannelIDs.appIssues.name, l10n.androidNotificationChannel_appIssues_name, channelDescription: - l10n.androidNotificationChannel_appIssues_description, + l10n.androidNotificationChannel_appIssues_description, onlyAlertOnce: true, importance: Importance.max, priority: Priority.max, @@ -153,7 +153,7 @@ Future runBackgroundTask({ DateTime.now().difference(settings.lastHeadlessRun!).abs() > BATTERY_SAVER_ENABLED_MINIMUM_TIME_BETWEEN_HEADLESS_RUNS; - if (shouldRunBasedOnBatterySaver && shouldRunBasedOnLastRun) { + if (!shouldRunBasedOnBatterySaver && !shouldRunBasedOnLastRun) { // We don't want to run the headless task too often when the battery saver is enabled. FlutterLogs.logInfo( LOG_TAG, diff --git a/pubspec.lock b/pubspec.lock index 16deb8f..0383709 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -261,10 +261,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -825,10 +825,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -953,18 +953,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" material_design_icons_flutter: dependency: "direct main" description: @@ -1366,10 +1366,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -1414,10 +1414,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" timezone: dependency: transitive description: @@ -1650,6 +1650,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1707,5 +1715,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.0" From 0c41598c224d30bd921ac5c2a303cd83632e26f7 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 15 Oct 2023 17:05:35 +0200 Subject: [PATCH 19/19] fix: Remove unnecessary Expended --- lib/App.dart | 276 +++++++++++++++++++++++++-------------------------- 1 file changed, 137 insertions(+), 139 deletions(-) diff --git a/lib/App.dart b/lib/App.dart index da50e91..9c127e4 100644 --- a/lib/App.dart +++ b/lib/App.dart @@ -62,115 +62,83 @@ class App extends StatelessWidget { child: DynamicColorBuilder( builder: (ColorScheme? lightColorScheme, ColorScheme? darkColorScheme) => - Expanded( - child: PlatformApp( - title: 'Locus', - material: (_, __) => MaterialAppData( - theme: (() { - if (lightColorScheme != null) { - return LIGHT_THEME_MATERIAL.copyWith( - colorScheme: settings.primaryColor == null - ? lightColorScheme - : createColorScheme( - lightColorScheme, - settings.primaryColor!, - Brightness.light, - ), - primaryColor: - settings.primaryColor ?? lightColorScheme.primary, - ); - } - + PlatformApp( + title: 'Locus', + material: (_, __) => MaterialAppData( + theme: (() { + if (lightColorScheme != null) { return LIGHT_THEME_MATERIAL.copyWith( colorScheme: settings.primaryColor == null - ? null + ? lightColorScheme : createColorScheme( - lightColorScheme ?? - ColorScheme.fromSwatch( - primarySwatch: - createMaterialColor(settings.primaryColor!), - ), + lightColorScheme, settings.primaryColor!, Brightness.light, ), - primaryColor: settings.primaryColor, + primaryColor: + settings.primaryColor ?? lightColorScheme.primary, ); - })(), - darkTheme: (() { - if (settings.getAndroidTheme() == AndroidTheme.miui) { - return DARK_THEME_MATERIAL_MIUI.copyWith( - colorScheme: settings.primaryColor == null - ? null - : createColorScheme( - const ColorScheme.dark(), - settings.primaryColor!, - Brightness.dark, - ), - primaryColor: settings.primaryColor, - elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - backgroundColor: - settings.primaryColor ?? MIUI_PRIMARY_COLOR, - foregroundColor: Colors.white, - splashFactory: NoSplash.splashFactory, - textStyle: const TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.w700, + } + + return LIGHT_THEME_MATERIAL.copyWith( + colorScheme: settings.primaryColor == null + ? null + : createColorScheme( + lightColorScheme ?? + ColorScheme.fromSwatch( + primarySwatch: + createMaterialColor(settings.primaryColor!), + ), + settings.primaryColor!, + Brightness.light, + ), + primaryColor: settings.primaryColor, + ); + })(), + darkTheme: (() { + if (settings.getAndroidTheme() == AndroidTheme.miui) { + return DARK_THEME_MATERIAL_MIUI.copyWith( + colorScheme: settings.primaryColor == null + ? null + : createColorScheme( + const ColorScheme.dark(), + settings.primaryColor!, + Brightness.dark, ), + primaryColor: settings.primaryColor, + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: + settings.primaryColor ?? MIUI_PRIMARY_COLOR, + foregroundColor: Colors.white, + splashFactory: NoSplash.splashFactory, + textStyle: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, ), ), - ); - } - - if (darkColorScheme != null) { - return DARK_THEME_MATERIAL.copyWith( - colorScheme: settings.primaryColor == null - ? darkColorScheme - : createColorScheme( - darkColorScheme, - settings.primaryColor!, - Brightness.dark, - ), - primaryColor: - settings.primaryColor ?? darkColorScheme.primary, - scaffoldBackgroundColor: HSLColor.fromColor( - settings.primaryColor ?? darkColorScheme.background) - .withLightness(0.08) - .toColor(), - dialogBackgroundColor: settings.primaryColor == null - ? darkColorScheme.background - : HSLColor.fromColor(settings.primaryColor!) - .withLightness(0.15) - .toColor(), - inputDecorationTheme: - DARK_THEME_MATERIAL.inputDecorationTheme.copyWith( - fillColor: settings.primaryColor == null - ? null - : HSLColor.fromColor(settings.primaryColor!) - .withLightness(0.3) - .withSaturation(.5) - .toColor(), - ), - ); - } + ), + ); + } + if (darkColorScheme != null) { return DARK_THEME_MATERIAL.copyWith( colorScheme: settings.primaryColor == null - ? null + ? darkColorScheme : createColorScheme( - const ColorScheme.dark(), + darkColorScheme, settings.primaryColor!, Brightness.dark, ), - primaryColor: settings.primaryColor, - scaffoldBackgroundColor: settings.primaryColor == null - ? null - : HSLColor.fromColor(settings.primaryColor!) - .withLightness(0.08) - .toColor(), + primaryColor: + settings.primaryColor ?? darkColorScheme.primary, + scaffoldBackgroundColor: HSLColor.fromColor( + settings.primaryColor ?? darkColorScheme.background) + .withLightness(0.08) + .toColor(), dialogBackgroundColor: settings.primaryColor == null - ? null + ? darkColorScheme.background : HSLColor.fromColor(settings.primaryColor!) .withLightness(0.15) .toColor(), @@ -184,60 +152,90 @@ class App extends StatelessWidget { .toColor(), ), ); - })(), - themeMode: ThemeMode.system, - ), - cupertino: (_, __) => CupertinoAppData( - theme: settings.primaryColor == null - ? LIGHT_THEME_CUPERTINO - : LIGHT_THEME_CUPERTINO.copyWith( - primaryColor: settings.primaryColor, - ), - ), - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - builder: (context, child) => Stack( - children: [ - const UpdateLocationHistory(), - const UniLinksHandler(), - const UpdateLastLocationToSettings(), - const RegisterBackgroundListeners(), - const UpdateLocaleToSettings(), - const HandleNotifications(), - const CheckViewAlarmsLive(), - const ManageQuickActions(), - const InitCurrentLocationFromSettings(), - const ShowUpdateDialog(), - const PublishTaskPositionsOnUpdate(), - if (child != null) child, - ], - ), - onGenerateRoute: (routeSettings) { - final screen = (() { - if (settings.getRequireBiometricAuthenticationOnStart()) { - return const BiometricsRequiredStartupScreen(); - } - - if (!settings.userHasSeenWelcomeScreen) { - return const WelcomeScreen(); - } + } - return const LocationsOverviewScreen(); - })(); + return DARK_THEME_MATERIAL.copyWith( + colorScheme: settings.primaryColor == null + ? null + : createColorScheme( + const ColorScheme.dark(), + settings.primaryColor!, + Brightness.dark, + ), + primaryColor: settings.primaryColor, + scaffoldBackgroundColor: settings.primaryColor == null + ? null + : HSLColor.fromColor(settings.primaryColor!) + .withLightness(0.08) + .toColor(), + dialogBackgroundColor: settings.primaryColor == null + ? null + : HSLColor.fromColor(settings.primaryColor!) + .withLightness(0.15) + .toColor(), + inputDecorationTheme: + DARK_THEME_MATERIAL.inputDecorationTheme.copyWith( + fillColor: settings.primaryColor == null + ? null + : HSLColor.fromColor(settings.primaryColor!) + .withLightness(0.3) + .withSaturation(.5) + .toColor(), + ), + ); + })(), + themeMode: ThemeMode.system, + ), + cupertino: (_, __) => CupertinoAppData( + theme: settings.primaryColor == null + ? LIGHT_THEME_CUPERTINO + : LIGHT_THEME_CUPERTINO.copyWith( + primaryColor: settings.primaryColor, + ), + ), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + builder: (context, child) => Stack( + children: [ + const UpdateLocationHistory(), + const UniLinksHandler(), + const UpdateLastLocationToSettings(), + const RegisterBackgroundListeners(), + const UpdateLocaleToSettings(), + const HandleNotifications(), + const CheckViewAlarmsLive(), + const ManageQuickActions(), + const InitCurrentLocationFromSettings(), + const ShowUpdateDialog(), + const PublishTaskPositionsOnUpdate(), + if (child != null) child, + ], + ), + onGenerateRoute: (routeSettings) { + final screen = (() { + if (settings.getRequireBiometricAuthenticationOnStart()) { + return const BiometricsRequiredStartupScreen(); + } - if (isCupertino(context)) { - return MaterialWithModalsPageRoute( - builder: (_) => screen, - settings: routeSettings, - ); + if (!settings.userHasSeenWelcomeScreen) { + return const WelcomeScreen(); } - return NativePageRoute( + return const LocationsOverviewScreen(); + })(); + + if (isCupertino(context)) { + return MaterialWithModalsPageRoute( builder: (_) => screen, - context: context, + settings: routeSettings, ); - }, - ), + } + + return NativePageRoute( + builder: (_) => screen, + context: context, + ); + }, ), ), );