machine_learning_controller.dart 3.1 KB

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