Fix background - foreground transition, quality fallback
This commit is contained in:
parent
7df500bc9c
commit
2bd4646796
|
@ -57,15 +57,15 @@ class PlayerHelper {
|
|||
}
|
||||
});
|
||||
//Start audio_service
|
||||
_startService();
|
||||
startService();
|
||||
}
|
||||
|
||||
Future _startService() async {
|
||||
Future startService() async {
|
||||
if (AudioService.running) return;
|
||||
await AudioService.start(
|
||||
backgroundTaskEntrypoint: backgroundTaskEntrypoint,
|
||||
androidEnableQueue: true,
|
||||
androidStopForegroundOnPause: true,
|
||||
androidStopForegroundOnPause: false,
|
||||
androidNotificationOngoing: false,
|
||||
androidNotificationClickStartsActivity: true,
|
||||
androidNotificationChannelDescription: 'Freezer',
|
||||
|
@ -74,6 +74,7 @@ class PlayerHelper {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
//Repeat toggle
|
||||
Future changeRepeat() async {
|
||||
//Change to next repeat type
|
||||
|
@ -97,7 +98,7 @@ class PlayerHelper {
|
|||
|
||||
//Replace queue, play specified track id
|
||||
Future _loadQueuePlay(List<MediaItem> queue, String trackId) async {
|
||||
await _startService();
|
||||
await startService();
|
||||
await settings.updateAudioServiceQuality();
|
||||
await AudioService.updateQueue(queue);
|
||||
await AudioService.playFromMediaId(trackId);
|
||||
|
@ -128,7 +129,7 @@ class PlayerHelper {
|
|||
}
|
||||
//Load tracks as queue, play track id, set queue source
|
||||
Future playFromTrackList(List<Track> tracks, String trackId, QueueSource queueSource) async {
|
||||
await _startService();
|
||||
await startService();
|
||||
|
||||
List<MediaItem> queue = tracks.map<MediaItem>((track) => track.toMediaItem()).toList();
|
||||
await setQueueSource(queueSource);
|
||||
|
@ -164,7 +165,7 @@ class PlayerHelper {
|
|||
}
|
||||
|
||||
Future setQueueSource(QueueSource queueSource) async {
|
||||
await _startService();
|
||||
await startService();
|
||||
|
||||
this.queueSource = queueSource;
|
||||
await AudioService.customAction('queueSource', queueSource.toJson());
|
||||
|
@ -276,10 +277,10 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
}
|
||||
|
||||
@override
|
||||
Future onSkipToNext() {
|
||||
Future onSkipToNext() async {
|
||||
//If repeating allowed
|
||||
if (repeatType == 2) {
|
||||
_skip(0);
|
||||
await _skip(0);
|
||||
return null;
|
||||
}
|
||||
_skip(1);
|
||||
|
@ -405,6 +406,12 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
onSeekTo(_audioPlayer.playbackEvent.position + offset);
|
||||
}
|
||||
|
||||
@override
|
||||
Future onUpdateMediaItem(MediaItem mediaItem) async {
|
||||
_queue[_queueIndex] = mediaItem;
|
||||
AudioServiceBackground.setMediaItem(mediaItem);
|
||||
}
|
||||
|
||||
//Audio interruptions
|
||||
@override
|
||||
void onAudioFocusLost(AudioInterruption interruption) {
|
||||
|
@ -492,7 +499,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
return url;
|
||||
}
|
||||
|
||||
Future<String> _getTrackUri(MediaItem mi) async {
|
||||
Future<String> _getTrackUri(MediaItem mi, {int quality}) async {
|
||||
String prefix = 'DEEZER|${mi.id}|';
|
||||
|
||||
//Check if song is available offline
|
||||
|
@ -505,13 +512,28 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
id: mi.id,
|
||||
playbackDetails: jsonDecode(mi.extras['playbackDetails']) //JSON Because of audio_service bug
|
||||
);
|
||||
ConnectivityResult conn = await Connectivity().checkConnectivity();
|
||||
if (conn == ConnectivityResult.wifi) {
|
||||
return prefix + t.getUrl(wifiQuality);
|
||||
|
||||
//Check connection
|
||||
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 {
|
||||
//Get url/path
|
||||
String url = uri;
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:freezer/api/deezer.dart';
|
|||
import 'package:freezer/api/player.dart';
|
||||
import 'package:freezer/ui/menu.dart';
|
||||
import 'package:freezer/ui/tiles.dart';
|
||||
import 'package:async/async.dart';
|
||||
|
||||
import 'cached_image.dart';
|
||||
import '../api/definitions.dart';
|
||||
|
@ -14,6 +15,7 @@ import 'player_bar.dart';
|
|||
|
||||
|
||||
|
||||
|
||||
class PlayerScreen extends StatefulWidget {
|
||||
@override
|
||||
_PlayerScreenState createState() => _PlayerScreenState();
|
||||
|
@ -29,25 +31,151 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: StreamBuilder(
|
||||
stream: AudioService.playbackStateStream,
|
||||
stream: StreamZip([AudioService.playbackStateStream, AudioService.currentMediaItemStream]),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
|
||||
//Disable lyrics when skipping songs, loading
|
||||
PlaybackState s = snapshot.data;
|
||||
if (s != null && s.processingState != AudioProcessingState.ready && s.processingState != AudioProcessingState.buffering) _lyrics = false;
|
||||
if (snapshot.data is PlaybackState &&
|
||||
snapshot.data.processingState != AudioProcessingState.ready &&
|
||||
snapshot.data.processingState != AudioProcessingState.buffering) _lyrics = false;
|
||||
|
||||
return OrientationBuilder(
|
||||
builder: (context, orientation) {
|
||||
//Landscape
|
||||
if (orientation == Orientation.landscape) {
|
||||
return Row(
|
||||
//When disconnected
|
||||
if (AudioService.currentMediaItem == null) {
|
||||
playerHelper.startService();
|
||||
return Center(child: CircularProgressIndicator(),);
|
||||
}
|
||||
|
||||
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,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
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(
|
||||
width: 320,
|
||||
height: 360,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
CachedImage(
|
||||
|
@ -57,199 +185,80 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||
artUri: AudioService.currentMediaItem.artUri,
|
||||
trackId: AudioService.currentMediaItem.id,
|
||||
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
|
||||
height: 320.0,
|
||||
height: 360.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 32,
|
||||
child: Column(
|
||||
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>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
|
||||
child: Container(
|
||||
width: 300,
|
||||
child: PlayerScreenTopRow(),
|
||||
)
|
||||
IconButton(
|
||||
icon: Icon(Icons.subtitles),
|
||||
onPressed: () {
|
||||
setState(() => _lyrics = !_lyrics);
|
||||
},
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
AudioService.currentMediaItem.extras['qualityString']
|
||||
),
|
||||
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);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
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);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue