mirror of
https://github.com/stonega/tsacdop
synced 2025-02-11 09:00:36 +01:00
modified: .circleci/config.yml
modified: lib/class/episodebrief.dart modified: lib/class/podcast_group.dart deleted: lib/class/podcastrss.dart deleted: lib/class/podcastrss.g.dart deleted: lib/class/podcasts.dart deleted: lib/class/podcasts.g.dart modified: lib/episodes/episodedetail.dart modified: lib/home/appbar/about.dart modified: lib/home/appbar/addpodcast.dart modified: lib/home/appbar/popupmenu.dart modified: lib/home/homescroll.dart modified: lib/local_storage/sqflite_localpodcast.dart modified: lib/main.dart modified: lib/podcasts/podcastdetail.dart modified: lib/podcasts/podcastgroup.dart modified: lib/podcasts/podcastlist.dart modified: lib/podcasts/podcastmanage.dart modified: lib/util/episodegrid.dart modified: lib/webfeed/domain/rss_itunes.dart modified: pubspec.lock modified: pubspec.yaml
This commit is contained in:
parent
69dfc393ba
commit
9d4bbc895a
@ -2,7 +2,7 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
- image: cirrusci/flutter:v1.13.6
|
- image: cirrusci/flutter:v1.14.6
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only: master
|
only: master
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class EpisodeBrief {
|
class EpisodeBrief {
|
||||||
final String title;
|
final String title;
|
||||||
String description;
|
String description;
|
||||||
final String pubDate;
|
final int pubDate;
|
||||||
final int enclosureLength;
|
final int enclosureLength;
|
||||||
final String enclosureUrl;
|
final String enclosureUrl;
|
||||||
final String feedTitle;
|
final String feedTitle;
|
||||||
@ -24,5 +26,16 @@ class EpisodeBrief {
|
|||||||
this.explicit,
|
this.explicit,
|
||||||
this.imagePath
|
this.imagePath
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String dateToString(){
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate);
|
||||||
|
var diffrence = DateTime.now().difference(date);
|
||||||
|
if(diffrence.inHours < 24) {
|
||||||
|
return '${diffrence.inHours} hours ago';
|
||||||
|
} else if (diffrence.inDays < 7){
|
||||||
|
return '${diffrence.inDays} days ago';}
|
||||||
|
else {
|
||||||
|
return DateFormat.yMMMd().format( DateTime.fromMillisecondsSinceEpoch(pubDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,8 @@ class GroupEntity {
|
|||||||
static GroupEntity fromJson(Map<String, Object> json) {
|
static GroupEntity fromJson(Map<String, Object> json) {
|
||||||
List<String> list = List.from(json['podcastList']);
|
List<String> list = List.from(json['podcastList']);
|
||||||
print(json['[podcastList']);
|
print(json['[podcastList']);
|
||||||
return
|
return GroupEntity(json['name'] as String, json['id'] as String,
|
||||||
GroupEntity(
|
json['color'] as String, list);
|
||||||
json['name'] as String,
|
|
||||||
json['id'] as String,
|
|
||||||
json['color'] as String,
|
|
||||||
list);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,11 +87,11 @@ class GroupList extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
storage.getGroups().then((loadgroups) async {
|
storage.getGroups().then((loadgroups) async {
|
||||||
_groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e)));
|
_groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e)));
|
||||||
await Future.forEach(_groups, (group) async {
|
await Future.forEach(_groups, (group) async {
|
||||||
await group.getPodcasts();
|
await group.getPodcasts();
|
||||||
});
|
});
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +120,7 @@ class GroupList extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future subscribe(PodcastLocal podcastLocal) async {
|
Future subscribe(PodcastLocal podcastLocal) async {
|
||||||
_groups[0].podcastList.add(podcastLocal.id);
|
_groups[0].podcastList.insert(0, podcastLocal.id);
|
||||||
_saveGroup();
|
_saveGroup();
|
||||||
await dbHelper.savePodcastLocal(podcastLocal);
|
await dbHelper.savePodcastLocal(podcastLocal);
|
||||||
await _groups[0].getPodcasts();
|
await _groups[0].getPodcasts();
|
||||||
@ -132,7 +128,7 @@ class GroupList extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<PodcastGroup> getPodcastGroup(String id) {
|
List<PodcastGroup> getPodcastGroup(String id) {
|
||||||
List<PodcastGroup> result =[];
|
List<PodcastGroup> result = [];
|
||||||
_groups.forEach((group) {
|
_groups.forEach((group) {
|
||||||
if (group.podcastList.contains(id)) {
|
if (group.podcastList.contains(id)) {
|
||||||
result.add(group);
|
result.add(group);
|
||||||
@ -141,38 +137,42 @@ class GroupList extends ChangeNotifier {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
changeGroup(String id, List<String> list) async{
|
changeGroup(String id, List<PodcastGroup> list) async {
|
||||||
_groups.forEach((group) {
|
_isLoading = true;
|
||||||
if (group.podcastList.contains(id)) {
|
notifyListeners();
|
||||||
group.podcastList.remove(id);
|
getPodcastGroup(id).forEach((group) {
|
||||||
}
|
group.podcastList.remove(id);
|
||||||
});
|
});
|
||||||
await Future.forEach(list, (s) {
|
list.forEach((s) {
|
||||||
_groups.forEach((group) async{
|
s.podcastList.insert(0, id);
|
||||||
if (group.name == s) group.podcastList.add(id);
|
|
||||||
await group.getPodcasts();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
_saveGroup();
|
_saveGroup();
|
||||||
|
await Future.forEach(_groups, (group) async {
|
||||||
|
await group.getPodcasts();
|
||||||
|
});
|
||||||
|
_isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
removePodcast(String id) async{
|
removePodcast(String id) async {
|
||||||
await Future.forEach(_groups, (group) async{
|
_isLoading = true;
|
||||||
if (group.podcastList.contains(id)) {
|
notifyListeners();
|
||||||
|
_groups.forEach((group) async {
|
||||||
group.podcastList.remove(id);
|
group.podcastList.remove(id);
|
||||||
await group.getPodcasts();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
_saveGroup();
|
_saveGroup();
|
||||||
await dbHelper.delPodcastLocal(id);
|
await dbHelper.delPodcastLocal(id);
|
||||||
notifyListeners();
|
await Future.forEach(_groups, (group) async {
|
||||||
|
await group.getPodcasts();
|
||||||
|
});
|
||||||
|
_isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
saveOrder(PodcastGroup group, List<PodcastLocal> podcasts) async{
|
saveOrder(PodcastGroup group, List<PodcastLocal> podcasts) async {
|
||||||
group.podcastList = podcasts.map((e) => e.id).toList();
|
group.podcastList = podcasts.map((e) => e.id).toList();
|
||||||
_saveGroup();
|
_saveGroup();
|
||||||
await group.getPodcasts();
|
await group.getPodcasts();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
part 'podcastrss.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class Podcastrss<R>{
|
|
||||||
@_ConvertR()
|
|
||||||
final R rss;
|
|
||||||
Podcastrss({this.rss});
|
|
||||||
factory Podcastrss.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$PodcastrssFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$PodcastrssToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertR<R> implements JsonConverter<R, Object>{
|
|
||||||
const _ConvertR();
|
|
||||||
@override
|
|
||||||
R fromJson(Object json){
|
|
||||||
return Rss.fromJson(json) as R;
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Object toJson(R object){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@JsonSerializable()
|
|
||||||
class Rss<C>{
|
|
||||||
@_ConvertC()
|
|
||||||
final C channel;
|
|
||||||
Rss({this.channel});
|
|
||||||
factory Rss.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$RssFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$RssToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertC<C> implements JsonConverter<C, Object>{
|
|
||||||
const _ConvertC();
|
|
||||||
@override
|
|
||||||
C fromJson(Object json){
|
|
||||||
return Channel.fromJson(json) as C;
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Object toJson(C object){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class Channel<E> {
|
|
||||||
final String title;
|
|
||||||
final String link;
|
|
||||||
final String description;
|
|
||||||
@_ConvertE()
|
|
||||||
final List<E> item;
|
|
||||||
Channel({this.title, this.link, this.description, this.item});
|
|
||||||
factory Channel.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ChannelFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$ChannelToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertE<E> implements JsonConverter<E, Object>{
|
|
||||||
const _ConvertE();
|
|
||||||
@override
|
|
||||||
E fromJson(Object json){
|
|
||||||
return EpisodeItem.fromJson(json) as E;
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Object toJson(E object){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class EpisodeItem{
|
|
||||||
final String title;
|
|
||||||
final String link;
|
|
||||||
final String pubDate;
|
|
||||||
final String description;
|
|
||||||
EpisodeItem({this.title, this.link, this.pubDate, this.description}
|
|
||||||
);
|
|
||||||
factory EpisodeItem.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$EpisodeItemFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$EpisodeItemToJson(this);
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'podcastrss.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
Podcastrss<R> _$PodcastrssFromJson<R>(Map<String, dynamic> json) {
|
|
||||||
return Podcastrss<R>(
|
|
||||||
rss: json['rss'] == null ? null : _ConvertR<R>().fromJson(json['rss']));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$PodcastrssToJson<R>(Podcastrss<R> instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'rss': instance.rss == null ? null : _ConvertR<R>().toJson(instance.rss)
|
|
||||||
};
|
|
||||||
|
|
||||||
Rss<C> _$RssFromJson<C>(Map<String, dynamic> json) {
|
|
||||||
return Rss<C>(
|
|
||||||
channel: json['channel'] == null
|
|
||||||
? null
|
|
||||||
: _ConvertC<C>().fromJson(json['channel']));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$RssToJson<C>(Rss<C> instance) => <String, dynamic>{
|
|
||||||
'channel': instance.channel == null
|
|
||||||
? null
|
|
||||||
: _ConvertC<C>().toJson(instance.channel)
|
|
||||||
};
|
|
||||||
|
|
||||||
Channel<E> _$ChannelFromJson<E>(Map<String, dynamic> json) {
|
|
||||||
return Channel<E>(
|
|
||||||
title: json['title'] as String,
|
|
||||||
link: json['link'] as String,
|
|
||||||
description: json['description'] as String,
|
|
||||||
item: (json['item'] as List)
|
|
||||||
?.map((e) => e == null ? null : _ConvertE<E>().fromJson(e))
|
|
||||||
?.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ChannelToJson<E>(Channel<E> instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'title': instance.title,
|
|
||||||
'link': instance.link,
|
|
||||||
'description': instance.description,
|
|
||||||
'item': instance.item
|
|
||||||
?.map((e) => e == null ? null : _ConvertE<E>().toJson(e))
|
|
||||||
?.toList()
|
|
||||||
};
|
|
||||||
|
|
||||||
EpisodeItem _$EpisodeItemFromJson(Map<String, dynamic> json) {
|
|
||||||
return EpisodeItem(
|
|
||||||
title: json['title'] as String,
|
|
||||||
link: json['link'] as String,
|
|
||||||
pubDate: json['pubDate'] as String,
|
|
||||||
description: json['description'] as String);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$EpisodeItemToJson(EpisodeItem instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'title': instance.title,
|
|
||||||
'link': instance.link,
|
|
||||||
'pubDate': instance.pubDate,
|
|
||||||
'description': instance.description
|
|
||||||
};
|
|
@ -1,112 +0,0 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
part 'podcasts.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class Podcast<E, F>{
|
|
||||||
final String version;
|
|
||||||
final String title;
|
|
||||||
@JsonKey(name: 'homepage_url')
|
|
||||||
final String homepageUrl;
|
|
||||||
@JsonKey(name: 'feed_url')
|
|
||||||
final String feedUrl;
|
|
||||||
final String description;
|
|
||||||
@JsonKey(name: '_fireside')
|
|
||||||
@_ConvertF()
|
|
||||||
final F fireSide;
|
|
||||||
@JsonKey(name: 'items')
|
|
||||||
@_ConvertE()
|
|
||||||
final List<E> items;
|
|
||||||
Podcast(
|
|
||||||
{this.version, this.title, this.homepageUrl, this.feedUrl, this.description, this.fireSide, this.items}
|
|
||||||
);
|
|
||||||
factory Podcast.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$PodcastFromJson<E, F>(json);
|
|
||||||
Map<String, dynamic> toJson() => _$PodcastToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertE<E> implements JsonConverter<E, Object>{
|
|
||||||
const _ConvertE();
|
|
||||||
@override
|
|
||||||
E fromJson(Object json){
|
|
||||||
return EpisodeItem.fromJson(json) as E;
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Object toJson(E object){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class _ConvertF<F> implements JsonConverter<F, Object>{
|
|
||||||
const _ConvertF();
|
|
||||||
@override
|
|
||||||
F fromJson(Object json){
|
|
||||||
return FireSide.fromJson(json) as F;
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Object toJson(F object){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class FireSide{
|
|
||||||
final String pubdate;
|
|
||||||
final bool explicit;
|
|
||||||
final String copyright;
|
|
||||||
final String owner;
|
|
||||||
final String image;
|
|
||||||
FireSide({this.pubdate, this.explicit, this.copyright, this.owner, this.image});
|
|
||||||
factory FireSide.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$FireSideFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$FireSideToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class EpisodeItem<A>{
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
final String url;
|
|
||||||
@JsonKey(name: 'content_text')
|
|
||||||
final String contentText;
|
|
||||||
@JsonKey(name: 'content_html')
|
|
||||||
final String contentHtml;
|
|
||||||
final String summary;
|
|
||||||
@JsonKey(name: 'date_published')
|
|
||||||
final String datePublished;
|
|
||||||
@_ConvertA()
|
|
||||||
final List<A> attachments;
|
|
||||||
EpisodeItem({this.id, this.title, this.url, this.contentText, this.contentHtml, this.summary, this.datePublished, this.attachments}
|
|
||||||
);
|
|
||||||
factory EpisodeItem.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$EpisodeItemFromJson<A>(json);
|
|
||||||
Map<String, dynamic> toJson() => _$EpisodeItemToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertA<A> implements JsonConverter<A, Object> {
|
|
||||||
const _ConvertA();
|
|
||||||
@override
|
|
||||||
A fromJson(Object json){
|
|
||||||
return Attachment.fromJson(json) as A;
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Object toJson(A object){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class Attachment{
|
|
||||||
final String url;
|
|
||||||
@JsonKey(name: 'mime_type')
|
|
||||||
final String mimeType;
|
|
||||||
@JsonKey(name: 'size_in_bytes')
|
|
||||||
final int sizeInBytes;
|
|
||||||
@JsonKey(name: 'duration_in_seconds')
|
|
||||||
final int durationInSeconds;
|
|
||||||
Attachment(
|
|
||||||
{this.url, this.mimeType, this.sizeInBytes, this.durationInSeconds}
|
|
||||||
);
|
|
||||||
factory Attachment.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$AttachmentFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$AttachmentToJson(this);
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'podcasts.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
Podcast<E, F> _$PodcastFromJson<E, F>(Map<String, dynamic> json) {
|
|
||||||
return Podcast<E, F>(
|
|
||||||
version: json['version'] as String,
|
|
||||||
title: json['title'] as String,
|
|
||||||
homepageUrl: json['homepage_url'] as String,
|
|
||||||
feedUrl: json['feed_url'] as String,
|
|
||||||
description: json['description'] as String,
|
|
||||||
fireSide: json['_fireside'] == null
|
|
||||||
? null
|
|
||||||
: _ConvertF<F>().fromJson(json['_fireside']),
|
|
||||||
items: (json['items'] as List)
|
|
||||||
?.map((e) => e == null ? null : _ConvertE<E>().fromJson(e))
|
|
||||||
?.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$PodcastToJson<E, F>(Podcast<E, F> instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'version': instance.version,
|
|
||||||
'title': instance.title,
|
|
||||||
'homepage_url': instance.homepageUrl,
|
|
||||||
'feed_url': instance.feedUrl,
|
|
||||||
'description': instance.description,
|
|
||||||
'_fireside': instance.fireSide == null
|
|
||||||
? null
|
|
||||||
: _ConvertF<F>().toJson(instance.fireSide),
|
|
||||||
'items': instance.items
|
|
||||||
?.map((e) => e == null ? null : _ConvertE<E>().toJson(e))
|
|
||||||
?.toList()
|
|
||||||
};
|
|
||||||
|
|
||||||
FireSide _$FireSideFromJson(Map<String, dynamic> json) {
|
|
||||||
return FireSide(
|
|
||||||
pubdate: json['pubdate'] as String,
|
|
||||||
explicit: json['explicit'] as bool,
|
|
||||||
copyright: json['copyright'] as String,
|
|
||||||
owner: json['owner'] as String,
|
|
||||||
image: json['image'] as String);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$FireSideToJson(FireSide instance) => <String, dynamic>{
|
|
||||||
'pubdate': instance.pubdate,
|
|
||||||
'explicit': instance.explicit,
|
|
||||||
'copyright': instance.copyright,
|
|
||||||
'owner': instance.owner,
|
|
||||||
'image': instance.image
|
|
||||||
};
|
|
||||||
|
|
||||||
EpisodeItem<A> _$EpisodeItemFromJson<A>(Map<String, dynamic> json) {
|
|
||||||
return EpisodeItem<A>(
|
|
||||||
id: json['id'] as String,
|
|
||||||
title: json['title'] as String,
|
|
||||||
url: json['url'] as String,
|
|
||||||
contentText: json['content_text'] as String,
|
|
||||||
contentHtml: json['content_html'] as String,
|
|
||||||
summary: json['summary'] as String,
|
|
||||||
datePublished: json['date_published'] as String,
|
|
||||||
attachments: (json['attachments'] as List)
|
|
||||||
?.map((e) => e == null ? null : _ConvertA<A>().fromJson(e))
|
|
||||||
?.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$EpisodeItemToJson<A>(EpisodeItem<A> instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'title': instance.title,
|
|
||||||
'url': instance.url,
|
|
||||||
'content_text': instance.contentText,
|
|
||||||
'content_html': instance.contentHtml,
|
|
||||||
'summary': instance.summary,
|
|
||||||
'date_published': instance.datePublished,
|
|
||||||
'attachments': instance.attachments
|
|
||||||
?.map((e) => e == null ? null : _ConvertA<A>().toJson(e))
|
|
||||||
?.toList()
|
|
||||||
};
|
|
||||||
|
|
||||||
Attachment _$AttachmentFromJson(Map<String, dynamic> json) {
|
|
||||||
return Attachment(
|
|
||||||
url: json['url'] as String,
|
|
||||||
mimeType: json['mime_type'] as String,
|
|
||||||
sizeInBytes: json['size_in_bytes'] as int,
|
|
||||||
durationInSeconds: json['duration_in_seconds'] as int);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$AttachmentToJson(Attachment instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'url': instance.url,
|
|
||||||
'mime_type': instance.mimeType,
|
|
||||||
'size_in_bytes': instance.sizeInBytes,
|
|
||||||
'duration_in_seconds': instance.durationInSeconds
|
|
||||||
};
|
|
@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:tsacdop/class/audiostate.dart';
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
@ -86,7 +87,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
height: 30.0,
|
height: 30.0,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Published ' +
|
'Published ' +
|
||||||
widget.episodeItem.pubDate.substring(0, 16),
|
DateFormat.yMMMd().format( DateTime.fromMillisecondsSinceEpoch(widget.episodeItem.pubDate)),
|
||||||
style: TextStyle(color: Colors.blue[500])),
|
style: TextStyle(color: Colors.blue[500])),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
|
|||||||
class AboutApp extends StatelessWidget {
|
class AboutApp extends StatelessWidget {
|
||||||
TextSpan buildTextSpan() {
|
TextSpan buildTextSpan() {
|
||||||
return TextSpan(children: [
|
return TextSpan(children: [
|
||||||
TextSpan(text: 'About Dopcast Player\n',style: TextStyle(fontSize: 20)),
|
TextSpan(text: 'Tsacdop\n',style: TextStyle(fontSize: 20)),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text:
|
text:
|
||||||
'Dopcast Player is a podcast client developed by flutter, is a simple, easy-use player.\n'),
|
'Tsacdop is a podcast client developed by flutter, is a simple, easy-use player.\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text:
|
text:
|
||||||
'Github https://github.com/stonga .\n'),
|
'Github https://github.com/stonga/tsacdop .\n'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,9 @@ class AboutApp extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
title: Text('About'),
|
title: Text('Tsacdop'),
|
||||||
|
centerTitle: true,
|
||||||
|
elevation: 0,
|
||||||
),
|
),
|
||||||
body: Container(
|
body: Container(
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
|
@ -5,6 +5,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:color_thief_flutter/color_thief_flutter.dart';
|
import 'package:color_thief_flutter/color_thief_flutter.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
@ -31,12 +32,18 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
systemNavigationBarColor: Colors.grey[100],
|
||||||
|
statusBarColor: Colors.green,
|
||||||
|
),
|
||||||
|
child: Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
|
brightness: Brightness.light,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
tooltip: 'Add',
|
tooltip: 'Add',
|
||||||
icon: const Icon(Icons.add_circle_outline),
|
icon: const Icon(Icons.add_circle_outline),
|
||||||
@ -56,6 +63,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Home(),
|
body: Home(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,50 +225,66 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
var importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
var importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
||||||
var groupList = Provider.of<GroupList>(context, listen: false);
|
var groupList = Provider.of<GroupList>(context, listen: false);
|
||||||
savePodcast(String rss) async {
|
savePodcast(String rss) async {
|
||||||
print(rss);
|
|
||||||
if (mounted) setState(() => _adding = true);
|
if (mounted) setState(() => _adding = true);
|
||||||
|
|
||||||
importOmpl.importState = ImportState.import;
|
importOmpl.importState = ImportState.import;
|
||||||
try {
|
try {
|
||||||
Response response = await Dio().get(rss);
|
Response response = await Dio().get(rss);
|
||||||
|
|
||||||
String _realUrl = response.realUri.toString();
|
|
||||||
if (mounted) setState(() => _issubscribe = true);
|
|
||||||
|
|
||||||
var _p = RssFeed.parse(response.data);
|
|
||||||
|
|
||||||
print(_p.title);
|
|
||||||
|
|
||||||
var dir = await getApplicationDocumentsDirectory();
|
|
||||||
|
|
||||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
|
||||||
_p.itunes.image.href,
|
|
||||||
options: Options(responseType: ResponseType.bytes));
|
|
||||||
|
|
||||||
img.Image image = img.decodeImage(imageResponse.data);
|
|
||||||
img.Image thumbnail = img.copyResize(image, width: 300);
|
|
||||||
String _uuid = Uuid().v4();
|
|
||||||
File("${dir.path}/$_uuid.png")
|
|
||||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
|
||||||
String _imagePath = "${dir.path}/$_uuid.png";
|
|
||||||
String _primaryColor =
|
|
||||||
await getColor(File("${dir.path}/$_uuid.png"));
|
|
||||||
PodcastLocal podcastLocal = PodcastLocal(
|
|
||||||
_p.title, _p.itunes.image.href, _realUrl, _primaryColor, _p.author, _uuid, _imagePath);
|
|
||||||
podcastLocal.description = _p.description;
|
|
||||||
groupList.subscribe(podcastLocal);
|
|
||||||
|
|
||||||
importOmpl.importState = ImportState.parse;
|
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
await dbHelper.savePodcastRss(_p, _uuid);
|
String _realUrl =
|
||||||
|
response.isRedirect ? response.realUri.toString() : rss;
|
||||||
|
bool _checkUrl = await dbHelper.checkPodcast(_realUrl);
|
||||||
|
if (_checkUrl) {
|
||||||
|
if (mounted) setState(() => _issubscribe = true);
|
||||||
|
|
||||||
importOmpl.importState = ImportState.complete;
|
var _p = RssFeed.parse(response.data);
|
||||||
|
|
||||||
|
print(_p.title);
|
||||||
|
|
||||||
|
var dir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
|
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||||
|
_p.itunes.image.href,
|
||||||
|
options: Options(responseType: ResponseType.bytes));
|
||||||
|
|
||||||
|
img.Image image = img.decodeImage(imageResponse.data);
|
||||||
|
img.Image thumbnail = img.copyResize(image, width: 300);
|
||||||
|
|
||||||
|
String _uuid = Uuid().v4();
|
||||||
|
File("${dir.path}/$_uuid.png")
|
||||||
|
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||||
|
String _imagePath = "${dir.path}/$_uuid.png";
|
||||||
|
String _primaryColor = await getColor(File("${dir.path}/$_uuid.png"));
|
||||||
|
PodcastLocal podcastLocal = PodcastLocal(
|
||||||
|
_p.title,
|
||||||
|
_p.itunes.image.href,
|
||||||
|
_realUrl,
|
||||||
|
_primaryColor,
|
||||||
|
_p.author,
|
||||||
|
_uuid,
|
||||||
|
_imagePath);
|
||||||
|
podcastLocal.description = _p.description;
|
||||||
|
groupList.subscribe(podcastLocal);
|
||||||
|
|
||||||
|
importOmpl.importState = ImportState.parse;
|
||||||
|
|
||||||
|
await dbHelper.savePodcastRss(_p, _uuid);
|
||||||
|
|
||||||
|
importOmpl.importState = ImportState.complete;
|
||||||
|
} else {
|
||||||
|
importOmpl.importState = ImportState.error;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'POdcast Subscribed Already',
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
);
|
||||||
|
await Future.delayed(Duration(seconds: 10));
|
||||||
|
importOmpl.importState = ImportState.stop;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
importOmpl.importState = ImportState.error;
|
importOmpl.importState = ImportState.error;
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: 'Network error, Subscribe failed',
|
msg: 'Network error, Subscribe failed',
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.TOP,
|
||||||
);
|
);
|
||||||
await Future.delayed(Duration(seconds: 10));
|
await Future.delayed(Duration(seconds: 10));
|
||||||
importOmpl.importState = ImportState.stop;
|
importOmpl.importState = ImportState.stop;
|
||||||
@ -271,12 +295,14 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 30.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
onTap: (){setState(() {
|
onTap: () {
|
||||||
_showDes = !_showDes;
|
setState(() {
|
||||||
});},
|
_showDes = !_showDes;
|
||||||
|
});
|
||||||
|
},
|
||||||
leading: ClipRRect(
|
leading: ClipRRect(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
@ -289,46 +315,57 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
),
|
),
|
||||||
title: Text(widget.onlinePodcast.title),
|
title: Text(widget.onlinePodcast.title),
|
||||||
subtitle: Text(widget.onlinePodcast.publisher),
|
subtitle: Text(widget.onlinePodcast.publisher),
|
||||||
trailing:
|
trailing: Row(
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(_showDes ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down),
|
Icon(_showDes
|
||||||
Padding(padding: EdgeInsets.only(right: 10.0)),
|
? Icons.keyboard_arrow_up
|
||||||
!_issubscribe
|
: Icons.keyboard_arrow_down),
|
||||||
? !_adding
|
Padding(padding: EdgeInsets.only(right: 10.0)),
|
||||||
? OutlineButton(
|
!_issubscribe
|
||||||
child:
|
? !_adding
|
||||||
Text('Subscribe', style: TextStyle(color: Colors.blue)),
|
? OutlineButton(
|
||||||
onPressed: () {
|
child: Text('Subscribe',
|
||||||
importOmpl.rssTitle = widget.onlinePodcast.title;
|
style: TextStyle(color: Colors.blue)),
|
||||||
savePodcast(widget.onlinePodcast.rss);
|
onPressed: () {
|
||||||
})
|
importOmpl.rssTitle = widget.onlinePodcast.title;
|
||||||
: OutlineButton(
|
savePodcast(widget.onlinePodcast.rss);
|
||||||
child: SizedBox(
|
})
|
||||||
height: 20,
|
: OutlineButton(
|
||||||
width: 20,
|
child: SizedBox(
|
||||||
child: CircularProgressIndicator(
|
height: 20,
|
||||||
strokeWidth: 2,
|
width: 20,
|
||||||
valueColor: AlwaysStoppedAnimation(Colors.blue),
|
child: CircularProgressIndicator(
|
||||||
)),
|
strokeWidth: 2,
|
||||||
onPressed: () {},
|
valueColor:
|
||||||
)
|
AlwaysStoppedAnimation(Colors.blue),
|
||||||
: OutlineButton(child: Text('Subscribe'), onPressed: (){}),
|
)),
|
||||||
|
onPressed: () {},
|
||||||
|
)
|
||||||
|
: OutlineButton(child: Text('Subscribe'), onPressed: () {}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_showDes ? Container(
|
_showDes
|
||||||
alignment: Alignment.centerLeft,
|
? Container(
|
||||||
decoration: BoxDecoration(
|
alignment: Alignment.centerLeft,
|
||||||
color: Colors.grey[300],
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10.0))
|
color: Colors.grey[300],
|
||||||
),
|
borderRadius: BorderRadius.only(
|
||||||
margin: EdgeInsets.only(left: 70, right: 50),
|
topRight: Radius.circular(15.0),
|
||||||
padding: EdgeInsets.all(10.0),
|
bottomLeft: Radius.circular(15.0),
|
||||||
child: Text(widget.onlinePodcast.description, maxLines: 3, overflow: TextOverflow.ellipsis,),
|
bottomRight: Radius.circular(15.0),
|
||||||
) : Center(),
|
)),
|
||||||
|
margin: EdgeInsets.only(left: 70, right: 50),
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
child: Text(
|
||||||
|
widget.onlinePodcast.description.trim(),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -47,10 +47,9 @@ class PopupMenu extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
||||||
GroupList groupList = Provider.of<GroupList>(context, listen: false);
|
GroupList groupList = Provider.of<GroupList>(context, listen: false);
|
||||||
_refreshAll() async{
|
_refreshAll() async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<PodcastLocal> podcastList =
|
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||||
await dbHelper.getPodcastLocalAll();
|
|
||||||
await Future.forEach(podcastList, (podcastLocal) async {
|
await Future.forEach(podcastList, (podcastLocal) async {
|
||||||
importOmpl.rssTitle = podcastLocal.title;
|
importOmpl.rssTitle = podcastLocal.title;
|
||||||
importOmpl.importState = ImportState.parse;
|
importOmpl.importState = ImportState.parse;
|
||||||
@ -62,10 +61,10 @@ class PopupMenu extends StatelessWidget {
|
|||||||
|
|
||||||
saveOmpl(String rss) async {
|
saveOmpl(String rss) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
try {
|
importOmpl.importState = ImportState.import;
|
||||||
importOmpl.importState = ImportState.import;
|
|
||||||
Response response = await Dio().get(rss);
|
|
||||||
|
|
||||||
|
Response response = await Dio().get(rss);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
var _p = RssFeed.parse(response.data);
|
var _p = RssFeed.parse(response.data);
|
||||||
var dir = await getApplicationDocumentsDirectory();
|
var dir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
@ -75,55 +74,84 @@ class PopupMenu extends StatelessWidget {
|
|||||||
img.Image image = img.decodeImage(imageResponse.data);
|
img.Image image = img.decodeImage(imageResponse.data);
|
||||||
img.Image thumbnail = img.copyResize(image, width: 300);
|
img.Image thumbnail = img.copyResize(image, width: 300);
|
||||||
String _uuid = Uuid().v4();
|
String _uuid = Uuid().v4();
|
||||||
String _realUrl = response.realUri.toString();
|
String _realUrl =
|
||||||
File("${dir.path}/$_uuid.png")
|
response.isRedirect ? response.realUri.toString() : rss;
|
||||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
print(_realUrl);
|
||||||
String _imagePath = "${dir.path}/$_uuid.png";
|
|
||||||
String _primaryColor =
|
|
||||||
await getColor(File("${dir.path}/$_uuid.png"));
|
|
||||||
|
|
||||||
PodcastLocal podcastLocal = PodcastLocal(
|
bool _checkUrl = await dbHelper.checkPodcast(_realUrl);
|
||||||
_p.title, _p.itunes.image.href, _realUrl, _primaryColor, _p.author, _uuid, _imagePath);
|
if (_checkUrl) {
|
||||||
|
File("${dir.path}/$_uuid.png")
|
||||||
|
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||||
|
String _imagePath = "${dir.path}/$_uuid.png";
|
||||||
|
String _primaryColor = await getColor(File("${dir.path}/$_uuid.png"));
|
||||||
|
|
||||||
podcastLocal.description = _p.description;
|
PodcastLocal podcastLocal = PodcastLocal(
|
||||||
|
_p.title,
|
||||||
groupList.subscribe(podcastLocal);
|
_p.itunes.image.href,
|
||||||
|
_realUrl,
|
||||||
|
_primaryColor,
|
||||||
|
_p.author,
|
||||||
|
_uuid,
|
||||||
|
_imagePath);
|
||||||
|
|
||||||
importOmpl.importState = ImportState.parse;
|
podcastLocal.description = _p.description;
|
||||||
|
|
||||||
await dbHelper.savePodcastRss(_p, _uuid);
|
groupList.subscribe(podcastLocal);
|
||||||
|
|
||||||
importOmpl.importState = ImportState.complete;
|
importOmpl.importState = ImportState.parse;
|
||||||
} catch (e) {
|
|
||||||
|
await dbHelper.savePodcastRss(_p, _uuid);
|
||||||
|
|
||||||
|
importOmpl.importState = ImportState.complete;
|
||||||
|
} else {
|
||||||
|
importOmpl.importState = ImportState.error;
|
||||||
|
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Podcast Subscribed Already',
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
);
|
||||||
|
await Future.delayed(Duration(seconds: 5));
|
||||||
|
importOmpl.importState = ImportState.stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
importOmpl.importState = ImportState.error;
|
importOmpl.importState = ImportState.error;
|
||||||
Fluttertoast.showToast(
|
|
||||||
msg: 'Network error, Subscribe failed',
|
Fluttertoast.showToast(
|
||||||
gravity: ToastGravity.BOTTOM,
|
msg: 'Network error, Subscribe failed',
|
||||||
);
|
gravity: ToastGravity.TOP,
|
||||||
await Future.delayed(Duration(seconds: 10));
|
);
|
||||||
importOmpl.importState = ImportState.stop;
|
await Future.delayed(Duration(seconds: 5));
|
||||||
|
importOmpl.importState = ImportState.stop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveOmpl(String path) async {
|
void _saveOmpl(String path) async {
|
||||||
File file = File(path);
|
File file = File(path);
|
||||||
String opml = file.readAsStringSync();
|
String opml = file.readAsStringSync();
|
||||||
try {
|
|
||||||
var content = xml.parse(opml);
|
var content = xml.parse(opml);
|
||||||
var total = content
|
var total = content
|
||||||
.findAllElements('outline')
|
.findAllElements('outline')
|
||||||
.map((ele) => OmplOutline.parse(ele))
|
.map((ele) => OmplOutline.parse(ele))
|
||||||
.toList();
|
.toList();
|
||||||
|
if (total.length == 0) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'File Not Valid',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
for (int i = 0; i < total.length; i++) {
|
for (int i = 0; i < total.length; i++) {
|
||||||
if (total[i].xmlUrl != null) {
|
if (total[i].xmlUrl != null) {
|
||||||
importOmpl.rssTitle = total[i].text;
|
importOmpl.rssTitle = total[i].text;
|
||||||
await saveOmpl(total[i].xmlUrl);
|
try{await saveOmpl(total[i].xmlUrl);}
|
||||||
|
catch (e){
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
print(total[i].text);
|
print(total[i].text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print('Import fisnished');
|
print('Import fisnished');
|
||||||
} catch (e) {
|
|
||||||
importOmpl.importState = ImportState.error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,20 +5,17 @@ import 'dart:async';
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/class/podcast_group.dart';
|
import 'package:tsacdop/class/podcast_group.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/class/importompl.dart';
|
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/episodes/episodedetail.dart';
|
import 'package:tsacdop/episodes/episodedetail.dart';
|
||||||
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||||
import 'package:tsacdop/podcasts/podcastmanage.dart';
|
import 'package:tsacdop/podcasts/podcastmanage.dart';
|
||||||
import 'package:tsacdop/util/pageroute.dart';
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
import 'package:tsacdop/class/settingstate.dart';
|
|
||||||
|
|
||||||
class ScrollPodcasts extends StatefulWidget {
|
class ScrollPodcasts extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -26,35 +23,11 @@ class ScrollPodcasts extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
||||||
var dir;
|
|
||||||
int _groupIndex;
|
int _groupIndex;
|
||||||
bool _loaded;
|
|
||||||
|
|
||||||
ImportState importState;
|
|
||||||
Update subscribeUpdate;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
subscribeUpdate = Provider.of<SettingState>(context).subscribeupdate;
|
|
||||||
if (subscribeUpdate == Update.backhome) {
|
|
||||||
setState(() {
|
|
||||||
_groupIndex = 0;
|
|
||||||
});
|
|
||||||
} else if (subscribeUpdate == Update.justupdate) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loaded = false;
|
|
||||||
_groupIndex = 0;
|
_groupIndex = 0;
|
||||||
getApplicationDocumentsDirectory().then((value) {
|
|
||||||
dir = value.path;
|
|
||||||
setState(() => _loaded = true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -66,10 +39,6 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
return isLoading
|
return isLoading
|
||||||
? Container(
|
? Container(
|
||||||
height: (_width - 20) / 3 + 110,
|
height: (_width - 20) / 3 + 110,
|
||||||
child: SizedBox(
|
|
||||||
height: 20.0,
|
|
||||||
width: 20.0,
|
|
||||||
child: CircularProgressIndicator()),
|
|
||||||
)
|
)
|
||||||
: DefaultTabController(
|
: DefaultTabController(
|
||||||
length: groups[_groupIndex].podcastList.length,
|
length: groups[_groupIndex].podcastList.length,
|
||||||
@ -159,7 +128,8 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
indicator: CircleTabIndicator(
|
indicator: CircleTabIndicator(
|
||||||
color: Colors.blue, radius: 3),
|
color: Colors.blue, radius: 3),
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: groups[_groupIndex].podcasts
|
tabs: groups[_groupIndex]
|
||||||
|
.podcasts
|
||||||
.map<Tab>((PodcastLocal podcastLocal) {
|
.map<Tab>((PodcastLocal podcastLocal) {
|
||||||
return Tab(
|
return Tab(
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
@ -168,10 +138,8 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
child: LimitedBox(
|
child: LimitedBox(
|
||||||
maxHeight: 50,
|
maxHeight: 50,
|
||||||
maxWidth: 50,
|
maxWidth: 50,
|
||||||
child: !_loaded
|
child: Image.file(
|
||||||
? CircularProgressIndicator()
|
File("${podcastLocal.imagePath}")),
|
||||||
: Image.file(File(
|
|
||||||
"${podcastLocal.imagePath}")),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -188,7 +156,8 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: groups[_groupIndex].podcasts
|
children: groups[_groupIndex]
|
||||||
|
.podcasts
|
||||||
.map<Widget>((PodcastLocal podcastLocal) {
|
.map<Widget>((PodcastLocal podcastLocal) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(color: Colors.grey[100]),
|
decoration: BoxDecoration(color: Colors.grey[100]),
|
||||||
@ -216,7 +185,6 @@ class PodcastPreview extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PodcastPreviewState extends State<PodcastPreview> {
|
class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
Future<List<EpisodeBrief>> episodes =
|
Future<List<EpisodeBrief>> episodes =
|
||||||
@ -265,24 +233,33 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(widget.podcastLocal.title,
|
Expanded(
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, color: _c)),
|
flex: 4,
|
||||||
Spacer(),
|
child: Text(widget.podcastLocal.title,
|
||||||
Material(
|
maxLines: 1,
|
||||||
color: Colors.transparent,
|
overflow: TextOverflow.visible,
|
||||||
child: IconButton(
|
style: TextStyle(fontWeight: FontWeight.bold, color: _c)),
|
||||||
icon: Icon(Icons.arrow_forward),
|
),
|
||||||
splashColor: Colors.transparent,
|
Expanded(
|
||||||
tooltip: 'See All',
|
flex: 1,
|
||||||
onPressed: () {
|
child: Container(
|
||||||
Navigator.push(
|
alignment: Alignment.centerRight,
|
||||||
context,
|
child: Material(
|
||||||
SlideLeftRoute(
|
color: Colors.transparent,
|
||||||
page: PodcastDetail(
|
child: IconButton(
|
||||||
podcastLocal: widget.podcastLocal,
|
icon: Icon(Icons.arrow_forward),
|
||||||
)),
|
tooltip: 'See All',
|
||||||
);
|
onPressed: () {
|
||||||
},
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
SlideLeftRoute(
|
||||||
|
page: PodcastDetail(
|
||||||
|
podcastLocal: widget.podcastLocal,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -296,8 +273,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
|||||||
class ShowEpisode extends StatelessWidget {
|
class ShowEpisode extends StatelessWidget {
|
||||||
final List<EpisodeBrief> podcast;
|
final List<EpisodeBrief> podcast;
|
||||||
final PodcastLocal podcastLocal;
|
final PodcastLocal podcastLocal;
|
||||||
ShowEpisode({Key key, this.podcast, this.podcastLocal})
|
ShowEpisode({Key key, this.podcast, this.podcastLocal}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double _width = MediaQuery.of(context).size.width;
|
double _width = MediaQuery.of(context).size.width;
|
||||||
@ -368,8 +344,8 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
height: _width / 18,
|
height: _width / 18,
|
||||||
width: _width / 18,
|
width: _width / 18,
|
||||||
child: Image.file(File(
|
child: Image.file(
|
||||||
"${podcastLocal.imagePath}")),
|
File("${podcastLocal.imagePath}")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -398,7 +374,8 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.bottomLeft,
|
alignment: Alignment.bottomLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
podcast[index].pubDate.substring(4, 16),
|
podcast[index].dateToString(),
|
||||||
|
//podcast[index].pubDate.substring(4, 16),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _width / 35,
|
fontSize: _width / 35,
|
||||||
color: _c,
|
color: _c,
|
||||||
|
@ -62,8 +62,10 @@ class DBHelper {
|
|||||||
Future<List<PodcastLocal>> getPodcastLocalAll() async {
|
Future<List<PodcastLocal>> getPodcastLocalAll() async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
'SELECT title, imageUrl, rssUrl, primaryColor, author, imagePath FROM PodcastLocal ORDER BY add_date DESC');
|
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath FROM PodcastLocal ORDER BY add_date DESC');
|
||||||
|
|
||||||
List<PodcastLocal> podcastLocal = List();
|
List<PodcastLocal> podcastLocal = List();
|
||||||
|
|
||||||
for (int i = 0; i < list.length; i++) {
|
for (int i = 0; i < list.length; i++) {
|
||||||
podcastLocal.add(PodcastLocal(
|
podcastLocal.add(PodcastLocal(
|
||||||
list[i]['title'],
|
list[i]['title'],
|
||||||
@ -77,6 +79,13 @@ class DBHelper {
|
|||||||
return podcastLocal;
|
return podcastLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> checkPodcast(String url) async {
|
||||||
|
var dbClient = await database;
|
||||||
|
List<Map> list = await dbClient
|
||||||
|
.rawQuery('SELECT id FROM PodcastLocal WHERE rssUrl = ?', [url]);
|
||||||
|
return list.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
Future savePodcastLocal(PodcastLocal podcastLocal) async {
|
Future savePodcastLocal(PodcastLocal podcastLocal) async {
|
||||||
print('podcast saved in sqllite');
|
print('podcast saved in sqllite');
|
||||||
int _milliseconds = DateTime.now().millisecondsSinceEpoch;
|
int _milliseconds = DateTime.now().millisecondsSinceEpoch;
|
||||||
@ -115,14 +124,36 @@ class DBHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DateTime _parsePubDate(String pubDate) {
|
DateTime _parsePubDate(String pubDate) {
|
||||||
if (pubDate == null) return null;
|
if (pubDate == null) return DateTime.now();
|
||||||
|
print(pubDate);
|
||||||
DateTime date;
|
DateTime date;
|
||||||
|
RegExp yyyy = RegExp(r'[1-2][0-9]{3}');
|
||||||
|
RegExp hhmm = RegExp(r'[0-2][0-9]\:[0-5][0-9]');
|
||||||
|
RegExp ddmmm = RegExp(r'[0-3][0-9]\s[A-Z][a-z]{2}');
|
||||||
|
RegExp mmDd = RegExp(r'([0-1]|\s)[0-9]\-[0-3][0-9]');
|
||||||
try {
|
try {
|
||||||
print('e');
|
date = DateFormat('EEE, dd MMM yyyy HH:mm:ss Z', 'en_US').parse(pubDate);
|
||||||
date = DateFormat('dd MMM yyyy HH:mm:ss Z', 'en_US').parse(pubDate);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('e');
|
try {
|
||||||
date = DateTime(0);
|
date = DateFormat('dd MMM yyyy HH:mm:ss Z', 'en_US').parse(pubDate);
|
||||||
|
} catch (e) {
|
||||||
|
try {
|
||||||
|
date = DateFormat('EEE, dd MMM yyyy HH:mm Z', 'en_US').parse(pubDate);
|
||||||
|
} catch (e) {
|
||||||
|
//parse date using regex, bug in parse maonth/day
|
||||||
|
String year = yyyy.stringMatch(pubDate);
|
||||||
|
String time = hhmm.stringMatch(pubDate);
|
||||||
|
String month = ddmmm.stringMatch(pubDate);
|
||||||
|
if (year != null && time != null && month != null) {
|
||||||
|
date = DateFormat('dd MMM yyyy HH:mm', 'en_US')
|
||||||
|
.parse(month + year + time);
|
||||||
|
} else if(year!=null && time!=null && month == null){
|
||||||
|
String month = mmDd.stringMatch(pubDate);
|
||||||
|
date = DateFormat('mm-dd yyyy HH:mm', 'en_US')
|
||||||
|
.parse(month +' ' + year +' '+ time);
|
||||||
|
} else {date = DateTime.now();}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
@ -144,32 +175,33 @@ class DBHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<int> savePodcastRss(RssFeed _p, String id) async {
|
Future<int> savePodcastRss(RssFeed _p, String id) async {
|
||||||
String _title;
|
|
||||||
String _url;
|
|
||||||
String _description;
|
|
||||||
int _duration;
|
|
||||||
int _result = _p.items.length;
|
int _result = _p.items.length;
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
|
String _description, _url;
|
||||||
for (int i = 0; i < _result; i++) {
|
for (int i = 0; i < _result; i++) {
|
||||||
print(_p.items[i].title);
|
print(_p.items[i].title);
|
||||||
_title = _p.items[i].itunes.title ?? _p.items[i].title;
|
if (_p.items[i].itunes.summary != null) {
|
||||||
_p.items[i].itunes.summary.contains('<')
|
_p.items[i].itunes.summary.contains('<')
|
||||||
? _description = _p.items[i].itunes.summary
|
? _description = _p.items[i].itunes.summary
|
||||||
: _description = _p.items[i].description;
|
: _description = _p.items[i].description;
|
||||||
|
} else {
|
||||||
|
_description = _p.items[i].description;
|
||||||
|
}
|
||||||
|
|
||||||
isXimalaya(_p.items[i].enclosure.url)
|
isXimalaya(_p.items[i].enclosure.url)
|
||||||
? _url = _p.items[i].enclosure.url.split('=').last
|
? _url = _p.items[i].enclosure.url.split('=').last
|
||||||
: _url = _p.items[i].enclosure.url;
|
: _url = _p.items[i].enclosure.url;
|
||||||
|
|
||||||
|
final _title = _p.items[i].itunes.title ?? _p.items[i].title;
|
||||||
final _length = _p.items[i].enclosure.length;
|
final _length = _p.items[i].enclosure.length;
|
||||||
final _pubDate = _p.items[i].pubDate;
|
final _pubDate = _p.items[i].pubDate;
|
||||||
|
print(_pubDate);
|
||||||
final _date = _parsePubDate(_pubDate);
|
final _date = _parsePubDate(_pubDate);
|
||||||
final _milliseconds = _date.millisecondsSinceEpoch;
|
final _milliseconds = _date.millisecondsSinceEpoch;
|
||||||
|
final _duration = _p.items[i].itunes.duration?.inMinutes ?? 0;
|
||||||
_duration = _p.items[i].itunes.duration?.inMinutes ?? 0;
|
|
||||||
|
|
||||||
final _explicit = getExplicit(_p.items[i].itunes.explicit);
|
final _explicit = getExplicit(_p.items[i].itunes.explicit);
|
||||||
if (_p.items[i].enclosure.url != null) {
|
|
||||||
|
if (_url != null) {
|
||||||
await dbClient.transaction((txn) {
|
await dbClient.transaction((txn) {
|
||||||
return txn.rawInsert(
|
return txn.rawInsert(
|
||||||
"""INSERT OR IGNORE INTO Episodes(title, enclosure_url, enclosure_length, pubDate,
|
"""INSERT OR IGNORE INTO Episodes(title, enclosure_url, enclosure_length, pubDate,
|
||||||
@ -194,10 +226,7 @@ class DBHelper {
|
|||||||
Future<int> updatePodcastRss(PodcastLocal podcastLocal) async {
|
Future<int> updatePodcastRss(PodcastLocal podcastLocal) async {
|
||||||
Response response = await Dio().get(podcastLocal.rssUrl);
|
Response response = await Dio().get(podcastLocal.rssUrl);
|
||||||
var _p = RssFeed.parse(response.data);
|
var _p = RssFeed.parse(response.data);
|
||||||
String _title;
|
String _url, _description;
|
||||||
String _url;
|
|
||||||
String _description;
|
|
||||||
int _duration;
|
|
||||||
int _result = _p.items.length;
|
int _result = _p.items.length;
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
int _count = Sqflite.firstIntValue(await dbClient.rawQuery(
|
int _count = Sqflite.firstIntValue(await dbClient.rawQuery(
|
||||||
@ -209,24 +238,27 @@ class DBHelper {
|
|||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < (_result - _count); i++) {
|
for (int i = 0; i < (_result - _count); i++) {
|
||||||
print(_p.items[i].title);
|
print(_p.items[i].title);
|
||||||
_title = _p.items[i].itunes.title ?? _p.items[i].title;
|
if (_p.items[i].itunes.summary != null) {
|
||||||
_p.items[i].itunes.summary.contains('<')
|
_p.items[i].itunes.summary.contains('<')
|
||||||
? _description = _p.items[i].itunes.summary
|
? _description = _p.items[i].itunes.summary
|
||||||
: _description = _p.items[i].description;
|
: _description = _p.items[i].description;
|
||||||
|
} else {
|
||||||
|
_description = _p.items[i].description;
|
||||||
|
}
|
||||||
|
|
||||||
isXimalaya(_p.items[i].enclosure.url)
|
isXimalaya(_p.items[i].enclosure.url)
|
||||||
? _url = _p.items[i].enclosure.url.split('=').last
|
? _url = _p.items[i].enclosure.url.split('=').last
|
||||||
: _url = _p.items[i].enclosure.url;
|
: _url = _p.items[i].enclosure.url;
|
||||||
|
|
||||||
|
final _title = _p.items[i].itunes.title ?? _p.items[i].title;
|
||||||
final _length = _p.items[i].enclosure.length;
|
final _length = _p.items[i].enclosure.length;
|
||||||
final _pubDate = _p.items[i].pubDate;
|
final _pubDate = _p.items[i].pubDate;
|
||||||
final _date = _parsePubDate(_pubDate);
|
final _date = _parsePubDate(_pubDate);
|
||||||
final _milliseconds = _date.millisecondsSinceEpoch;
|
final _milliseconds = _date.millisecondsSinceEpoch;
|
||||||
|
final _duration = _p.items[i].itunes.duration?.inMinutes ?? 0;
|
||||||
_duration = _p.items[i].itunes.duration?.inMinutes ?? 0;
|
|
||||||
|
|
||||||
final _explicit = getExplicit(_p.items[i].itunes.explicit);
|
final _explicit = getExplicit(_p.items[i].itunes.explicit);
|
||||||
if (_p.items[i].enclosure.url != null) {
|
|
||||||
|
if (_url != null) {
|
||||||
await dbClient.transaction((txn) {
|
await dbClient.transaction((txn) {
|
||||||
return txn.rawInsert(
|
return txn.rawInsert(
|
||||||
"""INSERT OR IGNORE INTO Episodes(title, enclosure_url, enclosure_length, pubDate,
|
"""INSERT OR IGNORE INTO Episodes(title, enclosure_url, enclosure_length, pubDate,
|
||||||
@ -251,20 +283,20 @@ class DBHelper {
|
|||||||
|
|
||||||
Future<List<EpisodeBrief>> getRssItem(String id) async {
|
Future<List<EpisodeBrief>> getRssItem(String id) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = List();
|
List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.pubDate, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.primaryColor
|
E.downloaded, P.primaryColor
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
where E.feed_id = ? ORDER BY E.milliseconds DESC""", [id]);
|
WHERE P.id = ? ORDER BY E.milliseconds DESC""", [id]);
|
||||||
for (int x = 0; x < list.length; x++) {
|
for (int x = 0; x < list.length; x++) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
list[x]['enclosure_url'],
|
list[x]['enclosure_url'],
|
||||||
list[x]['enclosure_length'],
|
list[x]['enclosure_length'],
|
||||||
list[x]['pubDate'],
|
list[x]['milliseconds'],
|
||||||
list[x]['feed_title'],
|
list[x]['feedTitle'],
|
||||||
list[x]['primaryColor'],
|
list[x]['primaryColor'],
|
||||||
list[x]['liked'],
|
list[x]['liked'],
|
||||||
list[x]['downloaded'],
|
list[x]['downloaded'],
|
||||||
@ -280,7 +312,7 @@ class DBHelper {
|
|||||||
List<EpisodeBrief> episodes = List();
|
List<EpisodeBrief> episodes = List();
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.pubDate, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.primaryColor
|
E.downloaded, P.primaryColor
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
where E.feed_id = ? ORDER BY E.milliseconds DESC LIMIT 3""", [id]);
|
where E.feed_id = ? ORDER BY E.milliseconds DESC LIMIT 3""", [id]);
|
||||||
@ -289,7 +321,7 @@ class DBHelper {
|
|||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
list[x]['enclosure_url'],
|
list[x]['enclosure_url'],
|
||||||
list[x]['enclosure_length'],
|
list[x]['enclosure_length'],
|
||||||
list[x]['pubDate'],
|
list[x]['milliseconds'],
|
||||||
list[x]['feed_title'],
|
list[x]['feed_title'],
|
||||||
list[x]['primaryColor'],
|
list[x]['primaryColor'],
|
||||||
list[x]['liked'],
|
list[x]['liked'],
|
||||||
@ -306,7 +338,7 @@ class DBHelper {
|
|||||||
EpisodeBrief episode;
|
EpisodeBrief episode;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.pubDate, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.primaryColor
|
E.downloaded, P.primaryColor
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
where E.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""",
|
where E.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""",
|
||||||
@ -317,7 +349,7 @@ class DBHelper {
|
|||||||
list.first['title'],
|
list.first['title'],
|
||||||
list.first['enclosure_url'],
|
list.first['enclosure_url'],
|
||||||
list.first['enclosure_length'],
|
list.first['enclosure_length'],
|
||||||
list.first['pubDate'],
|
list.first['milliseconds'],
|
||||||
list.first['feed_title'],
|
list.first['feed_title'],
|
||||||
list.first['primaryColor'],
|
list.first['primaryColor'],
|
||||||
list.first['liked'],
|
list.first['liked'],
|
||||||
@ -333,7 +365,7 @@ class DBHelper {
|
|||||||
List<EpisodeBrief> episodes = List();
|
List<EpisodeBrief> episodes = List();
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.pubDate, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.imagePath, P.primaryColor
|
E.downloaded, P.imagePath, P.primaryColor
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
ORDER BY E.milliseconds DESC LIMIT 99""");
|
ORDER BY E.milliseconds DESC LIMIT 99""");
|
||||||
@ -342,15 +374,14 @@ class DBHelper {
|
|||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
list[x]['enclosure_url'],
|
list[x]['enclosure_url'],
|
||||||
list[x]['enclosure_length'],
|
list[x]['enclosure_length'],
|
||||||
list[x]['pubDate'],
|
list[x]['milliseconds'],
|
||||||
list[x]['feed_title'],
|
list[x]['feed_title'],
|
||||||
list[x]['primaryColor'],
|
list[x]['primaryColor'],
|
||||||
list[x]['liked'],
|
list[x]['liked'],
|
||||||
list[x]['doanloaded'],
|
list[x]['doanloaded'],
|
||||||
list[x]['duration'],
|
list[x]['duration'],
|
||||||
list[x]['explicit'],
|
list[x]['explicit'],
|
||||||
list[x]['imagePath']
|
list[x]['imagePath']));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
@ -359,7 +390,7 @@ class DBHelper {
|
|||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = List();
|
List<EpisodeBrief> episodes = List();
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.pubDate, P.imagePath,
|
"""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.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
||||||
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
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 99""");
|
WHERE E.liked = 1 ORDER BY E.milliseconds DESC LIMIT 99""");
|
||||||
@ -368,7 +399,7 @@ class DBHelper {
|
|||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
list[x]['enclosure_url'],
|
list[x]['enclosure_url'],
|
||||||
list[x]['enclosure_length'],
|
list[x]['enclosure_length'],
|
||||||
list[x]['pubDate'],
|
list[x]['milliseconds'],
|
||||||
list[x]['feed_title'],
|
list[x]['feed_title'],
|
||||||
list[x]['primaryColor'],
|
list[x]['primaryColor'],
|
||||||
list[x]['liked'],
|
list[x]['liked'],
|
||||||
@ -417,7 +448,7 @@ class DBHelper {
|
|||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = List();
|
List<EpisodeBrief> episodes = List();
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.pubDate, P.imagePath,
|
"""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.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
||||||
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.downloaded != 'ND' ORDER BY E.download_date DESC LIMIT 99""");
|
WHERE E.downloaded != 'ND' ORDER BY E.download_date DESC LIMIT 99""");
|
||||||
@ -426,7 +457,7 @@ class DBHelper {
|
|||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
list[x]['enclosure_url'],
|
list[x]['enclosure_url'],
|
||||||
list[x]['enclosure_length'],
|
list[x]['enclosure_length'],
|
||||||
list[x]['pubDate'],
|
list[x]['milliseconds'],
|
||||||
list[x]['feed_title'],
|
list[x]['feed_title'],
|
||||||
list[x]['primaryColor'],
|
list[x]['primaryColor'],
|
||||||
list[x]['liked'],
|
list[x]['liked'],
|
||||||
@ -446,10 +477,10 @@ class DBHelper {
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getFeedDescription(String url) async {
|
Future<String> getFeedDescription(String id) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
'SELECT description FROM PodcastLocal WHERE enclosure_url = ?', [url]);
|
'SELECT description FROM PodcastLocal WHERE id = ?', [id]);
|
||||||
String description = list[0]['description'];
|
String description = list[0]['description'];
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
@ -458,7 +489,7 @@ class DBHelper {
|
|||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
EpisodeBrief episode;
|
EpisodeBrief episode;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.pubDate, P.imagePath,
|
"""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.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
||||||
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.enclosure_url = ?""", [url]);
|
WHERE E.enclosure_url = ?""", [url]);
|
||||||
@ -466,7 +497,7 @@ class DBHelper {
|
|||||||
list.first['title'],
|
list.first['title'],
|
||||||
list.first['enclosure_url'],
|
list.first['enclosure_url'],
|
||||||
list.first['enclosure_length'],
|
list.first['enclosure_length'],
|
||||||
list.first['pubDate'],
|
list.first['milliseconds'],
|
||||||
list.first['feed_title'],
|
list.first['feed_title'],
|
||||||
list.first['primaryColor'],
|
list.first['primaryColor'],
|
||||||
list.first['liked'],
|
list.first['liked'],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
@ -24,14 +23,13 @@ void main() async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await FlutterDownloader.initialize();
|
await FlutterDownloader.initialize();
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
await FlutterStatusbarcolor.setStatusBarColor(Colors.grey[100]);
|
|
||||||
await FlutterStatusbarcolor.setNavigationBarColor(Colors.grey[100]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
|
SystemUiOverlayStyle(statusBarColor: Colors.grey[100]));
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: 'TsacDop',
|
title: 'TsacDop',
|
||||||
|
@ -24,16 +24,17 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
result == 0 ?
|
result == 0 ?
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: 'No Update',
|
msg: 'No Update',
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.TOP,
|
||||||
)
|
)
|
||||||
: Fluttertoast.showToast(
|
: Fluttertoast.showToast(
|
||||||
msg: 'Updated $result Episodes',
|
msg: 'Updated $result Episodes',
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.TOP,
|
||||||
);
|
);
|
||||||
if(mounted) setState(() {});
|
if(mounted) setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal) async {
|
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal) async {
|
||||||
|
print(podcastLocal.id);
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<EpisodeBrief> episodes = await
|
List<EpisodeBrief> episodes = await
|
||||||
dbHelper.getRssItem(podcastLocal.id);
|
dbHelper.getRssItem(podcastLocal.id);
|
||||||
@ -43,25 +44,25 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.podcastLocal.title,),
|
title: Text(widget.podcastLocal.title,),
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
key: _refreshIndicatorKey,
|
key: _refreshIndicatorKey,
|
||||||
color: Colors.blue[500],
|
color: Colors.blue[500],
|
||||||
onRefresh: () => _updateRssItem(widget.podcastLocal),
|
onRefresh: () => _updateRssItem(widget.podcastLocal),
|
||||||
child: FutureBuilder<List<EpisodeBrief>>(
|
child: FutureBuilder<List<EpisodeBrief>>(
|
||||||
future: _getRssItem(widget.podcastLocal),
|
future: _getRssItem(widget.podcastLocal),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) print(snapshot.error);
|
if (snapshot.hasError) print(snapshot.error);
|
||||||
return (snapshot.hasData)
|
return (snapshot.hasData)
|
||||||
? EpisodeGrid(podcast: snapshot.data, showDownload: true, showFavorite: true, showNumber: true, heroTag: 'podcast',)
|
? EpisodeGrid(podcast: snapshot.data, showDownload: true, showFavorite: true, showNumber: true, heroTag: 'podcast',)
|
||||||
: Center(child: CircularProgressIndicator());
|
: Center(child: CircularProgressIndicator());
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import 'package:fluttertoast/fluttertoast.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tsacdop/class/podcast_group.dart';
|
import 'package:tsacdop/class/podcast_group.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
|
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||||
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
|
|
||||||
class PodcastGroupList extends StatefulWidget {
|
class PodcastGroupList extends StatefulWidget {
|
||||||
final PodcastGroup group;
|
final PodcastGroup group;
|
||||||
@ -32,10 +34,15 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
|
|||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: Duration(milliseconds: 800),
|
duration: Duration(milliseconds: 800),
|
||||||
width: _loadSave ? 70 : 0,
|
width: _loadSave ? 70 : 0,
|
||||||
height: 40,
|
height: 60,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
|
boxShadow: [BoxShadow(
|
||||||
|
color: Colors.grey[700],
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: Offset(1, 1),
|
||||||
|
),]
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -86,7 +93,7 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
|
|||||||
key: ObjectKey(podcastLocal.title),
|
key: ObjectKey(podcastLocal.title),
|
||||||
child: PodcastCard(
|
child: PodcastCard(
|
||||||
podcastLocal: podcastLocal,
|
podcastLocal: podcastLocal,
|
||||||
group: widget.group.name,
|
group: widget.group,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
@ -105,7 +112,7 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
|
|||||||
|
|
||||||
class PodcastCard extends StatefulWidget {
|
class PodcastCard extends StatefulWidget {
|
||||||
final PodcastLocal podcastLocal;
|
final PodcastLocal podcastLocal;
|
||||||
final String group;
|
final PodcastGroup group;
|
||||||
PodcastCard({this.podcastLocal, this.group, Key key}) : super(key: key);
|
PodcastCard({this.podcastLocal, this.group, Key key}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
_PodcastCardState createState() => _PodcastCardState();
|
_PodcastCardState createState() => _PodcastCardState();
|
||||||
@ -114,8 +121,8 @@ class PodcastCard extends StatefulWidget {
|
|||||||
class _PodcastCardState extends State<PodcastCard> {
|
class _PodcastCardState extends State<PodcastCard> {
|
||||||
bool _loadMenu;
|
bool _loadMenu;
|
||||||
bool _addGroup;
|
bool _addGroup;
|
||||||
List<String> _selectedGroups;
|
List<PodcastGroup> _selectedGroups;
|
||||||
List<String> _belongGroups;
|
List<PodcastGroup> _belongGroups;
|
||||||
Color _c;
|
Color _c;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -146,11 +153,8 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
||||||
double _width = MediaQuery.of(context).size.width;
|
double _width = MediaQuery.of(context).size.width;
|
||||||
var _groupList = Provider.of<GroupList>(context);
|
var _groupList = Provider.of<GroupList>(context);
|
||||||
_belongGroups = _groupList
|
_belongGroups = _groupList.getPodcastGroup(widget.podcastLocal.id);
|
||||||
.getPodcastGroup(widget.podcastLocal.id)
|
|
||||||
.map((e) => e.name)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -198,7 +202,7 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
children: _belongGroups.map((group) {
|
children: _belongGroups.map((group) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(right: 5.0),
|
padding: EdgeInsets.only(right: 5.0),
|
||||||
child: Text(group));
|
child: Text(group.name));
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -263,21 +267,20 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: _groupList.groups
|
children: _groupList.groups
|
||||||
.map<Widget>((PodcastGroup group){
|
.map<Widget>((PodcastGroup group) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(left: 5.0),
|
padding: EdgeInsets.only(left: 5.0),
|
||||||
child: FilterChip(
|
child: FilterChip(
|
||||||
key: ValueKey<String>(group.id),
|
key: ValueKey<String>(group.id),
|
||||||
label: Text(group.name),
|
label: Text(group.name),
|
||||||
selected:
|
selected: _selectedGroups.contains(group),
|
||||||
_selectedGroups.contains(group.name),
|
|
||||||
onSelected: (bool value) {
|
onSelected: (bool value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
_selectedGroups.remove(group.name);
|
_selectedGroups.remove(group);
|
||||||
print(group.name);
|
print(group.name);
|
||||||
} else {
|
} else {
|
||||||
_selectedGroups.add(group.name);
|
_selectedGroups.add(group);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -327,7 +330,15 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
: Row(
|
: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buttonOnMenu(Icon(Icons.fullscreen), () {}),
|
_buttonOnMenu(
|
||||||
|
Icon(Icons.fullscreen),
|
||||||
|
() => Navigator.push(
|
||||||
|
context,
|
||||||
|
ScaleRoute(
|
||||||
|
page: PodcastDetail(
|
||||||
|
podcastLocal: widget.podcastLocal,
|
||||||
|
)),
|
||||||
|
)),
|
||||||
_buttonOnMenu(Icon(Icons.add), () {
|
_buttonOnMenu(Icon(Icons.add), () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_addGroup = true;
|
_addGroup = true;
|
||||||
|
@ -25,9 +25,9 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||||||
String _description;
|
String _description;
|
||||||
bool _load;
|
bool _load;
|
||||||
|
|
||||||
void getDescription(String title) async {
|
void getDescription(String id) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
String description = await dbHelper.getFeedDescription(title);
|
String description = await dbHelper.getFeedDescription(id);
|
||||||
_description = description;
|
_description = description;
|
||||||
setState(() {
|
setState(() {
|
||||||
_load = true;
|
_load = true;
|
||||||
@ -38,7 +38,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_load = false;
|
_load = false;
|
||||||
getDescription(widget.podcastLocal.title);
|
getDescription(widget.podcastLocal.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -13,7 +13,7 @@ class PodcastManage extends StatefulWidget {
|
|||||||
class _PodcastManageState extends State<PodcastManage> {
|
class _PodcastManageState extends State<PodcastManage> {
|
||||||
Decoration getIndicator() {
|
Decoration getIndicator() {
|
||||||
return const UnderlineTabIndicator(
|
return const UnderlineTabIndicator(
|
||||||
borderSide: BorderSide(color: Colors.red, width: 2),
|
borderSide: BorderSide(color: Colors.red, width: 0),
|
||||||
insets: EdgeInsets.only(
|
insets: EdgeInsets.only(
|
||||||
top: 10.0,
|
top: 10.0,
|
||||||
));
|
));
|
||||||
@ -50,44 +50,57 @@ class _PodcastManageState extends State<PodcastManage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 4,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
labelPadding: EdgeInsets.only(
|
labelColor: Colors.black,
|
||||||
top: 5.0,
|
unselectedLabelColor: Colors.grey[500],
|
||||||
bottom: 6.0,
|
labelPadding: EdgeInsets.all(5.0),
|
||||||
left: 10.0,
|
|
||||||
right: 10.0),
|
|
||||||
indicator: getIndicator(),
|
indicator: getIndicator(),
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: _groups.map<Tab>((group) {
|
tabs: _groups.map<Tab>((group) {
|
||||||
return Tab(
|
return Tab(
|
||||||
child: Text(group.name),
|
child: Container(
|
||||||
|
height: 30.0,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 10.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(15)),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
group.name,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Expanded(
|
||||||
child: FlatButton(
|
flex: 1,
|
||||||
|
child: IconButton(
|
||||||
onPressed: () => showDialog(
|
onPressed: () => showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) =>
|
builder: (BuildContext context) =>
|
||||||
AddGroup()),
|
AddGroup()),
|
||||||
child: Icon(Icons.add)),
|
icon: Icon(Icons.add)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: _groups.map<Widget>((group) {
|
children: _groups.map<Widget>((group) {
|
||||||
return Container(
|
return Container(
|
||||||
key: ObjectKey(group),
|
key: ObjectKey(group),
|
||||||
child: PodcastGroupList(group: group));
|
child: PodcastGroupList(group: group));
|
||||||
}).toList(),),
|
}).toList(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -49,129 +49,133 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
? _c = Color.fromRGBO(
|
? _c = Color.fromRGBO(
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
||||||
return InkWell(
|
return Material(
|
||||||
onTap: () {
|
color: Colors.transparent,
|
||||||
Navigator.push(
|
child: InkWell(
|
||||||
context,
|
onTap: () {
|
||||||
ScaleRoute(
|
Navigator.push(
|
||||||
page: EpisodeDetail(
|
context,
|
||||||
episodeItem: podcast[index],
|
ScaleRoute(
|
||||||
heroTag: heroTag,
|
page: EpisodeDetail(
|
||||||
)),
|
episodeItem: podcast[index],
|
||||||
);
|
heroTag: heroTag,
|
||||||
},
|
)),
|
||||||
child: Container(
|
);
|
||||||
decoration: BoxDecoration(
|
},
|
||||||
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
child: Container(
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
color: Colors.grey[100],
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
width: 3.0,
|
border: Border.all(
|
||||||
),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.grey[100],
|
color: Colors.grey[100],
|
||||||
blurRadius: 1.0,
|
width: 3.0,
|
||||||
spreadRadius: 0.5,
|
|
||||||
),
|
),
|
||||||
]),
|
boxShadow: [
|
||||||
alignment: Alignment.center,
|
BoxShadow(
|
||||||
padding: EdgeInsets.all(8.0),
|
color: Colors.grey[100],
|
||||||
child: Column(
|
blurRadius: 1.0,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
spreadRadius: 0.5,
|
||||||
children: <Widget>[
|
),
|
||||||
Expanded(
|
]),
|
||||||
flex: 2,
|
alignment: Alignment.center,
|
||||||
child: Row(
|
padding: EdgeInsets.all(8.0),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
child: Column(
|
||||||
children: <Widget>[
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Hero(
|
children: <Widget>[
|
||||||
tag: podcast[index].enclosureUrl + heroTag,
|
Expanded(
|
||||||
child: Container(
|
flex: 2,
|
||||||
child: ClipRRect(
|
child: Row(
|
||||||
borderRadius: BorderRadius.all(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
Radius.circular(_width / 32)),
|
children: <Widget>[
|
||||||
child: Container(
|
Hero(
|
||||||
height: _width / 16,
|
tag: podcast[index].enclosureUrl + heroTag,
|
||||||
width: _width / 16,
|
child: Container(
|
||||||
child: Image.file(
|
child: ClipRRect(
|
||||||
File("${podcast[index].imagePath}")),
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(_width / 32)),
|
||||||
|
child: Container(
|
||||||
|
height: _width / 16,
|
||||||
|
width: _width / 16,
|
||||||
|
child: Image.file(File(
|
||||||
|
"${podcast[index].imagePath}")),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Spacer(),
|
||||||
Spacer(),
|
showNumber
|
||||||
showNumber
|
? Container(
|
||||||
? Container(
|
alignment: Alignment.topRight,
|
||||||
alignment: Alignment.topRight,
|
child: Text(
|
||||||
child: Text(
|
(podcast.length - index).toString(),
|
||||||
(podcast.length - index).toString(),
|
style: GoogleFonts.teko(
|
||||||
style: GoogleFonts.teko(
|
textStyle: TextStyle(
|
||||||
textStyle: TextStyle(
|
fontSize: _width / 24,
|
||||||
fontSize: _width / 24,
|
color: _c,
|
||||||
color: _c,
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
: Center(),
|
||||||
: Center(),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 5,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
padding: EdgeInsets.only(top: 2.0),
|
|
||||||
child: Text(
|
|
||||||
podcast[index].title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: _width / 32,
|
|
||||||
),
|
|
||||||
maxLines: 4,
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
flex: 5,
|
||||||
flex: 1,
|
child: Container(
|
||||||
child: Row(
|
alignment: Alignment.topLeft,
|
||||||
children: <Widget>[
|
padding: EdgeInsets.only(top: 2.0),
|
||||||
Align(
|
child: Text(
|
||||||
alignment: Alignment.bottomLeft,
|
podcast[index].title,
|
||||||
child: Text(
|
style: TextStyle(
|
||||||
podcast[index].pubDate.substring(4, 16),
|
fontSize: _width / 32,
|
||||||
style: TextStyle(
|
|
||||||
fontSize: _width / 35,
|
|
||||||
color: _c,
|
|
||||||
fontStyle: FontStyle.italic),
|
|
||||||
),
|
),
|
||||||
|
maxLines: 4,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
),
|
),
|
||||||
Spacer(),
|
),
|
||||||
showDownload
|
|
||||||
? DownloadIcon(episodeBrief: podcast[index])
|
|
||||||
: Center(),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(1),
|
|
||||||
),
|
|
||||||
showFavorite
|
|
||||||
? Container(
|
|
||||||
alignment: Alignment.bottomRight,
|
|
||||||
child: (podcast[index].liked == 0)
|
|
||||||
? Center()
|
|
||||||
: IconTheme(
|
|
||||||
data: IconThemeData(size: 15),
|
|
||||||
child: Icon(
|
|
||||||
Icons.favorite,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Center(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
],
|
flex: 1,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: Text(
|
||||||
|
podcast[index].dateToString(),
|
||||||
|
//podcast[index].pubDate.substring(4, 16),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: _width / 35,
|
||||||
|
color: _c,
|
||||||
|
fontStyle: FontStyle.italic),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
showDownload
|
||||||
|
? DownloadIcon(episodeBrief: podcast[index])
|
||||||
|
: Center(),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(1),
|
||||||
|
),
|
||||||
|
showFavorite
|
||||||
|
? Container(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: (podcast[index].liked == 0)
|
||||||
|
? Center()
|
||||||
|
: IconTheme(
|
||||||
|
data: IconThemeData(size: 15),
|
||||||
|
child: Icon(
|
||||||
|
Icons.favorite,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -45,11 +45,11 @@ class RssItunes {
|
|||||||
}
|
}
|
||||||
return RssItunes(
|
return RssItunes(
|
||||||
author: findElementOrNull(element, "itunes:author")?.text?.trim(),
|
author: findElementOrNull(element, "itunes:author")?.text?.trim(),
|
||||||
summary: findElementOrNull(element, "itunes:summary")?.text?.trim() ?? '',
|
summary: findElementOrNull(element, "itunes:summary")?.text?.trim(),
|
||||||
explicit: parseBoolLiteral(element, "itunes:explicit"),
|
explicit: parseBoolLiteral(element, "itunes:explicit"),
|
||||||
title: findElementOrNull(element, "itunes:title")?.text?.trim(),
|
title: findElementOrNull(element, "itunes:title")?.text?.trim(),
|
||||||
// subtitle: findElementOrNull(element, "itunes:subtitle")?.text?.trim(),
|
// subtitle: findElementOrNull(element, "itunes:subtitle")?.text?.trim(),
|
||||||
//owner: RssItunesOwner.parse(findElementOrNull(element, "itunes:owner")),
|
//owner: RssItunesOwner.parse(findElementOrNull(element, "itunes:owner")),
|
||||||
// keywords: findElementOrNull(element, "itunes:keywords")
|
// keywords: findElementOrNull(element, "itunes:keywords")
|
||||||
// ?.text
|
// ?.text
|
||||||
// ?.split(",")
|
// ?.split(",")
|
||||||
|
@ -118,13 +118,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.11.1"
|
||||||
flutter_statusbarcolor:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: flutter_statusbarcolor
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.3"
|
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -27,7 +27,6 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_statusbarcolor: ^0.2.3
|
|
||||||
json_annotation: any
|
json_annotation: any
|
||||||
sqflite: any
|
sqflite: any
|
||||||
flutter_html: ^0.11.1
|
flutter_html: ^0.11.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user