From 3f118a77c428069c659398dc3ade1bc4076f357d Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Sat, 8 Aug 2026 14:28:38 -0700 Subject: [PATCH] Fix branch after android testing. Add background data refresh, not just location refresh. Tested on a Pixel 10 Pro. These changes were needed to work on real hardware. Also, which background location updating was working, the data was not refreshing in the background. This also adds that. It will pull nodes in a 5km area around your location and update when you within 1km of the area. These values may need to be adjusted if there is increased load on the servers. --- lib/widgets/map/gps_controller.dart | 66 ++++++++++++++++++++++++++--- lib/widgets/map_view.dart | 10 +++++ 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/lib/widgets/map/gps_controller.dart b/lib/widgets/map/gps_controller.dart index 85142f8..9c47844 100644 --- a/lib/widgets/map/gps_controller.dart +++ b/lib/widgets/map/gps_controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:flutter_map_animations/flutter_map_animations.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; @@ -12,7 +13,7 @@ import '../../services/coordinate_validation.dart'; import '../../services/localization_service.dart'; import '../../models/osm_node.dart'; import '../../models/node_profile.dart'; - +import '../../services/geo_bounds.dart'; /// Simple GPS controller that handles precise location permissions only. /// Key principles: @@ -42,6 +43,10 @@ class GpsController { VoidCallback? _onMapMovedProgrammatically; bool Function()? _isUserInteracting; + LatLngBounds? _backgroundTrackingBounds; + bool _isFetchingBackgroundNodes = false; + Future Function(LatLngBounds)? _onFetchBackgroundNodes; + /// Get the current GPS location (if available) LatLng? get currentLocation => _currentLocation; @@ -57,6 +62,7 @@ class GpsController { required int Function() getProximityAlertDistance, required List Function(LatLng userLocation, int radiusMeters) getNearbyNodes, required List Function() getEnabledProfiles, + Future Function(LatLngBounds)? onFetchBackgroundNodes, VoidCallback? onMapMovedProgrammatically, bool Function()? isUserInteracting, }) async { @@ -70,6 +76,7 @@ class GpsController { _getProximityAlertDistance = getProximityAlertDistance; _getNearbyNodes = getNearbyNodes; _getEnabledProfiles = getEnabledProfiles; + _onFetchBackgroundNodes = onFetchBackgroundNodes; _onMapMovedProgrammatically = onMapMovedProgrammatically; _isUserInteracting = isUserInteracting; @@ -283,7 +290,7 @@ class GpsController { /// background delivery is armed (or dropped) immediately rather than at the /// next unrelated stream restart. void updateProximityAlertsEnabled(bool enabled) { - if (_positionSub == null || _streamIsBackgroundCapable == enabled) return; + if (_streamIsBackgroundCapable == enabled) return; debugPrint('[GpsController] Proximity alerts $enabled — rebuilding stream'); _stopLocationTracking(); @@ -302,12 +309,58 @@ class GpsController { _startPositionStream(); } + Future _checkBackgroundDataRefresh(LatLng userLocation) async { + final proximityEnabled = _getProximityAlertsEnabled?.call() ?? false; + + // Debug 1: Check the early return conditions + debugPrint('[GpsController-Background] proximityEnabled: $proximityEnabled, ' + 'onFetchBackgroundNodes null: ${_onFetchBackgroundNodes == null}, ' + 'isFetchingBackgroundNodes: $_isFetchingBackgroundNodes'); + + if (!proximityEnabled || _onFetchBackgroundNodes == null || _isFetchingBackgroundNodes) return; + + bool needsRefresh = false; + if (_backgroundTrackingBounds == null) { + debugPrint('[GpsController-Background] Bounds are null. Triggering initial refresh.'); + needsRefresh = true; + } else { + final bounds = _backgroundTrackingBounds!; + final northDist = Geolocator.distanceBetween(userLocation.latitude, userLocation.longitude, bounds.north, userLocation.longitude); + final southDist = Geolocator.distanceBetween(userLocation.latitude, userLocation.longitude, bounds.south, userLocation.longitude); + final eastDist = Geolocator.distanceBetween(userLocation.latitude, userLocation.longitude, userLocation.latitude, bounds.east); + final westDist = Geolocator.distanceBetween(userLocation.latitude, userLocation.longitude, userLocation.latitude, bounds.west); + + final minDist = [northDist, southDist, eastDist, westDist].reduce((a, b) => a < b ? a : b); + + // Debug 2: Print the closest distance to the edge + debugPrint('[GpsController-Background] Closest edge distance: ${minDist.toStringAsFixed(1)}m (Threshold: 1000m)'); + + if (minDist < 1000) { + debugPrint('[GpsController-Background] Threshold crossed. Triggering refresh.'); + needsRefresh = true; + } + } + + if (needsRefresh) { + _isFetchingBackgroundNodes = true; + try { + final newBounds = boundsAround(userLocation, 5000); + await _onFetchBackgroundNodes?.call(newBounds); + _backgroundTrackingBounds = newBounds; + debugPrint('[GpsController-Background] Successfully fetched and updated tracking bounds.'); + } catch (e) { + debugPrint('[GpsController-Background] Background fetch failed: $e'); + } finally { + _isFetchingBackgroundNodes = false; + } + } + } + /// Handle incoming GPS position void _onPositionReceived(Position position) { // Reject malformed fixes (occasionally NaN/Infinite from the platform // location stack) before they reach map state. if (!isValidLatitude(position.latitude) || !isValidLongitude(position.longitude)) { - debugPrint( '[GpsController] Ignoring invalid GPS position: ' 'lat=${position.latitude}, lng=${position.longitude}', @@ -315,7 +368,6 @@ class GpsController { return; } - final newLocation = LatLng(position.latitude, position.longitude); _currentLocation = newLocation; @@ -332,13 +384,13 @@ class GpsController { // Handle proximity alerts _checkProximityAlerts(newLocation); + _checkBackgroundDataRefresh(newLocation); // Handle follow-me animations _handleFollowMeUpdate(position, newLocation); } /// Handle GPS stream errors - void _onPositionError(dynamic error) { debugPrint('[GpsController] Position stream error: $error'); if (_hasLocation) { @@ -367,7 +419,6 @@ class GpsController { final enabledProfiles = _getEnabledProfiles?.call() ?? []; - ProximityAlertService().checkProximity( userLocation: userLocation, nodes: nearbyNodes, @@ -499,7 +550,8 @@ class GpsController { _getProximityAlertDistance = null; _getNearbyNodes = null; _getEnabledProfiles = null; + _onFetchBackgroundNodes = null; _onMapMovedProgrammatically = null; _isUserInteracting = null; } -} +} \ No newline at end of file diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index 96fdf35..4d28751 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -217,6 +217,16 @@ class MapViewState extends State with WidgetsBindingObserver { } return []; }, + onFetchBackgroundNodes: (bounds) async { + if (!mounted) return; + final appState = context.read(); + + NodeProviderWithCache.instance.fetchAndUpdate( + bounds: bounds, + profiles: appState.enabledProfiles, + uploadMode: appState.uploadMode, + ); + }, onMapMovedProgrammatically: () { // Refresh nodes when GPS controller moves the map _refreshNodesFromProvider();