Add filter in podcast detail page.

This commit is contained in:
stonegate 2020-07-24 02:42:25 +08:00
parent d9bb4cf987
commit c183683101
5 changed files with 977 additions and 811 deletions

View File

@ -10,6 +10,8 @@ import '../type/episodebrief.dart';
import '../webfeed/webfeed.dart';
import '../type/sub_history.dart';
enum Filter { downloaded, liked, search, all }
class DBHelper {
static Database _db;
Future<Database> get database async {
@ -491,7 +493,6 @@ class DBHelper {
var feed = RssFeed.parse(response.data);
String url, description;
feed.items.removeWhere((item) => item == null);
int result = feed.items.length;
var dbClient = await database;
int count = Sqflite.firstIntValue(await dbClient.rawQuery(
@ -556,85 +557,131 @@ class DBHelper {
}
}
Future<List<EpisodeBrief>> getRssItem(String id, int i, bool reverse) async {
Future<List<EpisodeBrief>> getRssItem(String id, int count, bool reverse,
{Filter filter = Filter.all, String query = ''}) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
if (i == -1) {
List<Map> list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
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
List<Map> list = [];
if (count == -1) {
switch (filter) {
case Filter.all:
list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? ORDER BY E.milliseconds ASC""", [id]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feedTitle'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
break;
case Filter.liked:
list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds ASC""", [id]);
break;
case Filter.downloaded:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.media_id != E.enclosure_url ORDER BY E.milliseconds ASC""",
[id]);
break;
case Filter.search:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds ASC""",
[id, '%$query%']);
break;
default:
}
return episodes;
} else if (reverse) {
List<Map> list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
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
WHERE P.id = ? ORDER BY E.milliseconds ASC LIMIT ?""", [id, i]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feedTitle'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
switch (filter) {
case Filter.all:
list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? ORDER BY E.milliseconds ASC LIMIT ?""", [id, count]);
break;
case Filter.liked:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds ASC LIMIT ?""",
[id, count]);
break;
case Filter.downloaded:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.enclosure_url != E.media_id ORDER BY E.milliseconds ASC LIMIT ?""",
[id, count]);
break;
case Filter.search:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds ASC LIMIT ?""",
[id, '%$query%', count]);
break;
default:
}
return episodes;
} else {
List<Map> list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
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
WHERE P.id = ? ORDER BY E.milliseconds DESC LIMIT ?""", [id, i]);
switch (filter) {
case Filter.all:
list = await dbClient
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? ORDER BY E.milliseconds DESC LIMIT ?""", [id, count]);
break;
case Filter.liked:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""",
[id, count]);
break;
case Filter.downloaded:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.enclosure_url != E.media_id ORDER BY E.milliseconds DESC LIMIT ?""",
[id, count]);
break;
case Filter.search:
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds DESC LIMIT ?""",
[id, '%$query%', count]);
break;
default:
}
}
if (list.isNotEmpty)
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feedTitle'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feedTitle'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
return episodes;
}
return episodes;
}
Future<List<EpisodeBrief>> getNewEpisodes(String id) async {
@ -644,36 +691,30 @@ class DBHelper {
if (id == 'all')
list = await dbClient.rawQuery(
"""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.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
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND P.auto_download = 1 ORDER BY E.milliseconds ASC""",
);
else
list = await dbClient.rawQuery(
"""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.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
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND E.feed_id = ? ORDER BY E.milliseconds ASC""",
[id]);
if (list.isNotEmpty)
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
return episodes;
}
@ -683,85 +724,45 @@ class DBHelper {
List<EpisodeBrief> episodes = [];
List<Map> list = await dbClient
.rawQuery("""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.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
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
where E.feed_id = ? ORDER BY E.milliseconds DESC LIMIT 2""", [id]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
return episodes;
}
//Future<EpisodeBrief> getRssItemDownload(String url) async {
// var dbClient = await database;
// EpisodeBrief episode;
// List<Map> list = await dbClient.rawQuery(
// """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.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
// where E.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""",
// [url]);
// if (list != null)
// episode = EpisodeBrief(
// list.first['title'],
// list.first['enclosure_url'],
// list.first['enclosure_length'],
// list.first['milliseconds'],
// list.first['feed_title'],
// list.first['primaryColor'],
// list.first['liked'],
// list.first['downloaded'],
// list.first['duration'],
// list.first['explicit'],
// list.first['imagePath'],
// list.first['media_id'],
// list.first['is_new'],
// list.first['skip_seconds']);
// return episode;
//}
Future<List<EpisodeBrief>> getRecentRssItem(int top) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
ORDER BY E.milliseconds DESC LIMIT ? """, [top]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
return episodes;
}
@ -774,27 +775,22 @@ class DBHelper {
List<String> s = group.map<String>((e) => "'$e'").toList();
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id in (${s.join(',')})
ORDER BY E.milliseconds DESC LIMIT ? """, [top]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
}
return episodes;
@ -805,87 +801,47 @@ class DBHelper {
List<EpisodeBrief> episodes = [];
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE is_new = 1 ORDER BY E.milliseconds DESC """,
);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
return episodes;
}
//Future<List<EpisodeBrief>> getNewRssItem(String id) async {
// var dbClient = await database;
// List<EpisodeBrief> episodes = [];
// 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 is_new = 1 AND downloaded != 'ND' AND P.id = ?ORDER BY E.milliseconds DESC """,
// [id],
// );
// 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>> getOutdatedEpisode(int deadline) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor 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 (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
return episodes;
}
@ -897,10 +853,9 @@ class DBHelper {
//Ordered by date
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
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor 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""",
);
@ -908,9 +863,8 @@ class DBHelper {
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor 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""",
);
@ -918,9 +872,8 @@ class DBHelper {
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor 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""",
);
@ -933,14 +886,9 @@ class DBHelper {
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds'],
downloadDate: i['download_date']),
);
}
@ -961,28 +909,23 @@ class DBHelper {
List<String> s = group.map<String>((e) => "'$e'").toList();
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
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE P.id in (${s.join(',')}) AND is_new = 1
ORDER BY E.milliseconds DESC""",
);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
}
return episodes;
@ -1014,48 +957,40 @@ class DBHelper {
if (sortBy == 0) {
List<Map> list = await dbClient.rawQuery(
"""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.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
P.title as feed_title, E.duration, E.explicit, P.primaryColor
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""", [i]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
} else {
List<Map> list = await dbClient.rawQuery(
"""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.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
P.title as feed_title, E.duration, E.explicit, P.primaryColor
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.liked = 1 ORDER BY E.liked_date DESC LIMIT ?""", [i]);
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['liked'],
i['downloaded'],
i['duration'],
i['explicit'],
i['imagePath'],
i['media_id'],
i['is_new'],
i['skip_seconds']));
i['title'],
i['enclosure_url'],
i['enclosure_length'],
i['milliseconds'],
i['feed_title'],
i['primaryColor'],
i['duration'],
i['explicit'],
i['imagePath'],
));
}
}
return episodes;
@ -1150,8 +1085,8 @@ class DBHelper {
EpisodeBrief episode;
List<Map> list = await dbClient.rawQuery(
"""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.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
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.enclosure_url = ?""", [url]);
if (list.isEmpty) {
return null;
@ -1163,14 +1098,11 @@ class DBHelper {
list.first['milliseconds'],
list.first['feed_title'],
list.first['primaryColor'],
list.first['liked'],
list.first['downloaded'],
list.first['duration'],
list.first['explicit'],
list.first['imagePath'],
list.first['media_id'],
list.first['is_new'],
list.first['skip_seconds']);
mediaId: list.first['media_id'],
skipSeconds: list.first['skip_seconds']);
return episode;
}
}
@ -1180,8 +1112,8 @@ class DBHelper {
EpisodeBrief episode;
List<Map> list = await dbClient.rawQuery(
"""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.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
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.media_id = ?""", [id]);
if (list.isEmpty)
return null;
@ -1193,14 +1125,11 @@ class DBHelper {
list.first['milliseconds'],
list.first['feed_title'],
list.first['primaryColor'],
list.first['liked'],
list.first['downloaded'],
list.first['duration'],
list.first['explicit'],
list.first['imagePath'],
list.first['media_id'],
list.first['is_new'],
list.first['skip_seconds']);
mediaId: list.first['media_id'],
skipSeconds: list.first['skip_seconds']);
return episode;
}
}

View File

@ -52,7 +52,46 @@ class _PodcastDetailState extends State<PodcastDetail> {
/// Default layout.
Layout _layout;
bool _scroll;
/// If true, stop grid load animation.
bool _scroll = false;
double _topHeight = 0;
ScrollController _controller;
/// Episodes num load first time.
int _top = 96;
/// Load more episodes when scroll to bottom.
bool _loadMore = false;
/// Change sort by.
bool _reverse = false;
/// Filter type.
Filter _filter = Filter.all;
/// Query string
String _query = '';
///Hide listened.
bool _hideListened = false;
@override
void initState() {
super.initState();
_loadMore = false;
_reverse = false;
_controller = ScrollController();
_scroll = false;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future _updateRssItem(BuildContext context, PodcastLocal podcastLocal) async {
var dbHelper = DBHelper();
final result = await dbHelper.updatePodcastRss(podcastLocal);
@ -99,15 +138,16 @@ class _PodcastDetailState extends State<PodcastDetail> {
if (mounted) setState(() {});
}
Future<List<EpisodeBrief>> _getRssItem(
PodcastLocal podcastLocal, int i, bool reverse) async {
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal,
{int count, bool reverse, Filter filter, String query}) async {
var dbHelper = DBHelper();
List<EpisodeBrief> episodes = [];
_episodeCount = await dbHelper.getPodcastCounts(podcastLocal.id);
KeyValueStorage storage = KeyValueStorage(podcastLayoutKey);
int index = await storage.getInt(defaultValue: 1);
if (_layout == null) _layout = Layout.values[index];
List<EpisodeBrief> episodes =
await dbHelper.getRssItem(podcastLocal.id, i, reverse);
episodes = await dbHelper.getRssItem(podcastLocal.id, count, reverse,
filter: filter, query: query);
if (podcastLocal.provider.contains('fireside')) {
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
await data.getData();
@ -117,17 +157,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
return episodes;
}
_launchUrl(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
Fluttertoast.showToast(
msg: '$url Invalid Link',
gravity: ToastGravity.TOP,
);
}
}
_markListened(String podcastId) async {
DBHelper dbHelper = DBHelper();
List<EpisodeBrief> episodes =
@ -234,6 +263,24 @@ class _PodcastDetailState extends State<PodcastDetail> {
);
}
_customPopupMenu(
{Widget child,
String tooltip,
List<PopupMenuEntry<int>> itemBuilder,
Function(int) onSelected}) =>
Material(
color: Colors.transparent,
child: PopupMenuButton<int>(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1,
tooltip: tooltip,
child: child,
itemBuilder: (context) => itemBuilder,
onSelected: (value) => onSelected(value),
),
);
_confirmMarkListened(BuildContext context) => generalDialog(
context,
title: Text(context.s.markConfirm),
@ -261,34 +308,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
],
);
double _topHeight = 0;
ScrollController _controller;
/// Episodes num load first time.
int _top;
/// Load more episodes when scroll to bottom.
bool _loadMore;
/// Change sort by.
bool _reverse;
@override
void initState() {
super.initState();
_loadMore = false;
_top = 99;
_reverse = false;
_controller = ScrollController();
_scroll = false;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Color _color = widget.podcastLocal.primaryColor.colorizedark();
@ -321,8 +340,11 @@ class _PodcastDetailState extends State<PodcastDetail> {
children: <Widget>[
Expanded(
child: FutureBuilder<List<EpisodeBrief>>(
future:
_getRssItem(widget.podcastLocal, _top, _reverse),
future: _getRssItem(widget.podcastLocal,
count: _top,
reverse: _reverse,
filter: _filter,
query: _query),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return (snapshot.hasData)
@ -339,16 +361,14 @@ class _PodcastDetailState extends State<PodcastDetail> {
Duration(seconds: 3));
if (mounted)
setState(() {
_top = _top + 33;
_top = _top + 36;
_loadMore = false;
});
}
if (_controller.offset > 0 &&
mounted &&
!_scroll)
setState(() {
_scroll = true;
});
setState(() => _scroll = true);
}),
physics:
const AlwaysScrollableScrollPhysics(),
@ -356,38 +376,47 @@ class _PodcastDetailState extends State<PodcastDetail> {
SliverAppBar(
brightness: Brightness.dark,
actions: <Widget>[
PopupMenuButton<int>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10))),
elevation: 2,
_customPopupMenu(
tooltip: s.menu,
itemBuilder: (context) => [
widget.podcastLocal.link != null
? PopupMenuItem(
value: 0,
child: Container(
padding: EdgeInsets.only(
left: 10),
child: Row(
children: <Widget>[
Icon(Icons.link,
color: Theme.of(
context)
.tabBarTheme
.labelColor),
Padding(
padding: EdgeInsets
.symmetric(
horizontal:
5.0),
),
Text(s.menuVisitSite),
],
onSelected: (int value) {
switch (value) {
case 0:
widget.podcastLocal.link
.launchUrl;
break;
case 1:
widget.podcastLocal.rssUrl
.launchUrl;
break;
case 2:
_confirmMarkListened(context);
break;
}
},
itemBuilder: [
if (widget.podcastLocal.link !=
null)
PopupMenuItem(
value: 0,
child: Container(
padding:
EdgeInsets.only(left: 10),
child: Row(
children: <Widget>[
Icon(Icons.link,
color: context
.textColor),
Padding(
padding: EdgeInsets
.symmetric(
horizontal:
5.0),
),
),
)
: Center(),
Text(s.menuVisitSite),
],
),
),
),
PopupMenuItem(
value: 1,
child: Container(
@ -397,9 +426,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
children: <Widget>[
Icon(
Icons.rss_feed,
color: Theme.of(context)
.tabBarTheme
.labelColor,
color: context.textColor,
),
Padding(
padding:
@ -427,9 +454,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
painter:
ListenedAllPainter(
context
.textTheme
.bodyText1
.color,
.textColor,
stroke: 2)),
),
Padding(
@ -445,35 +470,18 @@ class _PodcastDetailState extends State<PodcastDetail> {
),
),
],
onSelected: (int value) {
switch (value) {
case 0:
_launchUrl(
widget.podcastLocal.link);
break;
case 1:
_launchUrl(
widget.podcastLocal.rssUrl);
break;
case 2:
_confirmMarkListened(context);
break;
}
},
)
),
],
elevation: 0,
iconTheme: IconThemeData(
color: Colors.white,
),
expandedHeight: 150 +
MediaQuery.of(context).padding.top,
expandedHeight: 150 + context.paddingTop,
backgroundColor: _color,
floating: true,
pinned: true,
flexibleSpace: LayoutBuilder(builder:
(BuildContext context,
BoxConstraints constraints) {
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
_topHeight = constraints.biggest.height;
return FlexibleSpaceBar(
background: Stack(
@ -481,9 +489,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
Container(
margin: EdgeInsets.only(
top: 120 +
MediaQuery.of(context)
.padding
.top),
context.paddingTop),
padding: EdgeInsets.only(
left: 80, right: 120),
color: Colors.white10,
@ -506,18 +512,17 @@ class _PodcastDetailState extends State<PodcastDetail> {
style: TextStyle(
color:
Colors.white)),
widget.podcastLocal.provider
.isNotEmpty
? Text(
s.hostedOn(widget
.podcastLocal
.provider),
maxLines: 1,
style: TextStyle(
color: Colors
.white),
)
: Center(),
if (widget.podcastLocal
.provider.isNotEmpty)
Text(
s.hostedOn(widget
.podcastLocal
.provider),
maxLines: 1,
style: TextStyle(
color:
Colors.white),
),
],
),
),
@ -539,10 +544,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
],
),
title: _topHeight <
70 +
MediaQuery.of(context)
.padding
.top
70 + context.paddingTop
? Text(widget.podcastLocal.title,
maxLines: 1,
overflow:
@ -563,12 +565,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
children: <Widget>[
Material(
color: Colors.transparent,
child: PopupMenuButton<int>(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
10)),
elevation: 1,
child: _customPopupMenu(
tooltip: s.homeSubMenuSortBy,
child: Container(
height: 30,
@ -581,12 +578,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
children: <Widget>[
Text(s
.homeSubMenuSortBy),
Padding(
padding: EdgeInsets
.symmetric(
horizontal:
5),
),
SizedBox(width: 10),
Icon(
_reverse
? LineIcons
@ -597,7 +589,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
)
],
)),
itemBuilder: (context) => [
itemBuilder: [
PopupMenuItem(
value: 0,
child:
@ -619,6 +611,124 @@ class _PodcastDetailState extends State<PodcastDetail> {
},
),
),
Material(
color: Colors.transparent,
child: _customPopupMenu(
tooltip: 'Filter',
child: Container(
height: 30,
padding:
EdgeInsets.symmetric(
horizontal: 15),
child: Row(
mainAxisSize:
MainAxisSize.min,
children: <Widget>[
Text('Filter'),
SizedBox(width: 10),
Icon(
LineIcons
.filter_solid,
size: 18,
)
],
),
),
itemBuilder: [
PopupMenuItem(
value: 0,
child: Text('All'),
),
PopupMenuItem(
value: 1,
child: Text(
'Not listened'),
),
PopupMenuItem(
value: 2,
child: Text('Liked'),
),
PopupMenuItem(
value: 3,
child:
Text('Downloaded'),
),
PopupMenuItem(
value: 4,
child: Text('Search'),
),
],
onSelected: (value) {
switch (value) {
case 0:
if (_filter !=
Filter.all)
setState(() {
_hideListened =
false;
_filter =
Filter.all;
});
break;
case 1:
setState(() =>
_hideListened =
true);
break;
case 2:
if (_filter !=
Filter.liked)
setState(() =>
_filter = Filter
.liked);
break;
case 3:
if (_filter !=
Filter.downloaded)
setState(() =>
_filter = Filter
.downloaded);
break;
case 4:
showGeneralDialog(
context: context,
barrierDismissible:
true,
barrierLabel:
MaterialLocalizations
.of(
context)
.modalBarrierDismissLabel,
barrierColor:
Colors
.black54,
transitionDuration:
const Duration(
milliseconds:
200),
pageBuilder: (BuildContext
context,
Animation
animaiton,
Animation
secondaryAnimation) =>
SearchEpisdoe(
onSearch:
(query) {
setState(
() {
_query =
query;
_filter =
Filter.search;
});
},
));
break;
default:
}
}),
),
Spacer(),
Material(
color: Colors.transparent,
@ -692,6 +802,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
reverse: _reverse,
episodeCount: _episodeCount,
initNum: _scroll ? 0 : 12,
hideListened: _hideListened,
),
SliverList(
delegate: SliverChildBuilderDelegate(
@ -834,3 +945,111 @@ class _AboutPodcastState extends State<AboutPodcast> {
);
}
}
class SearchEpisdoe extends StatefulWidget {
SearchEpisdoe({this.onSearch, Key key}) : super(key: key);
final ValueChanged<String> onSearch;
@override
_SearchEpisodeState createState() => _SearchEpisodeState();
}
class _SearchEpisodeState extends State<SearchEpisdoe> {
TextEditingController _controller;
String _query;
int _error;
@override
void initState() {
super.initState();
_error = 0;
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor:
Theme.of(context).brightness == Brightness.light
? Color.fromRGBO(113, 113, 113, 1)
: Color.fromRGBO(5, 5, 5, 1),
),
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10))),
elevation: 1,
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
titlePadding:
const EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
actionsPadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
s.cancel,
style: TextStyle(color: Colors.grey[600]),
),
),
FlatButton(
onPressed: (_query != null && _query != '')
? () {
{
widget.onSearch(_query);
Navigator.of(context).pop();
}
}
: null,
child: Text(s.confirm,
style: TextStyle(color: Theme.of(context).accentColor)),
)
],
title: SizedBox(width: context.width - 160, child: Text(s.newGroup)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 10),
hintText: s.newGroup,
hintStyle: TextStyle(fontSize: 18),
filled: true,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: context.accentColor, width: 2.0),
),
enabledBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: context.accentColor, width: 2.0),
),
),
cursorRadius: Radius.circular(2),
autofocus: true,
maxLines: 1,
controller: _controller,
onChanged: (value) {
_query = value;
},
),
Container(
alignment: Alignment.centerLeft,
child: (_error == 1)
? Text(
s.groupExisted,
style: TextStyle(color: Colors.red[400]),
)
: Center(),
),
],
),
),
);
}
}

View File

@ -1,6 +1,5 @@
import 'dart:ui';
import 'package:intl/intl.dart';
import 'package:audio_service/audio_service.dart';
class EpisodeBrief {
@ -27,35 +26,18 @@ class EpisodeBrief {
this.pubDate,
this.feedTitle,
this.primaryColor,
this.liked,
this.downloaded,
this.duration,
this.explicit,
this.imagePath,
this.mediaId,
{this.mediaId,
this.liked,
this.downloaded,
this.isNew,
this.skipSeconds,
{this.description = '',
this.description = '',
this.downloadDate = 0})
: assert(enclosureUrl != null);
String ateToString() {
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
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() {
return MediaItem(
id: mediaId,

View File

@ -34,31 +34,24 @@ class EpisodeGrid extends StatelessWidget {
final int episodeCount;
final Layout layout;
final bool reverse;
final int initNum;
EpisodeGrid({
Key key,
@required this.episodes,
this.initNum = 12,
this.showDownload = false,
this.showFavorite = false,
this.showNumber = false,
this.episodeCount = 0,
this.layout = Layout.three,
this.reverse,
}) : super(key: key);
String _dateToString(BuildContext context, {int pubDate}) {
final s = context.s;
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
var difference = DateTime.now().toUtc().difference(date);
if (difference.inHours < 24) {
return s.hoursAgo(difference.inHours);
} else if (difference.inDays < 7) {
return s.daysAgo(difference.inDays);
} else {
return DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true).toLocal());
}
}
/// Hide listened episode.
final bool hideListened;
EpisodeGrid(
{Key key,
@required this.episodes,
this.initNum = 12,
this.showDownload = false,
this.showFavorite = false,
this.showNumber = false,
this.episodeCount = 0,
this.layout = Layout.three,
this.reverse,
this.hideListened = false})
: super(key: key);
Future<int> _isListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
@ -123,6 +116,7 @@ class EpisodeGrid extends StatelessWidget {
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
/// Episode title widget.
Widget _title(EpisodeBrief episode) => Container(
alignment:
layout == Layout.one ? Alignment.centerLeft : Alignment.topLeft,
@ -135,6 +129,7 @@ class EpisodeGrid extends StatelessWidget {
),
);
/// Circel avatar widget.
Widget _circleImage(BuildContext context,
{EpisodeBrief episode, Color color, bool boo}) =>
Container(
@ -170,6 +165,7 @@ class EpisodeGrid extends StatelessWidget {
: Center()
: Center();
/// New indicator widget.
Widget _isNewIndicator(EpisodeBrief episode) => episode.isNew == 1
? Container(
padding: EdgeInsets.symmetric(horizontal: 2),
@ -178,6 +174,7 @@ class EpisodeGrid extends StatelessWidget {
)
: Center();
/// Count indicator widget.
Widget _numberIndicater(BuildContext context, {int index, Color color}) =>
showNumber
? Container(
@ -196,9 +193,10 @@ class EpisodeGrid extends StatelessWidget {
)
: Center();
/// Pubdate widget
Widget _pubDate(BuildContext context, {EpisodeBrief episode, Color color}) =>
Text(
_dateToString(context, pubDate: episode.pubDate),
episode.pubDate.toDate(context),
style: TextStyle(
fontSize: context.width / 35,
color: color,
@ -259,332 +257,364 @@ class EpisodeGrid extends StatelessWidget {
bool isDownloaded = snapshot.data.item3;
bool tapToOpen = snapshot.data.item4;
List<int> menuList = snapshot.data.item5;
return Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
color: isListened > 0
? context.brightness == Brightness.light
? Colors.grey[200]
: Color.fromRGBO(40, 40, 40, 1)
: context.scaffoldBackgroundColor,
boxShadow: [
BoxShadow(
color: context.brightness == Brightness.light
? context.primaryColor
: Color.fromRGBO(40, 40, 40, 1),
blurRadius: 0.5,
spreadRadius: 0.5,
),
]),
alignment: Alignment.center,
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
border: Border.all(
color: context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
width: 1.0,
),
),
child: FocusedMenuHolder(
blurSize: 0.0,
menuItemExtent: 45,
menuBoxDecoration: BoxDecoration(
color: Colors.transparent,
borderRadius:
BorderRadius.all(Radius.circular(15.0))),
duration: Duration(milliseconds: 100),
tapMode:
tapToOpen ? TapMode.onTap : TapMode.onLongPress,
animateMenuItems: false,
blurBackgroundColor:
context.brightness == Brightness.light
? Colors.white38
: Colors.black38,
bottomOffsetHeight: 10,
menuOffset: 6,
menuItems: <FocusedMenuItem>[
FocusedMenuItem(
backgroundColor:
context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
title: Text(data.item1 != episodes[index]
? s.play
: s.playing),
trailingIcon: Icon(
LineIcons.play_circle_solid,
color: Theme.of(context).accentColor,
return (hideListened && isListened > 0)
? Center()
: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
color: isListened > 0
? context.brightness == Brightness.light
? Colors.grey[200]
: Color.fromRGBO(40, 40, 40, 1)
: context.scaffoldBackgroundColor,
boxShadow: [
BoxShadow(
color:
context.brightness == Brightness.light
? context.primaryColor
: Color.fromRGBO(40, 40, 40, 1),
blurRadius: 0.5,
spreadRadius: 0.5,
),
]),
alignment: Alignment.center,
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
border: Border.all(
color:
context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
width: 1.0,
),
onPressed: () {
if (data.item1 != episodes[index])
audio.episodeLoad(episodes[index]);
}),
menuList.contains(1)
? FocusedMenuItem(
backgroundColor:
context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
title: data.item2.contains(
episodes[index].enclosureUrl)
? Text(s.remove)
: Text(s.later),
trailingIcon: Icon(
LineIcons.clock_solid,
color: Colors.cyan,
),
onPressed: () {
if (!data.item2.contains(
episodes[index].enclosureUrl)) {
audio.addToPlaylist(episodes[index]);
Fluttertoast.showToast(
msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM,
);
} else {
audio
.delFromPlaylist(episodes[index]);
Fluttertoast.showToast(
msg: s.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM,
);
}
})
: null,
menuList.contains(2)
? FocusedMenuItem(
backgroundColor:
context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
title: isLiked
? Text(s.unlike)
: Text(s.like),
trailingIcon: Icon(LineIcons.heart,
color: Colors.red, size: 21),
onPressed: () async {
if (isLiked) {
await _setUnliked(
episodes[index].enclosureUrl);
audio.setEpisodeState = true;
Fluttertoast.showToast(
msg: s.unliked,
gravity: ToastGravity.BOTTOM,
);
} else {
await _saveLiked(
episodes[index].enclosureUrl);
audio.setEpisodeState = true;
Fluttertoast.showToast(
msg: s.liked,
gravity: ToastGravity.BOTTOM,
);
}
})
: null,
menuList.contains(3)
? FocusedMenuItem(
backgroundColor:
context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
title: isListened > 0
? Text(s.listened,
style: TextStyle(
color: context.textColor
.withOpacity(0.5)))
: Text(
s.markListened,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
child: FocusedMenuHolder(
blurSize: 0.0,
menuItemExtent: 45,
menuBoxDecoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15.0))),
duration: Duration(milliseconds: 100),
tapMode: tapToOpen
? TapMode.onTap
: TapMode.onLongPress,
animateMenuItems: false,
blurBackgroundColor:
context.brightness == Brightness.light
? Colors.white38
: Colors.black38,
bottomOffsetHeight: 10,
menuOffset: 6,
menuItems: <FocusedMenuItem>[
FocusedMenuItem(
backgroundColor: context.brightness ==
Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
title: Text(
data.item1 != episodes[index]
? s.play
: s.playing),
trailingIcon: Icon(
LineIcons.play_circle_solid,
color: Theme.of(context).accentColor,
),
onPressed: () {
if (data.item1 != episodes[index])
audio.episodeLoad(episodes[index]);
}),
menuList.contains(1)
? FocusedMenuItem(
backgroundColor: context
.brightness ==
Brightness.light
? context.primaryColor
: context
.scaffoldBackgroundColor,
title: data.item2.contains(
episodes[index]
.enclosureUrl)
? Text(s.remove)
: Text(s.later),
trailingIcon: Icon(
LineIcons.clock_solid,
color: Colors.cyan,
),
trailingIcon: SizedBox(
width: 23,
height: 23,
child: CustomPaint(
painter: ListenedAllPainter(
Colors.blue,
stroke: 1.5)),
),
onPressed: () async {
if (isListened < 1) {
await _markListened(episodes[index]);
audio.setEpisodeState = true;
Fluttertoast.showToast(
msg: s.markListened,
gravity: ToastGravity.BOTTOM,
);
}
})
: null,
menuList.contains(4)
? FocusedMenuItem(
backgroundColor:
context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
title: isDownloaded
? Text(s.downloaded,
style: TextStyle(
color: context.textColor
.withOpacity(0.5)))
: Text(s.download),
trailingIcon: Icon(
LineIcons.download_solid,
color: Colors.green),
onPressed: () {
if (!isDownloaded)
downloader.startTask(episodes[index]);
})
: null
],
action: action,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: layout == Layout.one ? 1 : 2,
child: Row(
onPressed: () {
if (!data.item2.contains(
episodes[index]
.enclosureUrl)) {
audio.addToPlaylist(
episodes[index]);
Fluttertoast.showToast(
msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM,
);
} else {
audio.delFromPlaylist(
episodes[index]);
Fluttertoast.showToast(
msg: s.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM,
);
}
})
: null,
menuList.contains(2)
? FocusedMenuItem(
backgroundColor: context
.brightness ==
Brightness.light
? context.primaryColor
: context
.scaffoldBackgroundColor,
title: isLiked
? Text(s.unlike)
: Text(s.like),
trailingIcon: Icon(LineIcons.heart,
color: Colors.red, size: 21),
onPressed: () async {
if (isLiked) {
await _setUnliked(
episodes[index]
.enclosureUrl);
audio.setEpisodeState = true;
Fluttertoast.showToast(
msg: s.unliked,
gravity: ToastGravity.BOTTOM,
);
} else {
await _saveLiked(episodes[index]
.enclosureUrl);
audio.setEpisodeState = true;
Fluttertoast.showToast(
msg: s.liked,
gravity: ToastGravity.BOTTOM,
);
}
})
: null,
menuList.contains(3)
? FocusedMenuItem(
backgroundColor: context
.brightness ==
Brightness.light
? context.primaryColor
: context
.scaffoldBackgroundColor,
title: isListened > 0
? Text(s.listened,
style: TextStyle(
color: context.textColor
.withOpacity(0.5)))
: Text(
s.markListened,
maxLines: 1,
overflow:
TextOverflow.ellipsis,
),
trailingIcon: SizedBox(
width: 23,
height: 23,
child: CustomPaint(
painter: ListenedAllPainter(
Colors.blue,
stroke: 1.5)),
),
onPressed: () async {
if (isListened < 1) {
await _markListened(
episodes[index]);
audio.setEpisodeState = true;
Fluttertoast.showToast(
msg: s.markListened,
gravity: ToastGravity.BOTTOM,
);
}
})
: null,
menuList.contains(4)
? FocusedMenuItem(
backgroundColor: context
.brightness ==
Brightness.light
? context.primaryColor
: context
.scaffoldBackgroundColor,
title: isDownloaded
? Text(s.downloaded,
style: TextStyle(
color: context.textColor
.withOpacity(0.5)))
: Text(s.download),
trailingIcon: Icon(
LineIcons.download_solid,
color: Colors.green),
onPressed: () {
if (!isDownloaded)
downloader
.startTask(episodes[index]);
})
: null
],
action: action,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: <Widget>[
layout != Layout.one
? _circleImage(context,
episode: episodes[index],
color: _c,
boo: boo)
: _pubDate(context,
episode: episodes[index],
color: _c),
Spacer(),
// _listenIndicater(context,
// episode: episodes[index],
// isListened: snapshot.data),
_isNewIndicator(episodes[index]),
_downloadIndicater(context,
episode: episodes[index],
isDownloaded: isDownloaded),
_numberIndicater(context,
index: index, color: _c)
],
),
),
Expanded(
flex: layout == Layout.one ? 3 : 5,
child: layout != Layout.one
? _title(episodes[index])
: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
_circleImage(context,
Expanded(
flex: layout == Layout.one ? 1 : 2,
child: Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: <Widget>[
layout != Layout.one
? _circleImage(context,
episode: episodes[index],
color: _c,
boo: boo)
: _pubDate(context,
episode: episodes[index],
color: _c),
Spacer(),
// _listenIndicater(context,
// episode: episodes[index],
// isListened: snapshot.data),
_isNewIndicator(episodes[index]),
_downloadIndicater(context,
episode: episodes[index],
color: _c,
boo: boo),
SizedBox(
width: 5,
),
Expanded(
child:
_title(episodes[index]))
isDownloaded: isDownloaded),
_numberIndicater(context,
index: index, color: _c)
],
),
),
Expanded(
flex: 1,
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
if (layout != Layout.one)
Align(
alignment: Alignment.bottomLeft,
child: _pubDate(context,
episode: episodes[index],
color: _c),
),
Spacer(),
layout != Layout.three &&
episodes[index].duration != 0
? Container(
alignment: Alignment.center,
child: Text(
_stringForSeconds(
episodes[index]
.duration
.toDouble()),
style: TextStyle(
fontSize: _width / 35),
),
)
: Center(),
episodes[index].duration == 0 ||
episodes[index]
.enclosureLength ==
null ||
episodes[index]
.enclosureLength ==
0 ||
layout == Layout.three
? Center()
: Text(
'|',
style: TextStyle(
fontSize: _width / 35,
// color: _c,
// fontStyle: FontStyle.italic,
),
),
layout != Layout.three &&
episodes[index]
.enclosureLength !=
null &&
episodes[index]
.enclosureLength !=
0
? Container(
alignment: Alignment.center,
child: Text(
((episodes[index]
.enclosureLength) ~/
1000000)
.toString() +
'MB',
style: TextStyle(
fontSize: _width / 35),
),
)
: Center(),
Padding(
padding: EdgeInsets.all(1),
),
showFavorite || layout != Layout.three
? isLiked
? IconTheme(
data: IconThemeData(
size: _width / 35),
child: Icon(
Icons.favorite,
color: Colors.red,
Expanded(
flex: layout == Layout.one ? 3 : 5,
child: layout != Layout.one
? _title(episodes[index])
: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
_circleImage(context,
episode:
episodes[index],
color: _c,
boo: boo),
SizedBox(
width: 5,
),
)
: Center()
: Center()
Expanded(
child: _title(
episodes[index]))
],
),
),
Expanded(
flex: 1,
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
if (layout != Layout.one)
Align(
alignment:
Alignment.bottomLeft,
child: _pubDate(context,
episode: episodes[index],
color: _c),
),
Spacer(),
layout != Layout.three &&
episodes[index]
.duration !=
0
? Container(
alignment:
Alignment.center,
child: Text(
_stringForSeconds(
episodes[index]
.duration
.toDouble()),
style: TextStyle(
fontSize:
_width / 35),
),
)
: Center(),
episodes[index].duration == 0 ||
episodes[index]
.enclosureLength ==
null ||
episodes[index]
.enclosureLength ==
0 ||
layout == Layout.three
? Center()
: Text(
'|',
style: TextStyle(
fontSize: _width / 35,
// color: _c,
// fontStyle: FontStyle.italic,
),
),
layout != Layout.three &&
episodes[index]
.enclosureLength !=
null &&
episodes[index]
.enclosureLength !=
0
? Container(
alignment:
Alignment.center,
child: Text(
((episodes[index]
.enclosureLength) ~/
1000000)
.toString() +
'MB',
style: TextStyle(
fontSize:
_width / 35),
),
)
: Center(),
Padding(
padding: EdgeInsets.all(1),
),
showFavorite ||
layout != Layout.three
? isLiked
? IconTheme(
data: IconThemeData(
size:
_width / 35),
child: Icon(
Icons.favorite,
color: Colors.red,
),
)
: Center()
: Center()
],
),
),
],
),
),
],
),
),
),
),
),
);
);
}),
),
),

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart';
import '../generated/l10n.dart';
@ -14,6 +15,7 @@ extension ContextExtension on BuildContext {
Brightness get brightness => Theme.of(this).brightness;
double get width => MediaQuery.of(this).size.width;
double get height => MediaQuery.of(this).size.height;
double get paddingTop => MediaQuery.of(this).padding.top;
TextTheme get textTheme => Theme.of(this).textTheme;
S get s => S.of(this);
}
@ -58,6 +60,10 @@ extension StringExtension on String {
await launch(this);
} else {
print('Could not launch $this');
Fluttertoast.showToast(
msg: '$this Invalid Link',
gravity: ToastGravity.TOP,
);
}
}