Initial commit: 91dj android client dev_1.0.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io' show Platform;
|
||||
import 'dart:ui' show window;
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
import 'package:screen_state/screen_state.dart';
|
||||
import 'package:volume_controller/volume_controller.dart';
|
||||
|
||||
/// Native runtime observation fields backed by platform plugins.
|
||||
class RuntimeObservationMonitor {
|
||||
final List<StreamSubscription<dynamic>> _subscriptions;
|
||||
final WidgetsBindingObserver? _metricsObserver;
|
||||
|
||||
const RuntimeObservationMonitor(
|
||||
this._subscriptions, {
|
||||
WidgetsBindingObserver? metricsObserver,
|
||||
}) : _metricsObserver = metricsObserver;
|
||||
|
||||
Future<void> dispose() async {
|
||||
for (final subscription in _subscriptions) {
|
||||
await subscription.cancel();
|
||||
}
|
||||
final metricsObserver = _metricsObserver;
|
||||
if (metricsObserver != null) {
|
||||
try {
|
||||
WidgetsBinding.instance.removeObserver(metricsObserver);
|
||||
} catch (_) {}
|
||||
}
|
||||
try {
|
||||
VolumeController().removeListener();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> collectRuntimeObservationFields() async {
|
||||
return {
|
||||
..._screenObservationFields(),
|
||||
'screen_brightness': await _readScreenBrightness(),
|
||||
'volume': await _readVolume(),
|
||||
'is_screen_on': _initialScreenOn(),
|
||||
};
|
||||
}
|
||||
|
||||
RuntimeObservationMonitor startRuntimeObservationMonitoring(
|
||||
void Function(Map<String, dynamic> fields) onFields,
|
||||
) {
|
||||
final subscriptions = <StreamSubscription<dynamic>>[];
|
||||
WidgetsBindingObserver? metricsObserver;
|
||||
|
||||
try {
|
||||
metricsObserver = _MetricsObserver(
|
||||
onMetricsChanged: () => onFields(_screenObservationFields()),
|
||||
);
|
||||
WidgetsBinding.instance.addObserver(metricsObserver);
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
subscriptions.add(ScreenBrightness().onCurrentBrightnessChanged.listen(
|
||||
(value) => onFields({
|
||||
'screen_brightness': _normalizedPercent(value),
|
||||
}), onError: (_) {
|
||||
onFields({'screen_brightness': -1});
|
||||
}));
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
subscriptions.add(VolumeController().listener((value) {
|
||||
onFields({'volume': _normalizedPercent(value)});
|
||||
}));
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
subscriptions.add(Screen().screenStateStream.listen((event) {
|
||||
onFields({'is_screen_on': _screenStateValue(event)});
|
||||
}, onError: (_) {
|
||||
onFields({'is_screen_on': -1});
|
||||
}));
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
return RuntimeObservationMonitor(
|
||||
subscriptions,
|
||||
metricsObserver: metricsObserver,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _screenObservationFields() {
|
||||
try {
|
||||
final size = window.physicalSize;
|
||||
final width = size.width.round();
|
||||
final height = size.height.round();
|
||||
final pixelRatio = (window.devicePixelRatio * 100).round();
|
||||
final dpi = (window.devicePixelRatio * 160).round();
|
||||
return {
|
||||
'screen_width': width,
|
||||
'screen_height': height,
|
||||
'screen_dpi': dpi,
|
||||
'device_pixel_ratio': pixelRatio,
|
||||
'orientation': width >= height ? 'landscape' : 'portrait',
|
||||
};
|
||||
} catch (_) {
|
||||
return const {
|
||||
'screen_width': -1,
|
||||
'screen_height': -1,
|
||||
'screen_dpi': -1,
|
||||
'device_pixel_ratio': -1,
|
||||
'orientation': '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> _readScreenBrightness() async {
|
||||
try {
|
||||
return _normalizedPercent(await ScreenBrightness().current);
|
||||
} catch (_) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> _readVolume() async {
|
||||
try {
|
||||
return _normalizedPercent(await VolumeController().getVolume());
|
||||
} catch (_) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _initialScreenOn() {
|
||||
try {
|
||||
return Platform.isAndroid || Platform.isIOS ? 1 : -1;
|
||||
} catch (_) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _screenStateValue(ScreenStateEvent event) {
|
||||
switch (event) {
|
||||
case ScreenStateEvent.SCREEN_ON:
|
||||
case ScreenStateEvent.SCREEN_UNLOCKED:
|
||||
return 1;
|
||||
case ScreenStateEvent.SCREEN_OFF:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int _normalizedPercent(num value) {
|
||||
final numeric = value.toDouble();
|
||||
if (!numeric.isFinite) return -1;
|
||||
final scaled = numeric <= 1 ? numeric * 100 : numeric;
|
||||
return scaled.clamp(0, 100).round();
|
||||
}
|
||||
|
||||
class _MetricsObserver with WidgetsBindingObserver {
|
||||
final void Function() onMetricsChanged;
|
||||
|
||||
const _MetricsObserver({required this.onMetricsChanged});
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
onMetricsChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user