fix(player): preserve volume when toggling mute (#1532)

* fix(player): preserve volume when toggling mute

Muting now keeps the last volume level memorized. Unmuting restores that level, including amplified values allowed by the configured maximum.

* fix(player): preserve volume for companion remote mute

Route the companion remote through the shared mute transition so it no longer overwrites the persisted volume or restores to 100%. Also correct the Dartdoc formatting for the transition record fields.
This commit is contained in:
Ryan Ernest
2026-07-11 12:46:58 +02:00
committed by GitHub
parent d86e328169
commit c86d40f8dd
7 changed files with 192 additions and 10 deletions
@@ -39,9 +39,9 @@ extension _VideoPlayerCompanionRemoteMethods on VideoPlayerScreenState {
receiver.onVolumeMute = () async {
if (player == null) return;
final settings = await SettingsService.getInstance();
final newVolume = player!.state.volume > 0 ? 0.0 : 100.0;
unawaited(player!.setVolume(newVolume));
unawaited(settings.write(SettingsService.volume, newVolume));
final transition = settings.resolveMuteToggle(player!.state.volume);
unawaited(player!.setVolume(transition.playerVolume));
unawaited(settings.write(SettingsService.volume, transition.persistedVolume));
};
receiver.onSubtitles = _cycleSubtitleTrack;
receiver.onAudioTracks = _cycleAudioTrack;
+3 -3
View File
@@ -344,9 +344,9 @@ class KeyboardShortcutsService extends ChangeNotifier {
onToggleFullscreen?.call();
break;
case 'mute_toggle':
final newVolume = player.state.volume > 0 ? 0.0 : 100.0;
player.setVolume(newVolume);
_settingsService.write(SettingsService.volume, newVolume);
final transition = _settingsService.resolveMuteToggle(player.state.volume);
player.setVolume(transition.playerVolume);
_settingsService.write(SettingsService.volume, transition.persistedVolume);
break;
case 'subtitle_toggle':
onToggleSubtitles?.call();
+15
View File
@@ -614,6 +614,21 @@ class SettingsService extends BaseSharedPreferencesService {
_cachedInstance = null;
}
/// Resolves a video mute toggle without replacing the saved volume with 0.
///
/// `persistedVolume` is the non-zero value callers should keep in [volume],
/// while `playerVolume` is the value to apply to the active player.
({double playerVolume, double persistedVolume}) resolveMuteToggle(double currentVolume) {
if (currentVolume.isFinite && currentVolume > 0) {
return (playerVolume: 0, persistedVolume: currentVolume);
}
final previousVolume = read(volume);
final candidate = previousVolume.isFinite && previousVolume > 0 ? previousVolume : volume.defaultValue;
final restoredVolume = candidate.clamp(0.0, read(maxVolume).toDouble()).toDouble();
return (playerVolume: restoredVolume, persistedVolume: restoredVolume);
}
static Map<String, String> defaultKeyboardShortcuts() => _defaultKeyboardShortcuts();
static Map<String, HotKey> defaultKeyboardHotkeys() => _defaultKeyboardHotkeys();
@@ -148,9 +148,9 @@ class _VolumeControlState extends State<VolumeControl> {
color: Colors.white,
),
onPressed: () async {
final newVolume = isMuted ? 100.0 : 0.0;
await widget.player.setVolume(newVolume);
await _settings.write(SettingsService.volume, newVolume);
final transition = _settings.resolveMuteToggle(widget.player.state.volume);
await widget.player.setVolume(transition.playerVolume);
await _settings.write(SettingsService.volume, transition.persistedVolume);
},
),
);