diff --git a/lib/api/player/audio_handler.dart b/lib/api/player/audio_handler.dart index 3b42b60..227446e 100644 --- a/lib/api/player/audio_handler.dart +++ b/lib/api/player/audio_handler.dart @@ -1,6 +1,5 @@ import 'package:audio_service/audio_service.dart'; import 'package:audio_session/audio_session.dart'; -import 'package:flutter/foundation.dart'; import 'package:freezer/api/cache.dart'; import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/audio_sources/deezer_audio_source.dart'; @@ -802,6 +801,13 @@ class AudioPlayerTask extends BaseAudioHandler { _queueAutoIncrement = q.length; queue.add(q); await _loadQueue(preload: false); + + //Send restored queue source to ui + customEvent.add({ + 'action': 'onRestore', + 'queueSource': queueSource!, + 'repeatMode': _repeatMode + }); } @override diff --git a/lib/api/player/player_helper.dart b/lib/api/player/player_helper.dart index adeeb39..6f8e754 100644 --- a/lib/api/player/player_helper.dart +++ b/lib/api/player/player_helper.dart @@ -15,7 +15,6 @@ class PlayerHelper { late StreamSubscription _customEventSubscription; late StreamSubscription _mediaItemSubscription; late StreamSubscription _playbackStateStreamSubscription; - QueueSource? queueSource; AudioServiceRepeatMode repeatType = AudioServiceRepeatMode.none; bool equalizerOpen = false; bool _shuffleEnabled = false; @@ -30,6 +29,9 @@ class PlayerHelper { // StreamController _visualizerController = StreamController.broadcast(); // Stream get visualizerStream => _visualizerController.stream; + final _queueSourceSubject = BehaviorSubject(); + ValueStream get queueSource => _queueSourceSubject.stream; + final _streamInfoSubject = BehaviorSubject(); ValueStream get streamInfo => _streamInfoSubject.stream; @@ -92,7 +94,7 @@ class PlayerHelper { switch (event['action']) { case 'onRestore': //Load queueSource from isolate - queueSource = event['queueSource'] as QueueSource; + _queueSourceSubject.add(event['queueSource'] as QueueSource); repeatType = event['repeatMode'] as AudioServiceRepeatMode; _queueIndex = getQueueIndex(); break; @@ -202,7 +204,7 @@ class PlayerHelper { //Play mix by track Future playMix(String trackId, String trackTitle) async { - List tracks = (await deezerAPI.playMix(trackId))!; + List tracks = await deezerAPI.playMix(trackId); await playFromTrackList( tracks, tracks[0].id, @@ -277,7 +279,7 @@ class PlayerHelper { Future playFromTrackList( List tracks, String? trackId, QueueSource queueSource) async { final queue = - tracks.map((track) => track!.toMediaItem()).toList(); + tracks.map((track) => track.toMediaItem()).toList(); await setQueueSource(queueSource); await _loadQueuePlay( queue, trackId == null ? 0 : queue.indexWhere((m) => m.id == trackId)); @@ -311,7 +313,7 @@ class PlayerHelper { } Future setQueueSource(QueueSource queueSource) async { - this.queueSource = queueSource; + _queueSourceSubject.add(queueSource); await audioHandler.customAction('queueSource', queueSource.toJson()); } diff --git a/lib/ui/library.dart b/lib/ui/library.dart index 9fc7f13..cb077d4 100644 --- a/lib/ui/library.dart +++ b/lib/ui/library.dart @@ -536,7 +536,7 @@ class _LibraryTracksState extends State { ), const SizedBox(height: 8.0), for (final track in allTracks) - TrackTile.fromTrack(track!, onTap: () { + TrackTile.fromTrack(track, onTap: () { playerHelper.playFromTrackList( allTracks, track.id, diff --git a/lib/ui/player_screen.dart b/lib/ui/player_screen.dart index f7844a9..82cb824 100644 --- a/lib/ui/player_screen.dart +++ b/lib/ui/player_screen.dart @@ -787,41 +787,46 @@ class PlaybackControls extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.max, - children: [ - playerHelper.queueSource?.source != 'show' - ? ShuffleButton(iconSize: size * 0.75) - : const ForwardReplay30Button(forward: false), - PrevNextButton(size, prev: true), - if (settings.enableFilledPlayButton) - Consumer(builder: (context, provider, _) { - final color = provider.dominantColor == null - ? Colors.transparent - : Theme.of(context).brightness == Brightness.light - ? provider.dominantColor! - : darken(provider.dominantColor!); - return PlayPauseButton(size * 2.25, - filled: true, - material3: settings.enableMaterial3PlayButton, - color: color, - iconColor: Color.lerp( - (ThemeData.estimateBrightnessForColor(color) == - Brightness.light - ? Colors.black - : Colors.white), - color, - 0.25)); - }) - else - PlayPauseButton(size * 1.25), - PrevNextButton(size), - playerHelper.queueSource?.source != 'show' - ? RepeatButton(size * 0.75) - : const ForwardReplay30Button(forward: true), - ], - ), + child: StreamBuilder( + stream: playerHelper.queueSource, + builder: (context, snapshot) { + final queueSource = snapshot.data; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + queueSource?.source != 'show' + ? ShuffleButton(iconSize: size * 0.75) + : const ForwardReplay30Button(forward: false), + PrevNextButton(size, prev: true), + if (settings.enableFilledPlayButton) + Consumer(builder: (context, provider, _) { + final color = provider.dominantColor == null + ? Colors.transparent + : Theme.of(context).brightness == Brightness.light + ? provider.dominantColor! + : darken(provider.dominantColor!); + return PlayPauseButton(size * 2.25, + filled: true, + material3: settings.enableMaterial3PlayButton, + color: color, + iconColor: Color.lerp( + (ThemeData.estimateBrightnessForColor(color) == + Brightness.light + ? Colors.black + : Colors.white), + color, + 0.25)); + }) + else + PlayPauseButton(size * 1.25), + PrevNextButton(size), + queueSource?.source != 'show' + ? RepeatButton(size * 0.75) + : const ForwardReplay30Button(forward: true), + ], + ); + }), ); } } @@ -997,22 +1002,29 @@ class PlayerScreenTopRow extends StatelessWidget { iconSize: size, splashRadius: size * 1.5, ), - if (playerHelper.queueSource != null) - Expanded( - child: RichText( - textAlign: TextAlign.center, - maxLines: 2, - text: TextSpan(children: [ - if (!short) - TextSpan( - text: '${'PLAYING FROM'.i18n}\n', - style: TextStyle( - fontWeight: FontWeight.bold, - letterSpacing: 1.5, - fontSize: (textSize ?? 38.sp) * 0.85)), - TextSpan(text: playerHelper.queueSource!.text ?? '') - ], style: TextStyle(fontSize: textSize ?? 38.sp))), - ), + Expanded( + child: StreamBuilder( + stream: playerHelper.queueSource, + builder: (context, snapshot) { + final queueSource = snapshot.data; + if (queueSource == null) { + return const SizedBox.shrink(); + } + return RichText( + textAlign: TextAlign.center, + maxLines: 2, + text: TextSpan(children: [ + if (!short) + TextSpan( + text: '${'PLAYING FROM'.i18n}\n', + style: TextStyle( + fontWeight: FontWeight.bold, + letterSpacing: 1.5, + fontSize: (textSize ?? 38.sp) * 0.85)), + TextSpan(text: queueSource.text ?? '') + ], style: TextStyle(fontSize: textSize ?? 38.sp))); + }), + ), showQueueButton ? IconButton( icon: Icon( @@ -1158,7 +1170,7 @@ class BottomBarControls extends StatelessWidget { @override Widget build(BuildContext context) { final iconSize = size * 0.9; - if (playerHelper.queueSource?.source == 'show') { + if (playerHelper.queueSource.valueOrNull?.source == 'show') { return Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceEvenly,