feat(player): periodic PlaybackState refresh for smooth external scrubber
_broadcastState only set updatePosition on transitions, so the lock- screen / Wear / Android Auto scrubber jumped in chunks (the in-app bar uses positionStream and was fine). Add _positionBroadcastTimer: a 1s periodic PlaybackState re-broadcast while actively playing so updateTime/updatePosition stay fresh and external surfaces interpolate smoothly. Idempotent (driven from _broadcastState, which the tick itself calls — guarded against pile-up), cancelled when not playing and in stop(). 6a: the notification progress bar now advances since MediaItem.duration was already set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,12 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
|||||||
/// cancelled the moment playback resumes or a new queue is set.
|
/// cancelled the moment playback resumes or a new queue is set.
|
||||||
Timer? _idleStopTimer;
|
Timer? _idleStopTimer;
|
||||||
|
|
||||||
|
/// Periodic PlaybackState re-broadcast while actively playing so
|
||||||
|
/// external surfaces (lock screen, Wear, Android Auto) interpolate the
|
||||||
|
/// scrubber smoothly. The in-app bar uses positionStream and doesn't
|
||||||
|
/// need this. Active only while playing; cancelled otherwise.
|
||||||
|
Timer? _positionBroadcastTimer;
|
||||||
|
|
||||||
/// Player volume captured when an OS duck interruption begins,
|
/// Player volume captured when an OS duck interruption begins,
|
||||||
/// restored when it ends. Null when not currently ducked.
|
/// restored when it ends. Null when not currently ducked.
|
||||||
double? _volumeBeforeDuck;
|
double? _volumeBeforeDuck;
|
||||||
@@ -544,6 +550,8 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
|||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
_idleStopTimer?.cancel();
|
_idleStopTimer?.cancel();
|
||||||
_idleStopTimer = null;
|
_idleStopTimer = null;
|
||||||
|
_positionBroadcastTimer?.cancel();
|
||||||
|
_positionBroadcastTimer = null;
|
||||||
try {
|
try {
|
||||||
await _player.stop();
|
await _player.stop();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@@ -712,6 +720,7 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
_reconcileIdleTimer();
|
_reconcileIdleTimer();
|
||||||
|
_reconcilePositionBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arms (or cancels) the idle-cleanup timer based on the current
|
/// Arms (or cancels) the idle-cleanup timer based on the current
|
||||||
@@ -746,6 +755,28 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl
|
|||||||
unawaited(stop());
|
unawaited(stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// While actively playing, keeps a 1s periodic PlaybackState re-
|
||||||
|
/// broadcast running so updateTime/updatePosition stay fresh and
|
||||||
|
/// external surfaces interpolate the scrubber smoothly. Idempotent and
|
||||||
|
/// driven from _broadcastState — which the periodic tick itself calls,
|
||||||
|
/// so the "already running" guard prevents pile-up. Cancels the moment
|
||||||
|
/// playback is no longer active.
|
||||||
|
void _reconcilePositionBroadcast() {
|
||||||
|
final ps = _player.processingState;
|
||||||
|
final active = _player.playing &&
|
||||||
|
ps != ProcessingState.idle &&
|
||||||
|
ps != ProcessingState.completed;
|
||||||
|
if (!active) {
|
||||||
|
_positionBroadcastTimer?.cancel();
|
||||||
|
_positionBroadcastTimer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_positionBroadcastTimer ??= Timer.periodic(
|
||||||
|
const Duration(seconds: 1),
|
||||||
|
(_) => _broadcastState(null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Configures the OS audio session for music playback and wires
|
/// Configures the OS audio session for music playback and wires
|
||||||
/// interruption + becoming-noisy handling. just_audio auto-activates /
|
/// interruption + becoming-noisy handling. just_audio auto-activates /
|
||||||
/// deactivates the session around playback once it's configured, but
|
/// deactivates the session around playback once it's configured, but
|
||||||
|
|||||||
Reference in New Issue
Block a user