0.5.3 - Download fixes, shuffle fix, sorting in library
This commit is contained in:
parent
952cf0f508
commit
2f471268c6
18 changed files with 556 additions and 167 deletions
|
@ -664,7 +664,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
tracks.sort((a, b) => a.title.compareTo(b.title));
|
||||
return tracks;
|
||||
case SortType.ARTIST:
|
||||
tracks.sort((a, b) => a.artists[0].name.compareTo(b.artists[0].name));
|
||||
tracks.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase()));
|
||||
return tracks;
|
||||
case SortType.REVERSE:
|
||||
return tracks.reversed.toList();
|
||||
|
|
|
@ -419,6 +419,13 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
}
|
||||
|
||||
|
||||
enum AlbumSortType {
|
||||
DEFAULT,
|
||||
REVERSE,
|
||||
ALPHABETIC,
|
||||
ARTIST
|
||||
}
|
||||
|
||||
class LibraryAlbums extends StatefulWidget {
|
||||
@override
|
||||
_LibraryAlbumsState createState() => _LibraryAlbumsState();
|
||||
|
@ -427,6 +434,25 @@ class LibraryAlbums extends StatefulWidget {
|
|||
class _LibraryAlbumsState extends State<LibraryAlbums> {
|
||||
|
||||
List<Album> _albums;
|
||||
AlbumSortType _sort = AlbumSortType.DEFAULT;
|
||||
|
||||
List<Album> get _sorted {
|
||||
List<Album> albums = List.from(_albums);
|
||||
switch (_sort) {
|
||||
case AlbumSortType.DEFAULT:
|
||||
return _albums;
|
||||
case AlbumSortType.REVERSE:
|
||||
return _albums.reversed.toList();
|
||||
case AlbumSortType.ALPHABETIC:
|
||||
albums.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()));
|
||||
return albums;
|
||||
case AlbumSortType.ARTIST:
|
||||
albums.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase()));
|
||||
return albums;
|
||||
}
|
||||
return albums;
|
||||
}
|
||||
|
||||
|
||||
Future _load() async {
|
||||
if (settings.offlineMode) return;
|
||||
|
@ -439,13 +465,44 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
|||
@override
|
||||
void initState() {
|
||||
_load();
|
||||
_sort = cache.albumSort??AlbumSortType.DEFAULT;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Albums'.i18n),),
|
||||
appBar: AppBar(
|
||||
title: Text('Albums'.i18n),
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
child: Icon(Icons.sort, size: 32.0),
|
||||
onSelected: (AlbumSortType s) async {
|
||||
setState(() => _sort = s);
|
||||
cache.albumSort = s;
|
||||
await cache.save();
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry<AlbumSortType>>[
|
||||
PopupMenuItem(
|
||||
value: AlbumSortType.DEFAULT,
|
||||
child: Text('Default'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: AlbumSortType.REVERSE,
|
||||
child: Text('Reverse'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: AlbumSortType.ALPHABETIC,
|
||||
child: Text('Alphabetic'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: AlbumSortType.ARTIST,
|
||||
child: Text('Artist'.i18n),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Container(height: 8.0,),
|
||||
|
@ -459,7 +516,7 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
|||
|
||||
if (_albums != null)
|
||||
...List.generate(_albums.length, (int i) {
|
||||
Album a = _albums[i];
|
||||
Album a = _sorted[i];
|
||||
return AlbumTile(
|
||||
a,
|
||||
onTap: () {
|
||||
|
@ -523,50 +580,148 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
|||
}
|
||||
}
|
||||
|
||||
enum ArtistSortType {
|
||||
DEFAULT,
|
||||
REVERSE,
|
||||
POPULARITY,
|
||||
ALPHABETIC
|
||||
}
|
||||
|
||||
class LibraryArtists extends StatefulWidget {
|
||||
@override
|
||||
_LibraryArtistsState createState() => _LibraryArtistsState();
|
||||
}
|
||||
|
||||
class _LibraryArtistsState extends State<LibraryArtists> {
|
||||
|
||||
List<Artist> _artists;
|
||||
ArtistSortType _sort = ArtistSortType.DEFAULT;
|
||||
bool _loading = true;
|
||||
bool _error = false;
|
||||
|
||||
List<Artist> get _sorted {
|
||||
List<Artist> artists = List.from(_artists);
|
||||
switch (_sort) {
|
||||
case ArtistSortType.DEFAULT:
|
||||
return _artists;
|
||||
case ArtistSortType.REVERSE:
|
||||
return _artists.reversed.toList();
|
||||
case ArtistSortType.POPULARITY:
|
||||
artists.sort((a, b) => b.fans - a.fans);
|
||||
return artists;
|
||||
case ArtistSortType.ALPHABETIC:
|
||||
artists.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||
return artists;
|
||||
}
|
||||
}
|
||||
|
||||
//Load data
|
||||
Future _load() async {
|
||||
setState(() => _loading = true);
|
||||
//Fetch
|
||||
List<Artist> data;
|
||||
try {
|
||||
data = await deezerAPI.getArtists();
|
||||
} catch (e) {}
|
||||
//Update UI
|
||||
setState(() {
|
||||
if (data != null) {
|
||||
_artists = data;
|
||||
} else {
|
||||
_error = true;
|
||||
}
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_sort = cache.artistSort;
|
||||
_load();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Artists'.i18n),),
|
||||
body: FutureBuilder(
|
||||
future: deezerAPI.getArtists(),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
|
||||
if (snapshot.hasError) return ErrorScreen();
|
||||
if (!snapshot.hasData) return Center(child: CircularProgressIndicator(),);
|
||||
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
...List.generate(snapshot.data.length, (i) {
|
||||
Artist a = snapshot.data[i];
|
||||
return ArtistHorizontalTile(
|
||||
a,
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => ArtistDetails(a))
|
||||
);
|
||||
},
|
||||
onHold: () {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultArtistMenu(a, onRemove: () {
|
||||
setState(() => {});
|
||||
});
|
||||
},
|
||||
);
|
||||
}),
|
||||
appBar: AppBar(
|
||||
title: Text('Artists'.i18n),
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
child: Icon(Icons.sort, size: 32.0),
|
||||
onSelected: (ArtistSortType s) async {
|
||||
setState(() => _sort = s);
|
||||
cache.artistSort = s;
|
||||
await cache.save();
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry<ArtistSortType>>[
|
||||
PopupMenuItem(
|
||||
value: ArtistSortType.DEFAULT,
|
||||
child: Text('Default'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ArtistSortType.REVERSE,
|
||||
child: Text('Reverse'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ArtistSortType.ALPHABETIC,
|
||||
child: Text('Alphabetic'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ArtistSortType.POPULARITY,
|
||||
child: Text('Popularity'.i18n),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
if (_loading)
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [CircularProgressIndicator()],
|
||||
),
|
||||
),
|
||||
|
||||
if (_error)
|
||||
Center(child: ErrorScreen()),
|
||||
|
||||
if (!_loading && !_error)
|
||||
...List.generate(_artists.length, (i) {
|
||||
Artist a = _sorted[i];
|
||||
return ArtistHorizontalTile(
|
||||
a,
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => ArtistDetails(a))
|
||||
);
|
||||
},
|
||||
onHold: () {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultArtistMenu(a, onRemove: () {
|
||||
setState(() {
|
||||
_artists.remove(a);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum PlaylistSortType {
|
||||
DEFAULT,
|
||||
REVERSE,
|
||||
ALPHABETIC,
|
||||
USER,
|
||||
TRACK_COUNT
|
||||
}
|
||||
|
||||
class LibraryPlaylists extends StatefulWidget {
|
||||
@override
|
||||
|
@ -576,6 +731,26 @@ class LibraryPlaylists extends StatefulWidget {
|
|||
class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
||||
|
||||
List<Playlist> _playlists;
|
||||
PlaylistSortType _sort = PlaylistSortType.DEFAULT;
|
||||
|
||||
List<Playlist> get _sorted {
|
||||
List<Playlist> playlists = List.from(_playlists);
|
||||
switch (_sort) {
|
||||
case PlaylistSortType.DEFAULT:
|
||||
return _playlists;
|
||||
case PlaylistSortType.REVERSE:
|
||||
return _playlists.reversed.toList();
|
||||
case PlaylistSortType.USER:
|
||||
playlists.sort((a, b) => (a.user.name??deezerAPI.userName).toLowerCase().compareTo((b.user.name??deezerAPI.userName).toLowerCase()));
|
||||
return playlists;
|
||||
case PlaylistSortType.TRACK_COUNT:
|
||||
playlists.sort((a, b) => b.trackCount - a.trackCount);
|
||||
return playlists;
|
||||
case PlaylistSortType.ALPHABETIC:
|
||||
playlists.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()));
|
||||
return playlists;
|
||||
}
|
||||
}
|
||||
|
||||
Future _load() async {
|
||||
if (!settings.offlineMode) {
|
||||
|
@ -588,6 +763,7 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
_sort = cache.libraryPlaylistSort;
|
||||
_load();
|
||||
super.initState();
|
||||
}
|
||||
|
@ -606,7 +782,41 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Playlists'.i18n),),
|
||||
appBar: AppBar(
|
||||
title: Text('Playlists'.i18n),
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
child: Icon(Icons.sort, size: 32.0),
|
||||
onSelected: (PlaylistSortType s) async {
|
||||
setState(() => _sort = s);
|
||||
cache.libraryPlaylistSort = s;
|
||||
await cache.save();
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry<PlaylistSortType>>[
|
||||
PopupMenuItem(
|
||||
value: PlaylistSortType.DEFAULT,
|
||||
child: Text('Default'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: PlaylistSortType.REVERSE,
|
||||
child: Text('Reverse'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: PlaylistSortType.USER,
|
||||
child: Text('User'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: PlaylistSortType.TRACK_COUNT,
|
||||
child: Text('Track count'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: PlaylistSortType.ALPHABETIC,
|
||||
child: Text('Alphabetic'.i18n),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
|
@ -652,7 +862,7 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
|
||||
if (_playlists != null)
|
||||
...List.generate(_playlists.length, (int i) {
|
||||
Playlist p = _playlists[i];
|
||||
Playlist p = _sorted[i];
|
||||
return PlaylistTile(
|
||||
p,
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
|
|
|
@ -90,15 +90,19 @@ class _LoginWidgetState extends State<LoginWidget> {
|
|||
//Try logging in
|
||||
try {
|
||||
deezerAPI.arl = settings.arl;
|
||||
bool resp = await deezerAPI.rawAuthorize(onError: (e) => _error = e.toString());
|
||||
bool resp = await deezerAPI.rawAuthorize(onError: (e) => setState(() => _error = e.toString()));
|
||||
if (resp == false) { //false, not null
|
||||
if (settings.arl.length != 192) {
|
||||
if (_error == null) _error = '';
|
||||
_error += 'Invalid ARL length!';
|
||||
}
|
||||
setState(() => settings.arl = null);
|
||||
errorDialog();
|
||||
}
|
||||
//On error show dialog and reset to null
|
||||
} catch (e) {
|
||||
_error = e;
|
||||
print('Login error: ' + e);
|
||||
_error = e.toString();
|
||||
print('Login error: ' + e.toString());
|
||||
setState(() => settings.arl = null);
|
||||
errorDialog();
|
||||
}
|
||||
|
|
|
@ -498,7 +498,7 @@ class _DeezerSettingsState extends State<DeezerSettings> {
|
|||
ListTile(
|
||||
title: Text('Proxy'.i18n),
|
||||
leading: Icon(Icons.vpn_key),
|
||||
subtitle: Text(settings.proxyAddress??'Not set'),
|
||||
subtitle: Text(settings.proxyAddress??'Not set'.i18n),
|
||||
onTap: () {
|
||||
String _new;
|
||||
showDialog(
|
||||
|
@ -606,7 +606,8 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
Container(height: 8.0),
|
||||
Text(
|
||||
'Valid variables are'.i18n + ': %artists%, %artist%, %title%, %album%, %trackNumber%, %0trackNumber%, %feats%, %playlistTrackNumber%, %0playlistTrackNumber%, %year%, %date%',
|
||||
'Valid variables are'.i18n + ': %artists%, %artist%, %title%, %album%, %trackNumber%, %0trackNumber%, %feats%, %playlistTrackNumber%, %0playlistTrackNumber%, %year%, %date%\n\n' +
|
||||
"If you want to use custom directory naming - use '/' as directory separator.".i18n,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
),
|
||||
|
@ -658,8 +659,8 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
Slider(
|
||||
min: 1,
|
||||
max: 6,
|
||||
divisions: 5,
|
||||
max: 16,
|
||||
divisions: 15,
|
||||
value: _downloadThreads,
|
||||
label: _downloadThreads.round().toString(),
|
||||
onChanged: (double v) => setState(() => _downloadThreads = v),
|
||||
|
@ -1040,15 +1041,6 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
|||
|
||||
String _version = '';
|
||||
|
||||
//Title, Subtitle, URL
|
||||
static final List<List<String>> credits = [
|
||||
['exttex', 'Developer'],
|
||||
['Bas Curtiz', 'Icon, logo, banner, design suggestions, tester'],
|
||||
['Deemix', 'Better app <3', 'https://codeberg.org/RemixDev/deemix'],
|
||||
['Tobs, Xandar Null, Francesco', 'Beta testers'],
|
||||
['Annexhack', 'Android Auto help']
|
||||
];
|
||||
|
||||
static final List<List<String>> translators = [
|
||||
['Xandar Null', 'Arabic'],
|
||||
['Markus', 'German'],
|
||||
|
@ -1111,22 +1103,51 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
|||
),
|
||||
ListTile(
|
||||
title: Text('Repository'.i18n),
|
||||
subtitle: Text('Source code, report issues there.'),
|
||||
subtitle: Text('Source code, report issues there.'.i18n),
|
||||
leading: Icon(Icons.code, color: Colors.green, size: 36.0),
|
||||
onTap: () {
|
||||
launch('https://notabug.org/exttex/freezer');
|
||||
},
|
||||
),
|
||||
Divider(),
|
||||
...List.generate(credits.length, (i) => ListTile(
|
||||
title: Text(credits[i][0]),
|
||||
subtitle: Text(credits[i][1]),
|
||||
ListTile(
|
||||
title: Text('exttex'),
|
||||
subtitle: Text('Developer'),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Bas Curtiz'),
|
||||
subtitle: Text('Icon, logo, banner, design suggestions, tester'),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Tobs'),
|
||||
subtitle: Text('Alpha testers'),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Deemix'),
|
||||
subtitle: Text('Better app <3'),
|
||||
onTap: () {
|
||||
if (credits[i].length >= 3) {
|
||||
launch(credits[i][2]);
|
||||
}
|
||||
launch('https://codeberg.org/RemixDev/deemix');
|
||||
},
|
||||
)),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Xandar Null'),
|
||||
subtitle: Text('Tester, translations help'),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Francesco'),
|
||||
subtitle: Text('Tester'),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
settings.primaryColor = Color(0xff333333);
|
||||
});
|
||||
updateTheme();
|
||||
settings.save();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Annexhack'),
|
||||
subtitle: Text('Android Auto help'),
|
||||
),
|
||||
Divider(),
|
||||
...List.generate(translators.length, (i) => ListTile(
|
||||
title: Text(translators[i][0]),
|
||||
|
|
|
@ -205,18 +205,21 @@ class ArtistHorizontalTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
artist.name,
|
||||
maxLines: 1,
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 2.0),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
artist.name,
|
||||
maxLines: 1,
|
||||
),
|
||||
leading: CachedImage(
|
||||
url: artist.picture.thumb,
|
||||
circular: true,
|
||||
),
|
||||
onTap: onTap,
|
||||
onLongPress: onHold,
|
||||
trailing: trailing,
|
||||
),
|
||||
leading: CachedImage(
|
||||
url: artist.picture.thumb,
|
||||
circular: true,
|
||||
),
|
||||
onTap: onTap,
|
||||
onLongPress: onHold,
|
||||
trailing: trailing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue