Fix background - foreground transition, quality fallback

This commit is contained in:
exttex 2020-06-24 16:52:53 +02:00
parent 7df500bc9c
commit 2bd4646796
2 changed files with 231 additions and 200 deletions

View file

@ -57,15 +57,15 @@ class PlayerHelper {
} }
}); });
//Start audio_service //Start audio_service
_startService(); startService();
} }
Future _startService() async { Future startService() async {
if (AudioService.running) return; if (AudioService.running) return;
await AudioService.start( await AudioService.start(
backgroundTaskEntrypoint: backgroundTaskEntrypoint, backgroundTaskEntrypoint: backgroundTaskEntrypoint,
androidEnableQueue: true, androidEnableQueue: true,
androidStopForegroundOnPause: true, androidStopForegroundOnPause: false,
androidNotificationOngoing: false, androidNotificationOngoing: false,
androidNotificationClickStartsActivity: true, androidNotificationClickStartsActivity: true,
androidNotificationChannelDescription: 'Freezer', androidNotificationChannelDescription: 'Freezer',
@ -74,6 +74,7 @@ class PlayerHelper {
); );
} }
//Repeat toggle //Repeat toggle
Future changeRepeat() async { Future changeRepeat() async {
//Change to next repeat type //Change to next repeat type
@ -97,7 +98,7 @@ class PlayerHelper {
//Replace queue, play specified track id //Replace queue, play specified track id
Future _loadQueuePlay(List<MediaItem> queue, String trackId) async { Future _loadQueuePlay(List<MediaItem> queue, String trackId) async {
await _startService(); await startService();
await settings.updateAudioServiceQuality(); await settings.updateAudioServiceQuality();
await AudioService.updateQueue(queue); await AudioService.updateQueue(queue);
await AudioService.playFromMediaId(trackId); await AudioService.playFromMediaId(trackId);
@ -128,7 +129,7 @@ class PlayerHelper {
} }
//Load tracks as queue, play track id, set queue source //Load tracks as queue, play track id, set queue source
Future playFromTrackList(List<Track> tracks, String trackId, QueueSource queueSource) async { Future playFromTrackList(List<Track> tracks, String trackId, QueueSource queueSource) async {
await _startService(); await startService();
List<MediaItem> queue = tracks.map<MediaItem>((track) => track.toMediaItem()).toList(); List<MediaItem> queue = tracks.map<MediaItem>((track) => track.toMediaItem()).toList();
await setQueueSource(queueSource); await setQueueSource(queueSource);
@ -164,7 +165,7 @@ class PlayerHelper {
} }
Future setQueueSource(QueueSource queueSource) async { Future setQueueSource(QueueSource queueSource) async {
await _startService(); await startService();
this.queueSource = queueSource; this.queueSource = queueSource;
await AudioService.customAction('queueSource', queueSource.toJson()); await AudioService.customAction('queueSource', queueSource.toJson());
@ -276,10 +277,10 @@ class AudioPlayerTask extends BackgroundAudioTask {
} }
@override @override
Future onSkipToNext() { Future onSkipToNext() async {
//If repeating allowed //If repeating allowed
if (repeatType == 2) { if (repeatType == 2) {
_skip(0); await _skip(0);
return null; return null;
} }
_skip(1); _skip(1);
@ -405,6 +406,12 @@ class AudioPlayerTask extends BackgroundAudioTask {
onSeekTo(_audioPlayer.playbackEvent.position + offset); onSeekTo(_audioPlayer.playbackEvent.position + offset);
} }
@override
Future onUpdateMediaItem(MediaItem mediaItem) async {
_queue[_queueIndex] = mediaItem;
AudioServiceBackground.setMediaItem(mediaItem);
}
//Audio interruptions //Audio interruptions
@override @override
void onAudioFocusLost(AudioInterruption interruption) { void onAudioFocusLost(AudioInterruption interruption) {
@ -492,7 +499,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
return url; return url;
} }
Future<String> _getTrackUri(MediaItem mi) async { Future<String> _getTrackUri(MediaItem mi, {int quality}) async {
String prefix = 'DEEZER|${mi.id}|'; String prefix = 'DEEZER|${mi.id}|';
//Check if song is available offline //Check if song is available offline
@ -505,13 +512,28 @@ class AudioPlayerTask extends BackgroundAudioTask {
id: mi.id, id: mi.id,
playbackDetails: jsonDecode(mi.extras['playbackDetails']) //JSON Because of audio_service bug playbackDetails: jsonDecode(mi.extras['playbackDetails']) //JSON Because of audio_service bug
); );
ConnectivityResult conn = await Connectivity().checkConnectivity();
if (conn == ConnectivityResult.wifi) { //Check connection
return prefix + t.getUrl(wifiQuality); if (quality == null) {
ConnectivityResult conn = await Connectivity().checkConnectivity();
quality = mobileQuality;
if (conn == ConnectivityResult.wifi) quality = wifiQuality;
}
String url = t.getUrl(quality);
//Quality fallback
Dio dio = Dio();
try {
await dio.head(url);
return prefix + url;
} catch (e) {
if (quality == 9) return _getTrackUri(mi, quality: 3);
if (quality == 3) return _getTrackUri(mi, quality: 1);
throw Exception('No available quality!');
} }
return prefix + t.getUrl(mobileQuality);
} }
Future<String> _getQualityString(String uri, Duration duration) async { Future<String> _getQualityString(String uri, Duration duration) async {
//Get url/path //Get url/path
String url = uri; String url = uri;

View file

@ -7,6 +7,7 @@ import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/player.dart'; import 'package:freezer/api/player.dart';
import 'package:freezer/ui/menu.dart'; import 'package:freezer/ui/menu.dart';
import 'package:freezer/ui/tiles.dart'; import 'package:freezer/ui/tiles.dart';
import 'package:async/async.dart';
import 'cached_image.dart'; import 'cached_image.dart';
import '../api/definitions.dart'; import '../api/definitions.dart';
@ -14,6 +15,7 @@ import 'player_bar.dart';
class PlayerScreen extends StatefulWidget { class PlayerScreen extends StatefulWidget {
@override @override
_PlayerScreenState createState() => _PlayerScreenState(); _PlayerScreenState createState() => _PlayerScreenState();
@ -29,25 +31,151 @@ class _PlayerScreenState extends State<PlayerScreen> {
return Scaffold( return Scaffold(
body: SafeArea( body: SafeArea(
child: StreamBuilder( child: StreamBuilder(
stream: AudioService.playbackStateStream, stream: StreamZip([AudioService.playbackStateStream, AudioService.currentMediaItemStream]),
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
//Disable lyrics when skipping songs, loading //Disable lyrics when skipping songs, loading
PlaybackState s = snapshot.data; if (snapshot.data is PlaybackState &&
if (s != null && s.processingState != AudioProcessingState.ready && s.processingState != AudioProcessingState.buffering) _lyrics = false; snapshot.data.processingState != AudioProcessingState.ready &&
snapshot.data.processingState != AudioProcessingState.buffering) _lyrics = false;
return OrientationBuilder( //When disconnected
builder: (context, orientation) { if (AudioService.currentMediaItem == null) {
//Landscape playerHelper.startService();
if (orientation == Orientation.landscape) { return Center(child: CircularProgressIndicator(),);
return Row( }
return OrientationBuilder(
builder: (context, orientation) {
//Landscape
if (orientation == Orientation.landscape) {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Container(
width: 320,
child: Stack(
children: <Widget>[
CachedImage(
url: AudioService.currentMediaItem.artUri,
),
if (_lyrics) LyricsWidget(
artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: 320.0,
),
],
),
)
),
SizedBox(
width: MediaQuery.of(context).size.width / 2 - 32,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
child: Container(
width: 300,
child: PlayerScreenTopRow(),
)
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context).primaryColor,
),
),
],
),
Container(
width: 320,
child: SeekBar(),
),
Container(
width: 320,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
width: 300,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
Text(
AudioService.currentMediaItem.extras['qualityString']
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
)
],
),
)
],
);
}
//Portrait
return Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Padding( Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 8), padding: EdgeInsets.fromLTRB(28, 16, 28, 0),
child: PlayerScreenTopRow()
),
Padding(
padding: EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Container( child: Container(
width: 320, height: 360,
child: Stack( child: Stack(
children: <Widget>[ children: <Widget>[
CachedImage( CachedImage(
@ -57,199 +185,80 @@ class _PlayerScreenState extends State<PlayerScreen> {
artUri: AudioService.currentMediaItem.artUri, artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id, trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics, lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: 320.0, height: 360.0,
), ),
], ],
), ),
) )
), ),
SizedBox( Column(
width: MediaQuery.of(context).size.width / 2 - 32, mainAxisSize: MainAxisSize.min,
child: Column( children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context).primaryColor,
),
),
],
),
SeekBar(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
//Container(height: 8.0,),
Padding(
padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Padding( IconButton(
padding: EdgeInsets.fromLTRB(8, 16, 8, 0), icon: Icon(Icons.subtitles),
child: Container( onPressed: () {
width: 300, setState(() => _lyrics = !_lyrics);
child: PlayerScreenTopRow(), },
)
), ),
Column( Text(
mainAxisSize: MainAxisSize.min, AudioService.currentMediaItem.extras['qualityString']
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context).primaryColor,
),
),
],
), ),
Container( IconButton(
width: 320, icon: Icon(Icons.more_vert),
child: SeekBar(), onPressed: () {
), Track t = Track.fromMediaItem(AudioService.currentMediaItem);
Container( MenuSheet m = MenuSheet(context);
width: 320, m.defaultTrackMenu(t);
child: Row( },
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
width: 300,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
Text(
AudioService.currentMediaItem.extras['qualityString']
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
) )
], ],
), ),
) )
], ],
); );
}
//Portrait },
return Column( );
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(28, 16, 28, 0),
child: PlayerScreenTopRow()
),
Padding(
padding: EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Container(
height: 360,
child: Stack(
children: <Widget>[
CachedImage(
url: AudioService.currentMediaItem.artUri,
),
if (_lyrics) LyricsWidget(
artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: 360.0,
),
],
),
)
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context).primaryColor,
),
),
],
),
SeekBar(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
//Container(height: 8.0,),
Padding(
padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
Text(
AudioService.currentMediaItem.extras['qualityString']
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
],
);
},
);
}, },
), ),
) )