Harden geofence automation and home editing

This commit is contained in:
Artem Kokos
2026-05-15 11:26:23 +07:00
parent 50748c6945
commit 8ffaa14b60
21 changed files with 718 additions and 160 deletions

View File

@@ -5,6 +5,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import '../services/location_platform_service.dart';
enum UserLocationIssue {
servicesDisabled,
permissionDenied,
@@ -46,6 +48,10 @@ final userLocationProvider =
() => UserLocationNotifier(),
);
final locationPlatformServiceProvider = Provider<LocationPlatformService>(
(ref) => DeviceLocationPlatformService(),
);
class UserLocationNotifier extends Notifier<UserLocation> {
StreamSubscription<Position>? _sub;
int _watchers = 0;
@@ -71,9 +77,21 @@ class UserLocationNotifier extends Notifier<UserLocation> {
/// стрим остановится только когда все вызовут stopWatching.
Future<void> startWatching() async {
_watchers++;
await _startWatchingIfPossible();
}
Future<void> ensureWatchingStarted() async {
if (_watchers == 0 || _sub != null) {
return;
}
await _startWatchingIfPossible();
}
Future<void> _startWatchingIfPossible() async {
final locationService = ref.read(locationPlatformServiceProvider);
if (_sub != null) return;
final permissionState = await _ensurePermission();
final permissionState = await _ensurePermission(requestIfDenied: false);
if (!permissionState.isGranted) {
state = permissionState.toLocation();
return;
@@ -81,7 +99,7 @@ class UserLocationNotifier extends Notifier<UserLocation> {
if (!state.hasPosition) {
try {
final last = await Geolocator.getLastKnownPosition();
final last = await locationService.getLastKnownPosition();
if (last != null) {
state = _fromPosition(last);
}
@@ -93,17 +111,19 @@ class UserLocationNotifier extends Notifier<UserLocation> {
distanceFilter: 20,
);
_sub = Geolocator.getPositionStream(locationSettings: settings).listen(
(pos) => state = _fromPosition(pos),
onError: (e) {
debugPrint('Ошибка стрима геолокации: $e');
state = UserLocation(
error: 'Не удалось отслеживать позицию: $e',
issue: UserLocationIssue.unavailable,
updatedAt: state.updatedAt,
_sub = locationService
.getPositionStream(locationSettings: settings)
.listen(
(pos) => state = _fromPosition(pos),
onError: (e) {
debugPrint('Ошибка стрима геолокации: $e');
state = UserLocation(
error: 'Не удалось отслеживать позицию: $e',
issue: UserLocationIssue.unavailable,
updatedAt: state.updatedAt,
);
},
);
},
);
}
/// Остановить отслеживание. Вызывать из dispose экрана.
@@ -116,20 +136,21 @@ class UserLocationNotifier extends Notifier<UserLocation> {
}
Future<void> refresh() async {
final permissionState = await _ensurePermission();
final locationService = ref.read(locationPlatformServiceProvider);
final permissionState = await _ensurePermission(requestIfDenied: false);
if (!permissionState.isGranted) {
state = permissionState.toLocation();
return;
}
try {
final last = await Geolocator.getLastKnownPosition();
final last = await locationService.getLastKnownPosition();
if (last != null) {
state = _fromPosition(last);
return;
}
final pos = await Geolocator.getCurrentPosition(
final pos = await locationService.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.low,
timeLimit: Duration(seconds: 10),
@@ -146,35 +167,40 @@ class UserLocationNotifier extends Notifier<UserLocation> {
}
Future<void> requestPermission() async {
await Geolocator.requestPermission();
final locationService = ref.read(locationPlatformServiceProvider);
await locationService.requestPermission();
if (_watchers > 0 && _sub == null) {
await startWatching();
await _startWatchingIfPossible();
return;
}
await refresh();
}
Future<void> openAppSettings() async {
await Geolocator.openAppSettings();
await ref.read(locationPlatformServiceProvider).openAppSettings();
}
Future<void> openLocationSettings() async {
await Geolocator.openLocationSettings();
await ref.read(locationPlatformServiceProvider).openLocationSettings();
}
/// Проверить сервис и пермишены. Возвращает null если всё ок,
/// иначе строку с причиной ошибки.
Future<_LocationPermissionState> _ensurePermission() async {
if (!await Geolocator.isLocationServiceEnabled()) {
Future<_LocationPermissionState> _ensurePermission({
required bool requestIfDenied,
}) async {
final locationService = ref.read(locationPlatformServiceProvider);
if (!await locationService.isLocationServiceEnabled()) {
return const _LocationPermissionState(
issue: UserLocationIssue.servicesDisabled,
message: 'Геолокация выключена',
);
}
var perm = await Geolocator.checkPermission();
if (perm == LocationPermission.denied) {
perm = await Geolocator.requestPermission();
var perm = await locationService.checkPermission();
if (perm == LocationPermission.denied && requestIfDenied) {
perm = await locationService.requestPermission();
}
if (perm == LocationPermission.denied) {
return const _LocationPermissionState(