Remove listened indicator and increase the color difference
Improve download manage page, support fliters
This commit is contained in:
parent
935566b304
commit
f4b56938ae
|
@ -269,14 +269,15 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 20.0, right: 20, bottom: 10),
|
left: 20.0, right: 20, bottom: 10),
|
||||||
defaultTextStyle:
|
defaultTextStyle:
|
||||||
GoogleFonts.libreBaskerville(
|
// GoogleFonts.libreBaskerville(
|
||||||
|
GoogleFonts.martel(
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
height: 1.8,
|
height: 1.8,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
data: _description,
|
data: _description,
|
||||||
linkStyle: TextStyle(
|
linkStyle: TextStyle(
|
||||||
color: Theme.of(context).accentColor,
|
color: context.accentColor,
|
||||||
// decoration: TextDecoration.underline,
|
// decoration: TextDecoration.underline,
|
||||||
textBaseline: TextBaseline.ideographic),
|
textBaseline: TextBaseline.ideographic),
|
||||||
onLinkTap: (url) {
|
onLinkTap: (url) {
|
||||||
|
@ -296,7 +297,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||||
_launchUrl(link.url);
|
_launchUrl(link.url);
|
||||||
},
|
},
|
||||||
text: _description,
|
text: _description,
|
||||||
style: GoogleFonts.libreBaskerville(
|
style: GoogleFonts.martel(
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
height: 1.8,
|
height: 1.8,
|
||||||
),
|
),
|
||||||
|
@ -384,8 +385,6 @@ class MenuBar extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MenuBarState extends State<MenuBar> {
|
class _MenuBarState extends State<MenuBar> {
|
||||||
bool _liked = false;
|
|
||||||
|
|
||||||
Future<PlayHistory> getPosition(EpisodeBrief episode) async {
|
Future<PlayHistory> getPosition(EpisodeBrief episode) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
return await dbHelper.getPosition(episode);
|
return await dbHelper.getPosition(episode);
|
||||||
|
@ -394,17 +393,14 @@ class _MenuBarState extends State<MenuBar> {
|
||||||
Future<int> saveLiked(String url) async {
|
Future<int> saveLiked(String url) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
int result = await dbHelper.setLiked(url);
|
int result = await dbHelper.setLiked(url);
|
||||||
if (result == 1 && mounted) setState(() => _liked = true);
|
setState(() {});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> setUnliked(String url) async {
|
Future<int> setUnliked(String url) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
int result = await dbHelper.setUniked(url);
|
int result = await dbHelper.setUniked(url);
|
||||||
if (result == 1 && mounted)
|
setState(() {});
|
||||||
setState(() {
|
|
||||||
_liked = false;
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,12 +414,6 @@ class _MenuBarState extends State<MenuBar> {
|
||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_liked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material(
|
Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
@ -495,7 +485,7 @@ class _MenuBarState extends State<MenuBar> {
|
||||||
future: _isLiked(widget.episodeItem),
|
future: _isLiked(widget.episodeItem),
|
||||||
initialData: false,
|
initialData: false,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
return (!snapshot.data && !_liked)
|
return (!snapshot.data)
|
||||||
? _buttonOnMenu(
|
? _buttonOnMenu(
|
||||||
Icon(
|
Icon(
|
||||||
Icons.favorite_border,
|
Icons.favorite_border,
|
||||||
|
@ -508,23 +498,13 @@ class _MenuBarState extends State<MenuBar> {
|
||||||
await Future.delayed(Duration(seconds: 2));
|
await Future.delayed(Duration(seconds: 2));
|
||||||
_overlayEntry?.remove();
|
_overlayEntry?.remove();
|
||||||
})
|
})
|
||||||
: (snapshot.data && !_liked)
|
|
||||||
? _buttonOnMenu(
|
|
||||||
Icon(
|
|
||||||
Icons.favorite,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
() => setUnliked(
|
|
||||||
widget.episodeItem.enclosureUrl))
|
|
||||||
: _buttonOnMenu(
|
: _buttonOnMenu(
|
||||||
Icon(
|
Icon(
|
||||||
Icons.favorite,
|
Icons.favorite,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
() {
|
() =>
|
||||||
setUnliked(widget.episodeItem.enclosureUrl);
|
setUnliked(widget.episodeItem.enclosureUrl));
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
DownloadButton(episode: widget.episodeItem),
|
DownloadButton(episode: widget.episodeItem),
|
||||||
|
@ -534,7 +514,7 @@ class _MenuBarState extends State<MenuBar> {
|
||||||
return data.contains(widget.episodeItem)
|
return data.contains(widget.episodeItem)
|
||||||
? _buttonOnMenu(
|
? _buttonOnMenu(
|
||||||
Icon(Icons.playlist_add_check,
|
Icon(Icons.playlist_add_check,
|
||||||
color: Theme.of(context).accentColor), () {
|
color: context.accentColor), () {
|
||||||
audio.delFromPlaylist(widget.episodeItem);
|
audio.delFromPlaylist(widget.episodeItem);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: 'Removed from playlist',
|
msg: 'Removed from playlist',
|
||||||
|
|
|
@ -376,14 +376,10 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PodcastPreview extends StatefulWidget {
|
class PodcastPreview extends StatelessWidget {
|
||||||
final PodcastLocal podcastLocal;
|
final PodcastLocal podcastLocal;
|
||||||
PodcastPreview({this.podcastLocal, Key key}) : super(key: key);
|
PodcastPreview({this.podcastLocal, Key key}) : super(key: key);
|
||||||
@override
|
|
||||||
_PodcastPreviewState createState() => _PodcastPreviewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PodcastPreviewState extends State<PodcastPreview> {
|
|
||||||
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<EpisodeBrief> episodes = await dbHelper.getRssItemTop(podcastLocal.id);
|
List<EpisodeBrief> episodes = await dbHelper.getRssItemTop(podcastLocal.id);
|
||||||
|
@ -393,8 +389,8 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||||
? widget.podcastLocal.primaryColor.colorizedark()
|
? podcastLocal.primaryColor.colorizedark()
|
||||||
: widget.podcastLocal.primaryColor.colorizeLight();
|
: podcastLocal.primaryColor.colorizeLight();
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -402,7 +398,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
selector: (_, worker) => worker.created,
|
selector: (_, worker) => worker.created,
|
||||||
builder: (context, created, child) {
|
builder: (context, created, child) {
|
||||||
return FutureBuilder<List<EpisodeBrief>>(
|
return FutureBuilder<List<EpisodeBrief>>(
|
||||||
future: _getRssItemTop(widget.podcastLocal),
|
future: _getRssItemTop(podcastLocal),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
print(snapshot.error);
|
print(snapshot.error);
|
||||||
|
@ -411,7 +407,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
return (snapshot.hasData)
|
return (snapshot.hasData)
|
||||||
? ShowEpisode(
|
? ShowEpisode(
|
||||||
episodes: snapshot.data,
|
episodes: snapshot.data,
|
||||||
podcastLocal: widget.podcastLocal,
|
podcastLocal: podcastLocal,
|
||||||
)
|
)
|
||||||
: Container(
|
: Container(
|
||||||
padding: EdgeInsets.all(5.0),
|
padding: EdgeInsets.all(5.0),
|
||||||
|
@ -429,7 +425,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 4,
|
flex: 4,
|
||||||
child: Text(widget.podcastLocal.title,
|
child: Text(podcastLocal.title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.visible,
|
overflow: TextOverflow.visible,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, color: _c)),
|
style: TextStyle(fontWeight: FontWeight.bold, color: _c)),
|
||||||
|
@ -450,11 +446,11 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
context,
|
context,
|
||||||
SlideLeftHideRoute(
|
SlideLeftHideRoute(
|
||||||
transitionPage: PodcastDetail(
|
transitionPage: PodcastDetail(
|
||||||
podcastLocal: widget.podcastLocal,
|
podcastLocal: podcastLocal,
|
||||||
hide: playerRunning,
|
hide: playerRunning,
|
||||||
),
|
),
|
||||||
page: PodcastDetail(
|
page: PodcastDetail(
|
||||||
podcastLocal: widget.podcastLocal,
|
podcastLocal: podcastLocal,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -606,7 +602,7 @@ class ShowEpisode extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
),
|
),
|
||||||
isListened > 0.95
|
isListened > 0
|
||||||
? Text('Listened',
|
? Text('Listened',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.textColor.withOpacity(0.5)))
|
color: context.textColor.withOpacity(0.5)))
|
||||||
|
@ -671,7 +667,7 @@ class ShowEpisode extends StatelessWidget {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (isListened < 0.95) {
|
if (isListened < 1) {
|
||||||
await _markListened(episode);
|
await _markListened(episode);
|
||||||
audio.setEpisodeState = true;
|
audio.setEpisodeState = true;
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
|
|
|
@ -24,6 +24,7 @@ const String favLayoutKey = 'favLayoutKey';
|
||||||
const String downloadLayoutKey = 'downloadLayoutKey';
|
const String downloadLayoutKey = 'downloadLayoutKey';
|
||||||
const String autoDownloadNetworkKey = 'autoDownloadNetwork';
|
const String autoDownloadNetworkKey = 'autoDownloadNetwork';
|
||||||
const String episodePopupMenuKey = 'episodePopupMenuKey';
|
const String episodePopupMenuKey = 'episodePopupMenuKey';
|
||||||
|
const String autoDeleteKey = 'autoDeleteKey';
|
||||||
|
|
||||||
class KeyValueStorage {
|
class KeyValueStorage {
|
||||||
final String key;
|
final String key;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
@ -272,9 +274,7 @@ class DBHelper {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
List<Map> list =
|
List<Map> list =
|
||||||
await dbClient.rawQuery("""SELECT listen_time FROM PlayHistory
|
await dbClient.rawQuery("SELECT listen_time FROM PlayHistory WHERE enclosure_url = ?", [url]);
|
||||||
WHERE enclosure_url = ?
|
|
||||||
""", [url]);
|
|
||||||
if (list.length == 0)
|
if (list.length == 0)
|
||||||
return 0;
|
return 0;
|
||||||
else {
|
else {
|
||||||
|
@ -708,35 +708,35 @@ class DBHelper {
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<EpisodeBrief> getRssItemDownload(String url) async {
|
//Future<EpisodeBrief> getRssItemDownload(String url) async {
|
||||||
var dbClient = await database;
|
// var dbClient = await database;
|
||||||
EpisodeBrief episode;
|
// EpisodeBrief episode;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
// List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
// """SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
// E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
// E.downloaded, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
// FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
where E.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""",
|
// where E.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""",
|
||||||
[url]);
|
// [url]);
|
||||||
|
|
||||||
if (list != null)
|
// if (list != null)
|
||||||
episode = EpisodeBrief(
|
// episode = EpisodeBrief(
|
||||||
list.first['title'],
|
// list.first['title'],
|
||||||
list.first['enclosure_url'],
|
// list.first['enclosure_url'],
|
||||||
list.first['enclosure_length'],
|
// list.first['enclosure_length'],
|
||||||
list.first['milliseconds'],
|
// list.first['milliseconds'],
|
||||||
list.first['feed_title'],
|
// list.first['feed_title'],
|
||||||
list.first['primaryColor'],
|
// list.first['primaryColor'],
|
||||||
list.first['liked'],
|
// list.first['liked'],
|
||||||
list.first['downloaded'],
|
// list.first['downloaded'],
|
||||||
list.first['duration'],
|
// list.first['duration'],
|
||||||
list.first['explicit'],
|
// list.first['explicit'],
|
||||||
list.first['imagePath'],
|
// list.first['imagePath'],
|
||||||
list.first['media_id'],
|
// list.first['media_id'],
|
||||||
list.first['is_new'],
|
// list.first['is_new'],
|
||||||
list.first['skip_seconds']);
|
// list.first['skip_seconds']);
|
||||||
return episode;
|
// return episode;
|
||||||
}
|
//}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> getRecentRssItem(int top) async {
|
Future<List<EpisodeBrief>> getRecentRssItem(int top) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
|
@ -861,6 +861,105 @@ class DBHelper {
|
||||||
// }
|
// }
|
||||||
// return episodes;
|
// return episodes;
|
||||||
//}
|
//}
|
||||||
|
Future<List<EpisodeBrief>> getOutdatedEpisode(int days) async {
|
||||||
|
var dbClient = await database;
|
||||||
|
List<EpisodeBrief> episodes = [];
|
||||||
|
if (days > 0) {
|
||||||
|
int deadline =
|
||||||
|
DateTime.now().subtract(Duration(days: days)).millisecondsSinceEpoch;
|
||||||
|
List<Map> list = await dbClient
|
||||||
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
|
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||||
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE E.download_date < ? AND E.enclosure_url != E.media_id
|
||||||
|
ORDER BY E.milliseconds DESC""", [deadline]);
|
||||||
|
for (int x = 0; x < list.length; x++) {
|
||||||
|
episodes.add(EpisodeBrief(
|
||||||
|
list[x]['title'],
|
||||||
|
list[x]['enclosure_url'],
|
||||||
|
list[x]['enclosure_length'],
|
||||||
|
list[x]['milliseconds'],
|
||||||
|
list[x]['feed_title'],
|
||||||
|
list[x]['primaryColor'],
|
||||||
|
list[x]['liked'],
|
||||||
|
list[x]['downloaded'],
|
||||||
|
list[x]['duration'],
|
||||||
|
list[x]['explicit'],
|
||||||
|
list[x]['imagePath'],
|
||||||
|
list[x]['media_id'],
|
||||||
|
list[x]['is_new'],
|
||||||
|
list[x]['skip_seconds']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return episodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<EpisodeBrief>> getDownloadedEpisode(int mode) async {
|
||||||
|
var dbClient = await database;
|
||||||
|
List<EpisodeBrief> episodes = [];
|
||||||
|
List<Map> list;
|
||||||
|
if (mode == 0)
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||||
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
|
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||||
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE E.enclosure_url != E.media_id
|
||||||
|
ORDER BY E.download_date DESC""",
|
||||||
|
);
|
||||||
|
else if (mode == 1)
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||||
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
|
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||||
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE E.enclosure_url != E.media_id
|
||||||
|
ORDER BY E.download_date ASC""",
|
||||||
|
);
|
||||||
|
else if (mode == 2)
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||||
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
|
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||||
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE E.enclosure_url != E.media_id
|
||||||
|
ORDER BY E.enclosure_length DESC""",
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int x = 0; x < list.length; x++) {
|
||||||
|
int size;
|
||||||
|
if (list[x]['enclosure_length'] == null ||
|
||||||
|
list[x]['enclosure_length'] == 0) {
|
||||||
|
String uri = list[x]['media_id'];
|
||||||
|
FileStat fileStat = await File(uri.substring(6)).stat();
|
||||||
|
size = fileStat.size;
|
||||||
|
await dbClient.rawUpdate(
|
||||||
|
"UPDATE Episodes SET enclosure_length = ?, WHERE media_id = ?",
|
||||||
|
[size, uri]);
|
||||||
|
} else
|
||||||
|
size = list[x]['enclosure_length'];
|
||||||
|
episodes.add(
|
||||||
|
EpisodeBrief(
|
||||||
|
list[x]['title'],
|
||||||
|
list[x]['enclosure_url'],
|
||||||
|
size,
|
||||||
|
list[x]['milliseconds'],
|
||||||
|
list[x]['feed_title'],
|
||||||
|
list[x]['primaryColor'],
|
||||||
|
list[x]['liked'],
|
||||||
|
list[x]['downloaded'],
|
||||||
|
list[x]['duration'],
|
||||||
|
list[x]['explicit'],
|
||||||
|
list[x]['imagePath'],
|
||||||
|
list[x]['media_id'],
|
||||||
|
list[x]['is_new'],
|
||||||
|
list[x]['skip_seconds'],
|
||||||
|
downloadDate: list[x]['download_date']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return episodes;
|
||||||
|
}
|
||||||
|
|
||||||
Future<int> removeAllNewMark() async {
|
Future<int> removeAllNewMark() async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
|
@ -995,8 +1094,8 @@ class DBHelper {
|
||||||
|
|
||||||
Future<bool> isDownloaded(String url) async {
|
Future<bool> isDownloaded(String url) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient.rawQuery(
|
||||||
.rawQuery("SELECT downloaded FROM Episodes WHERE enclosure_url = ?", [url]);
|
"SELECT downloaded FROM Episodes WHERE enclosure_url = ?", [url]);
|
||||||
return list.first['downloaded'] == 'ND' ? false : true;
|
return list.first['downloaded'] == 'ND' ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,12 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
import '../type/episodebrief.dart';
|
import '../type/episodebrief.dart';
|
||||||
|
import '../util/context_extension.dart';
|
||||||
|
import '../state/download_state.dart';
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
class DownloadsManage extends StatefulWidget {
|
class DownloadsManage extends StatefulWidget {
|
||||||
|
@ -18,25 +21,23 @@ class DownloadsManage extends StatefulWidget {
|
||||||
class _DownloadsManageState extends State<DownloadsManage> {
|
class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
//Downloaded size
|
//Downloaded size
|
||||||
int _size;
|
int _size;
|
||||||
|
int _mode;
|
||||||
//Downloaded files
|
//Downloaded files
|
||||||
int _fileNum;
|
int _fileNum;
|
||||||
bool _loadEpisodes;
|
|
||||||
bool _clearing;
|
bool _clearing;
|
||||||
|
bool _onlyListened;
|
||||||
List<EpisodeBrief> _selectedList;
|
List<EpisodeBrief> _selectedList;
|
||||||
List<EpisodeBrief> _episodes = [];
|
|
||||||
|
|
||||||
_getDownloadedRssItem() async {
|
Future<List<EpisodeBrief>> _getDownloadedEpisode(int mode) async {
|
||||||
_episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
final tasks = await FlutterDownloader.loadTasksWithRawQuery(
|
|
||||||
query: "SELECT * FROM task WHERE status = 3");
|
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
await Future.forEach(tasks, (task) async {
|
episodes = await dbHelper.getDownloadedEpisode(mode);
|
||||||
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(task.url);
|
return episodes;
|
||||||
_episodes.add(episode);
|
}
|
||||||
});
|
|
||||||
setState(() {
|
Future<int> _isListened(EpisodeBrief episode) async {
|
||||||
_loadEpisodes = true;
|
DBHelper dbHelper = DBHelper();
|
||||||
});
|
return await dbHelper.isListened(episode.enclosureUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getStorageSize() async {
|
_getStorageSize() async {
|
||||||
|
@ -58,12 +59,13 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
_delSelectedEpisodes() async {
|
_delSelectedEpisodes() async {
|
||||||
setState(() => _clearing = true);
|
setState(() => _clearing = true);
|
||||||
await Future.forEach(_selectedList, (EpisodeBrief episode) async {
|
await Future.forEach(_selectedList, (EpisodeBrief episode) async {
|
||||||
await FlutterDownloader.remove(
|
var downloader = Provider.of<DownloadState>(context, listen: false);
|
||||||
taskId: episode.downloaded, shouldDeleteContent: true);
|
await downloader.removeTask(episode);
|
||||||
var dbHelper = DBHelper();
|
// await FlutterDownloader.remove(
|
||||||
await dbHelper.delDownloaded(episode.enclosureUrl);
|
// taskId: episode.downloaded, shouldDeleteContent: true);
|
||||||
setState(() =>
|
// var dbHelper = DBHelper();
|
||||||
_episodes.removeWhere((e) => e.enclosureUrl == episode.enclosureUrl));
|
// await dbHelper.delDownloaded(episode.enclosureUrl);
|
||||||
|
setState(() {});
|
||||||
});
|
});
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -90,10 +92,10 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_clearing = false;
|
_clearing = false;
|
||||||
_loadEpisodes = false;
|
|
||||||
_selectedList = [];
|
_selectedList = [];
|
||||||
|
_mode = 0;
|
||||||
|
_onlyListened = false;
|
||||||
_getStorageSize();
|
_getStorageSize();
|
||||||
_getDownloadedRssItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -107,9 +109,8 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Downloads'),
|
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: context.primaryColor,
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
@ -119,13 +120,14 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
),
|
|
||||||
Container(
|
Container(
|
||||||
height: 100.0,
|
height: 140.0,
|
||||||
padding: EdgeInsets.only(bottom: 20, left: 60),
|
color: context.primaryColor,
|
||||||
alignment: Alignment.centerLeft,
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 20),
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Total ',
|
text: 'Total ',
|
||||||
|
@ -143,7 +145,9 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _fileNum < 2 ? ' episode' : ' episodes ',
|
text: _fileNum < 2
|
||||||
|
? ' episode'
|
||||||
|
: ' episodes ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).accentColor,
|
color: Theme.of(context).accentColor,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
|
@ -166,22 +170,127 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_loadEpisodes
|
Spacer(),
|
||||||
? Expanded(
|
SizedBox(
|
||||||
child: ListView.builder(
|
height: 40,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: PopupMenuButton<int>(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10))),
|
||||||
|
elevation: 1,
|
||||||
|
tooltip: 'Sort By',
|
||||||
|
child: Container(
|
||||||
|
height: 40,
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Sory by'),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 5),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
_mode == 0
|
||||||
|
? LineIcons
|
||||||
|
.hourglass_start_solid
|
||||||
|
: _mode == 1
|
||||||
|
? LineIcons
|
||||||
|
.hourglass_half_solid
|
||||||
|
: LineIcons.save,
|
||||||
|
size: 18,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 0,
|
||||||
|
child: Text('Newest first'),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 1,
|
||||||
|
child: Text('Oldest first'),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 2,
|
||||||
|
child: Text('Size'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSelected: (value) {
|
||||||
|
if (value == 0)
|
||||||
|
setState(() => _mode = 0);
|
||||||
|
else if (value == 1)
|
||||||
|
setState(() => _mode = 1);
|
||||||
|
else if (value == 2)
|
||||||
|
setState(() => _mode = 2);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
//Spacer(),
|
||||||
|
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_onlyListened = !_onlyListened;
|
||||||
|
}),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
),
|
||||||
|
Text('Listened Only'),
|
||||||
|
Checkbox(
|
||||||
|
value: _onlyListened,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_onlyListened = value;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: FutureBuilder<List<EpisodeBrief>>(
|
||||||
|
future: _getDownloadedEpisode(_mode),
|
||||||
|
initialData: [],
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
var _episodes = snapshot.data;
|
||||||
|
return ListView.builder(
|
||||||
itemCount: _episodes.length,
|
itemCount: _episodes.length,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Column(
|
return FutureBuilder(
|
||||||
|
future: _isListened(_episodes[index]),
|
||||||
|
initialData: 0,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return (_onlyListened &&
|
||||||
|
snapshot.data > 0)
|
||||||
|
? Center()
|
||||||
|
: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (_selectedList
|
if (_selectedList.contains(
|
||||||
.contains(_episodes[index])) {
|
_episodes[index])) {
|
||||||
setState(() => _selectedList
|
setState(() => _selectedList
|
||||||
.removeWhere((episode) =>
|
.removeWhere((episode) =>
|
||||||
episode.enclosureUrl ==
|
episode
|
||||||
|
.enclosureUrl ==
|
||||||
_episodes[index]
|
_episodes[index]
|
||||||
.enclosureUrl));
|
.enclosureUrl));
|
||||||
} else {
|
} else {
|
||||||
|
@ -196,9 +305,15 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
title: Text(
|
title: Text(
|
||||||
_episodes[index].title,
|
_episodes[index].title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow:
|
||||||
|
TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
subtitle: _episodes[index]
|
subtitle: Row(
|
||||||
|
children: [
|
||||||
|
Text(_episodes[index]
|
||||||
|
.downloadDateToString()),
|
||||||
|
SizedBox(width: 20),
|
||||||
|
_episodes[index]
|
||||||
.enclosureLength !=
|
.enclosureLength !=
|
||||||
0
|
0
|
||||||
? Text(((_episodes[index]
|
? Text(((_episodes[index]
|
||||||
|
@ -206,18 +321,25 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
1000000)
|
1000000)
|
||||||
.toString() +
|
.toString() +
|
||||||
' Mb')
|
' Mb')
|
||||||
: Center(),
|
: SizedBox(
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
trailing: Checkbox(
|
trailing: Checkbox(
|
||||||
value: _selectedList
|
value: _selectedList.contains(
|
||||||
.contains(_episodes[index]),
|
_episodes[index]),
|
||||||
onChanged: (bool boo) {
|
onChanged: (bool boo) {
|
||||||
if (boo) {
|
if (boo) {
|
||||||
setState(() => _selectedList
|
setState(() =>
|
||||||
.add(_episodes[index]));
|
_selectedList.add(
|
||||||
|
_episodes[
|
||||||
|
index]));
|
||||||
} else {
|
} else {
|
||||||
setState(() => _selectedList
|
setState(() => _selectedList
|
||||||
.removeWhere((episode) =>
|
.removeWhere((episode) =>
|
||||||
episode.enclosureUrl ==
|
episode
|
||||||
|
.enclosureUrl ==
|
||||||
_episodes[index]
|
_episodes[index]
|
||||||
.enclosureUrl));
|
.enclosureUrl));
|
||||||
}
|
}
|
||||||
|
@ -229,15 +351,17 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: CircularProgressIndicator(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AnimatedPositioned(
|
AnimatedPositioned(
|
||||||
duration: Duration(milliseconds: 800),
|
duration: Duration(milliseconds: 800),
|
||||||
curve: Curves.elasticInOut,
|
curve: Curves.elasticInOut,
|
||||||
left: MediaQuery.of(context).size.width / 2 - 50,
|
left: context.width / 2 - 50,
|
||||||
bottom: _selectedList.length == 0 ? -100 : 30,
|
bottom: _selectedList.length == 0 ? -100 : 30,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => _delSelectedEpisodes(),
|
onTap: () => _delSelectedEpisodes(),
|
||||||
|
|
|
@ -300,14 +300,13 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return snapshot.hasData
|
return snapshot.hasData
|
||||||
? ListView.builder(
|
? ListView.builder(
|
||||||
shrinkWrap: true,
|
// shrinkWrap: true,
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
itemCount: snapshot.data.length,
|
itemCount: snapshot.data.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
bool _status = snapshot.data[index].status;
|
bool _status = snapshot.data[index].status;
|
||||||
return Container(
|
return Container(
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: context.scaffoldBackgroundColor,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|
|
@ -180,7 +180,6 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
||||||
text: 'Favorite tab', key: favLayoutKey),
|
text: 'Favorite tab', key: favLayoutKey),
|
||||||
_setDefaultGridView(context,
|
_setDefaultGridView(context,
|
||||||
text: 'Downlaod tab', key: downloadLayoutKey),
|
text: 'Downlaod tab', key: downloadLayoutKey),
|
||||||
Divider(height: 2),
|
|
||||||
]),
|
]),
|
||||||
Divider(height: 2)
|
Divider(height: 2)
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -21,7 +21,8 @@ List<Libries> google = [
|
||||||
List<Libries> fonts = [
|
List<Libries> fonts = [
|
||||||
Libries('Libre Baskerville', font,
|
Libries('Libre Baskerville', font,
|
||||||
"https://fonts.google.com/specimen/Libre+Baskerville"),
|
"https://fonts.google.com/specimen/Libre+Baskerville"),
|
||||||
Libries('Teko', font, "https://fonts.google.com/specimen/Teko")
|
Libries('Teko', font, "https://fonts.google.com/specimen/Teko"),
|
||||||
|
Libries('Martel', font, "https://fonts.google.com/specimen/Martel")
|
||||||
];
|
];
|
||||||
|
|
||||||
List<Libries> plugins = [
|
List<Libries> plugins = [
|
||||||
|
@ -68,5 +69,6 @@ List<Libries> plugins = [
|
||||||
Libries('Rxdart', apacheLicense, 'https://pub.dev/packages/rxdart'),
|
Libries('Rxdart', apacheLicense, 'https://pub.dev/packages/rxdart'),
|
||||||
Libries('flutter_isolate', mit, 'https://pub.dev/packages/flutter_isolate'),
|
Libries('flutter_isolate', mit, 'https://pub.dev/packages/flutter_isolate'),
|
||||||
Libries('auto_animated', mit, 'https://pub.dev/packages/auto_animated'),
|
Libries('auto_animated', mit, 'https://pub.dev/packages/auto_animated'),
|
||||||
Libries('wc_flutter_share', apacheLicense, 'https://pub.dev/packages/wc_flutter_share')
|
Libries('wc_flutter_share', apacheLicense,
|
||||||
|
'https://pub.dev/packages/wc_flutter_share')
|
||||||
];
|
];
|
||||||
|
|
|
@ -39,6 +39,21 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
|
||||||
leading: icon,
|
leading: icon,
|
||||||
title: Text(text),
|
title: Text(text),
|
||||||
subtitle: Text(description),
|
subtitle: Text(description),
|
||||||
|
onTap: e == 0
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (e >= 10) {
|
||||||
|
int index = menu.indexOf(e);
|
||||||
|
menu.remove(e);
|
||||||
|
menu.insert(index, e - 10);
|
||||||
|
_saveEpisodeMene(menu);
|
||||||
|
} else if (e < 10) {
|
||||||
|
int index = menu.indexOf(e);
|
||||||
|
menu.remove(e);
|
||||||
|
menu.insert(index, e + 10);
|
||||||
|
_saveEpisodeMene(menu);
|
||||||
|
}
|
||||||
|
},
|
||||||
trailing: Checkbox(
|
trailing: Checkbox(
|
||||||
value: e < 10,
|
value: e < 10,
|
||||||
onChanged: e == 0
|
onChanged: e == 0
|
||||||
|
@ -131,11 +146,8 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
return _popupMenuItem(menu, e,
|
return _popupMenuItem(menu, e,
|
||||||
icon: Icon(
|
icon: Icon(LineIcons.heart,
|
||||||
LineIcons.heart,
|
color: Colors.red, size: 21),
|
||||||
color: Colors.red,
|
|
||||||
size: 21
|
|
||||||
),
|
|
||||||
text: 'Like',
|
text: 'Like',
|
||||||
description: 'Add episode to favorite');
|
description: 'Add episode to favorite');
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -179,12 +179,6 @@ class _SettingsState extends State<Settings>
|
||||||
leading: Icon(LineIcons.play_circle),
|
leading: Icon(LineIcons.play_circle),
|
||||||
title: Text('Play'),
|
title: Text('Play'),
|
||||||
subtitle: Text('Playlist and player'),
|
subtitle: Text('Playlist and player'),
|
||||||
// trailing: Selector<AudioPlayerNotifier, bool>(
|
|
||||||
// selector: (_, audio) => audio.autoPlay,
|
|
||||||
// builder: (_, data, __) => Switch(
|
|
||||||
// value: data,
|
|
||||||
// onChanged: (boo) => audio.autoPlaySwitch = boo),
|
|
||||||
// ),
|
|
||||||
),
|
),
|
||||||
Divider(height: 2),
|
Divider(height: 2),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|
|
@ -30,11 +30,6 @@ class _StorageSettingState extends State<StorageSetting>
|
||||||
setState(() => _value = _animation.value);
|
setState(() => _value = _animation.value);
|
||||||
});
|
});
|
||||||
_controller.forward();
|
_controller.forward();
|
||||||
// _controller.addStatusListener((status) {
|
|
||||||
// if (status == AnimationStatus.completed) {
|
|
||||||
// _controller.reset();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +39,22 @@ class _StorageSettingState extends State<StorageSetting>
|
||||||
return value != 0;
|
return value != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> _getAutoDeleteDays() async {
|
||||||
|
KeyValueStorage storage = KeyValueStorage(autoDeleteKey);
|
||||||
|
int days = await storage.getInt();
|
||||||
|
if (days == 0) {
|
||||||
|
storage.saveInt(30);
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setAutoDeleteDays(int days) async {
|
||||||
|
KeyValueStorage storage = KeyValueStorage(autoDeleteKey);
|
||||||
|
await storage.saveInt(days);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
_setAudtDownloadNetwork(bool boo) async {
|
_setAudtDownloadNetwork(bool boo) async {
|
||||||
KeyValueStorage storage = KeyValueStorage(autoDownloadNetworkKey);
|
KeyValueStorage storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||||
await storage.saveInt(boo ? 1 : 0);
|
await storage.saveInt(boo ? 1 : 0);
|
||||||
|
@ -186,10 +197,41 @@ class _StorageSettingState extends State<StorageSetting>
|
||||||
subtitle: Text('Manage downloaded audio files'),
|
subtitle: Text('Manage downloaded audio files'),
|
||||||
),
|
),
|
||||||
Divider(height: 2),
|
Divider(height: 2),
|
||||||
|
FutureBuilder<int>(
|
||||||
|
future: _getAutoDeleteDays(),
|
||||||
|
initialData: 30,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.only(left: 80.0, right: 20),
|
||||||
|
title: Text('Auto delete downloads after'),
|
||||||
|
subtitle: Text('Default 30 days.'),
|
||||||
|
trailing: DropdownButton(
|
||||||
|
hint: snapshot.data == -1
|
||||||
|
? Text('Never')
|
||||||
|
: Text(snapshot.data.toString() + 'days'),
|
||||||
|
underline: Center(),
|
||||||
|
elevation: 1,
|
||||||
|
value: snapshot.data,
|
||||||
|
onChanged: (value) async {
|
||||||
|
await _setAutoDeleteDays(value);
|
||||||
|
},
|
||||||
|
items: <int>[-1, 10, 30]
|
||||||
|
.map<DropdownMenuItem<int>>((e) {
|
||||||
|
return DropdownMenuItem<int>(
|
||||||
|
value: e,
|
||||||
|
child: e == -1
|
||||||
|
? Text('Never')
|
||||||
|
: Text(e.toString() + ' days'));
|
||||||
|
}).toList()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: EdgeInsets.only(left: 80.0, right: 25),
|
contentPadding: EdgeInsets.only(left: 80.0, right: 25),
|
||||||
// leading: Icon(Icons.colorize),
|
// leading: Icon(Icons.colorize),
|
||||||
title: Text('Cache'),
|
title: Text('Audio cache'),
|
||||||
subtitle: Text('Audio cache max size'),
|
subtitle: Text('Audio cache max size'),
|
||||||
trailing: Text.rich(TextSpan(
|
trailing: Text.rich(TextSpan(
|
||||||
text: '${(_value ~/ 100) * 100}',
|
text: '${(_value ~/ 100) * 100}',
|
||||||
|
|
|
@ -4,10 +4,10 @@ import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
import '../type/episodebrief.dart';
|
import '../type/episodebrief.dart';
|
||||||
|
@ -37,6 +37,10 @@ class DownloadState extends ChangeNotifier {
|
||||||
List<EpisodeTask> _episodeTasks = [];
|
List<EpisodeTask> _episodeTasks = [];
|
||||||
List<EpisodeTask> get episodeTasks => _episodeTasks;
|
List<EpisodeTask> get episodeTasks => _episodeTasks;
|
||||||
|
|
||||||
|
DownloadState() {
|
||||||
|
_autoDelete();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addListener(VoidCallback listener) async {
|
void addListener(VoidCallback listener) async {
|
||||||
_loadTasks();
|
_loadTasks();
|
||||||
|
@ -55,7 +59,6 @@ class DownloadState extends ChangeNotifier {
|
||||||
_episodeTasks.add(EpisodeTask(episode, task.taskId,
|
_episodeTasks.add(EpisodeTask(episode, task.taskId,
|
||||||
progress: task.progress, status: task.status));
|
progress: task.progress, status: task.status));
|
||||||
});
|
});
|
||||||
print(_episodeTasks.length);
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,4 +208,19 @@ class DownloadState extends ChangeNotifier {
|
||||||
_episodeTasks.removeWhere(
|
_episodeTasks.removeWhere(
|
||||||
(element) => element.episode.enclosureUrl == episode.enclosureUrl);
|
(element) => element.episode.enclosureUrl == episode.enclosureUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_autoDelete() async {
|
||||||
|
print('Start auto delete outdated episodes');
|
||||||
|
KeyValueStorage autoDeleteStorage = KeyValueStorage(autoDeleteKey);
|
||||||
|
int autoDelete = await autoDeleteStorage.getInt();
|
||||||
|
if (autoDelete == 0)
|
||||||
|
await autoDeleteStorage.saveInt(30);
|
||||||
|
else if (autoDelete > 0) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getOutdatedEpisode(autoDelete);
|
||||||
|
if (episodes.length > 0) {
|
||||||
|
await Future.forEach(episodes, (episode) => delTask(episode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:audio_service/audio_service.dart';
|
||||||
|
|
||||||
class EpisodeBrief {
|
class EpisodeBrief {
|
||||||
final String title;
|
final String title;
|
||||||
String description;
|
final String description;
|
||||||
final int pubDate;
|
final int pubDate;
|
||||||
final int enclosureLength;
|
final int enclosureLength;
|
||||||
final String enclosureUrl;
|
final String enclosureUrl;
|
||||||
|
@ -17,6 +17,7 @@ class EpisodeBrief {
|
||||||
final String mediaId;
|
final String mediaId;
|
||||||
final int isNew;
|
final int isNew;
|
||||||
final int skipSeconds;
|
final int skipSeconds;
|
||||||
|
final int downloadDate;
|
||||||
EpisodeBrief(
|
EpisodeBrief(
|
||||||
this.title,
|
this.title,
|
||||||
this.enclosureUrl,
|
this.enclosureUrl,
|
||||||
|
@ -31,7 +32,10 @@ class EpisodeBrief {
|
||||||
this.imagePath,
|
this.imagePath,
|
||||||
this.mediaId,
|
this.mediaId,
|
||||||
this.isNew,
|
this.isNew,
|
||||||
this.skipSeconds);
|
this.skipSeconds,
|
||||||
|
{this.description = '',
|
||||||
|
this.downloadDate = 0})
|
||||||
|
: assert(enclosureUrl != null);
|
||||||
|
|
||||||
String dateToString() {
|
String dateToString() {
|
||||||
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
|
||||||
|
@ -50,6 +54,23 @@ class EpisodeBrief {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String downloadDateToString() {
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(downloadDate);
|
||||||
|
var diffrence = DateTime.now().toUtc().difference(date);
|
||||||
|
if (diffrence.inHours < 1) {
|
||||||
|
return '1 hour ago';
|
||||||
|
} else if (diffrence.inHours < 24) {
|
||||||
|
return '${diffrence.inHours} hours ago';
|
||||||
|
} else if (diffrence.inHours == 24) {
|
||||||
|
return '1 day ago';
|
||||||
|
} else if (diffrence.inDays < 7) {
|
||||||
|
return '${diffrence.inDays} days ago';
|
||||||
|
} else {
|
||||||
|
return DateFormat.yMMMd().format(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true).toLocal());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MediaItem toMediaItem() {
|
MediaItem toMediaItem() {
|
||||||
return MediaItem(
|
return MediaItem(
|
||||||
id: mediaId,
|
id: mediaId,
|
||||||
|
|
|
@ -13,20 +13,16 @@ class PodcastLocal {
|
||||||
final String description;
|
final String description;
|
||||||
int upateCount;
|
int upateCount;
|
||||||
int episodeCount;
|
int episodeCount;
|
||||||
PodcastLocal(
|
PodcastLocal(this.title, this.imageUrl, this.rssUrl, this.primaryColor,
|
||||||
this.title,
|
this.author, this.id, this.imagePath, this.provider, this.link,
|
||||||
this.imageUrl,
|
{this.description = '', this.upateCount = 0, this.episodeCount = 0})
|
||||||
this.rssUrl,
|
: assert(rssUrl != null);
|
||||||
this.primaryColor,
|
@override
|
||||||
this.author,
|
bool operator ==(Object podcastLocal) =>
|
||||||
this.id,
|
podcastLocal is PodcastLocal &&
|
||||||
this.imagePath,
|
podcastLocal.rssUrl == rssUrl &&
|
||||||
this.provider,
|
podcastLocal.id == id;
|
||||||
this.link,
|
|
||||||
{
|
@override
|
||||||
this.description ='',
|
int get hashCode => id.hashCode + rssUrl.hashCode;
|
||||||
this.upateCount = 0,
|
|
||||||
this.episodeCount = 0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,36 +118,37 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
|
|
||||||
Widget _listenIndicater(BuildContext context,
|
Widget _listenIndicater(BuildContext context,
|
||||||
{EpisodeBrief episode, int isListened}) =>
|
{EpisodeBrief episode, int isListened}) =>
|
||||||
Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief, bool>>(
|
Center();
|
||||||
selector: (_, audio) => Tuple2(audio.episode, audio.playerRunning),
|
// Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief, bool>>(
|
||||||
builder: (_, data, __) {
|
// selector: (_, audio) => Tuple2(audio.episode, audio.playerRunning),
|
||||||
return (episode.enclosureUrl == data.item1?.enclosureUrl &&
|
// builder: (_, data, __) {
|
||||||
data.item2)
|
// return (episode.enclosureUrl == data.item1?.enclosureUrl &&
|
||||||
? Container(
|
// data.item2)
|
||||||
height: 20,
|
// ? Container(
|
||||||
width: 20,
|
// height: 20,
|
||||||
margin: EdgeInsets.symmetric(horizontal: 2),
|
// width: 20,
|
||||||
decoration: BoxDecoration(
|
// margin: EdgeInsets.symmetric(horizontal: 2),
|
||||||
shape: BoxShape.circle,
|
// decoration: BoxDecoration(
|
||||||
),
|
// shape: BoxShape.circle,
|
||||||
child: WaveLoader(color: context.accentColor))
|
// ),
|
||||||
: layout != Layout.three && isListened > 0
|
// child: WaveLoader(color: context.accentColor))
|
||||||
? Container(
|
// : layout != Layout.three && isListened > 0
|
||||||
height: 20,
|
// ? Container(
|
||||||
width: 20,
|
// height: 20,
|
||||||
margin: EdgeInsets.symmetric(horizontal: 2),
|
// width: 20,
|
||||||
padding: EdgeInsets.all(2),
|
// margin: EdgeInsets.symmetric(horizontal: 2),
|
||||||
decoration: BoxDecoration(
|
// padding: EdgeInsets.all(2),
|
||||||
color: context.accentColor,
|
// decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
// color: context.accentColor,
|
||||||
),
|
// shape: BoxShape.circle,
|
||||||
child: CustomPaint(
|
// ),
|
||||||
painter: ListenedAllPainter(
|
// child: CustomPaint(
|
||||||
Colors.white,
|
// painter: ListenedAllPainter(
|
||||||
)),
|
// Colors.white,
|
||||||
)
|
// )),
|
||||||
: Center();
|
// )
|
||||||
});
|
// : Center();
|
||||||
|
// });
|
||||||
|
|
||||||
Widget _downloadIndicater(BuildContext context, {EpisodeBrief episode}) =>
|
Widget _downloadIndicater(BuildContext context, {EpisodeBrief episode}) =>
|
||||||
showDownload || layout != Layout.three
|
showDownload || layout != Layout.three
|
||||||
|
@ -294,7 +295,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
),
|
),
|
||||||
isListened > 0.95
|
isListened > 0
|
||||||
? Text('Listened',
|
? Text('Listened',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.textColor.withOpacity(0.5)))
|
color: context.textColor.withOpacity(0.5)))
|
||||||
|
@ -359,7 +360,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (isListened < 0.95) {
|
if (isListened < 1) {
|
||||||
await _markListened(episode);
|
await _markListened(episode);
|
||||||
audio.setEpisodeState = true;
|
audio.setEpisodeState = true;
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
|
@ -368,7 +369,6 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
if (!isDownload) downloader.startTask(episode);
|
if (!isDownload) downloader.startTask(episode);
|
||||||
break;
|
break;
|
||||||
|
@ -428,7 +428,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
BorderRadius.all(Radius.circular(5.0)),
|
BorderRadius.all(Radius.circular(5.0)),
|
||||||
color: snapshot.data > 0
|
color: snapshot.data > 0
|
||||||
? context.brightness == Brightness.light
|
? context.brightness == Brightness.light
|
||||||
? context.primaryColor
|
? Colors.grey[200]
|
||||||
: Color.fromRGBO(40, 40, 40, 1)
|
: Color.fromRGBO(40, 40, 40, 1)
|
||||||
: context.scaffoldBackgroundColor,
|
: context.scaffoldBackgroundColor,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
|
@ -488,9 +488,9 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
episode: episodes[index],
|
episode: episodes[index],
|
||||||
color: _c),
|
color: _c),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
_listenIndicater(context,
|
// _listenIndicater(context,
|
||||||
episode: episodes[index],
|
// episode: episodes[index],
|
||||||
isListened: snapshot.data),
|
// isListened: snapshot.data),
|
||||||
_downloadIndicater(context,
|
_downloadIndicater(context,
|
||||||
episode: episodes[index]),
|
episode: episodes[index]),
|
||||||
_isNewIndicator(episodes[index]),
|
_isNewIndicator(episodes[index]),
|
||||||
|
|
Loading…
Reference in New Issue