import 'package:flutter/foundation.dart'; import 'package:kooltab2/utils/app_update_util.dart'; import 'package:ota_update/ota_update.dart'; import '../models/settings.dart'; import '../services/settings_service.dart'; import '../utils/app_config.dart'; class SettingsViewModel extends ChangeNotifier { final SettingsService settingsService; SettingsViewModel({required this.settingsService}); AppSettings _settings = AppSettings.defaults; bool _isLoading = false; bool _hasLoaded = false; String? _errorMessage; final AppUpdateUtil _updater = AppUpdateUtil(serverUrl: kUpdateServerUrl); bool _checkingForUpdates = false; bool get checkingForUpdates => _checkingForUpdates; AppSettings get settings => _settings; bool get isLoading => _isLoading; bool get hasLoaded => _hasLoaded; String? get errorMessage => _errorMessage; Future ensureLoaded() async { if (_hasLoaded || _isLoading) return; await load(); } Future load() async { if (_isLoading) return; _isLoading = true; _errorMessage = null; notifyListeners(); try { _settings = await settingsService.getSettings(); } catch (e) { debugPrint('SettingsViewModel: load error: $e'); _errorMessage = 'Could not load settings.'; } finally { _hasLoaded = true; _isLoading = false; notifyListeners(); } } Future checkForUpdates() async { if (_checkingForUpdates) return null; _checkingForUpdates = true; notifyListeners(); try { return await _updater.checkForUpdate(); } finally { _checkingForUpdates = false; notifyListeners(); } } Future installUpdate( UpdateInfo update, { void Function(double progress)? onProgress, }) async { final url = update.download.startsWith("http") ? update.download : "$kUpdateServerUrl${update.download}"; debugPrint("Download Url: $url"); final stream = OtaUpdate().execute( url, destinationFilename: "update.apk", sha256checksum: update.sha256, ); await for (final event in stream) { debugPrint("OTA: ${event.status} ${event.value}"); if (event.status == OtaStatus.DOWNLOADING) { final progress = double.tryParse(event.value ?? "0") ?? 0; onProgress?.call(progress / 100); } if (event.status == OtaStatus.DOWNLOAD_ERROR) { throw Exception("Download failed: ${event.value}"); } if (event.status == OtaStatus.INSTALLATION_ERROR) { throw Exception("Installation failed: ${event.value}"); } } } /// Just flips the preference flag. Caller is responsible for having /// already set/verified the actual PIN via PinLockViewModel first. Future updatePinRequired(bool value) => _save(_settings.copyWith(pinRequired: value)); Future updateThemeMode(AppThemeMode mode) => _save(_settings.copyWith(themeMode: mode)); Future updateLanguage(AppLanguage language) => _save(_settings.copyWith(language: language)); Future _save(AppSettings updated) async { final previous = _settings; _settings = updated; _errorMessage = null; notifyListeners(); try { await settingsService.saveSettings(updated); } catch (e) { debugPrint('SettingsViewModel: save error: $e'); _settings = previous; _errorMessage = 'Could not save settings.'; notifyListeners(); } } }