64 lines
1.9 KiB
Dart
64 lines
1.9 KiB
Dart
|
|
// Auto dark/bright from the tablet's light sensor. The Redmi Pad SE has no separate one:
|
||
|
|
// Xiaomi derives it from the front camera ("camera_light_Sensor") and offers it as
|
||
|
|
// Android's ordinary light sensor, the one auto-brightness reads. No camera permission.
|
||
|
|
|
||
|
|
import 'package:flutter/services.dart';
|
||
|
|
|
||
|
|
/// Light levels in lux, about once a second, from MainActivity. Ends at once (an error,
|
||
|
|
/// which [Themes.followLight] ignores) where there is no sensor or no Android side.
|
||
|
|
Stream<double> lightLevels() =>
|
||
|
|
const EventChannel('yukart/light')
|
||
|
|
.receiveBroadcastStream()
|
||
|
|
.map((e) => (e as num).toDouble());
|
||
|
|
|
||
|
|
/// Dark or bright from light levels, with a gap and a hold so dusk, shade and
|
||
|
|
/// passing lights do not flicker it.
|
||
|
|
class LightSwitch {
|
||
|
|
/// Dark at or below this.
|
||
|
|
static const darkAt = 15.0;
|
||
|
|
|
||
|
|
/// Bright at or above this; in between, whatever it was.
|
||
|
|
static const brightAt = 60.0;
|
||
|
|
|
||
|
|
/// How long a level must last before it switches.
|
||
|
|
static const hold = Duration(seconds: 5);
|
||
|
|
|
||
|
|
bool? _dark;
|
||
|
|
DateTime? _since;
|
||
|
|
|
||
|
|
bool? get isDark => _dark;
|
||
|
|
|
||
|
|
/// A change is waiting out its hold.
|
||
|
|
bool get pending => _since != null;
|
||
|
|
|
||
|
|
/// The hold has passed with no reading against it (a sensor that reports only on
|
||
|
|
/// change goes quiet): make the waiting change.
|
||
|
|
void settle() {
|
||
|
|
if (_since == null) return;
|
||
|
|
_dark = !_dark!;
|
||
|
|
_since = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Feed one reading; returns the verdict (null until a reading outside the gap).
|
||
|
|
bool? feed(double lux, DateTime now) {
|
||
|
|
final want = lux <= darkAt
|
||
|
|
? true
|
||
|
|
: lux >= brightAt
|
||
|
|
? false
|
||
|
|
: _dark;
|
||
|
|
if (_dark == null) {
|
||
|
|
// The first clear reading decides at once; one in the gap leaves it open.
|
||
|
|
_dark = want;
|
||
|
|
} else if (want == _dark) {
|
||
|
|
_since = null;
|
||
|
|
} else {
|
||
|
|
_since ??= now;
|
||
|
|
if (now.difference(_since!) >= hold) {
|
||
|
|
_dark = want;
|
||
|
|
_since = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return _dark;
|
||
|
|
}
|
||
|
|
}
|