Checklist v4 from contracts: step 31, each wheel by hand
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
+69
-6
@@ -24,6 +24,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'light.dart';
|
||||
import 'sun.dart';
|
||||
|
||||
/// The HUD face (smart-serow's DIN 1451). Opt-in: a widget names it in its own
|
||||
/// TextStyle; everything else keeps the basic font, which covers CJK where DIN does not.
|
||||
@@ -120,21 +121,36 @@ class Palette {
|
||||
);
|
||||
}
|
||||
|
||||
/// Which variant shows: auto (the light sensor, else Android's dark theme), or one held fixed.
|
||||
/// Which variant shows: auto (a light sensor, else the sun, else Android's dark theme),
|
||||
/// or one held fixed.
|
||||
enum VariantMode { auto, dark, bright }
|
||||
|
||||
/// A theme: both variants, and which one is showing. Listen to it to follow a switch.
|
||||
/// In auto it follows the tablet's light sensor ([followLight]); without one, Android's
|
||||
/// dark theme (by hand or on its schedule). The mode is kept across restarts.
|
||||
/// In auto it follows a light sensor ([followLight]) where the device lets an app read
|
||||
/// one; else the sun at the car's last position ([setPosition]); else Android's dark
|
||||
/// theme (by hand or on its schedule). The mode and the position are kept across restarts.
|
||||
class Themes extends ChangeNotifier with WidgetsBindingObserver {
|
||||
Themes(this.name, this.dark, this.bright, [this._prefs]) {
|
||||
Themes(
|
||||
this.name,
|
||||
this.dark,
|
||||
this.bright, [
|
||||
this._prefs,
|
||||
this._clock = DateTime.now,
|
||||
]) {
|
||||
_mode =
|
||||
VariantMode.values.asNameMap()[_prefs?.getString(_key)] ??
|
||||
VariantMode.auto;
|
||||
final (lat, lon) = (_prefs?.getDouble(_latKey), _prefs?.getDouble(_lonKey));
|
||||
if (lat != null && lon != null) _position = (lat, lon);
|
||||
_sunDark = _sunNow();
|
||||
_sunTimer = Timer.periodic(const Duration(minutes: 1), (_) => _sunCheck());
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
static const _key = 'variant_mode';
|
||||
static const _latKey = 'sun_lat';
|
||||
static const _lonKey = 'sun_lon';
|
||||
final DateTime Function() _clock;
|
||||
|
||||
final String name;
|
||||
final Palette dark;
|
||||
@@ -157,7 +173,7 @@ class Themes extends ChangeNotifier with WidgetsBindingObserver {
|
||||
/// The last light level, in lux, while a sensor reports (for Settings, to tune by).
|
||||
final lux = ValueNotifier<double?>(null);
|
||||
|
||||
/// Follow a light sensor in auto. Errors (no sensor) leave auto on Android's dark theme.
|
||||
/// Follow a light sensor in auto. Errors (no sensor, or refused) leave auto to the sun.
|
||||
void followLight(Stream<double> levels) {
|
||||
void changed(bool? before) {
|
||||
if (_light.isDark != before && _mode == VariantMode.auto) {
|
||||
@@ -190,9 +206,51 @@ class Themes extends ChangeNotifier with WidgetsBindingObserver {
|
||||
);
|
||||
}
|
||||
|
||||
(double, double)? _position;
|
||||
bool? _sunDark;
|
||||
late final Timer _sunTimer;
|
||||
|
||||
/// The car's position, for the sun. Kept (to 0.1°, about 10 km: plenty for the sun)
|
||||
/// so auto knows the sun before the first fix.
|
||||
void setPosition(double lat, double lon) {
|
||||
double r(double x) => (x * 10).round() / 10;
|
||||
final p = (r(lat), r(lon));
|
||||
if (p == _position) return;
|
||||
_position = p;
|
||||
_prefs?.setDouble(_latKey, p.$1);
|
||||
_prefs?.setDouble(_lonKey, p.$2);
|
||||
_sunCheck();
|
||||
}
|
||||
|
||||
/// The sun's elevation now, in degrees, or null with no position yet.
|
||||
double? get sunElevationNow {
|
||||
final p = _position;
|
||||
return p == null ? null : sunElevation(p.$1, p.$2, _clock());
|
||||
}
|
||||
|
||||
bool? _sunNow() {
|
||||
final e = sunElevationNow;
|
||||
return e == null ? null : e < darkBelowDeg;
|
||||
}
|
||||
|
||||
void _sunCheck() {
|
||||
final now = _sunNow();
|
||||
if (now == _sunDark) return;
|
||||
_sunDark = now;
|
||||
if (_mode == VariantMode.auto && _light.isDark == null) notifyListeners();
|
||||
}
|
||||
|
||||
/// What auto follows now, in words, for Settings.
|
||||
String get autoSource => _light.isDark != null
|
||||
? 'the light sensor'
|
||||
: _sunDark != null
|
||||
? 'the sun'
|
||||
: "Android's dark theme";
|
||||
|
||||
bool get isDark => switch (_mode) {
|
||||
VariantMode.auto =>
|
||||
_light.isDark ??
|
||||
_sunDark ??
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark,
|
||||
VariantMode.dark => true,
|
||||
@@ -206,11 +264,16 @@ class Themes extends ChangeNotifier with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
void didChangePlatformBrightness() {
|
||||
if (_mode == VariantMode.auto && _light.isDark == null) notifyListeners();
|
||||
if (_mode == VariantMode.auto &&
|
||||
_light.isDark == null &&
|
||||
_sunDark == null) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_sunTimer.cancel();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_holdTimer?.cancel();
|
||||
lux.dispose();
|
||||
|
||||
Reference in New Issue
Block a user