0.6.8 - bug fixes
This commit is contained in:
parent
c3a26b0e3b
commit
ff239aaf86
@ -247,17 +247,28 @@ public class Deezer {
|
||||
return original + ".mp3";
|
||||
}
|
||||
|
||||
public static String generateUserUploadedMP3Filename(String original, JSONObject privateJson) throws Exception {
|
||||
//Remove unavailable tags
|
||||
String[] ignored = {"%feats%", "%trackNumber%", "%0trackNumber%", "%year%", "%date%"};
|
||||
// public static String generateUserUploadedMP3Filename(String original, JSONObject privateJson) throws Exception {
|
||||
// //Remove unavailable tags
|
||||
// String[] ignored = {"%feats%", "%trackNumber%", "%0trackNumber%", "%year%", "%date%"};
|
||||
// for (String i : ignored) {
|
||||
// original = original.replaceAll(i, "");
|
||||
// }
|
||||
// //Basic tags
|
||||
// original = original.replaceAll("%title%", privateJson.getString("SNG_TITLE"));
|
||||
// original = original.replaceAll("%album%", privateJson.getString("ALB_TITLE"));
|
||||
// original = original.replaceAll("%artist%", privateJson.getString("ART_NAME"));
|
||||
// original = original.replaceAll("%artists%", privateJson.getString("ART_NAME"));
|
||||
// return original;
|
||||
// }
|
||||
|
||||
//Deezer patched something so getting metadata of user uploaded MP3s is not working anymore
|
||||
public static String generateUserUploadedMP3Filename(String original, String title) throws Exception {
|
||||
String[] ignored = {"%feats%", "%trackNumber%", "%0trackNumber%", "%year%", "%date%", "%album%", "%artist%", "%artists%"};
|
||||
for (String i : ignored) {
|
||||
original = original.replaceAll(i, "");
|
||||
}
|
||||
//Basic tags
|
||||
original = original.replaceAll("%title%", privateJson.getString("SNG_TITLE"));
|
||||
original = original.replaceAll("%album%", privateJson.getString("ALB_TITLE"));
|
||||
original = original.replaceAll("%artist%", privateJson.getString("ART_NAME"));
|
||||
original = original.replaceAll("%artists%", privateJson.getString("ART_NAME"));
|
||||
|
||||
original = original.replace("%title%", sanitize(title));
|
||||
return original;
|
||||
}
|
||||
|
||||
|
@ -96,10 +96,8 @@ public class DownloadService extends Service {
|
||||
public void onDestroy() {
|
||||
//Cancel notifications
|
||||
notificationManager.cancelAll();
|
||||
|
||||
//Logger
|
||||
logger.close();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@ -116,8 +114,10 @@ public class DownloadService extends Service {
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
//Get messenger
|
||||
if (intent != null)
|
||||
if (intent != null) {
|
||||
activityMessenger = intent.getParcelableExtra("activityMessenger");
|
||||
}
|
||||
|
||||
|
||||
//return super.onStartCommand(intent, flags, startId);
|
||||
//Prevent battery savers I guess
|
||||
@ -295,24 +295,24 @@ public class DownloadService extends Service {
|
||||
while (deezer.authorizing)
|
||||
try {Thread.sleep(50);} catch (Exception ignored) {}
|
||||
|
||||
//Fetch metadata
|
||||
try {
|
||||
JSONObject privateRaw = deezer.callGWAPI("deezer.pageTrack", "{\"sng_id\": \"" + download.trackId + "\"}");
|
||||
privateJson = privateRaw.getJSONObject("results").getJSONObject("DATA");
|
||||
if (privateRaw.getJSONObject("results").has("LYRICS")) {
|
||||
lyricsData = privateRaw.getJSONObject("results").getJSONObject("LYRICS");
|
||||
}
|
||||
//Don't fetch meta if user uploaded mp3
|
||||
if (!download.isUserUploaded()) {
|
||||
//Don't fetch meta if user uploaded mp3
|
||||
if (!download.isUserUploaded()) {
|
||||
try {
|
||||
JSONObject privateRaw = deezer.callGWAPI("deezer.pageTrack", "{\"sng_id\": \"" + download.trackId + "\"}");
|
||||
privateJson = privateRaw.getJSONObject("results").getJSONObject("DATA");
|
||||
if (privateRaw.getJSONObject("results").has("LYRICS")) {
|
||||
lyricsData = privateRaw.getJSONObject("results").getJSONObject("LYRICS");
|
||||
}
|
||||
trackJson = Deezer.callPublicAPI("track", download.trackId);
|
||||
albumJson = Deezer.callPublicAPI("album", Integer.toString(trackJson.getJSONObject("album").getInt("id")));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to fetch track and album metadata! " + e.toString(), download);
|
||||
e.printStackTrace();
|
||||
download.state = Download.DownloadState.ERROR;
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to fetch track and album metadata! " + e.toString(), download);
|
||||
e.printStackTrace();
|
||||
download.state = Download.DownloadState.ERROR;
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
//Fallback
|
||||
@ -339,7 +339,7 @@ public class DownloadService extends Service {
|
||||
//Check file
|
||||
try {
|
||||
if (download.isUserUploaded()) {
|
||||
outFile = new File(Deezer.generateUserUploadedMP3Filename(download.path, privateJson));
|
||||
outFile = new File(Deezer.generateUserUploadedMP3Filename(download.path, download.title));
|
||||
} else {
|
||||
outFile = new File(Deezer.generateFilename(download.path, trackJson, albumJson, qualityInfo.quality));
|
||||
}
|
||||
@ -700,6 +700,8 @@ public class DownloadService extends Service {
|
||||
//Start/Resume
|
||||
case SERVICE_START_DOWNLOAD:
|
||||
running = true;
|
||||
if (downloads.size() == 0)
|
||||
loadDownloads();
|
||||
updateQueue();
|
||||
updateState();
|
||||
break;
|
||||
|
@ -144,8 +144,9 @@ public class MainActivity extends FlutterActivity {
|
||||
}
|
||||
//Start/Resume downloading
|
||||
if (call.method.equals("start")) {
|
||||
//Connected
|
||||
sendMessage(DownloadService.SERVICE_START_DOWNLOAD, null);
|
||||
result.success(null);
|
||||
result.success(serviceBound);
|
||||
return;
|
||||
}
|
||||
//Stop downloading
|
||||
@ -239,17 +240,24 @@ public class MainActivity extends FlutterActivity {
|
||||
}));
|
||||
}
|
||||
|
||||
//Start/Bind/Reconnect to download service
|
||||
private void connectService() {
|
||||
if (serviceBound)
|
||||
return;
|
||||
//Create messenger
|
||||
activityMessenger = new Messenger(new IncomingHandler(this));
|
||||
//Start
|
||||
Intent intent = new Intent(this, DownloadService.class);
|
||||
intent.putExtra("activityMessenger", activityMessenger);
|
||||
startService(intent);
|
||||
bindService(intent, connection, BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
//Bind downloader service
|
||||
activityMessenger = new Messenger(new IncomingHandler(this));
|
||||
Intent intent = new Intent(this, DownloadService.class);
|
||||
intent.putExtra("activityMessenger", activityMessenger);
|
||||
startService(intent);
|
||||
bindService(intent, connection, 0);
|
||||
connectService();
|
||||
//Get DB
|
||||
DownloadsDatabase dbHelper = new DownloadsDatabase(getApplicationContext());
|
||||
db = dbHelper.getWritableDatabase();
|
||||
@ -274,17 +282,18 @@ public class MainActivity extends FlutterActivity {
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
Log.e(this.getLocalClassName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
//Try reconnect
|
||||
connectService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
//Unbind service on exit
|
||||
if (serviceBound) {
|
||||
unbindService(connection);
|
||||
serviceBound = false;
|
||||
}
|
||||
db.close();
|
||||
}
|
||||
|
||||
@ -294,6 +303,12 @@ public class MainActivity extends FlutterActivity {
|
||||
//Stop server
|
||||
if (streamServer != null)
|
||||
streamServer.stop();
|
||||
|
||||
//Unbind service on exit
|
||||
if (serviceBound) {
|
||||
unbindService(connection);
|
||||
serviceBound = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Connection to download service
|
||||
@ -302,12 +317,14 @@ public class MainActivity extends FlutterActivity {
|
||||
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||||
serviceMessenger = new Messenger(iBinder);
|
||||
serviceBound = true;
|
||||
Log.d("DD", "Service Bound!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
serviceMessenger = null;
|
||||
serviceBound = false;
|
||||
Log.d("DD", "Service UnBound!");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,6 +36,7 @@ class DownloadManager {
|
||||
|
||||
//Start/Resume downloads
|
||||
Future start() async {
|
||||
//Returns whether service is bound or not, the delay is really shitty/hacky way, until i find a real solution
|
||||
await updateServiceSettings();
|
||||
await platform.invokeMethod('start');
|
||||
}
|
||||
|
@ -319,6 +319,8 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
|
||||
//Queue
|
||||
List<MediaItem> _queue = <MediaItem>[];
|
||||
List<MediaItem> _originalQueue;
|
||||
bool _shuffle = false;
|
||||
int _queueIndex = 0;
|
||||
ConcatenatingAudioSource _audioSource;
|
||||
|
||||
@ -471,6 +473,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
//Update buffering state
|
||||
_skipState = AudioProcessingState.skippingToPrevious;
|
||||
|
||||
|
||||
//Normal skip to previous
|
||||
_queueIndex--;
|
||||
await _player.seekToPrevious();
|
||||
@ -542,7 +545,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
if (_skipState != null) return _skipState;
|
||||
//SRC: audio_service example
|
||||
switch (_player.processingState) {
|
||||
case ProcessingState.none:
|
||||
case ProcessingState.idle:
|
||||
return AudioProcessingState.stopped;
|
||||
case ProcessingState.loading:
|
||||
return AudioProcessingState.connecting;
|
||||
@ -586,8 +589,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
_audioSource = ConcatenatingAudioSource(children: sources);
|
||||
//Load in just_audio
|
||||
try {
|
||||
await _player.load(_audioSource, initialPosition: Duration.zero, initialIndex: qi);
|
||||
// await _player.seek(Duration.zero, index: qi);
|
||||
await _player.setAudioSource(_audioSource, initialIndex: qi, initialPosition: Duration.zero);
|
||||
} catch (e) {
|
||||
//Error loading tracks
|
||||
}
|
||||
@ -599,7 +601,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
String url = await _getTrackUrl(mi);
|
||||
if (url == null) return null;
|
||||
if (url.startsWith('http')) return ProgressiveAudioSource(Uri.parse(url));
|
||||
return AudioSource.uri(Uri.parse(url));
|
||||
return AudioSource.uri(Uri.parse(url), tag: mi.id);
|
||||
}
|
||||
|
||||
Future _getTrackUrl(MediaItem mediaItem, {int quality}) async {
|
||||
@ -655,10 +657,26 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
await this._loadQueueFile();
|
||||
//Shuffle
|
||||
if (name == 'shuffle') {
|
||||
_queue.shuffle();
|
||||
AudioServiceBackground.setQueue(_queue);
|
||||
String originalId = mediaItem.id;
|
||||
if (!_shuffle) {
|
||||
_shuffle = true;
|
||||
_originalQueue = List.from(_queue);
|
||||
_queue.shuffle();
|
||||
|
||||
} else {
|
||||
_shuffle = false;
|
||||
_queue = _originalQueue;
|
||||
_originalQueue = null;
|
||||
}
|
||||
|
||||
//Broken
|
||||
// _queueIndex = _queue.indexWhere((mi) => mi.id == originalId);
|
||||
_queueIndex = 0;
|
||||
AudioServiceBackground.setQueue(_queue);
|
||||
AudioServiceBackground.setMediaItem(mediaItem);
|
||||
await _player.stop();
|
||||
await _loadQueue();
|
||||
await _player.play();
|
||||
}
|
||||
//Android auto callback
|
||||
if (name == 'screenAndroidAuto' && _androidAutoCallback != null) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -307,6 +307,11 @@ const language_en_us = {
|
||||
"Episodes": "Episodes",
|
||||
"Show all episodes": "Show all episodes",
|
||||
"Album cover resolution": "Album cover resolution",
|
||||
"WARNING: Resolutions above 1200 aren't officially supported": "WARNING: Resolutions above 1200 aren't officially supported"
|
||||
"WARNING: Resolutions above 1200 aren't officially supported": "WARNING: Resolutions above 1200 aren't officially supported",
|
||||
|
||||
//0.6.8:
|
||||
"Album removed from library!": "Album removed from library!",
|
||||
"Remove offline": "Remove offline",
|
||||
"Playlist removed from library!": "Playlist removed from library!"
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttericon/font_awesome5_icons.dart';
|
||||
@ -18,17 +19,34 @@ import 'cached_image.dart';
|
||||
import 'tiles.dart';
|
||||
import 'menu.dart';
|
||||
|
||||
class AlbumDetails extends StatelessWidget {
|
||||
class AlbumDetails extends StatefulWidget {
|
||||
|
||||
Album album;
|
||||
AlbumDetails(this.album, {Key key}): super(key: key);
|
||||
|
||||
AlbumDetails(this.album);
|
||||
@override
|
||||
_AlbumDetailsState createState() => _AlbumDetailsState();
|
||||
}
|
||||
|
||||
class _AlbumDetailsState extends State<AlbumDetails> {
|
||||
|
||||
Album album;
|
||||
bool _loading = true;
|
||||
bool _error = false;
|
||||
|
||||
Future _loadAlbum() async {
|
||||
//Get album from API, if doesn't have tracks
|
||||
if (this.album.tracks == null || this.album.tracks.length == 0) {
|
||||
this.album = await deezerAPI.album(album.id);
|
||||
try {
|
||||
Album a = await deezerAPI.album(album.id);
|
||||
//Preserve library
|
||||
a.library = album.library;
|
||||
setState(() => album = a);
|
||||
} catch (e) {
|
||||
setState(() => _error = true);
|
||||
}
|
||||
}
|
||||
setState(() => _loading = false);
|
||||
}
|
||||
|
||||
//Get count of CDs in album
|
||||
@ -40,164 +58,176 @@ class AlbumDetails extends StatelessWidget {
|
||||
return c;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
this.album = widget.album;
|
||||
_loadAlbum();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: FutureBuilder(
|
||||
future: _loadAlbum(),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
|
||||
//Wait for data
|
||||
if (snapshot.connectionState != ConnectionState.done) return Center(child: CircularProgressIndicator(),);
|
||||
//On error
|
||||
if (snapshot.hasError) return ErrorScreen();
|
||||
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
//Album art, title, artists
|
||||
Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(height: 8.0,),
|
||||
ZoomableImage(
|
||||
url: album.art.full,
|
||||
width: MediaQuery.of(context).size.width / 2,
|
||||
rounded: true,
|
||||
body: _error ? ErrorScreen() : _loading ? Center(child: CircularProgressIndicator()) :
|
||||
ListView(
|
||||
children: <Widget>[
|
||||
//Album art, title, artists
|
||||
Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(height: 8.0,),
|
||||
ZoomableImage(
|
||||
url: album.art.full,
|
||||
width: MediaQuery.of(context).size.width / 2,
|
||||
rounded: true,
|
||||
),
|
||||
Container(height: 8,),
|
||||
Text(
|
||||
album.title,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
Container(height: 8,),
|
||||
),
|
||||
Text(
|
||||
album.artistString,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryColor
|
||||
),
|
||||
),
|
||||
Container(height: 4.0),
|
||||
if (album.releaseDate != null && album.releaseDate.length >= 4)
|
||||
Text(
|
||||
album.title,
|
||||
album.releaseDate,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
Text(
|
||||
album.artistString,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryColor
|
||||
),
|
||||
),
|
||||
Container(height: 4.0),
|
||||
if (album.releaseDate != null && album.releaseDate.length >= 4)
|
||||
Text(
|
||||
album.releaseDate,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Theme.of(context).disabledColor
|
||||
),
|
||||
),
|
||||
Container(height: 8.0,),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(height: 8.0,),
|
||||
],
|
||||
),
|
||||
FreezerDivider(),
|
||||
//Details
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.audiotrack, size: 32.0,),
|
||||
Container(width: 8.0, height: 42.0,), //Height to adjust card height
|
||||
Text(
|
||||
album.tracks.length.toString(),
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.timelapse, size: 32.0,),
|
||||
Container(width: 8.0,),
|
||||
Text(
|
||||
album.durationString,
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.people, size: 32.0,),
|
||||
Container(width: 8.0,),
|
||||
Text(
|
||||
album.fansString,
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
FreezerDivider(),
|
||||
//Details
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.audiotrack, size: 32.0,),
|
||||
Container(width: 8.0, height: 42.0,), //Height to adjust card height
|
||||
Text(
|
||||
album.tracks.length.toString(),
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.timelapse, size: 32.0,),
|
||||
Container(width: 8.0,),
|
||||
Text(
|
||||
album.durationString,
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.people, size: 32.0,),
|
||||
Container(width: 8.0,),
|
||||
Text(
|
||||
album.fansString,
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
FreezerDivider(),
|
||||
//Options (offline, download...)
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
FlatButton(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.favorite, size: 32),
|
||||
Container(width: 4,),
|
||||
Text('Library'.i18n)
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
),
|
||||
FreezerDivider(),
|
||||
//Options (offline, download...)
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
FlatButton(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon((album.library??false)? Icons.favorite : Icons.favorite_border, size: 32),
|
||||
Container(width: 4,),
|
||||
Text('Library'.i18n)
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
//Add to library
|
||||
if (!album.library) {
|
||||
await deezerAPI.addFavoriteAlbum(album.id);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Added to library'.i18n,
|
||||
msg: 'Added to library'.i18n,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
setState(() => album.library = true);
|
||||
return;
|
||||
}
|
||||
//Remove
|
||||
await deezerAPI.removeAlbum(album.id);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Album removed from library!'.i18n,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
},
|
||||
);
|
||||
setState(() => album.library = false);
|
||||
},
|
||||
),
|
||||
MakeAlbumOffline(album: album),
|
||||
FlatButton(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.file_download, size: 32.0,),
|
||||
Container(width: 4,),
|
||||
Text('Download'.i18n)
|
||||
],
|
||||
),
|
||||
MakeAlbumOffline(album: album),
|
||||
FlatButton(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.file_download, size: 32.0,),
|
||||
Container(width: 4,),
|
||||
Text('Download'.i18n)
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
if (await downloadManager.addOfflineAlbum(album, private: false, context: context) != false)
|
||||
MenuSheet(context).showDownloadStartedToast();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
if (await downloadManager.addOfflineAlbum(album, private: false, context: context) != false)
|
||||
MenuSheet(context).showDownloadStartedToast();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
FreezerDivider(),
|
||||
...List.generate(cdCount, (cdi) {
|
||||
List<Track> tracks = album.tracks.where((t) => (t.diskNumber??1) == cdi + 1).toList();
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Text(
|
||||
'Disk'.i18n.toUpperCase() + ' ${cdi + 1}',
|
||||
style: TextStyle(
|
||||
),
|
||||
FreezerDivider(),
|
||||
...List.generate(cdCount, (cdi) {
|
||||
List<Track> tracks = album.tracks.where((t) => (t.diskNumber??1) == cdi + 1).toList();
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Text(
|
||||
'Disk'.i18n.toUpperCase() + ' ${cdi + 1}',
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
fontWeight: FontWeight.w300
|
||||
),
|
||||
),
|
||||
),
|
||||
...List.generate(tracks.length, (i) => TrackTile(
|
||||
),
|
||||
...List.generate(tracks.length, (i) => TrackTile(
|
||||
tracks[i],
|
||||
onTap: () {
|
||||
playerHelper.playFromAlbum(album, tracks[i].id);
|
||||
@ -206,14 +236,12 @@ class AlbumDetails extends StatelessWidget {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultTrackMenu(tracks[i]);
|
||||
}
|
||||
))
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
))
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -812,200 +840,220 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ListView(
|
||||
body: DraggableScrollbar.rrect(
|
||||
controller: _scrollController,
|
||||
children: <Widget>[
|
||||
Container(height: 4.0,),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
CachedImage(
|
||||
url: playlist.image.full,
|
||||
height: MediaQuery.of(context).size.width / 2 - 8,
|
||||
rounded: true,
|
||||
fullThumb: true,
|
||||
),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width / 2 - 8,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
playlist.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 3,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
Container(height: 4.0),
|
||||
Text(
|
||||
playlist.user.name??'',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontSize: 17.0
|
||||
),
|
||||
),
|
||||
Container(height: 10.0),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.audiotrack,
|
||||
size: 32.0,
|
||||
),
|
||||
Container(width: 8.0,),
|
||||
Text((playlist.trackCount??playlist.tracks.length).toString(), style: TextStyle(fontSize: 16),)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.timelapse,
|
||||
size: 32.0,
|
||||
),
|
||||
Container(width: 8.0,),
|
||||
Text(playlist.durationString, style: TextStyle(fontSize: 16),)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (playlist.description != null && playlist.description.length > 0)
|
||||
FreezerDivider(),
|
||||
if (playlist.description != null && playlist.description.length > 0)
|
||||
Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
playlist.description ?? '',
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
FreezerDivider(),
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
MakePlaylistOffline(playlist),
|
||||
IconButton(
|
||||
icon: Icon(Icons.favorite, size: 32),
|
||||
onPressed: () async {
|
||||
await deezerAPI.addPlaylist(playlist.id);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Added to library'.i18n,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.file_download, size: 32.0,),
|
||||
onPressed: () async {
|
||||
if (await downloadManager.addOfflinePlaylist(playlist, private: false, context: context) != false)
|
||||
MenuSheet(context).showDownloadStartedToast();
|
||||
},
|
||||
),
|
||||
PopupMenuButton(
|
||||
child: Icon(Icons.sort, size: 32.0),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
onSelected: (SortType s) async {
|
||||
if (playlist.tracks.length < playlist.trackCount) {
|
||||
//Preload whole playlist
|
||||
playlist = await deezerAPI.fullPlaylist(playlist.id);
|
||||
}
|
||||
setState(() => _sort.type = s);
|
||||
|
||||
//Save sort type to cache
|
||||
int index = Sorting.index(SortSourceTypes.PLAYLIST, id: playlist.id);
|
||||
if (index == null) {
|
||||
cache.sorts.add(_sort);
|
||||
} else {
|
||||
cache.sorts[index] = _sort;
|
||||
}
|
||||
await cache.save();
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry<SortType>>[
|
||||
PopupMenuItem(
|
||||
value: SortType.DEFAULT,
|
||||
child: Text('Default'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.ALPHABETIC,
|
||||
child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.ARTIST,
|
||||
child: Text('Artist'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.DATE_ADDED,
|
||||
child: Text('Date added'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(_sort.reverse ? FontAwesome5.sort_alpha_up : FontAwesome5.sort_alpha_down),
|
||||
onPressed: () => _reverse(),
|
||||
),
|
||||
Container(width: 4.0)
|
||||
],
|
||||
),
|
||||
),
|
||||
FreezerDivider(),
|
||||
...List.generate(playlist.tracks.length, (i) {
|
||||
Track t = sorted[i];
|
||||
return TrackTile(
|
||||
t,
|
||||
onTap: () {
|
||||
Playlist p = Playlist(
|
||||
title: playlist.title,
|
||||
id: playlist.id,
|
||||
tracks: sorted
|
||||
);
|
||||
playerHelper.playFromPlaylist(p, t.id);
|
||||
},
|
||||
onHold: () {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultTrackMenu(t, options: [
|
||||
(playlist.user.id == deezerAPI.userId) ? m.removeFromPlaylist(t, playlist) : Container(width: 0, height: 0,)
|
||||
]);
|
||||
}
|
||||
);
|
||||
}),
|
||||
if (_loading)
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
children: <Widget>[
|
||||
Container(height: 4.0,),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
CircularProgressIndicator()
|
||||
CachedImage(
|
||||
url: playlist.image.full,
|
||||
height: MediaQuery.of(context).size.width / 2 - 8,
|
||||
rounded: true,
|
||||
fullThumb: true,
|
||||
),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width / 2 - 8,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
playlist.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 3,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
Container(height: 4.0),
|
||||
Text(
|
||||
playlist.user.name??'',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontSize: 17.0
|
||||
),
|
||||
),
|
||||
Container(height: 10.0),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.audiotrack,
|
||||
size: 32.0,
|
||||
),
|
||||
Container(width: 8.0,),
|
||||
Text((playlist.trackCount??playlist.tracks.length).toString(), style: TextStyle(fontSize: 16),)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.timelapse,
|
||||
size: 32.0,
|
||||
),
|
||||
Container(width: 8.0,),
|
||||
Text(playlist.durationString, style: TextStyle(fontSize: 16),)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_error)
|
||||
ErrorScreen()
|
||||
],
|
||||
if (playlist.description != null && playlist.description.length > 0)
|
||||
FreezerDivider(),
|
||||
if (playlist.description != null && playlist.description.length > 0)
|
||||
Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
playlist.description ?? '',
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
FreezerDivider(),
|
||||
Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
MakePlaylistOffline(playlist),
|
||||
|
||||
if (playlist.user.name != deezerAPI.userName)
|
||||
IconButton(
|
||||
icon: Icon(playlist.library ? Icons.favorite : Icons.favorite_outline, size: 32),
|
||||
onPressed: () async {
|
||||
//Add to library
|
||||
if (!playlist.library) {
|
||||
await deezerAPI.addPlaylist(playlist.id);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Added to library'.i18n,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
setState(() => playlist.library = true);
|
||||
return;
|
||||
}
|
||||
//Remove
|
||||
await deezerAPI.removePlaylist(playlist.id);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Playlist removed from library!'.i18n,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM
|
||||
);
|
||||
setState(() => playlist.library = false);
|
||||
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.file_download, size: 32.0,),
|
||||
onPressed: () async {
|
||||
if (await downloadManager.addOfflinePlaylist(playlist, private: false, context: context) != false)
|
||||
MenuSheet(context).showDownloadStartedToast();
|
||||
},
|
||||
),
|
||||
PopupMenuButton(
|
||||
child: Icon(Icons.sort, size: 32.0),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
onSelected: (SortType s) async {
|
||||
if (playlist.tracks.length < playlist.trackCount) {
|
||||
//Preload whole playlist
|
||||
playlist = await deezerAPI.fullPlaylist(playlist.id);
|
||||
}
|
||||
setState(() => _sort.type = s);
|
||||
|
||||
//Save sort type to cache
|
||||
int index = Sorting.index(SortSourceTypes.PLAYLIST, id: playlist.id);
|
||||
if (index == null) {
|
||||
cache.sorts.add(_sort);
|
||||
} else {
|
||||
cache.sorts[index] = _sort;
|
||||
}
|
||||
await cache.save();
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry<SortType>>[
|
||||
PopupMenuItem(
|
||||
value: SortType.DEFAULT,
|
||||
child: Text('Default'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.ALPHABETIC,
|
||||
child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.ARTIST,
|
||||
child: Text('Artist'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.DATE_ADDED,
|
||||
child: Text('Date added'.i18n, style: popupMenuTextStyle()),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(_sort.reverse ? FontAwesome5.sort_alpha_up : FontAwesome5.sort_alpha_down),
|
||||
onPressed: () => _reverse(),
|
||||
),
|
||||
Container(width: 4.0)
|
||||
],
|
||||
),
|
||||
),
|
||||
FreezerDivider(),
|
||||
...List.generate(playlist.tracks.length, (i) {
|
||||
Track t = sorted[i];
|
||||
return TrackTile(
|
||||
t,
|
||||
onTap: () {
|
||||
Playlist p = Playlist(
|
||||
title: playlist.title,
|
||||
id: playlist.id,
|
||||
tracks: sorted
|
||||
);
|
||||
playerHelper.playFromPlaylist(p, t.id);
|
||||
},
|
||||
onHold: () {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultTrackMenu(t, options: [
|
||||
(playlist.user.id == deezerAPI.userId) ? m.removeFromPlaylist(t, playlist) : Container(width: 0, height: 0,)
|
||||
]);
|
||||
}
|
||||
);
|
||||
}),
|
||||
if (_loading)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
CircularProgressIndicator()
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_error)
|
||||
ErrorScreen()
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
@ -12,18 +13,19 @@ import 'package:freezer/api/player.dart';
|
||||
import 'package:freezer/ui/details_screens.dart';
|
||||
import 'package:freezer/ui/error.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/ui/cached_image.dart';
|
||||
import 'package:numberpicker/numberpicker.dart';
|
||||
import 'package:share/share.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../api/definitions.dart';
|
||||
import 'cached_image.dart';
|
||||
|
||||
class MenuSheet {
|
||||
|
||||
BuildContext context;
|
||||
Function navigateCallback;
|
||||
|
||||
MenuSheet(this.context);
|
||||
MenuSheet(this.context, {this.navigateCallback});
|
||||
|
||||
//===================
|
||||
// DEFAULT
|
||||
@ -273,9 +275,13 @@ class MenuSheet {
|
||||
leading: Icon(Icons.recent_actors),
|
||||
onTap: () {
|
||||
_close();
|
||||
Navigator.of(context).push(
|
||||
navigatorKey.currentState.push(
|
||||
MaterialPageRoute(builder: (context) => ArtistDetails(a))
|
||||
);
|
||||
|
||||
if (this.navigateCallback != null) {
|
||||
this.navigateCallback();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -288,9 +294,13 @@ class MenuSheet {
|
||||
leading: Icon(Icons.album),
|
||||
onTap: () {
|
||||
_close();
|
||||
Navigator.of(context).push(
|
||||
navigatorKey.currentState.push(
|
||||
MaterialPageRoute(builder: (context) => AlbumDetails(a))
|
||||
);
|
||||
|
||||
if (this.navigateCallback != null) {
|
||||
this.navigateCallback();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -303,12 +313,22 @@ class MenuSheet {
|
||||
},
|
||||
);
|
||||
|
||||
Widget offlineTrack(Track track) => ListTile(
|
||||
title: Text('Offline'.i18n),
|
||||
leading: Icon(Icons.offline_pin),
|
||||
onTap: () async {
|
||||
await downloadManager.addOfflineTrack(track, private: true, context: context);
|
||||
_close();
|
||||
Widget offlineTrack(Track track) => FutureBuilder(
|
||||
future: downloadManager.checkOffline(track: track),
|
||||
builder: (context, snapshot) {
|
||||
bool isOffline = snapshot.data??(track.offline??false);
|
||||
return ListTile(
|
||||
title: Text(isOffline ? 'Remove offline'.i18n : 'Offline'.i18n),
|
||||
leading: Icon(Icons.offline_pin),
|
||||
onTap: () async {
|
||||
if (isOffline) {
|
||||
await downloadManager.removeOfflineTracks([track]);
|
||||
} else {
|
||||
await downloadManager.addOfflineTrack(track, private: true, context: context);
|
||||
}
|
||||
_close();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -51,16 +51,16 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||
|
||||
//Update notification
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
statusBarColor: palette.dominantColor.color.withOpacity(0.5)
|
||||
statusBarColor: palette.dominantColor.color.withOpacity(0.7)
|
||||
));
|
||||
|
||||
setState(() => _bgGradient = LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [palette.dominantColor.color.withOpacity(0.5), Color.fromARGB(0, 0, 0, 0)],
|
||||
colors: [palette.dominantColor.color.withOpacity(0.7), Color.fromARGB(0, 0, 0, 0)],
|
||||
stops: [
|
||||
0.0,
|
||||
0.4
|
||||
0.6
|
||||
]
|
||||
));
|
||||
}
|
||||
@ -408,7 +408,9 @@ class PlayerMenuButton extends StatelessWidget {
|
||||
icon: Icon(Icons.more_vert, size: ScreenUtil().setWidth(46)),
|
||||
onPressed: () {
|
||||
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
|
||||
MenuSheet m = MenuSheet(context);
|
||||
MenuSheet m = MenuSheet(context, navigateCallback: () {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
if (AudioService.currentMediaItem.extras['show'] == null)
|
||||
m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]);
|
||||
else
|
||||
|
@ -1362,7 +1362,7 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
||||
subtitle: Text('Official Discord server'.i18n),
|
||||
leading: Icon(FontAwesome5.discord, color: Color(0xff7289da), size: 36.0),
|
||||
onTap: () {
|
||||
launch('https://discord.gg/7ap654Tp3z');
|
||||
launch('https://discord.gg/qwJpa3r4dQ');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
@ -1373,6 +1373,14 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
||||
launch('https://git.rip/freezer/');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Donate'),
|
||||
subtitle: Text('You should rather support your favorite artists, instead of this app!'),
|
||||
leading: Icon(FontAwesome5.paypal, color: Colors.blue, size: 36.0),
|
||||
onTap: () {
|
||||
launch('https://paypal.me/exttex');
|
||||
},
|
||||
),
|
||||
FreezerDivider(),
|
||||
ListTile(
|
||||
title: Text('exttex'),
|
||||
|
@ -517,21 +517,21 @@ packages:
|
||||
name: just_audio
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.6"
|
||||
version: "0.6.1"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "2.0.0"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
version: "0.2.0"
|
||||
language_pickers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -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.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.6.7+1
|
||||
version: 0.6.8+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.8.0 <3.0.0"
|
||||
@ -79,7 +79,7 @@ dependencies:
|
||||
audio_session: ^0.0.9
|
||||
audio_service:
|
||||
path: ./audio_service
|
||||
just_audio: ^0.5.6
|
||||
just_audio: 0.6.1
|
||||
# path: ./just_audio
|
||||
|
||||
# cupertino_icons: ^0.1.3
|
||||
|
Loading…
Reference in New Issue
Block a user