machine_learning_controller.dart 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import "dart:async";
  2. import "dart:io";
  3. import "package:battery_info/battery_info_plugin.dart";
  4. import "package:battery_info/model/android_battery_info.dart";
  5. import "package:logging/logging.dart";
  6. import "package:photos/core/event_bus.dart";
  7. import "package:photos/events/machine_learning_control_event.dart";
  8. class MachineLearningController {
  9. MachineLearningController._privateConstructor();
  10. static final MachineLearningController instance =
  11. MachineLearningController._privateConstructor();
  12. final _logger = Logger("MachineLearningController");
  13. static const kMaximumTemperature = 42; // 42 degree celsius
  14. static const kMinimumBatteryLevel = 20; // 20%
  15. static const kDefaultInteractionTimeout = Duration(seconds: 15);
  16. static const kUnhealthyStates = ["over_heat", "over_voltage", "dead"];
  17. bool _isDeviceHealthy = true;
  18. bool _isUserInteracting = true;
  19. bool _isRunningML = false;
  20. late Timer _userInteractionTimer;
  21. get canRunML => _isDeviceHealthy && !_isUserInteracting;
  22. void init() {
  23. if (Platform.isAndroid) {
  24. _startInteractionTimer();
  25. BatteryInfoPlugin()
  26. .androidBatteryInfoStream
  27. .listen((AndroidBatteryInfo? batteryInfo) {
  28. _onBatteryStateUpdate(batteryInfo);
  29. });
  30. } else {
  31. // Always run Machine Learning on iOS
  32. Bus.instance.fire(MachineLearningControlEvent(true));
  33. }
  34. }
  35. void onUserInteraction() {
  36. if (Platform.isIOS) {
  37. return;
  38. }
  39. if (!_isUserInteracting) {
  40. _logger.info("User is interacting with the app");
  41. _isUserInteracting = true;
  42. _fireControlEvent();
  43. }
  44. _resetTimer();
  45. }
  46. void _fireControlEvent() {
  47. final shouldRunML = canRunML;
  48. if (shouldRunML != _isRunningML) {
  49. _isRunningML = shouldRunML;
  50. _logger.info(
  51. "Firing event with device health: $_isDeviceHealthy and user interaction: $_isUserInteracting",
  52. );
  53. Bus.instance.fire(MachineLearningControlEvent(shouldRunML));
  54. }
  55. }
  56. void _startInteractionTimer({Duration timeout = kDefaultInteractionTimeout}) {
  57. _userInteractionTimer = Timer(timeout, () {
  58. _logger.info("User is not interacting with the app");
  59. _isUserInteracting = false;
  60. _fireControlEvent();
  61. });
  62. }
  63. void _resetTimer() {
  64. _userInteractionTimer.cancel();
  65. _startInteractionTimer();
  66. }
  67. void _onBatteryStateUpdate(AndroidBatteryInfo? batteryInfo) {
  68. _logger.info("Battery info: ${batteryInfo!.toJson()}");
  69. _isDeviceHealthy = _computeIsDeviceHealthy(batteryInfo);
  70. _fireControlEvent();
  71. }
  72. bool _computeIsDeviceHealthy(AndroidBatteryInfo info) {
  73. return _hasSufficientBattery(info.batteryLevel ?? kMinimumBatteryLevel) &&
  74. _isAcceptableTemperature(info.temperature ?? kMaximumTemperature) &&
  75. _isBatteryHealthy(info.health ?? "");
  76. }
  77. bool _hasSufficientBattery(int batteryLevel) {
  78. return batteryLevel >= kMinimumBatteryLevel;
  79. }
  80. bool _isAcceptableTemperature(int temperature) {
  81. return temperature <= kMaximumTemperature;
  82. }
  83. bool _isBatteryHealthy(String health) {
  84. return !kUnhealthyStates.contains(health);
  85. }
  86. }