0.6.7 - shows in search, album art resolution
This commit is contained in:
parent
babd12bae2
commit
c3a26b0e3b
18 changed files with 369 additions and 28 deletions
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
@ -585,6 +586,33 @@ class MenuSheet {
|
|||
},
|
||||
);
|
||||
|
||||
Widget wakelock() => ListTile(
|
||||
title: Text('Keep the screen on'.i18n),
|
||||
leading: Icon(Icons.screen_lock_portrait),
|
||||
onTap: () async {
|
||||
_close();
|
||||
if (cache.wakelock == null)
|
||||
cache.wakelock = false;
|
||||
//Enable
|
||||
if (!cache.wakelock) {
|
||||
Wakelock.enable();
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Wakelock enabled!'.i18n,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
cache.wakelock = true;
|
||||
return;
|
||||
}
|
||||
//Disable
|
||||
Wakelock.disable();
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Wakelock disabled!'.i18n,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
cache.wakelock = false;
|
||||
},
|
||||
);
|
||||
|
||||
void _close() => Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
|
|
|
@ -280,7 +280,7 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
|||
children: <Widget>[
|
||||
Container(
|
||||
height: ScreenUtil().setSp(64),
|
||||
child: AudioService.currentMediaItem.displayTitle.length >= 24 ?
|
||||
child: AudioService.currentMediaItem.displayTitle.length >= 26 ?
|
||||
Marquee(
|
||||
text: AudioService.currentMediaItem.displayTitle,
|
||||
style: TextStyle(
|
||||
|
@ -410,12 +410,12 @@ class PlayerMenuButton extends StatelessWidget {
|
|||
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
|
||||
MenuSheet m = MenuSheet(context);
|
||||
if (AudioService.currentMediaItem.extras['show'] == null)
|
||||
m.defaultTrackMenu(t, options: [m.sleepTimer()]);
|
||||
m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]);
|
||||
else
|
||||
m.defaultShowEpisodeMenu(
|
||||
Show.fromJson(jsonDecode(AudioService.currentMediaItem.extras['show'])),
|
||||
ShowEpisode.fromMediaItem(AudioService.currentMediaItem),
|
||||
options: [m.sleepTimer()]
|
||||
options: [m.sleepTimer(), m.wakelock()]
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -771,6 +771,13 @@ class _QueueScreenState extends State<QueueScreen> {
|
|||
Navigator.of(context).pop();
|
||||
},
|
||||
key: Key(t.id),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: () async {
|
||||
await AudioService.removeQueueItem(t.toMediaItem());
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter/src/services/keyboard_key.dart';
|
|||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:freezer/api/player.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/ui/details_screens.dart';
|
||||
import 'package:freezer/ui/elements.dart';
|
||||
import 'package:freezer/ui/home_screen.dart';
|
||||
|
@ -657,6 +658,91 @@ class SearchResultsScreen extends StatelessWidget {
|
|||
MaterialPageRoute(builder: (context) => SearchResultPlaylists(results.playlists))
|
||||
);
|
||||
},
|
||||
),
|
||||
FreezerDivider()
|
||||
];
|
||||
}
|
||||
|
||||
//Shows
|
||||
List<Widget> shows = [];
|
||||
if (results.shows != null && results.shows.length != 0) {
|
||||
shows = [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
|
||||
child: Text(
|
||||
'Shows'.i18n,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
),
|
||||
...List.generate(3, (i) {
|
||||
if (results.shows.length <= i) return Container(height: 0, width: 0,);
|
||||
Show s = results.shows[i];
|
||||
return ShowTile(
|
||||
s,
|
||||
onTap: () async {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => ShowScreen(s)
|
||||
));
|
||||
},
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
title: Text('Show all shows'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => ShowListScreen(results.shows))
|
||||
);
|
||||
},
|
||||
),
|
||||
FreezerDivider()
|
||||
];
|
||||
}
|
||||
|
||||
//Episodes
|
||||
List<Widget> episodes = [];
|
||||
if (results.episodes != null && results.episodes.length != 0) {
|
||||
episodes = [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
|
||||
child: Text(
|
||||
'Episodes'.i18n,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
),
|
||||
...List.generate(3, (i) {
|
||||
if (results.episodes.length <= i) return Container(height: 0, width: 0,);
|
||||
ShowEpisode e = results.episodes[i];
|
||||
return ShowEpisodeTile(
|
||||
e,
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.more_vert),
|
||||
onPressed: () {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultShowEpisodeMenu(e.show, e);
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
//Load entire show, then play
|
||||
List<ShowEpisode> episodes = await deezerAPI.allShowEpisodes(e.show.id);
|
||||
await playerHelper.playShowEpisode(e.show, episodes, index: episodes.indexWhere((ep) => e.id == ep.id));
|
||||
},
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
title: Text('Show all episodes'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => EpisodeListScreen(results.episodes))
|
||||
);
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
||||
|
@ -670,7 +756,11 @@ class SearchResultsScreen extends StatelessWidget {
|
|||
Container(height: 8.0,),
|
||||
...artists,
|
||||
Container(height: 8.0,),
|
||||
...playlists
|
||||
...playlists,
|
||||
Container(height: 8.0,),
|
||||
...shows,
|
||||
Container(height: 8.0,),
|
||||
...episodes
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -773,3 +863,64 @@ class SearchResultPlaylists extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ShowListScreen extends StatelessWidget {
|
||||
|
||||
final List<Show> shows;
|
||||
ShowListScreen(this.shows);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: FreezerAppBar('Shows'.i18n),
|
||||
body: ListView.builder(
|
||||
itemCount: shows.length,
|
||||
itemBuilder: (context, i) {
|
||||
Show s = shows[i];
|
||||
return ShowTile(
|
||||
s,
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => ShowScreen(s)
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EpisodeListScreen extends StatelessWidget {
|
||||
|
||||
final List<ShowEpisode> episodes;
|
||||
EpisodeListScreen(this.episodes);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: FreezerAppBar('Episodes'.i18n),
|
||||
body: ListView.builder(
|
||||
itemCount: episodes.length,
|
||||
itemBuilder: (context, i) {
|
||||
ShowEpisode e = episodes[i];
|
||||
return ShowEpisodeTile(
|
||||
e,
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.more_vert),
|
||||
onPressed: () {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultShowEpisodeMenu(e.show, e);
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
//Load entire show, then play
|
||||
List<ShowEpisode> episodes = await deezerAPI.allShowEpisodes(e.show.id);
|
||||
await playerHelper.playShowEpisode(e.show, episodes, index: episodes.indexWhere((ep) => e.id == ep.id));
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -856,6 +856,27 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
leading: Icon(Icons.image)
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Album cover resolution'.i18n),
|
||||
subtitle: Text("WARNING: Resolutions above 1200 aren't officially supported".i18n),
|
||||
leading: Icon(Icons.image),
|
||||
trailing: Container(
|
||||
width: 75.0,
|
||||
child: DropdownButton<int>(
|
||||
value: settings.albumArtResolution,
|
||||
items: [400, 800, 1000, 1200, 1400, 1600, 1800].map<DropdownMenuItem<int>>((int i) => DropdownMenuItem<int>(
|
||||
value: i,
|
||||
child: Text(i.toString()),
|
||||
)).toList(),
|
||||
onChanged: (int n) async {
|
||||
setState(() {
|
||||
settings.albumArtResolution = n;
|
||||
});
|
||||
await settings.save();
|
||||
},
|
||||
)
|
||||
)
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Create .nomedia files'.i18n),
|
||||
subtitle: Text('To prevent gallery being filled with album art'.i18n),
|
||||
|
@ -872,7 +893,7 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
title: Text('Artist separator'.i18n),
|
||||
leading: Icon(WebSymbols.tag),
|
||||
trailing: Container(
|
||||
width: 100.0,
|
||||
width: 75.0,
|
||||
child: TextField(
|
||||
controller: _artistSeparatorController,
|
||||
onChanged: (s) async {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttericon/octicons_icons.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
|
||||
import '../api/definitions.dart';
|
||||
|
@ -25,6 +27,7 @@ class TrackTile extends StatefulWidget {
|
|||
class _TrackTileState extends State<TrackTile> {
|
||||
|
||||
StreamSubscription _subscription;
|
||||
bool _isOffline = false;
|
||||
|
||||
bool get nowPlaying {
|
||||
if (AudioService.currentMediaItem == null) return false;
|
||||
|
@ -37,6 +40,9 @@ class _TrackTileState extends State<TrackTile> {
|
|||
_subscription = AudioService.currentMediaItemStream.listen((event) {
|
||||
setState(() {});
|
||||
});
|
||||
//Check if offline
|
||||
downloadManager.checkOffline(track: widget.track).then((b) => setState(() => _isOffline = b));
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -70,9 +76,18 @@ class _TrackTileState extends State<TrackTile> {
|
|||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if ((_isOffline??false))
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 2.0),
|
||||
child: Icon(
|
||||
Octicons.primitive_dot,
|
||||
color: Colors.green,
|
||||
size: 12.0,
|
||||
),
|
||||
),
|
||||
if (widget.track.explicit??false)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 2.0),
|
||||
child: Text(
|
||||
'E',
|
||||
style: TextStyle(
|
||||
|
@ -80,9 +95,12 @@ class _TrackTileState extends State<TrackTile> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 2.0),
|
||||
child: Text(widget.track.durationString),
|
||||
Container(
|
||||
width: 42.0,
|
||||
child: Text(
|
||||
widget.track.durationString,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
widget.trailing??Container(width: 0, height: 0)
|
||||
],
|
||||
|
@ -497,6 +515,38 @@ class ShowCard extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class ShowTile extends StatelessWidget {
|
||||
|
||||
final Show show;
|
||||
final Function onTap;
|
||||
final Function onHold;
|
||||
|
||||
ShowTile(this.show, {this.onTap, this.onHold});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
show.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
show.description,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onTap: onTap,
|
||||
onLongPress: onHold,
|
||||
leading: CachedImage(
|
||||
url: show.art.thumb,
|
||||
width: 48,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ShowEpisodeTile extends StatelessWidget {
|
||||
|
||||
final ShowEpisode episode;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue