227 lines
6.8 KiB
Dart
227 lines
6.8 KiB
Dart
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<GeofenceDiagnostics>
|
||
>(GeofenceDiagnosticsNotifier.new);
|
||
|
||
class GeofenceDiagnosticsNotifier
|
||
extends Notifier<LoadState<GeofenceDiagnostics>> {
|
||
bool _refreshing = false;
|
||
|
||
@override
|
||
LoadState<GeofenceDiagnostics> build() {
|
||
return const LoadState.idle(GeofenceDiagnostics.initial());
|
||
}
|
||
|
||
Future<void> 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<void> requestLocationPermission() async {
|
||
await Geolocator.requestPermission();
|
||
await refresh();
|
||
}
|
||
|
||
Future<void> requestBackgroundLocationPermission() async {
|
||
final result = await Geolocator.requestPermission();
|
||
if (!hasBackgroundLocationAccess(result)) {
|
||
await Geolocator.openAppSettings();
|
||
}
|
||
await refresh();
|
||
}
|
||
|
||
Future<void> requestNotificationPermission() async {
|
||
await ref
|
||
.read(geofenceNotificationsServiceProvider)
|
||
.requestNotificationsPermission();
|
||
await refresh();
|
||
}
|
||
|
||
Future<void> openAppSettings() async {
|
||
await Geolocator.openAppSettings();
|
||
}
|
||
|
||
Future<void> 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,
|
||
);
|
||
}
|
||
}
|