search: change chip functionality +
wip: fix resuming audio handler after stop
This commit is contained in:
parent
6816bdc112
commit
bb4448731e
|
|
@ -266,12 +266,12 @@ class DeezerAPI {
|
|||
//Tracks
|
||||
if (uri.pathSegments[0] == 'track') {
|
||||
String id = await SpotifyScrapper.convertTrack(spotifyUri);
|
||||
return DeezerLinkResponse(type: DeezerLinkType.TRACK, id: id);
|
||||
return DeezerLinkResponse(type: DeezerMediaType.track, id: id);
|
||||
}
|
||||
//Albums
|
||||
if (uri.pathSegments[0] == 'album') {
|
||||
String id = await SpotifyScrapper.convertAlbum(spotifyUri);
|
||||
return DeezerLinkResponse(type: DeezerLinkType.ALBUM, id: id);
|
||||
return DeezerLinkResponse(type: DeezerMediaType.album, id: id);
|
||||
}
|
||||
} catch (e) {
|
||||
// we don't care about errors apparently
|
||||
|
|
|
|||
|
|
@ -1196,22 +1196,31 @@ enum HomePageSectionLayout {
|
|||
|
||||
enum RepeatType { NONE, LIST, TRACK }
|
||||
|
||||
enum DeezerLinkType { TRACK, ALBUM, ARTIST, PLAYLIST }
|
||||
enum DeezerMediaType {
|
||||
track,
|
||||
album,
|
||||
artist,
|
||||
playlist,
|
||||
show,
|
||||
episode,
|
||||
}
|
||||
|
||||
class DeezerLinkResponse {
|
||||
DeezerLinkType? type;
|
||||
DeezerMediaType? type;
|
||||
String? id;
|
||||
|
||||
DeezerLinkResponse({this.type, this.id});
|
||||
|
||||
//String to DeezerLinkType
|
||||
static typeFromString(String t) {
|
||||
t = t.toLowerCase().trim();
|
||||
if (t == 'album') return DeezerLinkType.ALBUM;
|
||||
if (t == 'artist') return DeezerLinkType.ARTIST;
|
||||
if (t == 'playlist') return DeezerLinkType.PLAYLIST;
|
||||
if (t == 'track') return DeezerLinkType.TRACK;
|
||||
return null;
|
||||
return const <String, DeezerMediaType>{
|
||||
'album': DeezerMediaType.album,
|
||||
'artist': DeezerMediaType.artist,
|
||||
'playlist': DeezerMediaType.playlist,
|
||||
'track': DeezerMediaType.track,
|
||||
'show': DeezerMediaType.show,
|
||||
'episode': DeezerMediaType.episode,
|
||||
}[t.toLowerCase().trim()];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
await session.configure(const AudioSessionConfiguration.music());
|
||||
|
||||
_box = await Hive.openLazyBox('playback', path: await Paths.cacheDir());
|
||||
_init();
|
||||
_init(shouldLoadQueue: false);
|
||||
|
||||
await _loadQueueFile();
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
Future<void> _init({required bool shouldLoadQueue}) async {
|
||||
_player = AudioPlayer(
|
||||
handleInterruptions: !_ignoreInterruptions,
|
||||
androidApplyAudioAttributes: true,
|
||||
|
|
@ -270,12 +270,17 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
.onConnectivityChanged
|
||||
.listen(_determineAudioQualityByResult));
|
||||
}
|
||||
|
||||
if (shouldLoadQueue) {
|
||||
await _loadQueue(preload: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _maybeResume() {
|
||||
if (!_disposed) return Future.value();
|
||||
|
||||
return _init();
|
||||
return Future.value();
|
||||
_logger.fine('resuming audioHandler.');
|
||||
return _init(shouldLoadQueue: true);
|
||||
}
|
||||
|
||||
/// Determine the [AudioQuality] to use according to current connection
|
||||
|
|
@ -340,7 +345,8 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
@override
|
||||
Future play() async {
|
||||
await _maybeResume();
|
||||
_player.play();
|
||||
_logger.fine('playing...');
|
||||
await _player.play();
|
||||
//Restore position and queue index on play
|
||||
if (_lastPosition != null) {
|
||||
_player.seek(_lastPosition, index: _lastQueueIndex);
|
||||
|
|
@ -773,9 +779,13 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
Future<void> stop() async {
|
||||
await _saveQueue();
|
||||
_disposed = true;
|
||||
_player.dispose();
|
||||
// save state
|
||||
_lastPosition = _player.position;
|
||||
_lastQueueIndex = _queueIndex;
|
||||
await _player.stop();
|
||||
// await _player.dispose();
|
||||
for (final subscription in _subscriptions) {
|
||||
subscription.cancel();
|
||||
await subscription.cancel();
|
||||
}
|
||||
await super.stop();
|
||||
}
|
||||
|
|
@ -952,27 +962,29 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
if (parsed == null) return;
|
||||
|
||||
switch (parsed.type!) {
|
||||
case DeezerLinkType.TRACK:
|
||||
case DeezerMediaType.track:
|
||||
final track = await _deezerAPI.track(parsed.id!);
|
||||
_logger.fine('playing from track mix: ${jsonEncode(track)}');
|
||||
unawaited(playerHelper.playSearchMix(track.id, track.title!));
|
||||
break;
|
||||
case DeezerLinkType.ALBUM:
|
||||
case DeezerMediaType.album:
|
||||
final album = await _deezerAPI.album(parsed.id!);
|
||||
_logger.fine('playing from album: ${album.title}');
|
||||
unawaited(playerHelper.playFromAlbum(album));
|
||||
break;
|
||||
case DeezerLinkType.ARTIST:
|
||||
case DeezerMediaType.artist:
|
||||
final artist = await _deezerAPI.artist(parsed.id!);
|
||||
_logger.fine('playing from artist top: ${artist.name!}');
|
||||
unawaited(
|
||||
playerHelper.playFromTopTracks(artist.topTracks!, null, artist));
|
||||
break;
|
||||
case DeezerLinkType.PLAYLIST:
|
||||
case DeezerMediaType.playlist:
|
||||
final fullPlaylist = await _deezerAPI.playlist(parsed.id!);
|
||||
_logger.fine('playing from playlist: ${fullPlaylist.title!}');
|
||||
unawaited(playerHelper.playFromPlaylist(fullPlaylist));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,17 +71,7 @@ class FancyScaffoldState extends State<FancyScaffold>
|
|||
begin: widget.bottomPanelHeight / MediaQuery.of(context).size.height,
|
||||
end: 1.0,
|
||||
).animate(dragController);
|
||||
return WillPopScope(
|
||||
onWillPop: () {
|
||||
if (statusNotifier.value == AnimationStatus.completed ||
|
||||
statusNotifier.value == AnimationStatus.reverse) {
|
||||
dragController.fling(velocity: -1.0);
|
||||
return Future.value(false);
|
||||
}
|
||||
|
||||
return Future.value(true);
|
||||
},
|
||||
child: Stack(
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Scaffold(
|
||||
|
|
@ -150,10 +140,15 @@ class FancyScaffoldState extends State<FancyScaffold>
|
|||
builder: (context, state, _) => Stack(
|
||||
children: [
|
||||
if (state != AnimationStatus.dismissed)
|
||||
Positioned.fill(
|
||||
PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (_) =>
|
||||
dragController.fling(velocity: -1.0),
|
||||
child: Positioned.fill(
|
||||
key: const Key('player_screen'),
|
||||
child: widget.expandedPanel,
|
||||
),
|
||||
),
|
||||
if (state != AnimationStatus.completed)
|
||||
Positioned(
|
||||
top: 0,
|
||||
|
|
@ -176,7 +171,6 @@ class FancyScaffoldState extends State<FancyScaffold>
|
|||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -552,8 +552,8 @@ class _SpotifyImporterV2MainState extends State<SpotifyImporterV2Main> {
|
|||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => WillPopScope(
|
||||
onWillPop: () => Future.value(false),
|
||||
builder: (context) => PopScope(
|
||||
canPop: false,
|
||||
child: AlertDialog(
|
||||
title: Text("Please wait...".i18n),
|
||||
content: const Row(
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ FutureOr openScreenByURL(BuildContext context, String url) async {
|
|||
if (res == null) return;
|
||||
|
||||
switch (res.type) {
|
||||
case DeezerLinkType.TRACK:
|
||||
case DeezerMediaType.track:
|
||||
Track t = await deezerAPI.track(res.id!);
|
||||
MenuSheet(context).defaultTrackMenu(t, optionsTop: [
|
||||
MenuSheetOption(Text('Play'.i18n),
|
||||
|
|
@ -34,15 +34,15 @@ FutureOr openScreenByURL(BuildContext context, String url) async {
|
|||
onTap: () => playerHelper.playSearchMixDeferred(t)),
|
||||
]);
|
||||
break;
|
||||
case DeezerLinkType.ALBUM:
|
||||
case DeezerMediaType.album:
|
||||
Album a = await deezerAPI.album(res.id);
|
||||
return Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (context) => AlbumDetails(a)));
|
||||
case DeezerLinkType.ARTIST:
|
||||
case DeezerMediaType.artist:
|
||||
Artist a = await deezerAPI.artist(res.id);
|
||||
return Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (context) => ArtistDetails(a)));
|
||||
case DeezerLinkType.PLAYLIST:
|
||||
case DeezerMediaType.playlist:
|
||||
Playlist p = await deezerAPI.playlist(res.id);
|
||||
if (p.tracks == null || p.tracks!.isEmpty) {
|
||||
ScaffoldMessenger.of(context)
|
||||
|
|
@ -446,15 +446,9 @@ class SearchResultsScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
||||
final _tracksKey = GlobalKey();
|
||||
final _albumsKey = GlobalKey();
|
||||
final _artistsKey = GlobalKey();
|
||||
final _playlistsKey = GlobalKey();
|
||||
final _showsKey = GlobalKey();
|
||||
final _episodesKey = GlobalKey();
|
||||
|
||||
SearchResults? _results;
|
||||
Object? _error;
|
||||
DeezerMediaType? _page;
|
||||
|
||||
Future _search() async {
|
||||
try {
|
||||
|
|
@ -474,6 +468,24 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
Widget buildListFor(DeezerMediaType page) {
|
||||
switch (page) {
|
||||
case DeezerMediaType.track:
|
||||
return TrackListScreen(_results!.tracks, null);
|
||||
case DeezerMediaType.album:
|
||||
return AlbumListScreen(_results!.albums);
|
||||
case DeezerMediaType.playlist:
|
||||
return PlaylistListScreen(_results!.playlists);
|
||||
case DeezerMediaType.episode:
|
||||
return EpisodeListScreen(_results!.episodes);
|
||||
case DeezerMediaType.show:
|
||||
return ShowListScreen(_results!.shows);
|
||||
|
||||
case DeezerMediaType.artist:
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildFromDeezerItem(DeezerMediaItem item) {
|
||||
switch (item) {
|
||||
case final Track track:
|
||||
|
|
@ -539,62 +551,62 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
ListView(scrollDirection: Axis.horizontal, children: [
|
||||
if (_results!.tracks != null &&
|
||||
_results!.tracks!.isNotEmpty) ...[
|
||||
ActionChip(
|
||||
FilterChip(
|
||||
elevation: 1.0,
|
||||
label: Text('Tracks'.i18n),
|
||||
onPressed: () => Scrollable.ensureVisible(
|
||||
_tracksKey.currentContext!,
|
||||
duration: const Duration(milliseconds: 500))),
|
||||
selected: _page == DeezerMediaType.track,
|
||||
onSelected: (selected) => setState(() => _page =
|
||||
selected ? DeezerMediaType.track : null)),
|
||||
const SizedBox(width: 8.0),
|
||||
],
|
||||
if (_results!.albums != null &&
|
||||
_results!.albums!.isNotEmpty) ...[
|
||||
ActionChip(
|
||||
FilterChip(
|
||||
elevation: 1.0,
|
||||
label: Text('Albums'.i18n),
|
||||
onPressed: () => Scrollable.ensureVisible(
|
||||
_albumsKey.currentContext!,
|
||||
duration: const Duration(milliseconds: 500))),
|
||||
const SizedBox(width: 8.0),
|
||||
],
|
||||
if (_results!.artists != null &&
|
||||
_results!.artists!.isNotEmpty) ...[
|
||||
ActionChip(
|
||||
elevation: 1.0,
|
||||
label: Text('Artists'.i18n),
|
||||
onPressed: () => Scrollable.ensureVisible(
|
||||
_artistsKey.currentContext!,
|
||||
duration: const Duration(milliseconds: 500))),
|
||||
selected: _page == DeezerMediaType.album,
|
||||
onSelected: (selected) => setState(() => _page =
|
||||
selected ? DeezerMediaType.album : null)),
|
||||
const SizedBox(width: 8.0),
|
||||
],
|
||||
// if (_results!.artists != null &&
|
||||
// _results!.artists!.isNotEmpty) ...[
|
||||
// FilterChip(
|
||||
// elevation: 1.0,
|
||||
// label: Text('Artists'.i18n),
|
||||
// selected: _page == DeezerMediaType.artist,
|
||||
// onSelected: (selected) => setState(() => _page =
|
||||
// selected ? DeezerMediaType.artist : null)),
|
||||
// const SizedBox(width: 8.0),
|
||||
// ],
|
||||
if (_results!.playlists != null &&
|
||||
_results!.playlists!.isNotEmpty) ...[
|
||||
ActionChip(
|
||||
FilterChip(
|
||||
elevation: 1.0,
|
||||
label: Text('Playlists'.i18n),
|
||||
onPressed: () => Scrollable.ensureVisible(
|
||||
_playlistsKey.currentContext!,
|
||||
duration: const Duration(milliseconds: 500))),
|
||||
selected: _page == DeezerMediaType.playlist,
|
||||
onSelected: (selected) => setState(() => _page =
|
||||
selected ? DeezerMediaType.playlist : null)),
|
||||
const SizedBox(width: 8.0),
|
||||
],
|
||||
if (_results!.shows != null &&
|
||||
_results!.shows!.isNotEmpty) ...[
|
||||
ActionChip(
|
||||
FilterChip(
|
||||
elevation: 1.0,
|
||||
label: Text('Shows'.i18n),
|
||||
onPressed: () => Scrollable.ensureVisible(
|
||||
_showsKey.currentContext!,
|
||||
duration: const Duration(milliseconds: 500))),
|
||||
selected: _page == DeezerMediaType.show,
|
||||
onSelected: (selected) => setState(() => _page =
|
||||
selected ? DeezerMediaType.show : null)),
|
||||
const SizedBox(width: 8.0),
|
||||
],
|
||||
if (_results!.episodes != null &&
|
||||
_results!.episodes!.isNotEmpty) ...[
|
||||
ActionChip(
|
||||
FilterChip(
|
||||
elevation: 1.0,
|
||||
label: Text('Episodes'.i18n),
|
||||
onPressed: () => Scrollable.ensureVisible(
|
||||
_episodesKey.currentContext!,
|
||||
duration: const Duration(milliseconds: 500))),
|
||||
selected: _page == DeezerMediaType.episode,
|
||||
onSelected: (selected) => setState(() => _page =
|
||||
selected ? DeezerMediaType.episode : null)),
|
||||
const SizedBox(width: 8.0),
|
||||
],
|
||||
]),
|
||||
|
|
@ -606,7 +618,14 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
? ErrorScreen(message: _error.toString())
|
||||
: _results == null
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Builder(
|
||||
: PopScope(
|
||||
canPop: _page == null,
|
||||
onPopInvoked: (didPop) {
|
||||
if (_page != null) {
|
||||
setState(() => _page = null);
|
||||
}
|
||||
},
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final results = _results!;
|
||||
if (results.empty) {
|
||||
|
|
@ -624,9 +643,11 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
);
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
if (_page != null) {
|
||||
return buildListFor(_page!);
|
||||
}
|
||||
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
if (results.topResult != null &&
|
||||
results.topResult!.isNotEmpty) ...[
|
||||
|
|
@ -657,7 +678,6 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
if (results.tracks != null &&
|
||||
results.tracks!.isNotEmpty) ...[
|
||||
Padding(
|
||||
key: _tracksKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 4.0),
|
||||
child: Text(
|
||||
|
|
@ -679,11 +699,8 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
}),
|
||||
ListTile(
|
||||
title: Text('Show all tracks'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushRoute(
|
||||
builder: (context) => TrackListScreen(
|
||||
results.tracks, null));
|
||||
},
|
||||
onTap: () => setState(
|
||||
() => _page = DeezerMediaType.track),
|
||||
),
|
||||
const FreezerDivider(),
|
||||
],
|
||||
|
|
@ -691,7 +708,6 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
results.albums!.isNotEmpty) ...[
|
||||
const SizedBox(height: 8.0),
|
||||
Padding(
|
||||
key: _albumsKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 4.0),
|
||||
child: Text(
|
||||
|
|
@ -715,11 +731,8 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
}),
|
||||
ListTile(
|
||||
title: Text('Show all albums'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushRoute(
|
||||
builder: (context) =>
|
||||
AlbumListScreen(results.albums));
|
||||
},
|
||||
onTap: () => setState(
|
||||
() => _page = DeezerMediaType.album),
|
||||
),
|
||||
const FreezerDivider()
|
||||
],
|
||||
|
|
@ -727,7 +740,6 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
results.artists!.isNotEmpty) ...[
|
||||
const SizedBox(height: 8.0),
|
||||
Padding(
|
||||
key: _artistsKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0, horizontal: 16.0),
|
||||
child: Text(
|
||||
|
|
@ -778,7 +790,6 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
results.playlists!.isNotEmpty) ...[
|
||||
const SizedBox(height: 8.0),
|
||||
Padding(
|
||||
key: _playlistsKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0, horizontal: 16.0),
|
||||
child: Text(
|
||||
|
|
@ -808,12 +819,8 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
),
|
||||
ListTile(
|
||||
title: Text('Show all playlists'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushRoute(
|
||||
builder: (context) =>
|
||||
SearchResultPlaylists(
|
||||
results.playlists));
|
||||
},
|
||||
onTap: () => setState(
|
||||
() => _page = DeezerMediaType.playlist),
|
||||
),
|
||||
const FreezerDivider(),
|
||||
],
|
||||
|
|
@ -821,7 +828,6 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
results.shows!.isNotEmpty) ...[
|
||||
const SizedBox(height: 8.0),
|
||||
Padding(
|
||||
key: _showsKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0, horizontal: 16.0),
|
||||
child: Text(
|
||||
|
|
@ -843,11 +849,8 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
),
|
||||
ListTile(
|
||||
title: Text('Show all shows'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushRoute(
|
||||
builder: (context) =>
|
||||
ShowListScreen(results.shows));
|
||||
},
|
||||
onTap: () => setState(
|
||||
() => _page = DeezerMediaType.show),
|
||||
),
|
||||
const FreezerDivider()
|
||||
],
|
||||
|
|
@ -855,7 +858,6 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
results.episodes!.isNotEmpty) ...[
|
||||
const SizedBox(height: 8.0),
|
||||
Padding(
|
||||
key: _episodesKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0, horizontal: 16.0),
|
||||
child: Text(
|
||||
|
|
@ -894,16 +896,14 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
),
|
||||
ListTile(
|
||||
title: Text('Show all episodes'.i18n),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushRoute(
|
||||
builder: (context) => EpisodeListScreen(
|
||||
results.episodes));
|
||||
})
|
||||
onTap: () => setState(
|
||||
() => _page = DeezerMediaType.episode),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -917,9 +917,7 @@ class TrackListScreen extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Tracks'.i18n)),
|
||||
body: ListView.builder(
|
||||
return ListView.builder(
|
||||
itemCount: tracks!.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
Track t = tracks![i];
|
||||
|
|
@ -939,7 +937,6 @@ class TrackListScreen extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -951,9 +948,7 @@ class AlbumListScreen extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Albums'.i18n)),
|
||||
body: ListView.builder(
|
||||
return ListView.builder(
|
||||
itemCount: albums!.length,
|
||||
itemBuilder: (context, i) {
|
||||
Album? a = albums![i];
|
||||
|
|
@ -969,20 +964,17 @@ class AlbumListScreen extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SearchResultPlaylists extends StatelessWidget {
|
||||
class PlaylistListScreen extends StatelessWidget {
|
||||
final List<Playlist?>? playlists;
|
||||
const SearchResultPlaylists(this.playlists, {super.key});
|
||||
const PlaylistListScreen(this.playlists, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Playlists'.i18n)),
|
||||
body: ListView.builder(
|
||||
return ListView.builder(
|
||||
itemCount: playlists!.length,
|
||||
itemBuilder: (context, i) {
|
||||
Playlist? p = playlists![i];
|
||||
|
|
@ -998,7 +990,6 @@ class SearchResultPlaylists extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1009,9 +1000,7 @@ class ShowListScreen extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Shows'.i18n)),
|
||||
body: ListView.builder(
|
||||
return ListView.builder(
|
||||
itemCount: shows!.length,
|
||||
itemBuilder: (context, i) {
|
||||
Show s = shows![i];
|
||||
|
|
@ -1023,7 +1012,6 @@ class ShowListScreen extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1034,9 +1022,7 @@ class EpisodeListScreen extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Episodes'.i18n)),
|
||||
body: ListView.builder(
|
||||
return ListView.builder(
|
||||
itemCount: episodes!.length,
|
||||
itemBuilder: (context, i) {
|
||||
ShowEpisode e = episodes![i];
|
||||
|
|
@ -1061,6 +1047,6 @@ class EpisodeListScreen extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1528,6 +1528,9 @@ class _GeneralSettingsState extends State<GeneralSettings> {
|
|||
ScaffoldMessenger.of(context).snack('Copied'.i18n);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('DEBUG: stop audioHandler'),
|
||||
onTap: () => audioHandler.stop()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue