2024-09-28 01:27:56 -04:00
|
|
|
import 'dart:async';
|
|
|
|
|
import 'dart:math';
|
|
|
|
|
|
|
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
|
import 'package:sensors_plus/sensors_plus.dart';
|
2025-11-28 17:05:35 +08:00
|
|
|
import 'package:vaani/features/settings/models/app_settings.dart';
|
2024-09-28 01:27:56 -04:00
|
|
|
|
|
|
|
|
final _logger = Logger('ShakeDetector');
|
|
|
|
|
|
|
|
|
|
class ShakeDetector {
|
|
|
|
|
final ShakeDetectionSettings _settings;
|
|
|
|
|
final Function()? onShakeDetected;
|
|
|
|
|
|
|
|
|
|
ShakeDetector(
|
|
|
|
|
this._settings,
|
|
|
|
|
this.onShakeDetected, {
|
|
|
|
|
startImmediately = true,
|
|
|
|
|
}) {
|
|
|
|
|
_logger.fine('ShakeDetector created with settings: $_settings');
|
|
|
|
|
if (startImmediately) {
|
|
|
|
|
start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StreamSubscription? _accelerometerSubscription;
|
|
|
|
|
|
|
|
|
|
int _currentShakeCount = 0;
|
|
|
|
|
|
|
|
|
|
DateTime _lastShakeTime = DateTime.now();
|
|
|
|
|
|
2024-09-30 02:34:13 -04:00
|
|
|
final StreamController<UserAccelerometerEvent>
|
|
|
|
|
_detectedShakeStreamController = StreamController.broadcast();
|
|
|
|
|
|
2024-09-28 01:27:56 -04:00
|
|
|
void start() {
|
|
|
|
|
if (_accelerometerSubscription != null) {
|
|
|
|
|
_logger.warning('ShakeDetector is already running');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_accelerometerSubscription =
|
|
|
|
|
userAccelerometerEventStream(samplingPeriod: _settings.samplingPeriod)
|
|
|
|
|
.listen((event) {
|
2024-09-30 02:34:13 -04:00
|
|
|
_logger.finest('RMS: ${event.rms}');
|
2024-09-28 01:27:56 -04:00
|
|
|
if (event.rms > _settings.threshold) {
|
|
|
|
|
_currentShakeCount++;
|
|
|
|
|
|
|
|
|
|
if (_currentShakeCount >= _settings.shakeTriggerCount &&
|
|
|
|
|
!isCoolDownNeeded()) {
|
|
|
|
|
_logger.fine('Shake detected $_currentShakeCount times');
|
|
|
|
|
|
|
|
|
|
onShakeDetected?.call();
|
2024-09-30 02:34:13 -04:00
|
|
|
_detectedShakeStreamController.add(event);
|
2024-09-28 01:27:56 -04:00
|
|
|
|
|
|
|
|
_lastShakeTime = DateTime.now();
|
|
|
|
|
_currentShakeCount = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
_currentShakeCount = 0;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_logger.fine('ShakeDetector started');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stop() {
|
|
|
|
|
_currentShakeCount = 0;
|
|
|
|
|
_accelerometerSubscription?.cancel();
|
|
|
|
|
_accelerometerSubscription = null;
|
2024-09-30 02:34:13 -04:00
|
|
|
_detectedShakeStreamController.close();
|
2024-09-28 01:27:56 -04:00
|
|
|
_logger.fine('ShakeDetector stopped');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isCoolDownNeeded() {
|
|
|
|
|
return _lastShakeTime
|
|
|
|
|
.add(_settings.shakeTriggerCoolDown)
|
|
|
|
|
.isAfter(DateTime.now());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension UserAccelerometerEventRMS on UserAccelerometerEvent {
|
|
|
|
|
double get rms => sqrt(x * x + y * y + z * z);
|
|
|
|
|
}
|