| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- import 'dart:async';
- import 'package:flutter/foundation.dart';
- import '../../data/models/location_reading.dart';
- import '../../data/services/telemetry_bridge.dart';
- import '../../data/services/weather_service.dart';
- /// Location-based temperature fallback used when OBD-II is not the source.
- ///
- /// Persistence and the Settings switch live in [TelemetrySettingsController];
- /// this controller only owns permission, fetching and freshness.
- class WeatherController extends ChangeNotifier {
- WeatherController(this._platform, {WeatherService? weatherService})
- : _weatherService = weatherService ?? WeatherService();
- final TelemetryPlatform _platform;
- final WeatherService _weatherService;
- bool _enabled = false;
- bool _loading = false;
- bool _active = false;
- bool _disposed = false;
- double? _temperatureCelsius;
- String? _locationName;
- String? _error;
- Timer? _refreshTimer;
- bool get enabled => _enabled;
- bool get loading => _loading;
- double? get temperatureCelsius => _temperatureCelsius;
- String? get locationName => _locationName;
- String? get error => _error;
- bool get hasTemperature =>
- _enabled && _temperatureCelsius != null && _locationName != null;
- /// Restores the persisted weather preference at cold start without prompting
- /// for permission. [start] fetches only when location is already granted.
- void restoreEnabled(bool enabled) {
- _enabled = enabled;
- }
- Future<void> start() async {
- if (!_enabled || _active) return;
- _active = true;
- await refresh();
- _refreshTimer?.cancel();
- _refreshTimer = Timer.periodic(
- const Duration(minutes: 15),
- (_) => unawaited(refresh()),
- );
- }
- void stop() {
- _active = false;
- _refreshTimer?.cancel();
- _refreshTimer = null;
- }
- /// Enables the fallback and asks for location access at that moment.
- Future<bool> setEnabled(bool value) async {
- if (value == _enabled) return true;
- if (value) {
- final granted = await _platform.ensureLocationPermission();
- if (!granted) {
- _error = 'Location permission is required for weather.';
- _notify();
- return false;
- }
- }
- _enabled = value;
- if (value) {
- _active = true;
- await refresh();
- _refreshTimer?.cancel();
- _refreshTimer = Timer.periodic(
- const Duration(minutes: 15),
- (_) => unawaited(refresh()),
- );
- } else {
- stop();
- _temperatureCelsius = null;
- _locationName = null;
- _error = null;
- }
- _notify();
- return true;
- }
- Future<void> refresh() async {
- if (!_enabled || _loading) return;
- _loading = true;
- _notify();
- try {
- final LocationReading location;
- try {
- location = await _platform.getLocation();
- } on TelemetryException catch (problem) {
- _error = problem.message;
- _temperatureCelsius = null;
- _locationName = null;
- return;
- }
- final temperature = await _weatherService.temperatureCelsius(
- latitude: location.latitude,
- longitude: location.longitude,
- );
- if (temperature == null) {
- _error = 'Weather is unavailable.';
- _temperatureCelsius = null;
- _locationName = null;
- return;
- }
- _temperatureCelsius = temperature;
- _locationName = location.displayPlace;
- _error = null;
- } finally {
- _loading = false;
- _notify();
- }
- }
- void _notify() {
- if (!_disposed) notifyListeners();
- }
- @override
- void dispose() {
- _disposed = true;
- _refreshTimer?.cancel();
- super.dispose();
- }
- }
|