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