import 'package:flutter/foundation.dart'; import 'parse.dart'; /// The camera's storage (SD card, or internal storage on cameras that have it). @immutable class StorageInfo { const StorageInfo({ this.location, this.inserted = false, this.initializing = false, this.formatting = false, this.formatted = false, this.full = false, this.error = false, this.readOnly = false, this.invalidFormat = false, this.verified = false, this.totalMB, this.remainingMB, this.availableCaptureCount, this.availableRecordingSeconds, }); factory StorageInfo.fromMap(Map m) => StorageInfo( location: asString(m['location']), inserted: asBool(m['inserted']) ?? false, initializing: asBool(m['initializing']) ?? false, formatting: asBool(m['formatting']) ?? false, formatted: asBool(m['formatted']) ?? false, full: asBool(m['full']) ?? false, error: asBool(m['error']) ?? false, readOnly: asBool(m['readOnly']) ?? false, invalidFormat: asBool(m['invalidFormat']) ?? false, verified: asBool(m['verified']) ?? false, totalMB: asInt(m['totalMB']), remainingMB: asInt(m['remainingMB']), availableCaptureCount: asInt(m['availableCaptureCount']), availableRecordingSeconds: asInt(m['availableRecordingSeconds']), ); final String? location; // SDCARD / INTERNAL_STORAGE final bool inserted; final bool initializing; final bool formatting; final bool formatted; final bool full; final bool error; final bool readOnly; final bool invalidFormat; final bool verified; final int? totalMB; final int? remainingMB; final int? availableCaptureCount; final int? availableRecordingSeconds; /// One word for the HUD's storage chip, worst condition first. String get status { if (!inserted) return 'No SD'; if (error) return 'SD error'; if (invalidFormat) return 'Format SD'; if (formatting) return 'Formatting'; if (initializing) return 'Initializing'; if (full) return 'SD full'; if (readOnly) return 'Read-only'; return 'OK'; } bool get usable => inserted && !error && !invalidFormat && !formatting && !initializing && !full; } /// Camera state from the bridge: recording, exposure read-back, storage and /// the watched camera settings. Recording and exposure arrive as their own /// `camera` / `exposure` events; storage and settings as the `storage` and /// `cameraSettings` groups of the telemetry tick (≤2 Hz). class CameraState extends ChangeNotifier { bool isRecording = false; int recordSeconds = 0; /// `SHOOT_PHOTO` / `RECORD_VIDEO` / `PLAYBACK` / `MEDIA_DOWNLOAD`. String? mode; // Exposure read-back, in the same labels the setters accept. String? iso; // "100", "AUTO", … String? shutter; // "1/240" String? aperture; // "f/2.8" String? ev; // "-0.3" StorageInfo? storage; bool? aeLock; String? focusMode; // MANUAL / AUTO / AFC String? photoAspectRatio; // RATIO_4_3 / RATIO_16_9 / RATIO_3_2 String? photoFileFormat; // JPEG / RAW / RAW_AND_JPEG String? videoResolution; // RESOLUTION_3840x2160 … String? videoFrameRate; // FRAME_RATE_29_DOT_970_FPS … String? videoFileFormat; // MOV / MP4 String? colorProfile; // NONE / D_LOG / … DateTime? updatedAt; void applySystem(Map e) { isRecording = asBool(e['isRecording']) ?? false; recordSeconds = asInt(e['recordingTimeSeconds']) ?? recordSeconds; mode = asString(e['mode']) ?? mode; notifyListeners(); } void applyExposure(Map e) { iso = asString(e['iso']); shutter = asString(e['shutter']); aperture = asString(e['aperture']); ev = asString(e['ev']); notifyListeners(); } /// From the telemetry tick; an absent group means the camera never reported it. void applyTelemetry({Map? storage, Map? settings}) { this.storage = storage == null ? null : StorageInfo.fromMap(storage); aeLock = asBool(settings?['aeLock']); focusMode = asString(settings?['focusMode']); photoAspectRatio = asString(settings?['photoAspectRatio']); photoFileFormat = asString(settings?['photoFileFormat']); videoResolution = asString(settings?['videoResolution']); videoFrameRate = asString(settings?['videoFrameRate']); videoFileFormat = asString(settings?['videoFileFormat']); colorProfile = asString(settings?['colorProfile']); updatedAt = DateTime.now(); notifyListeners(); } void clear() { isRecording = false; recordSeconds = 0; mode = iso = shutter = aperture = ev = null; storage = null; aeLock = null; focusMode = photoAspectRatio = photoFileFormat = videoResolution = videoFrameRate = videoFileFormat = colorProfile = null; updatedAt = null; notifyListeners(); } }