mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-10 13:09:29 +00:00
feat: add shake detection functionality (#36)
* feat: add shake detection functionality and integrate vibration support * feat: add shake detector settings page
This commit is contained in:
parent
2e3b1de529
commit
b229c4f2f5
25 changed files with 1423 additions and 158 deletions
138
lib/features/shake_detection/providers/shake_detector.dart
Normal file
138
lib/features/shake_detection/providers/shake_detector.dart
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:vaani/features/player/providers/audiobook_player.dart'
|
||||
show audiobookPlayerProvider, simpleAudiobookPlayerProvider;
|
||||
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart'
|
||||
show sleepTimerProvider;
|
||||
import 'package:vaani/settings/app_settings_provider.dart'
|
||||
show appSettingsProvider;
|
||||
import 'package:vaani/settings/models/app_settings.dart';
|
||||
import 'package:vibration/vibration.dart';
|
||||
|
||||
import '../core/shake_detector.dart' as core;
|
||||
|
||||
part 'shake_detector.g.dart';
|
||||
|
||||
Logger _logger = Logger('ShakeDetector');
|
||||
|
||||
@riverpod
|
||||
class ShakeDetector extends _$ShakeDetector {
|
||||
@override
|
||||
core.ShakeDetector? build() {
|
||||
final appSettings = ref.watch(appSettingsProvider);
|
||||
final shakeDetectionSettings = appSettings.shakeDetectionSettings;
|
||||
|
||||
if (!shakeDetectionSettings.isEnabled) {
|
||||
_logger.fine('Shake detection is disabled');
|
||||
return null;
|
||||
}
|
||||
final player = ref.watch(audiobookPlayerProvider);
|
||||
if (!player.playing && !shakeDetectionSettings.isActiveWhenPaused) {
|
||||
_logger.fine(
|
||||
'Shake detection is disabled when paused and player is not playing',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
// if sleep timer is not enabled, shake detection should not be enabled
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
if (!shakeDetectionSettings.isPlaybackManagementEnabled &&
|
||||
sleepTimer == null) {
|
||||
_logger.fine('No playback management is enabled and sleep timer is off, '
|
||||
'so shake detection is disabled');
|
||||
return null;
|
||||
}
|
||||
|
||||
_logger.fine('Creating shake detector');
|
||||
final detector = core.ShakeDetector(
|
||||
shakeDetectionSettings,
|
||||
() {
|
||||
doShakeAction(
|
||||
shakeDetectionSettings.shakeAction,
|
||||
ref: ref,
|
||||
);
|
||||
shakeDetectionSettings.feedback.forEach(postShakeFeedback);
|
||||
},
|
||||
);
|
||||
ref.onDispose(detector.dispose);
|
||||
return detector;
|
||||
}
|
||||
|
||||
void doShakeAction(
|
||||
ShakeAction shakeAction, {
|
||||
required Ref ref,
|
||||
}) {
|
||||
final player = ref.watch(simpleAudiobookPlayerProvider);
|
||||
switch (shakeAction) {
|
||||
case ShakeAction.resetSleepTimer:
|
||||
_logger.fine('Resetting sleep timer');
|
||||
ref.read(sleepTimerProvider.notifier).restartTimer();
|
||||
break;
|
||||
case ShakeAction.fastForward:
|
||||
_logger.fine('Fast forwarding');
|
||||
player.seek(player.position + const Duration(seconds: 30));
|
||||
break;
|
||||
case ShakeAction.rewind:
|
||||
_logger.fine('Rewinding');
|
||||
player.seek(player.position - const Duration(seconds: 30));
|
||||
break;
|
||||
case ShakeAction.playPause:
|
||||
if (player.book == null) {
|
||||
_logger.warning('No book is loaded');
|
||||
break;
|
||||
}
|
||||
player.togglePlayPause();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> postShakeFeedback(ShakeDetectedFeedback feedback) async {
|
||||
switch (feedback) {
|
||||
case ShakeDetectedFeedback.vibrate:
|
||||
_logger.fine('Vibrating');
|
||||
|
||||
if (await Vibration.hasAmplitudeControl() ?? false) {
|
||||
Vibration.vibrate(amplitude: 128, duration: 200);
|
||||
break;
|
||||
}
|
||||
|
||||
if (await Vibration.hasVibrator() ?? false) {
|
||||
Vibration.vibrate();
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.warning('No vibration support');
|
||||
|
||||
break;
|
||||
case ShakeDetectedFeedback.beep:
|
||||
_logger.fine('Beeping');
|
||||
final player = AudioPlayer();
|
||||
await player.setAsset('assets/sounds/beep.mp3');
|
||||
await player.setVolume(0.5);
|
||||
await player.play();
|
||||
await player.dispose();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension on ShakeDetectionSettings {
|
||||
bool get isActiveWhenPaused {
|
||||
// If the shake action is play/pause, it should be required when not playing
|
||||
return shakeAction == ShakeAction.playPause;
|
||||
}
|
||||
|
||||
bool get isPlaybackManagementEnabled {
|
||||
return {ShakeAction.playPause, ShakeAction.fastForward, ShakeAction.rewind}
|
||||
.contains(shakeAction);
|
||||
}
|
||||
|
||||
bool get shouldActOnSleepTimer {
|
||||
return {ShakeAction.resetSleepTimer}.contains(shakeAction);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue