Files
hjsq_android/analytics_sdk/lib/utils/runtime_observation_native.dart
T
2026-10-08 11:37:25 +07:00

165 lines
4.2 KiB
Dart

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();
}
}