242 lines
8.8 KiB
Dart
242 lines
8.8 KiB
Dart
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
|
|
||
|
|
import '../core/contracts/player_gestures.dart';
|
||
|
|
import 'shared/settings_persistence.dart';
|
||
|
|
|
||
|
|
class PlayerSettingsState {
|
||
|
|
final String hwdecMode;
|
||
|
|
final double volume;
|
||
|
|
final int seekForwardSeconds;
|
||
|
|
final int seekBackwardSeconds;
|
||
|
|
|
||
|
|
final double keyboardHoldLeftSpeed;
|
||
|
|
final double keyboardHoldRightSpeed;
|
||
|
|
|
||
|
|
final GestureAction mouseLeftClickAction;
|
||
|
|
final GestureAction mouseLeftDoubleClickAction;
|
||
|
|
final GestureAction mouseRightClickAction;
|
||
|
|
final GestureAction mouseRightDoubleClickAction;
|
||
|
|
final bool autoPip;
|
||
|
|
final bool pipShowEpisodeActions;
|
||
|
|
final double? androidBrightness;
|
||
|
|
|
||
|
|
const PlayerSettingsState({
|
||
|
|
this.hwdecMode = 'auto-copy',
|
||
|
|
this.volume = 100.0,
|
||
|
|
this.seekForwardSeconds = 15,
|
||
|
|
this.seekBackwardSeconds = 15,
|
||
|
|
this.keyboardHoldLeftSpeed = 0.5,
|
||
|
|
this.keyboardHoldRightSpeed = 3.0,
|
||
|
|
this.mouseLeftClickAction = GestureAction.playPause,
|
||
|
|
this.mouseLeftDoubleClickAction = GestureAction.fullscreen,
|
||
|
|
this.mouseRightClickAction = GestureAction.toggleControls,
|
||
|
|
this.mouseRightDoubleClickAction = GestureAction.none,
|
||
|
|
this.autoPip = true,
|
||
|
|
this.pipShowEpisodeActions = true,
|
||
|
|
this.androidBrightness,
|
||
|
|
});
|
||
|
|
|
||
|
|
PlayerSettingsState copyWith({
|
||
|
|
String? hwdecMode,
|
||
|
|
double? volume,
|
||
|
|
int? seekForwardSeconds,
|
||
|
|
int? seekBackwardSeconds,
|
||
|
|
double? keyboardHoldLeftSpeed,
|
||
|
|
double? keyboardHoldRightSpeed,
|
||
|
|
GestureAction? mouseLeftClickAction,
|
||
|
|
GestureAction? mouseLeftDoubleClickAction,
|
||
|
|
GestureAction? mouseRightClickAction,
|
||
|
|
GestureAction? mouseRightDoubleClickAction,
|
||
|
|
bool? autoPip,
|
||
|
|
bool? pipShowEpisodeActions,
|
||
|
|
double? androidBrightness,
|
||
|
|
}) => PlayerSettingsState(
|
||
|
|
hwdecMode: hwdecMode ?? this.hwdecMode,
|
||
|
|
volume: volume ?? this.volume,
|
||
|
|
seekForwardSeconds: seekForwardSeconds ?? this.seekForwardSeconds,
|
||
|
|
seekBackwardSeconds: seekBackwardSeconds ?? this.seekBackwardSeconds,
|
||
|
|
keyboardHoldLeftSpeed: keyboardHoldLeftSpeed ?? this.keyboardHoldLeftSpeed,
|
||
|
|
keyboardHoldRightSpeed:
|
||
|
|
keyboardHoldRightSpeed ?? this.keyboardHoldRightSpeed,
|
||
|
|
mouseLeftClickAction: mouseLeftClickAction ?? this.mouseLeftClickAction,
|
||
|
|
mouseLeftDoubleClickAction:
|
||
|
|
mouseLeftDoubleClickAction ?? this.mouseLeftDoubleClickAction,
|
||
|
|
mouseRightClickAction: mouseRightClickAction ?? this.mouseRightClickAction,
|
||
|
|
mouseRightDoubleClickAction:
|
||
|
|
mouseRightDoubleClickAction ?? this.mouseRightDoubleClickAction,
|
||
|
|
autoPip: autoPip ?? this.autoPip,
|
||
|
|
pipShowEpisodeActions: pipShowEpisodeActions ?? this.pipShowEpisodeActions,
|
||
|
|
androidBrightness: androidBrightness ?? this.androidBrightness,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
class PlayerSettingsNotifier extends AsyncNotifier<PlayerSettingsState>
|
||
|
|
with SettingsPersistence {
|
||
|
|
static const _keyHwdec = 'smplayer.player_hwdec';
|
||
|
|
static const _legacyKeyEnginePreference = 'smplayer.player_engine_preference';
|
||
|
|
static const _keyVolume = 'smplayer.player_volume';
|
||
|
|
static const _keySeekForward = 'smplayer.player_seek_forward';
|
||
|
|
static const _keySeekBackward = 'smplayer.player_seek_backward';
|
||
|
|
static const _keyKbdHoldLeft = 'smplayer.gesture_kbd_hold_left_speed';
|
||
|
|
static const _keyKbdHoldRight = 'smplayer.gesture_kbd_hold_right_speed';
|
||
|
|
static const _keyMouseLeftClick = 'smplayer.gesture_mouse_left_click';
|
||
|
|
static const _keyMouseLeftDblClick = 'smplayer.gesture_mouse_left_dbl_click';
|
||
|
|
static const _keyMouseRightClick = 'smplayer.gesture_mouse_right_click';
|
||
|
|
static const _keyMouseRightDblClick =
|
||
|
|
'smplayer.gesture_mouse_right_dbl_click';
|
||
|
|
static const _keyAutoPip = 'smplayer.player_auto_pip';
|
||
|
|
static const _keyPipEpisodeActions = 'smplayer.player_pip_episode_actions';
|
||
|
|
static const _keyAndroidBrightness = 'smplayer.player_android_brightness';
|
||
|
|
|
||
|
|
static const _speedAllowed = <double>[
|
||
|
|
0.5,
|
||
|
|
0.75,
|
||
|
|
1.0,
|
||
|
|
1.25,
|
||
|
|
1.5,
|
||
|
|
1.75,
|
||
|
|
2.0,
|
||
|
|
2.5,
|
||
|
|
3.0,
|
||
|
|
4.0,
|
||
|
|
8.0,
|
||
|
|
];
|
||
|
|
|
||
|
|
static double _normalizeSpeed(double v, double fallback) {
|
||
|
|
for (final s in _speedAllowed) {
|
||
|
|
if ((s - v).abs() < 0.001) return s;
|
||
|
|
}
|
||
|
|
return fallback;
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<PlayerSettingsState> build() async {
|
||
|
|
final prefs = await getPrefs();
|
||
|
|
if (prefs.containsKey(_keyHwdec)) {
|
||
|
|
await prefs.remove(_keyHwdec);
|
||
|
|
}
|
||
|
|
const hwdec = 'auto-copy';
|
||
|
|
final volume = prefs.getDouble(_keyVolume) ?? 100.0;
|
||
|
|
final seekFwd = prefs.getInt(_keySeekForward) ?? 15;
|
||
|
|
final seekBwd = prefs.getInt(_keySeekBackward) ?? 15;
|
||
|
|
if (prefs.containsKey(_legacyKeyEnginePreference)) {
|
||
|
|
await prefs.remove(_legacyKeyEnginePreference);
|
||
|
|
}
|
||
|
|
final holdLeft = _normalizeSpeed(
|
||
|
|
prefs.getDouble(_keyKbdHoldLeft) ?? 0.5,
|
||
|
|
0.5,
|
||
|
|
);
|
||
|
|
final holdRight = _normalizeSpeed(
|
||
|
|
prefs.getDouble(_keyKbdHoldRight) ?? 3.0,
|
||
|
|
3.0,
|
||
|
|
);
|
||
|
|
final autoPip = prefs.getBool(_keyAutoPip) ?? true;
|
||
|
|
final pipEpisodeActions = prefs.getBool(_keyPipEpisodeActions) ?? true;
|
||
|
|
final androidBrightness = prefs
|
||
|
|
.getDouble(_keyAndroidBrightness)
|
||
|
|
?.clamp(0.0, 1.0);
|
||
|
|
return PlayerSettingsState(
|
||
|
|
hwdecMode: hwdec,
|
||
|
|
volume: volume.clamp(0.0, 200.0),
|
||
|
|
seekForwardSeconds: seekFwd.clamp(5, 120),
|
||
|
|
seekBackwardSeconds: seekBwd.clamp(5, 120),
|
||
|
|
keyboardHoldLeftSpeed: holdLeft,
|
||
|
|
keyboardHoldRightSpeed: holdRight,
|
||
|
|
mouseLeftClickAction: gestureActionFromName(
|
||
|
|
prefs.getString(_keyMouseLeftClick),
|
||
|
|
GestureAction.playPause,
|
||
|
|
),
|
||
|
|
mouseLeftDoubleClickAction: gestureActionFromName(
|
||
|
|
prefs.getString(_keyMouseLeftDblClick),
|
||
|
|
GestureAction.fullscreen,
|
||
|
|
),
|
||
|
|
mouseRightClickAction: gestureActionFromName(
|
||
|
|
prefs.getString(_keyMouseRightClick),
|
||
|
|
GestureAction.toggleControls,
|
||
|
|
),
|
||
|
|
mouseRightDoubleClickAction: gestureActionFromName(
|
||
|
|
prefs.getString(_keyMouseRightDblClick),
|
||
|
|
GestureAction.none,
|
||
|
|
),
|
||
|
|
autoPip: autoPip,
|
||
|
|
pipShowEpisodeActions: pipEpisodeActions,
|
||
|
|
androidBrightness: androidBrightness,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setVolume(double volume) async {
|
||
|
|
final clamped = volume.clamp(0.0, 200.0);
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(volume: clamped));
|
||
|
|
await persistField(_keyVolume, clamped);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setSeekForwardSeconds(int seconds) async {
|
||
|
|
final clamped = seconds.clamp(5, 120);
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(seekForwardSeconds: clamped));
|
||
|
|
await persistField(_keySeekForward, clamped);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setSeekBackwardSeconds(int seconds) async {
|
||
|
|
final clamped = seconds.clamp(5, 120);
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(seekBackwardSeconds: clamped));
|
||
|
|
await persistField(_keySeekBackward, clamped);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setKeyboardHoldLeftSpeed(double v) async {
|
||
|
|
final normalized = _normalizeSpeed(v, 0.5);
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(
|
||
|
|
current.copyWith(keyboardHoldLeftSpeed: normalized),
|
||
|
|
);
|
||
|
|
await persistField(_keyKbdHoldLeft, normalized);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setKeyboardHoldRightSpeed(double v) async {
|
||
|
|
final normalized = _normalizeSpeed(v, 3.0);
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(
|
||
|
|
current.copyWith(keyboardHoldRightSpeed: normalized),
|
||
|
|
);
|
||
|
|
await persistField(_keyKbdHoldRight, normalized);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setMouseLeftClickAction(GestureAction a) async {
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(mouseLeftClickAction: a));
|
||
|
|
await persistField(_keyMouseLeftClick, a.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setMouseLeftDoubleClickAction(GestureAction a) async {
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(mouseLeftDoubleClickAction: a));
|
||
|
|
await persistField(_keyMouseLeftDblClick, a.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setMouseRightClickAction(GestureAction a) async {
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(mouseRightClickAction: a));
|
||
|
|
await persistField(_keyMouseRightClick, a.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setMouseRightDoubleClickAction(GestureAction a) async {
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(mouseRightDoubleClickAction: a));
|
||
|
|
await persistField(_keyMouseRightDblClick, a.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setAndroidBrightness(double brightness) async {
|
||
|
|
final clamped = brightness.clamp(0.0, 1.0);
|
||
|
|
final current = state.value ?? const PlayerSettingsState();
|
||
|
|
state = AsyncValue.data(current.copyWith(androidBrightness: clamped));
|
||
|
|
await persistField(_keyAndroidBrightness, clamped);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
final playerSettingsProvider =
|
||
|
|
AsyncNotifierProvider<PlayerSettingsNotifier, PlayerSettingsState>(
|
||
|
|
PlayerSettingsNotifier.new,
|
||
|
|
);
|