import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geolocator/geolocator.dart'; import '../../../app/load_state.dart'; import '../../../models/home_config.dart'; import '../geofence_logic.dart'; import '../models/geofence_diagnostics.dart'; import '../models/geofence_runtime_state.dart'; import '../services/geofence_notifications_service.dart'; import '../services/geofence_runtime_store.dart'; import 'homes_providers.dart'; final geofenceRuntimeStoreProvider = Provider((ref) => GeofenceRuntimeStore()); final geofenceNotificationsServiceProvider = Provider( (ref) => GeofenceNotificationsService(), ); final geofenceDiagnosticsProvider = NotifierProvider< GeofenceDiagnosticsNotifier, LoadState >(GeofenceDiagnosticsNotifier.new); class GeofenceDiagnosticsNotifier extends Notifier> { bool _refreshing = false; @override LoadState build() { return const LoadState.idle(GeofenceDiagnostics.initial()); } Future refresh() async { if (_refreshing) return; _refreshing = true; final previous = state.data; state = LoadState.loading(previous); try { final currentHome = ref.read(currentHomeProvider); final runtime = await ref.read(geofenceRuntimeStoreProvider).load(); if (currentHome == null) { state = LoadState.data( GeofenceDiagnostics( activeHome: null, status: GeofenceStatusKind.noActiveHome, runtime: runtime, locationServicesEnabled: true, locationPermission: LocationPermission.denied, notificationsEnabled: true, ), ); return; } if (!currentHome.geofenceEnabled) { state = LoadState.data( GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.disabled, runtime: runtime, locationServicesEnabled: true, locationPermission: LocationPermission.denied, notificationsEnabled: true, ), ); return; } if (!currentHome.hasCoordinates) { state = LoadState.data( GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.missingCoordinates, runtime: runtime, locationServicesEnabled: true, locationPermission: LocationPermission.denied, notificationsEnabled: true, ), ); return; } final locationServicesEnabled = await Geolocator.isLocationServiceEnabled(); final locationPermission = await Geolocator.checkPermission(); final notificationsEnabled = await ref .read(geofenceNotificationsServiceProvider) .areNotificationsEnabled(); final diagnostics = _buildDiagnostics( currentHome: currentHome, runtime: runtime, locationServicesEnabled: locationServicesEnabled, locationPermission: locationPermission, notificationsEnabled: notificationsEnabled, ); state = LoadState.data(diagnostics); } catch (error) { state = LoadState.error( previous, 'Не удалось обновить состояние geofence: $error', ); } finally { _refreshing = false; } } Future requestLocationPermission() async { await Geolocator.requestPermission(); await refresh(); } Future requestBackgroundLocationPermission() async { final result = await Geolocator.requestPermission(); if (!hasBackgroundLocationAccess(result)) { await Geolocator.openAppSettings(); } await refresh(); } Future requestNotificationPermission() async { await ref .read(geofenceNotificationsServiceProvider) .requestNotificationsPermission(); await refresh(); } Future openAppSettings() async { await Geolocator.openAppSettings(); } Future openLocationSettings() async { await Geolocator.openLocationSettings(); } GeofenceDiagnostics _buildDiagnostics({ required HomeConfig currentHome, required GeofenceRuntimeState runtime, required bool locationServicesEnabled, required LocationPermission locationPermission, required bool notificationsEnabled, }) { if (!locationServicesEnabled) { return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.locationServicesDisabled, runtime: runtime, locationServicesEnabled: false, locationPermission: locationPermission, notificationsEnabled: notificationsEnabled, ); } if (!hasForegroundLocationAccess(locationPermission)) { return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.locationPermissionDenied, runtime: runtime, locationServicesEnabled: true, locationPermission: locationPermission, notificationsEnabled: notificationsEnabled, ); } if (!hasBackgroundLocationAccess(locationPermission)) { return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.backgroundPermissionDenied, runtime: runtime, locationServicesEnabled: true, locationPermission: locationPermission, notificationsEnabled: notificationsEnabled, ); } if (!notificationsEnabled) { return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.notificationsPermissionDenied, runtime: runtime, locationServicesEnabled: true, locationPermission: locationPermission, notificationsEnabled: false, ); } final failureAt = runtime.failureAtFor(currentHome.id); final retryRemaining = geofenceRetryRemaining(failureAt); if (retryRemaining != null) { return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.cooldown, runtime: runtime, locationServicesEnabled: true, locationPermission: locationPermission, notificationsEnabled: true, retryRemaining: retryRemaining, detail: runtime.failureMessageFor(currentHome.id), ); } if (runtime.isTriggeredFor(currentHome.id)) { return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.triggered, runtime: runtime, locationServicesEnabled: true, locationPermission: locationPermission, notificationsEnabled: true, ); } return GeofenceDiagnostics( activeHome: currentHome, status: GeofenceStatusKind.ready, runtime: runtime, locationServicesEnabled: true, locationPermission: locationPermission, notificationsEnabled: true, ); } }