P7a of the GO 4-parity plan. The rail's GRID button becomes MODES, which opens a picker of the product's intelligent flight modes with first-use terms; Waypoints is the first one built. Waypoints opens on the big map: tap to add waypoints or POIs, long-press to drag, "+" to insert on a leg, and a point editor for altitude, speed, heading (route / custom / POI), gimbal (set or aim at POI), camera action and hover, and turn radius, with a swipe-to-adjust readout. Route settings, a local mission library and undo sit in a tool column. Start runs the SafetyGate's pre-start check in the slide-to-confirm dialog, then uploads and starts; a run panel shows upload and flight progress with Pause, Resume and Stop. MissionBridge now listens to the waypoint operator and builds the full V1 mission (per-waypoint headings, turn direction, gimbal pitch, actions). Not yet run on a simulator or aircraft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
4.0 KiB
Dart
113 lines
4.0 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
/// The video overlay grid (camera menu → Other, and the rail's GRID button).
|
|
enum GridStyle {
|
|
off('Off'),
|
|
thirds('Grid'),
|
|
diagonals('Grid + diagonals');
|
|
|
|
const GridStyle(this.label);
|
|
final String label;
|
|
}
|
|
|
|
/// What a long press on the live view does (GO 4's "Long Press" setting); a
|
|
/// single tap always focuses, or meters on fixed-focus cameras.
|
|
enum LongPressAction {
|
|
meter('Spot meter'),
|
|
focus('Focus'),
|
|
off('Nothing');
|
|
|
|
const LongPressAction(this.label);
|
|
final String label;
|
|
}
|
|
|
|
/// Device-local switches for the flight screen, persisted across launches.
|
|
abstract final class FlyPrefs {
|
|
static const String _classicKey = 'fly.classicHud';
|
|
static const String _perfKey = 'fly.perfOverlay';
|
|
static const String _gridKey = 'fly.grid';
|
|
static const String _centerKey = 'fly.centerPoint';
|
|
static const String _longPressKey = 'fly.longPress';
|
|
static const String _routeKey = 'fly.showRoute';
|
|
static const String _termsKey = 'fly.modeTerms';
|
|
|
|
/// Open the pre-P2 `FlightControlPage` instead of the HUD v2 — kept
|
|
/// reachable until P3's safety flows pass their simulator run (plan P2/P3).
|
|
static final ValueNotifier<bool> classicHud = ValueNotifier<bool>(false);
|
|
|
|
/// Flutter's performance overlay on the flight screen, for the P2 "no jank at
|
|
/// 10 Hz" check.
|
|
static final ValueNotifier<bool> perfOverlay = ValueNotifier<bool>(false);
|
|
|
|
static final ValueNotifier<GridStyle> grid = ValueNotifier<GridStyle>(GridStyle.off);
|
|
static final ValueNotifier<bool> centerPoint = ValueNotifier<bool>(true);
|
|
static final ValueNotifier<LongPressAction> longPress = ValueNotifier<LongPressAction>(LongPressAction.meter);
|
|
|
|
/// Draw the flown trail on the map (settings → General).
|
|
static final ValueNotifier<bool> showRoute = ValueNotifier<bool>(true);
|
|
|
|
/// Intelligent flight modes whose first-use terms the pilot accepted (plan P7), by name.
|
|
static final ValueNotifier<Set<String>> acceptedModeTerms = ValueNotifier<Set<String>>(<String>{});
|
|
|
|
static SharedPreferences? _prefs;
|
|
|
|
static Future<void> load() async {
|
|
try {
|
|
_prefs = await SharedPreferences.getInstance();
|
|
classicHud.value = _prefs!.getBool(_classicKey) ?? false;
|
|
perfOverlay.value = _prefs!.getBool(_perfKey) ?? false;
|
|
grid.value = _byName(GridStyle.values, _prefs!.getString(_gridKey)) ?? GridStyle.off;
|
|
centerPoint.value = _prefs!.getBool(_centerKey) ?? true;
|
|
longPress.value = _byName(LongPressAction.values, _prefs!.getString(_longPressKey)) ?? LongPressAction.meter;
|
|
showRoute.value = _prefs!.getBool(_routeKey) ?? true;
|
|
acceptedModeTerms.value = (_prefs!.getStringList(_termsKey) ?? const <String>[]).toSet();
|
|
} catch (_) {
|
|
// Storage unavailable: run on the defaults.
|
|
}
|
|
}
|
|
|
|
static Future<void> setClassicHud(bool on) async {
|
|
classicHud.value = on;
|
|
await _prefs?.setBool(_classicKey, on);
|
|
}
|
|
|
|
static Future<void> setPerfOverlay(bool on) async {
|
|
perfOverlay.value = on;
|
|
await _prefs?.setBool(_perfKey, on);
|
|
}
|
|
|
|
static Future<void> setGrid(GridStyle g) async {
|
|
grid.value = g;
|
|
await _prefs?.setString(_gridKey, g.name);
|
|
}
|
|
|
|
static Future<void> setCenterPoint(bool on) async {
|
|
centerPoint.value = on;
|
|
await _prefs?.setBool(_centerKey, on);
|
|
}
|
|
|
|
static Future<void> setLongPress(LongPressAction a) async {
|
|
longPress.value = a;
|
|
await _prefs?.setString(_longPressKey, a.name);
|
|
}
|
|
|
|
static Future<void> setShowRoute(bool on) async {
|
|
showRoute.value = on;
|
|
await _prefs?.setBool(_routeKey, on);
|
|
}
|
|
|
|
static Future<void> acceptModeTerms(String mode) async {
|
|
if (acceptedModeTerms.value.contains(mode)) return;
|
|
acceptedModeTerms.value = <String>{...acceptedModeTerms.value, mode};
|
|
await _prefs?.setStringList(_termsKey, acceptedModeTerms.value.toList()..sort());
|
|
}
|
|
|
|
static T? _byName<T extends Enum>(List<T> values, String? name) {
|
|
for (final T v in values) {
|
|
if (v.name == name) return v;
|
|
}
|
|
return null;
|
|
}
|
|
}
|