Add filter in podcast detail page.
This commit is contained in:
parent
c183683101
commit
6596566634
|
@ -17,7 +17,7 @@ import '../type/episodebrief.dart';
|
|||
import '../type/play_histroy.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import 'episode_download.dart';
|
||||
|
||||
class EpisodeDetail extends StatefulWidget {
|
||||
|
|
|
@ -127,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"feedbackGithub" : MessageLookupByLibrary.simpleMessage("Submit issue"),
|
||||
"feedbackPlay" : MessageLookupByLibrary.simpleMessage("Rate on Play"),
|
||||
"feedbackTelegram" : MessageLookupByLibrary.simpleMessage("Join group"),
|
||||
"filter" : MessageLookupByLibrary.simpleMessage("Filter"),
|
||||
"fonts" : MessageLookupByLibrary.simpleMessage("Fonts"),
|
||||
"from" : m5,
|
||||
"goodNight" : MessageLookupByLibrary.simpleMessage("Good Night"),
|
||||
|
@ -209,6 +210,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"removedAt" : m21,
|
||||
"save" : MessageLookupByLibrary.simpleMessage("Save"),
|
||||
"schedule" : MessageLookupByLibrary.simpleMessage("Schedule"),
|
||||
"search" : MessageLookupByLibrary.simpleMessage("Search"),
|
||||
"searchEpisode" : MessageLookupByLibrary.simpleMessage("Search episode"),
|
||||
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("Invalid RSS link"),
|
||||
"searchPodcast" : MessageLookupByLibrary.simpleMessage("Search podcast"),
|
||||
"secondsAgo" : m22,
|
||||
|
|
|
@ -127,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"feedbackGithub" : MessageLookupByLibrary.simpleMessage("Soumettre un problème"),
|
||||
"feedbackPlay" : MessageLookupByLibrary.simpleMessage("Noter sur le PlayStore"),
|
||||
"feedbackTelegram" : MessageLookupByLibrary.simpleMessage("Joindre le groupe"),
|
||||
"filter" : MessageLookupByLibrary.simpleMessage("Filter"),
|
||||
"fonts" : MessageLookupByLibrary.simpleMessage("Polices"),
|
||||
"from" : m5,
|
||||
"goodNight" : MessageLookupByLibrary.simpleMessage("Bonne nuit"),
|
||||
|
@ -209,6 +210,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"removedAt" : m21,
|
||||
"save" : MessageLookupByLibrary.simpleMessage("Sauvegarder"),
|
||||
"schedule" : MessageLookupByLibrary.simpleMessage("Programmation"),
|
||||
"search" : MessageLookupByLibrary.simpleMessage("Search"),
|
||||
"searchEpisode" : MessageLookupByLibrary.simpleMessage("Search episode"),
|
||||
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("Lien RSS invalide"),
|
||||
"searchPodcast" : MessageLookupByLibrary.simpleMessage("Chercher un podcast"),
|
||||
"secondsAgo" : m22,
|
||||
|
|
|
@ -127,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"feedbackGithub" : MessageLookupByLibrary.simpleMessage("提交Issue"),
|
||||
"feedbackPlay" : MessageLookupByLibrary.simpleMessage("Play评价"),
|
||||
"feedbackTelegram" : MessageLookupByLibrary.simpleMessage("加入小组"),
|
||||
"filter" : MessageLookupByLibrary.simpleMessage("过滤"),
|
||||
"fonts" : MessageLookupByLibrary.simpleMessage("字体"),
|
||||
"from" : m5,
|
||||
"goodNight" : MessageLookupByLibrary.simpleMessage("晚安"),
|
||||
|
@ -209,6 +210,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"removedAt" : m21,
|
||||
"save" : MessageLookupByLibrary.simpleMessage("保存"),
|
||||
"schedule" : MessageLookupByLibrary.simpleMessage("定时"),
|
||||
"search" : MessageLookupByLibrary.simpleMessage("搜索"),
|
||||
"searchEpisode" : MessageLookupByLibrary.simpleMessage("搜索节目"),
|
||||
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("RSS 链接错误"),
|
||||
"searchPodcast" : MessageLookupByLibrary.simpleMessage("搜索播客"),
|
||||
"secondsAgo" : m22,
|
||||
|
|
|
@ -548,6 +548,16 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Filter`
|
||||
String get filter {
|
||||
return Intl.message(
|
||||
'Filter',
|
||||
name: 'filter',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Fonts`
|
||||
String get fonts {
|
||||
return Intl.message(
|
||||
|
@ -1386,6 +1396,26 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Search`
|
||||
String get search {
|
||||
return Intl.message(
|
||||
'Search',
|
||||
name: 'search',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Search episode`
|
||||
String get searchEpisode {
|
||||
return Intl.message(
|
||||
'Search episode',
|
||||
name: 'searchEpisode',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Invalid RSS link`
|
||||
String get searchInvalidRss {
|
||||
return Intl.message(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tsacdop/util/custompaint.dart';
|
||||
import 'package:tsacdop/util/custom_widget.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../util/extension_helper.dart';
|
||||
|
@ -64,9 +64,9 @@ class AboutApp extends StatelessWidget {
|
|||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
appBar: AppBar(
|
||||
title: Text(s.homeToprightMenuAbout),
|
||||
),
|
||||
appBar: AppBar(title: Center()
|
||||
// Text(s.homeToprightMenuAbout),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
|
|
|
@ -15,7 +15,7 @@ import '../local_storage/sqflite_localpodcast.dart';
|
|||
import '../local_storage/key_value_storage.dart';
|
||||
import '../util/pageroute.dart';
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import '../util/custom_slider.dart';
|
||||
import '../episodes/episode_detail.dart';
|
||||
import 'playlist.dart';
|
||||
|
|
|
@ -18,7 +18,7 @@ import '../local_storage/key_value_storage.dart';
|
|||
import '../util/episodegrid.dart';
|
||||
import '../util/mypopupmenu.dart';
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import '../state/download_state.dart';
|
||||
import '../state/podcast_group.dart';
|
||||
import '../state/setting_state.dart';
|
||||
|
|
|
@ -17,7 +17,7 @@ import '../state/podcast_group.dart';
|
|||
import '../state/download_state.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
import '../state/audio_state.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import '../util/pageroute.dart';
|
||||
import '../util/extension_helper.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
|
|
|
@ -12,7 +12,7 @@ import '../state/audio_state.dart';
|
|||
import '../type/episodebrief.dart';
|
||||
import '../type/playlist.dart';
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
|
||||
class PlaylistPage extends StatefulWidget {
|
||||
@override
|
||||
|
|
|
@ -112,6 +112,8 @@
|
|||
},
|
||||
"feedbackTelegram": "Join group",
|
||||
"@feedbackTelegram": {},
|
||||
"filter": "Filter",
|
||||
"@filter": {},
|
||||
"fonts": "Fonts",
|
||||
"@fonts": {},
|
||||
"from": "From {time}",
|
||||
|
@ -333,6 +335,10 @@
|
|||
"@save": {},
|
||||
"schedule": "Schedule",
|
||||
"@schedule": {},
|
||||
"search": "Search",
|
||||
"@search": {},
|
||||
"searchEpisode": "Search episode",
|
||||
"@searchEpisode": {},
|
||||
"searchInvalidRss": "Invalid RSS link",
|
||||
"@searchInvalidRss": {},
|
||||
"searchPodcast": "Search podcast",
|
||||
|
|
|
@ -112,6 +112,8 @@
|
|||
},
|
||||
"feedbackTelegram": "Joindre le groupe",
|
||||
"@feedbackTelegram": {},
|
||||
"filter": "Filter",
|
||||
"@filter": {},
|
||||
"fonts": "Polices",
|
||||
"@fonts": {},
|
||||
"from": "De {time}",
|
||||
|
@ -333,6 +335,10 @@
|
|||
"@save": {},
|
||||
"schedule": "Programmation",
|
||||
"@schedule": {},
|
||||
"search": "Search",
|
||||
"@search": {},
|
||||
"searchEpisode": "Search episode",
|
||||
"@searchEpisode": {},
|
||||
"searchInvalidRss": "Lien RSS invalide",
|
||||
"@searchInvalidRss": {},
|
||||
"searchPodcast": "Chercher un podcast",
|
||||
|
|
|
@ -112,6 +112,8 @@
|
|||
},
|
||||
"feedbackTelegram": "加入小组",
|
||||
"@feedbackTelegram": {},
|
||||
"filter": "过滤",
|
||||
"@filter": {},
|
||||
"fonts": "字体",
|
||||
"@fonts": {},
|
||||
"from": "自{time}",
|
||||
|
@ -333,6 +335,10 @@
|
|||
"@save": {},
|
||||
"schedule": "定时",
|
||||
"@schedule": {},
|
||||
"search": "搜索",
|
||||
"@search": {},
|
||||
"searchEpisode": "搜索节目",
|
||||
"@searchEpisode": {},
|
||||
"searchInvalidRss": "RSS 链接错误",
|
||||
"@searchInvalidRss": {},
|
||||
"searchPodcast": "搜索播客",
|
||||
|
|
|
@ -558,7 +558,9 @@ class DBHelper {
|
|||
}
|
||||
|
||||
Future<List<EpisodeBrief>> getRssItem(String id, int count, bool reverse,
|
||||
{Filter filter = Filter.all, String query = ''}) async {
|
||||
{Filter filter = Filter.all,
|
||||
String query = '',
|
||||
bool hideListened = false}) async {
|
||||
var dbClient = await database;
|
||||
List<EpisodeBrief> episodes = [];
|
||||
List<Map> list = [];
|
||||
|
@ -634,15 +636,15 @@ class DBHelper {
|
|||
} else {
|
||||
switch (filter) {
|
||||
case Filter.all:
|
||||
list = await dbClient
|
||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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 ?""",
|
||||
|
@ -650,7 +652,7 @@ class DBHelper {
|
|||
break;
|
||||
case Filter.downloaded:
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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 ?""",
|
||||
|
@ -658,7 +660,7 @@ class DBHelper {
|
|||
break;
|
||||
case Filter.search:
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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 ?""",
|
||||
|
@ -667,20 +669,38 @@ class DBHelper {
|
|||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
}
|
||||
if (list.isNotEmpty) {
|
||||
if (!hideListened)
|
||||
for (var i in list) {
|
||||
episodes.add(EpisodeBrief(
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feedTitle'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
else
|
||||
for (var i in list) {
|
||||
int listened = await isListened(i['enclosure_url']);
|
||||
if (listened == 0)
|
||||
episodes.add(EpisodeBrief(
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feedTitle'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
|
@ -690,14 +710,14 @@ class DBHelper {
|
|||
List<Map> list;
|
||||
if (id == 'all')
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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""",
|
||||
|
@ -705,6 +725,30 @@ class DBHelper {
|
|||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
Future<List<EpisodeBrief>> getRssItemTop(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.is_new,
|
||||
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'],
|
||||
|
@ -714,31 +758,7 @@ class DBHelper {
|
|||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
Future<List<EpisodeBrief>> getRssItemTop(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.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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['is_new']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
@ -746,23 +766,23 @@ class DBHelper {
|
|||
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,
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
@ -773,24 +793,24 @@ class DBHelper {
|
|||
List<EpisodeBrief> episodes = [];
|
||||
if (group.length > 0) {
|
||||
List<String> s = group.map<String>((e) => "'$e'").toList();
|
||||
List<Map> list = await dbClient
|
||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
}
|
||||
return episodes;
|
||||
|
@ -800,23 +820,23 @@ class DBHelper {
|
|||
var dbClient = await database;
|
||||
List<EpisodeBrief> episodes = [];
|
||||
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.is_new,
|
||||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
@ -824,24 +844,24 @@ class DBHelper {
|
|||
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,
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
|
||||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
@ -853,7 +873,7 @@ class DBHelper {
|
|||
//Ordered by date
|
||||
if (mode == 0)
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, E.is_new,
|
||||
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
|
||||
|
@ -862,7 +882,7 @@ class DBHelper {
|
|||
//Ordered by date
|
||||
else if (mode == 1)
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,E.is_new,
|
||||
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
|
||||
|
@ -871,7 +891,7 @@ class DBHelper {
|
|||
//Ordered by size
|
||||
else if (mode == 2)
|
||||
list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,E.is_new,
|
||||
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
|
||||
|
@ -889,6 +909,7 @@ class DBHelper {
|
|||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new'],
|
||||
downloadDate: i['download_date']),
|
||||
);
|
||||
}
|
||||
|
@ -908,7 +929,7 @@ class DBHelper {
|
|||
if (group.length > 0) {
|
||||
List<String> s = group.map<String>((e) => "'$e'").toList();
|
||||
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.is_new,
|
||||
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
|
||||
|
@ -916,16 +937,16 @@ class DBHelper {
|
|||
);
|
||||
for (var i in list) {
|
||||
episodes.add(EpisodeBrief(
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
}
|
||||
return episodes;
|
||||
|
@ -957,40 +978,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, P.primaryColor
|
||||
P.title as feed_title, E.duration, E.explicit, P.primaryColor, E.is_new
|
||||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
} 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, P.primaryColor
|
||||
P.title as feed_title, E.duration, E.explicit, P.primaryColor, E.is_new
|
||||
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['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
));
|
||||
i['title'],
|
||||
i['enclosure_url'],
|
||||
i['enclosure_length'],
|
||||
i['milliseconds'],
|
||||
i['feed_title'],
|
||||
i['primaryColor'],
|
||||
i['duration'],
|
||||
i['explicit'],
|
||||
i['imagePath'],
|
||||
i['is_new']));
|
||||
}
|
||||
}
|
||||
return episodes;
|
||||
|
@ -1085,7 +1106,7 @@ 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, P.skip_seconds,
|
||||
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,E.is_new,
|
||||
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) {
|
||||
|
@ -1101,6 +1122,7 @@ class DBHelper {
|
|||
list.first['duration'],
|
||||
list.first['explicit'],
|
||||
list.first['imagePath'],
|
||||
list.first['is_new'],
|
||||
mediaId: list.first['media_id'],
|
||||
skipSeconds: list.first['skip_seconds']);
|
||||
return episode;
|
||||
|
@ -1112,7 +1134,7 @@ 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, P.skip_seconds,
|
||||
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,E.is_new,
|
||||
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)
|
||||
|
@ -1128,6 +1150,7 @@ class DBHelper {
|
|||
list.first['duration'],
|
||||
list.first['explicit'],
|
||||
list.first['imagePath'],
|
||||
list.first['is_new'],
|
||||
mediaId: list.first['media_id'],
|
||||
skipSeconds: list.first['skip_seconds']);
|
||||
return episode;
|
||||
|
|
|
@ -23,7 +23,7 @@ import '../util/episodegrid.dart';
|
|||
import '../home/audioplayer.dart';
|
||||
import '../type/fireside_data.dart';
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import '../util/general_dialog.dart';
|
||||
import '../state/audio_state.dart';
|
||||
|
||||
|
@ -147,7 +147,8 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
int index = await storage.getInt(defaultValue: 1);
|
||||
if (_layout == null) _layout = Layout.values[index];
|
||||
episodes = await dbHelper.getRssItem(podcastLocal.id, count, reverse,
|
||||
filter: filter, query: query);
|
||||
filter: filter, query: query, hideListened: _hideListened);
|
||||
|
||||
if (podcastLocal.provider.contains('fireside')) {
|
||||
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
||||
await data.getData();
|
||||
|
@ -263,24 +264,6 @@ 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),
|
||||
|
@ -308,6 +291,350 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
],
|
||||
);
|
||||
|
||||
_customPopupMenu(
|
||||
{Widget child,
|
||||
String tooltip,
|
||||
List<PopupMenuEntry<int>> itemBuilder,
|
||||
Function(int) onSelected,
|
||||
bool clip = true}) =>
|
||||
Material(
|
||||
key: UniqueKey(),
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
clipBehavior: clip ? Clip.hardEdge : Clip.none,
|
||||
child: PopupMenuButton<int>(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
elevation: 1,
|
||||
tooltip: tooltip,
|
||||
child: child,
|
||||
itemBuilder: (context) => itemBuilder,
|
||||
onSelected: (value) => onSelected(value),
|
||||
),
|
||||
);
|
||||
Widget _rightTopMenu(BuildContext context) {
|
||||
final s = context.s;
|
||||
return _customPopupMenu(
|
||||
tooltip: s.menu,
|
||||
clip: false,
|
||||
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),
|
||||
),
|
||||
Text(s.menuVisitSite),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.rss_feed,
|
||||
color: context.textColor,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
),
|
||||
Text(s.menuViewRSS),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CustomPaint(
|
||||
painter:
|
||||
ListenedAllPainter(context.textColor, stroke: 2)),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
),
|
||||
Text(
|
||||
s.menuMarkAllListened,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _actionBar(BuildContext context) {
|
||||
final s = context.s;
|
||||
return Container(
|
||||
height: 30,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
SizedBox(width: 10),
|
||||
_customPopupMenu(
|
||||
tooltip: s.homeSubMenuSortBy,
|
||||
child: Container(
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
border: Border.all(color: context.primaryColorDark),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(s.homeSubMenuSortBy),
|
||||
SizedBox(width: 5),
|
||||
Icon(
|
||||
_reverse
|
||||
? LineIcons.hourglass_start_solid
|
||||
: LineIcons.hourglass_end_solid,
|
||||
size: 18,
|
||||
)
|
||||
],
|
||||
)),
|
||||
itemBuilder: [
|
||||
PopupMenuItem(
|
||||
value: 0,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(s.newestFirst),
|
||||
Spacer(),
|
||||
if (!_reverse) DotIndicator()
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(s.oldestFirst),
|
||||
Spacer(),
|
||||
if (_reverse) DotIndicator()
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
onSelected: (value) {
|
||||
if (value == 0)
|
||||
setState(() => _reverse = false);
|
||||
else if (value == 1) setState(() => _reverse = true);
|
||||
},
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
_customPopupMenu(
|
||||
tooltip: s.filter,
|
||||
child: Container(
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
border: Border.all(color: context.primaryColorDark)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(s.filter),
|
||||
SizedBox(width: 5),
|
||||
Icon(
|
||||
LineIcons.filter_solid,
|
||||
size: 18,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: [
|
||||
PopupMenuItem(
|
||||
value: 0,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(s.all),
|
||||
Spacer(),
|
||||
if (_filter == Filter.all) DotIndicator(),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(s.homeTabMenuFavotite),
|
||||
Spacer(),
|
||||
if (_filter == Filter.liked) DotIndicator()
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(s.downloaded),
|
||||
Spacer(),
|
||||
if (_filter == Filter.downloaded) DotIndicator()
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 5, bottom: 5, left: 2, right: 2),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: context.textColor.withOpacity(0.2))),
|
||||
child: _query == ''
|
||||
? Row(
|
||||
children: [
|
||||
Text(s.search,
|
||||
style: TextStyle(
|
||||
color: context.textColor
|
||||
.withOpacity(0.4))),
|
||||
Spacer()
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(_query,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: context.accentColor)),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
],
|
||||
onSelected: (value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
if (_filter != Filter.all)
|
||||
setState(() {
|
||||
_filter = Filter.all;
|
||||
_query = '';
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
if (_filter != Filter.liked)
|
||||
setState(() {
|
||||
_query = '';
|
||||
_filter = Filter.liked;
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
if (_filter != Filter.downloaded)
|
||||
setState(() {
|
||||
_query = '';
|
||||
_filter = Filter.downloaded;
|
||||
});
|
||||
break;
|
||||
case 3:
|
||||
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,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
child: IconButton(
|
||||
icon: SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: HideListened(
|
||||
hideListened: _hideListened,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() => _hideListened = !_hideListened);
|
||||
},
|
||||
)),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
if (_layout == Layout.three)
|
||||
setState(() {
|
||||
_layout = Layout.one;
|
||||
});
|
||||
else if (_layout == Layout.two)
|
||||
setState(() {
|
||||
_layout = Layout.three;
|
||||
});
|
||||
else
|
||||
setState(() {
|
||||
_layout = Layout.two;
|
||||
});
|
||||
},
|
||||
icon: _layout == Layout.three
|
||||
? IconPainter(
|
||||
LayoutPainter(0, context.textColor),
|
||||
)
|
||||
: _layout == Layout.two
|
||||
? IconPainter(
|
||||
LayoutPainter(1, context.textColor),
|
||||
)
|
||||
: IconPainter(
|
||||
LayoutPainter(4, context.textColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color _color = widget.podcastLocal.primaryColor.colorizedark();
|
||||
|
@ -375,103 +702,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
slivers: <Widget>[
|
||||
SliverAppBar(
|
||||
brightness: Brightness.dark,
|
||||
actions: <Widget>[
|
||||
_customPopupMenu(
|
||||
tooltip: s.menu,
|
||||
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),
|
||||
),
|
||||
Text(s.menuVisitSite),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.only(left: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.rss_feed,
|
||||
color: context.textColor,
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text(s.menuViewRSS),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.only(left: 10),
|
||||
child: Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CustomPaint(
|
||||
painter:
|
||||
ListenedAllPainter(
|
||||
context
|
||||
.textColor,
|
||||
stroke: 2)),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text(
|
||||
s.menuMarkAllListened,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
actions: <Widget>[_rightTopMenu(context)],
|
||||
elevation: 0,
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.white,
|
||||
|
@ -559,250 +790,18 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
child: hostsList(context, _hosts),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 30,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: _customPopupMenu(
|
||||
tooltip: s.homeSubMenuSortBy,
|
||||
child: Container(
|
||||
height: 30,
|
||||
padding:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 15),
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(s
|
||||
.homeSubMenuSortBy),
|
||||
SizedBox(width: 10),
|
||||
Icon(
|
||||
_reverse
|
||||
? LineIcons
|
||||
.hourglass_start_solid
|
||||
: LineIcons
|
||||
.hourglass_end_solid,
|
||||
size: 18,
|
||||
)
|
||||
],
|
||||
)),
|
||||
itemBuilder: [
|
||||
PopupMenuItem(
|
||||
value: 0,
|
||||
child:
|
||||
Text(s.newestFirst),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child:
|
||||
Text(s.oldestFirst),
|
||||
)
|
||||
],
|
||||
onSelected: (value) {
|
||||
if (value == 0)
|
||||
setState(() =>
|
||||
_reverse = false);
|
||||
else if (value == 1)
|
||||
setState(() =>
|
||||
_reverse = true);
|
||||
},
|
||||
),
|
||||
),
|
||||
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,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
if (_layout == Layout.three)
|
||||
setState(() {
|
||||
_layout = Layout.one;
|
||||
});
|
||||
else if (_layout ==
|
||||
Layout.two)
|
||||
setState(() {
|
||||
_layout = Layout.three;
|
||||
});
|
||||
else
|
||||
setState(() {
|
||||
_layout = Layout.two;
|
||||
});
|
||||
},
|
||||
icon: _layout == Layout.three
|
||||
? SizedBox(
|
||||
height: 10,
|
||||
width: 30,
|
||||
child: CustomPaint(
|
||||
painter: LayoutPainter(
|
||||
0,
|
||||
context
|
||||
.textTheme
|
||||
.bodyText1
|
||||
.color),
|
||||
),
|
||||
)
|
||||
: _layout == Layout.two
|
||||
? SizedBox(
|
||||
height: 10,
|
||||
width: 30,
|
||||
child:
|
||||
CustomPaint(
|
||||
painter: LayoutPainter(
|
||||
1,
|
||||
context
|
||||
.textTheme
|
||||
.bodyText1
|
||||
.color),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
height: 10,
|
||||
width: 30,
|
||||
child:
|
||||
CustomPaint(
|
||||
painter: LayoutPainter(
|
||||
4,
|
||||
context
|
||||
.textTheme
|
||||
.bodyText1
|
||||
.color),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
child: _actionBar(context)),
|
||||
EpisodeGrid(
|
||||
episodes: snapshot.data,
|
||||
showFavorite: true,
|
||||
showNumber: true,
|
||||
showNumber: _filter == Filter.all &&
|
||||
!_hideListened
|
||||
? true
|
||||
: false,
|
||||
layout: _layout,
|
||||
reverse: _reverse,
|
||||
episodeCount: _episodeCount,
|
||||
initNum: _scroll ? 0 : 12,
|
||||
hideListened: _hideListened,
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
|
@ -866,14 +865,6 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||
if (mounted) setState(() => _load = true);
|
||||
}
|
||||
|
||||
_launchUrl(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -906,7 +897,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||
children: <Widget>[
|
||||
Linkify(
|
||||
onOpen: (link) {
|
||||
_launchUrl(link.url);
|
||||
link.url.launchUrl;
|
||||
},
|
||||
text: _description,
|
||||
linkStyle: TextStyle(
|
||||
|
@ -920,7 +911,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||
)
|
||||
: Linkify(
|
||||
onOpen: (link) {
|
||||
_launchUrl(link.url);
|
||||
link.url.launchUrl;
|
||||
},
|
||||
text: _description,
|
||||
linkStyle: TextStyle(
|
||||
|
@ -933,7 +924,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||
return Linkify(
|
||||
text: _description,
|
||||
onOpen: (link) {
|
||||
_launchUrl(link.url);
|
||||
link.url.launchUrl;
|
||||
},
|
||||
linkStyle: TextStyle(
|
||||
color: Theme.of(context).accentColor,
|
||||
|
@ -956,12 +947,10 @@ class SearchEpisdoe extends StatefulWidget {
|
|||
class _SearchEpisodeState extends State<SearchEpisdoe> {
|
||||
TextEditingController _controller;
|
||||
String _query;
|
||||
int _error;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_error = 0;
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
|
||||
|
@ -1007,18 +996,18 @@ class _SearchEpisodeState extends State<SearchEpisdoe> {
|
|||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(s.confirm,
|
||||
style: TextStyle(color: Theme.of(context).accentColor)),
|
||||
child:
|
||||
Text(s.confirm, style: TextStyle(color: context.accentColor)),
|
||||
)
|
||||
],
|
||||
title: SizedBox(width: context.width - 160, child: Text(s.newGroup)),
|
||||
title: SizedBox(width: context.width - 160, child: Text(s.search)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
hintText: s.newGroup,
|
||||
hintText: s.searchEpisode,
|
||||
hintStyle: TextStyle(fontSize: 18),
|
||||
filled: true,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
|
@ -1038,15 +1027,6 @@ class _SearchEpisodeState extends State<SearchEpisdoe> {
|
|||
_query = value;
|
||||
},
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: (_error == 1)
|
||||
? Text(
|
||||
s.groupExisted,
|
||||
style: TextStyle(color: Colors.red[400]),
|
||||
)
|
||||
: Center(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
|
|||
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/episodegrid.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import '../local_storage/key_value_storage.dart';
|
||||
import 'popup_menu.dart';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:line_icons/line_icons.dart';
|
|||
import 'package:flare_flutter/flare_actor.dart';
|
||||
|
||||
import '../util/extension_helper.dart';
|
||||
import '../util/custompaint.dart';
|
||||
import '../util/custom_widget.dart';
|
||||
import '../local_storage/key_value_storage.dart';
|
||||
|
||||
class PopupMenuSetting extends StatefulWidget {
|
||||
|
|
|
@ -29,10 +29,10 @@ class EpisodeBrief {
|
|||
this.duration,
|
||||
this.explicit,
|
||||
this.imagePath,
|
||||
this.isNew,
|
||||
{this.mediaId,
|
||||
this.liked,
|
||||
this.downloaded,
|
||||
this.isNew,
|
||||
this.skipSeconds,
|
||||
this.description = '',
|
||||
this.downloadDate = 0})
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'dart:ui' as ui;
|
|||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'extension_helper.dart';
|
||||
|
||||
//Layout change indicator
|
||||
class LayoutPainter extends CustomPainter {
|
||||
double scale;
|
||||
|
@ -175,8 +177,8 @@ class MarkListenedPainter extends CustomPainter {
|
|||
_path.lineTo(size.width / 3, size.height * 3 / 4);
|
||||
_path.moveTo(size.width / 2, size.height * 3 / 8);
|
||||
_path.lineTo(size.width / 2, size.height * 5 / 8);
|
||||
// _path.moveTo(size.width * 2 / 3, size.height * 4 / 9);
|
||||
// _path.lineTo(size.width * 2 / 3, size.height * 5 / 9);
|
||||
// _path.moveTo(size.width * 2 / 3, size.height * 4 / 9);
|
||||
// _path.lineTo(size.width * 2 / 3, size.height * 5 / 9);
|
||||
_path.moveTo(size.width / 2, size.height * 13 / 18);
|
||||
_path.lineTo(size.width * 5 / 6, size.height * 13 / 18);
|
||||
_path.moveTo(size.width * 2 / 3, size.height * 5 / 9);
|
||||
|
@ -191,6 +193,108 @@ class MarkListenedPainter extends CustomPainter {
|
|||
}
|
||||
}
|
||||
|
||||
/// Hide listened painter.
|
||||
class HideListenedPainter extends CustomPainter {
|
||||
Color color;
|
||||
Color backgroundColor;
|
||||
double fraction;
|
||||
double stroke;
|
||||
HideListenedPainter(
|
||||
{this.color, this.stroke = 1.0, this.backgroundColor, this.fraction});
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Paint _paint = Paint()
|
||||
..color = color
|
||||
..strokeWidth = stroke
|
||||
..strokeCap = StrokeCap.round
|
||||
..style = PaintingStyle.stroke;
|
||||
Paint _linePaint = Paint()
|
||||
..color = backgroundColor
|
||||
..strokeWidth = stroke * 2
|
||||
..strokeCap = StrokeCap.round
|
||||
..style = PaintingStyle.stroke;
|
||||
Path _path = Path();
|
||||
|
||||
_path.moveTo(size.width / 6, size.height * 3 / 8);
|
||||
_path.lineTo(size.width / 6, size.height * 5 / 8);
|
||||
_path.moveTo(size.width / 3, size.height / 4);
|
||||
_path.lineTo(size.width / 3, size.height * 3 / 4);
|
||||
_path.moveTo(size.width / 2, size.height / 8);
|
||||
_path.lineTo(size.width / 2, size.height * 7 / 8);
|
||||
_path.moveTo(size.width * 5 / 6, size.height * 3 / 8);
|
||||
_path.lineTo(size.width * 5 / 6, size.height * 5 / 8);
|
||||
_path.moveTo(size.width * 2 / 3, size.height / 4);
|
||||
_path.lineTo(size.width * 2 / 3, size.height * 3 / 4);
|
||||
|
||||
canvas.drawPath(_path, _paint);
|
||||
if (fraction > 0)
|
||||
canvas.drawLine(
|
||||
Offset(size.width, size.height) / 5,
|
||||
Offset(size.width, size.height) / 5 +
|
||||
Offset(size.width, size.height) * 3 / 5 * fraction,
|
||||
_linePaint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class HideListened extends StatefulWidget {
|
||||
final bool hideListened;
|
||||
HideListened({this.hideListened, Key key}) : super(key: key);
|
||||
@override
|
||||
_HideListenedState createState() => _HideListenedState();
|
||||
}
|
||||
|
||||
class _HideListenedState extends State<HideListened>
|
||||
with SingleTickerProviderStateMixin {
|
||||
double _fraction = 0.0;
|
||||
Animation animation;
|
||||
AnimationController _controller;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller =
|
||||
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
|
||||
animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
|
||||
..addListener(() {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_fraction = animation.value;
|
||||
});
|
||||
});
|
||||
if (widget.hideListened) _controller.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(HideListened oldWidget) {
|
||||
if (oldWidget.hideListened != widget.hideListened) {
|
||||
if (widget.hideListened)
|
||||
_controller.forward();
|
||||
else
|
||||
_controller.reverse();
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
painter: HideListenedPainter(
|
||||
fraction: _fraction,
|
||||
color: context.textColor,
|
||||
backgroundColor: context.accentColor));
|
||||
}
|
||||
}
|
||||
|
||||
//Add new episode to palylist
|
||||
class AddToPlaylistPainter extends CustomPainter {
|
||||
Color _color;
|
||||
|
@ -727,3 +831,39 @@ class _HeartOpenState extends State<HeartOpen>
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Icon painter.
|
||||
class IconPainter extends StatelessWidget {
|
||||
const IconPainter(this.painter, {this.height = 10, this.width = 30, Key key})
|
||||
: super(key: key);
|
||||
final double height;
|
||||
final double width;
|
||||
final CustomPainter painter;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: CustomPaint(
|
||||
painter: painter,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A dot.
|
||||
class DotIndicator extends StatelessWidget {
|
||||
DotIndicator({this.radius = 8, Key key})
|
||||
: assert(radius > 0),
|
||||
super(key: key);
|
||||
final double radius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: radius,
|
||||
height: radius,
|
||||
decoration:
|
||||
BoxDecoration(shape: BoxShape.circle, color: context.accentColor));
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ import '../episodes/episode_detail.dart';
|
|||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../local_storage/key_value_storage.dart';
|
||||
import 'extension_helper.dart';
|
||||
import 'custompaint.dart';
|
||||
import 'custom_widget.dart';
|
||||
|
||||
enum Layout { three, two, one }
|
||||
|
||||
|
@ -35,23 +35,20 @@ class EpisodeGrid extends StatelessWidget {
|
|||
final Layout layout;
|
||||
final bool reverse;
|
||||
|
||||
/// Count of animation items.
|
||||
final int initNum;
|
||||
|
||||
/// 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);
|
||||
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);
|
||||
|
||||
Future<int> _isListened(EpisodeBrief episode) async {
|
||||
DBHelper dbHelper = DBHelper();
|
||||
|
@ -257,364 +254,332 @@ class EpisodeGrid extends StatelessWidget {
|
|||
bool isDownloaded = snapshot.data.item3;
|
||||
bool tapToOpen = snapshot.data.item4;
|
||||
List<int> menuList = snapshot.data.item5;
|
||||
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:
|
||||
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,
|
||||
),
|
||||
onPressed: () {
|
||||
if (data.item1 != episodes[index])
|
||||
audio.episodeLoad(episodes[index]);
|
||||
}),
|
||||
menuList.contains(1)
|
||||
? FocusedMenuItem(
|
||||
backgroundColor:
|
||||
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,
|
||||
),
|
||||
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,
|
||||
: 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,
|
||||
),
|
||||
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(
|
||||
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(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
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],
|
||||
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,
|
||||
episode:
|
||||
episodes[index],
|
||||
color: _c,
|
||||
boo: boo),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
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()
|
||||
],
|
||||
),
|
||||
),
|
||||
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,
|
||||
episode: episodes[index],
|
||||
color: _c,
|
||||
boo: boo),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
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()
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue