Cached image performace, background audio bug

This commit is contained in:
exttex 2020-06-24 15:19:14 +02:00
parent ed087bc583
commit 7df500bc9c
7 changed files with 334 additions and 312 deletions

View File

@ -61,7 +61,7 @@ class DeezerAPI {
'gateway_input': gatewayInput 'gateway_input': gatewayInput
}, },
data: jsonEncode(params??{}), data: jsonEncode(params??{}),
options: Options(responseType: ResponseType.json, sendTimeout: 7000, receiveTimeout: 7000) options: Options(responseType: ResponseType.json, sendTimeout: 5000, receiveTimeout: 5000)
); );
return response.data; return response.data;
} }

View File

@ -545,8 +545,15 @@ class AudioPlayerTask extends BackgroundAudioTask {
source = 'Stream'; source = 'Stream';
} }
//Calculate //Calculate
int bitrate = ((size / 125) / duration.inSeconds).floor(); return '$format ${_bitrateString(size, duration.inSeconds)} ($source)';
return '$format ${bitrate}kbps ($source)'; }
String _bitrateString(int size, int duration) {
int bitrate = ((size / 125) / duration).floor();
//Prettify
if (bitrate > 315 && bitrate < 325) return '320kbps';
if (bitrate > 125 && bitrate < 135) return '128kbps';
return '${bitrate}kbps';
} }
//Magic number to string, source: https://en.wikipedia.org/wiki/List_of_file_signatures //Magic number to string, source: https://en.wikipedia.org/wiki/List_of_file_signatures
@ -564,17 +571,15 @@ class AudioPlayerTask extends BackgroundAudioTask {
@override @override
void onTaskRemoved() async { void onTaskRemoved() async {
await _saveQueue(); await onStop();
onStop();
} }
@override @override
Future onStop() async { Future onStop() async {
await _saveQueue(); _audioPlayer.stop();
if (_playing != null) _audioPlayer.stop();
if (_playerStateSub != null) _playerStateSub.cancel(); if (_playerStateSub != null) _playerStateSub.cancel();
if (_eventSub != null) _eventSub.cancel(); if (_eventSub != null) _eventSub.cancel();
await _saveQueue();
await super.onStop(); await super.onStop();
} }

View File

@ -124,6 +124,7 @@ class _CachedImageState extends State<CachedImage> {
ImageProvider _image = AssetImage('assets/cover.jpg'); ImageProvider _image = AssetImage('assets/cover.jpg');
double _opacity = 0.0; double _opacity = 0.0;
bool _disposed = false; bool _disposed = false;
String _prevUrl;
Future<ImageProvider> _getImage() async { Future<ImageProvider> _getImage() async {
//Image already path //Image already path
@ -146,6 +147,7 @@ class _CachedImageState extends State<CachedImage> {
_image = image; _image = image;
_opacity = 1.0; _opacity = 1.0;
}); });
_prevUrl = widget.url;
} }
@override @override
@ -162,6 +164,7 @@ class _CachedImageState extends State<CachedImage> {
@override @override
void didUpdateWidget(CachedImage oldWidget) { void didUpdateWidget(CachedImage oldWidget) {
if (_prevUrl == widget.url) return;
_load(); _load();
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }

View File

@ -126,99 +126,108 @@ class _HomePageScreenState extends State<HomePageScreen> {
return ErrorScreen(); return ErrorScreen();
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ crossAxisAlignment: CrossAxisAlignment.start,
...List.generate(_homePage.sections.length, (i) { children: List.generate(_homePage.sections.length, (i) {
HomePageSection section = _homePage.sections[i]; HomePageSection section = _homePage.sections[i];
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: [
Padding( Padding(
child: Text( child: Text(
section.title, section.title,
textAlign: TextAlign.left, textAlign: TextAlign.left,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 24.0), style: TextStyle(fontSize: 24.0),
),
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0)
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List<Widget>.generate(section.items.length, (i) {
HomePageItem item = section.items[i];
switch (item.type) {
case HomePageItemType.SMARTTRACKLIST:
return SmartTrackListTile(
item.value,
onTap: () {
playerHelper.playFromSmartTrackList(item.value);
},
);
case HomePageItemType.ALBUM:
return AlbumCard(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AlbumDetails(item.value)
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultAlbumMenu(item.value);
},
);
case HomePageItemType.ARTIST:
return ArtistTile(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ArtistDetails(item.value)
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultArtistMenu(item.value);
},
);
case HomePageItemType.PLAYLIST:
return PlaylistCardTile(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PlaylistDetails(item.value)
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultPlaylistMenu(item.value);
},
);
case HomePageItemType.CHANNEL:
return ChannelTile(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: Text(item.value.title.toString()),),
body: HomePageScreen(channel: item.value,),
)
));
},
);
}
return Container(height: 0, width: 0);
}),
), ),
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0)
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(section.items.length, (i) {
HomePageItem item = section.items[i];
return HomePageItemWidget(item);
}),
), ),
Container(height: 16.0,) ),
], ],
); );
}) }),
],
), ),
); );
} }
} }
class HomePageItemWidget extends StatelessWidget {
HomePageItem item;
HomePageItemWidget(this.item);
@override
Widget build(BuildContext context) {
switch (item.type) {
case HomePageItemType.SMARTTRACKLIST:
return SmartTrackListTile(
item.value,
onTap: () {
playerHelper.playFromSmartTrackList(item.value);
},
);
case HomePageItemType.ALBUM:
return AlbumCard(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AlbumDetails(item.value)
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultAlbumMenu(item.value);
},
);
case HomePageItemType.ARTIST:
return ArtistTile(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ArtistDetails(item.value)
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultArtistMenu(item.value);
},
);
case HomePageItemType.PLAYLIST:
return PlaylistCardTile(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PlaylistDetails(item.value)
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultPlaylistMenu(item.value);
},
);
case HomePageItemType.CHANNEL:
return ChannelTile(
item.value,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: Text(item.value.title.toString()),),
body: HomePageScreen(channel: item.value,),
)
));
},
);
}
return Container(height: 0, width: 0);
}
}

View File

@ -27,229 +27,231 @@ class _PlayerScreenState extends State<PlayerScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: StreamBuilder( body: SafeArea(
stream: AudioService.playbackStateStream, child: StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) { stream: AudioService.playbackStateStream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
//Disable lyrics when skipping songs, loading //Disable lyrics when skipping songs, loading
PlaybackState s = snapshot.data; PlaybackState s = snapshot.data;
if (s != null && s.processingState != AudioProcessingState.ready && s.processingState != AudioProcessingState.buffering) _lyrics = false; if (s != null && s.processingState != AudioProcessingState.ready && s.processingState != AudioProcessingState.buffering) _lyrics = false;
return OrientationBuilder( return OrientationBuilder(
builder: (context, orientation) { builder: (context, orientation) {
//Landscape //Landscape
if (orientation == Orientation.landscape) { if (orientation == Orientation.landscape) {
return Row( return Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[ children: <Widget>[
Padding( Padding(
padding: EdgeInsets.fromLTRB(16, 32, 16, 8), padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Container( child: Container(
width: 320, width: 320,
child: Stack( child: Stack(
children: <Widget>[ children: <Widget>[
CachedImage( CachedImage(
url: AudioService.currentMediaItem.artUri, 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, 42, 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
), ),
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)
],
), ),
Container(height: 4,), ),
Text( Padding(
AudioService.currentMediaItem.displaySubtitle, padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
maxLines: 1, child: Container(
textAlign: TextAlign.center, width: 300,
overflow: TextOverflow.clip, child: Row(
style: TextStyle( mainAxisSize: MainAxisSize.max,
fontSize: 18.0, mainAxisAlignment: MainAxisAlignment.spaceBetween,
color: Theme.of(context).primaryColor, 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,
), ),
], ],
), ),
Container( )
width: 320, ),
child: SeekBar(), 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( ),
width: 320, Container(height: 4,),
child: Row( Text(
mainAxisAlignment: MainAxisAlignment.spaceAround, AudioService.currentMediaItem.displaySubtitle,
mainAxisSize: MainAxisSize.max, maxLines: 1,
children: <Widget>[ textAlign: TextAlign.center,
PrevNextButton(iconSize, prev: true,), overflow: TextOverflow.clip,
PlayPauseButton(iconSize), style: TextStyle(
PrevNextButton(iconSize) fontSize: 18.0,
], color: Theme.of(context).primaryColor,
),
), ),
Padding( ),
padding: EdgeInsets.fromLTRB(8, 0, 8, 16), ],
child: Container( ),
width: 300, SeekBar(),
child: Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max,
children: <Widget>[ children: <Widget>[
IconButton( PrevNextButton(iconSize, prev: true,),
icon: Icon(Icons.subtitles), PlayPauseButton(iconSize),
onPressed: () { PrevNextButton(iconSize)
setState(() => _lyrics = !_lyrics); ],
}, ),
), //Container(height: 8.0,),
Text( Padding(
AudioService.currentMediaItem.extras['qualityString'] padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
), child: Row(
IconButton( mainAxisSize: MainAxisSize.max,
icon: Icon(Icons.more_vert), mainAxisAlignment: MainAxisAlignment.spaceBetween,
onPressed: () { children: <Widget>[
Track t = Track.fromMediaItem(AudioService.currentMediaItem); IconButton(
MenuSheet m = MenuSheet(context); icon: Icon(Icons.subtitles),
m.defaultTrackMenu(t); 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, 28, 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);
},
)
],
),
)
],
);
},
);
},
) )
); );
} }

View File

@ -230,7 +230,8 @@ class PlaylistCardTile extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0), style: TextStyle(fontSize: 16.0),
), ),
) ),
Container(height: 8.0,)
], ],
), ),
) )
@ -272,7 +273,8 @@ class SmartTrackListTile extends StatelessWidget {
fontSize: 16.0 fontSize: 16.0
), ),
), ),
) ),
Container(height: 8.0,)
], ],
), ),
), ),
@ -315,7 +317,8 @@ class AlbumCard extends StatelessWidget {
fontSize: 16.0 fontSize: 16.0
), ),
), ),
) ),
Container(height: 8.0,)
], ],
), ),
) )
@ -350,9 +353,9 @@ class ChannelTile extends StatelessWidget {
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
fontSize: 18.0, fontSize: 18.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: _textColor() color: _textColor()
), ),
), ),
), ),

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.1.0 version: 0.1.0+1
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.7.0 <3.0.0"