From 16567a719945478d2dcc38aabecc782c858c3220 Mon Sep 17 00:00:00 2001 From: stonegate Date: Thu, 20 Feb 2020 17:09:21 +0800 Subject: [PATCH] new file: lib/class/podcast_group.dart modified: lib/class/podcastlocal.dart modified: lib/class/settingstate.dart modified: lib/episodes/episodedetail.dart modified: lib/episodes/episodedownload.dart modified: lib/home/appbar/addpodcast.dart modified: lib/home/appbar/popupmenu.dart modified: lib/home/audio_player.dart modified: lib/home/home.dart modified: lib/home/homescroll.dart modified: lib/home/hometab.dart new file: lib/local_storage/key_value_storage.dart renamed: lib/class/sqflite_localpodcast.dart -> lib/local_storage/sqflite_localpodcast.dart modified: lib/main.dart modified: lib/podcasts/podcastdetail.dart new file: lib/podcasts/podcastgroup.dart modified: lib/podcasts/podcastlist.dart modified: lib/podcasts/podcastmanage.dart modified: lib/util/episodegrid.dart modified: lib/webfeed/domain/rss_item.dart modified: lib/webfeed/domain/rss_itunes.dart modified: pubspec.lock modified: pubspec.yaml Add podcast group support Manage group with provider --- lib/class/podcast_group.dart | 158 +++++++ lib/class/podcastlocal.dart | 8 +- lib/class/settingstate.dart | 16 +- lib/episodes/episodedetail.dart | 112 ++--- lib/episodes/episodedownload.dart | 75 ++-- lib/home/appbar/addpodcast.dart | 17 +- lib/home/appbar/popupmenu.dart | 16 +- lib/home/audio_player.dart | 10 +- lib/home/home.dart | 25 -- lib/home/homescroll.dart | 234 ++++++---- lib/home/hometab.dart | 2 +- lib/local_storage/key_value_storage.dart | 34 ++ .../sqflite_localpodcast.dart | 108 ++--- lib/main.dart | 16 +- lib/podcasts/podcastdetail.dart | 2 +- lib/podcasts/podcastgroup.dart | 398 ++++++++++++++++++ lib/podcasts/podcastlist.dart | 163 +++---- lib/podcasts/podcastmanage.dart | 326 ++++++++------ lib/util/episodegrid.dart | 6 +- lib/webfeed/domain/rss_item.dart | 10 +- lib/webfeed/domain/rss_itunes.dart | 2 +- pubspec.lock | 53 ++- pubspec.yaml | 8 +- 23 files changed, 1275 insertions(+), 524 deletions(-) create mode 100644 lib/class/podcast_group.dart create mode 100644 lib/local_storage/key_value_storage.dart rename lib/{class => local_storage}/sqflite_localpodcast.dart (86%) create mode 100644 lib/podcasts/podcastgroup.dart diff --git a/lib/class/podcast_group.dart b/lib/class/podcast_group.dart new file mode 100644 index 0000000..3599030 --- /dev/null +++ b/lib/class/podcast_group.dart @@ -0,0 +1,158 @@ +import 'dart:collection'; +import 'dart:core'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:tsacdop/local_storage/key_value_storage.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; +import 'package:uuid/uuid.dart'; +import 'package:tsacdop/class/podcastlocal.dart'; + +class GroupEntity { + final String name; + final String id; + final String color; + final List podcastList; + + GroupEntity(this.name, this.id, this.color, this.podcastList); + + Map toJson() { + return {'name': name, 'id': id, 'color': color, 'podcastList': podcastList}; + } + + static GroupEntity fromJson(Map json) { + List list = List.from(json['podcastList']); + print(json['[podcastList']); + return + GroupEntity( + json['name'] as String, + json['id'] as String, + json['color'] as String, + list); + } +} + +class PodcastGroup { + final String name; + final String id; + final String color; + final List podcastList; + + PodcastGroup(this.name, + {this.color = '#000000', String id, List podcastList}) + : id = id ?? Uuid().v4(), + podcastList = podcastList ?? []; + + Future getPodcasts() async { + var dbHelper = DBHelper(); + if (podcastList != []) { + _podcasts = await dbHelper.getPodcastLocal(podcastList, 0); + } + } + + List _podcasts; + + List get podcasts => _podcasts; + + GroupEntity toEntity() { + return GroupEntity(name, id, color, podcastList); + } + + static PodcastGroup fromEntity(GroupEntity entity) { + return PodcastGroup( + entity.name, + id: entity.id, + color: entity.color, + podcastList: entity.podcastList, + ); + } +} + +class GroupList extends ChangeNotifier { + List _groups; + DBHelper dbHelper = DBHelper(); + UnmodifiableListView get groups => + UnmodifiableListView(_groups); + + KeyValueStorage storage = KeyValueStorage('groups'); + GroupList({List groups}) : _groups = groups ?? []; + + bool _isLoading = false; + + bool get isLoading => _isLoading; + + @override + void addListener(VoidCallback listener) { + super.addListener(listener); + loadGroups(); + } + + Future loadGroups() async { + _isLoading = true; + notifyListeners(); + storage.getGroups().then((loadgroups) async { + _groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e))); + await Future.forEach(_groups, (group) async { + await group.getPodcasts(); + }); + _isLoading = false; + notifyListeners(); + }); + } + + Future addGroup(PodcastGroup podcastGroup) async { + _groups.add(podcastGroup); + _saveGroup(); + notifyListeners(); + } + + Future delGroup(PodcastGroup podcastGroup) async { + _groups.remove(podcastGroup); + notifyListeners(); + _saveGroup(); + } + + void updateGroup(PodcastGroup podcastGroup) { + var oldGroup = _groups.firstWhere((it) => it.id == podcastGroup.id); + var index = _groups.indexOf(oldGroup); + _groups.replaceRange(index, index + 1, [podcastGroup]); + notifyListeners(); + _saveGroup(); + } + + void _saveGroup() { + storage.saveGroup(_groups.map((it) => it.toEntity()).toList()); + } + + Future subscribe(PodcastLocal podcastLocal) async { + _groups[0].podcastList.add(podcastLocal.id); + _saveGroup(); + await dbHelper.savePodcastLocal(podcastLocal); + await _groups[0].getPodcasts(); + notifyListeners(); + } + + List getPodcastGroup(String id) { + List result =[]; + _groups.forEach((group) { + if (group.podcastList.contains(id)) { + result.add(group); + } + }); + return result; + } + + changeGroup(String id, List l) { + _groups.forEach((group) { + if (group.podcastList.contains(id)) { + group.podcastList.remove(id); + } + }); + l.forEach((s) { + _groups.forEach((group) { + if (group.name == s) group.podcastList.add(id); + }); + }); + notifyListeners(); + _saveGroup(); + } +} diff --git a/lib/class/podcastlocal.dart b/lib/class/podcastlocal.dart index 47dd50c..8cdacf8 100644 --- a/lib/class/podcastlocal.dart +++ b/lib/class/podcastlocal.dart @@ -1,4 +1,6 @@ +import 'package:uuid/uuid.dart'; + class PodcastLocal { final String title; final String imageUrl; @@ -6,5 +8,7 @@ class PodcastLocal { final String author; String description; final String primaryColor; - PodcastLocal(this.title, this.imageUrl, this.rssUrl, this.primaryColor, this.author); -} \ No newline at end of file + final String id; + PodcastLocal(this.title, this.imageUrl, this.rssUrl, this.primaryColor, this.author,{String id}) : id = id ?? Uuid().v4(); +} + diff --git a/lib/class/settingstate.dart b/lib/class/settingstate.dart index 78b5b51..b04f0da 100644 --- a/lib/class/settingstate.dart +++ b/lib/class/settingstate.dart @@ -1,19 +1,13 @@ import 'package:flutter/foundation.dart'; -enum Setting {start, stop} +//two types podcast update, backhome nedd to back to default grooup. +enum Update {backhome, justupdate} class SettingState extends ChangeNotifier{ - Setting _subscribeupdate; - Setting get subscribeupdate => _subscribeupdate; - set subscribeUpdate(Setting s){ + Update _subscribeupdate; + Update get subscribeupdate => _subscribeupdate; + set subscribeUpdate(Update s){ _subscribeupdate = s; notifyListeners(); } - Setting _themeupdate; - Setting get themeUpdate => _themeupdate; - set themeUpdate(Setting s){ - _themeupdate = s; - notifyListeners(); - } - } diff --git a/lib/episodes/episodedetail.dart b/lib/episodes/episodedetail.dart index 9b95843..cd38bb6 100644 --- a/lib/episodes/episodedetail.dart +++ b/lib/episodes/episodedetail.dart @@ -1,22 +1,23 @@ +import 'dart:io'; import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/episodebrief.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'episodedownload.dart'; -enum DownloadState { stop, load, donwload, complete, error } - class EpisodeDetail extends StatefulWidget { final EpisodeBrief episodeItem; final String heroTag; - EpisodeDetail({this.episodeItem, this.heroTag, Key key}) : super(key: key); + final String path; + EpisodeDetail({this.episodeItem, this.heroTag, this.path, Key key}) + : super(key: key); @override _EpisodeDetailState createState() => _EpisodeDetailState(); @@ -26,7 +27,10 @@ class _EpisodeDetailState extends State { final textstyle = TextStyle(fontSize: 15.0, color: Colors.black); double downloadProgress; bool _loaddes; + String path; Future getSDescription(String title) async { + var dir = await getApplicationDocumentsDirectory(); + path = dir.path; var dbHelper = DBHelper(); widget.episodeItem.description = await dbHelper.getDescription(title); if (mounted) @@ -62,7 +66,7 @@ class _EpisodeDetailState extends State { ), body: Container( color: Colors.grey[100], - padding: EdgeInsets.all(12.0), + padding: EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -77,7 +81,7 @@ class _EpisodeDetailState extends State { alignment: Alignment.topLeft, child: Text( widget.episodeItem.title, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headline5, ), ), Container( @@ -144,22 +148,26 @@ class _EpisodeDetailState extends State { child: Container( padding: EdgeInsets.only(left: 12.0, right: 12.0, top: 5.0), child: SingleChildScrollView( - child: (widget.episodeItem.description != null && _loaddes) - ? Html( - data: widget.episodeItem.description, - onLinkTap: (url) { - _launchUrl(url); - }, - useRichText: true, - ) + child: _loaddes + ? (widget.episodeItem.description.contains('<')) + ? Html( + data: widget.episodeItem.description, + onLinkTap: (url) { + _launchUrl(url); + }, + useRichText: true, + ) + : Container( + alignment: Alignment.topLeft, + child: Text(widget.episodeItem.description)) : Center(), ), ), ), MenuBar( - episodeItem: widget.episodeItem, - heroTag: widget.heroTag, - ), + episodeItem: widget.episodeItem, + heroTag: widget.heroTag, + path: widget.path), ], ), ), @@ -168,9 +176,11 @@ class _EpisodeDetailState extends State { } class MenuBar extends StatefulWidget { + final String path; final EpisodeBrief episodeItem; final String heroTag; - MenuBar({this.episodeItem, this.heroTag, Key key}) : super(key: key); + MenuBar({this.episodeItem, this.heroTag, this.path, Key key}) + : super(key: key); @override _MenuBarState createState() => _MenuBarState(); } @@ -204,7 +214,7 @@ class _MenuBarState extends State { _like = widget.episodeItem.liked; } - Widget _buttonOnMenu(Widget widget, Function() onTap) => Material( + Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material( color: Colors.transparent, child: InkWell( onTap: onTap, @@ -232,7 +242,8 @@ class _MenuBarState extends State { (widget.episodeItem.title == urlChange.title && urlChange.audioState == AudioState.play) ? ImageRotate( - url: widget.episodeItem.imageUrl, + title: widget.episodeItem.feedTitle, + path: widget.path, ) : Hero( tag: widget.episodeItem.enclosureUrl + widget.heroTag, @@ -244,9 +255,8 @@ class _MenuBarState extends State { height: 30.0, width: 30.0, color: Colors.white, - child: CachedNetworkImage( - imageUrl: widget.episodeItem.imageUrl, - ), + child: Image.file(File( + "${widget.path}/${widget.episodeItem.feedTitle}.png")), ), ), ), @@ -298,10 +308,18 @@ class _MenuBarState extends State { alignment: Alignment.center, height: 50.0, padding: EdgeInsets.symmetric(horizontal: 20.0), - child:Row( + child: Row( children: [ - Text('Play Now', style: TextStyle(color: Colors.blue, fontSize: 15, fontWeight: FontWeight.bold,)), - Icon(Icons.play_arrow, color: Colors.blue,), + Text('Play Now', + style: TextStyle( + color: Colors.blue, + fontSize: 15, + fontWeight: FontWeight.bold, + )), + Icon( + Icons.play_arrow, + color: Colors.blue, + ), ], ), ), @@ -487,8 +505,9 @@ class _WaveLoaderState extends State } class ImageRotate extends StatefulWidget { - final String url; - ImageRotate({this.url, Key key}) : super(key: key); + final String title; + final String path; + ImageRotate({this.title, this.path, Key key}) : super(key: key); @override _ImageRotateState createState() => _ImageRotateState(); } @@ -541,9 +560,7 @@ class _ImageRotateState extends State height: 30.0, width: 30.0, color: Colors.white, - child: CachedNetworkImage( - imageUrl: widget.url, - ), + child: Image.file(File("${widget.path}/${widget.title}.png")), ), ), ), @@ -590,35 +607,26 @@ class LoveOpen extends StatefulWidget { class _LoveOpenState extends State with SingleTickerProviderStateMixin { Animation _animationA; - Animation _animationB; AnimationController _controller; - var rect = RelativeRect.fromLTRB(100, 100, 100, 100); - var rectend = RelativeRect.fromLTRB(0, 0, 0, 0); - @override void initState() { super.initState(); _controller = AnimationController( vsync: this, - duration: Duration(milliseconds: 500), + duration: Duration(milliseconds: 100), ); _animationA = Tween(begin: 0.0, end: 1.0).animate(_controller) ..addListener(() { if (mounted) setState(() {}); }); - _animationB = - RelativeRectTween(begin: rect, end: rectend).animate(_controller) - ..addListener(() { - if (mounted) setState(() {}); - }); _controller.forward(); _controller.addStatusListener((status) { if (status == AnimationStatus.completed) { _controller.reset(); - } + } }); } @@ -635,8 +643,8 @@ class _LoveOpenState extends State scale: _animationA, alignment: Alignment.center, child: Transform.rotate( - angle: angle, - child: SizedBox( + angle: angle, + child: SizedBox( height: 5 * scale, width: 6 * scale, child: CustomPaint( @@ -658,22 +666,22 @@ class _LoveOpenState extends State children: [ Row( children: [ - _littleHeart(1.3, 10, -math.pi/6), - _littleHeart(1.5, 3, 0), + _littleHeart(0.5, 10, -math.pi / 6), + _littleHeart(1.2, 3, 0), ], ), Row( children: [ - _littleHeart(0.8, 6, math.pi*1.5), - _littleHeart(1.2, 24, math.pi/2), + _littleHeart(0.8, 6, math.pi * 1.5), + _littleHeart(0.9, 24, math.pi / 2), ], ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - _littleHeart(1, 8,-math.pi*0.7), - _littleHeart(1.3, 8, math.pi), - _littleHeart(1.1, 3, -math.pi*1.2) + _littleHeart(1, 8, -math.pi * 0.7), + _littleHeart(0.8, 8, math.pi), + _littleHeart(0.6, 3, -math.pi * 1.2) ], ), ], diff --git a/lib/episodes/episodedownload.dart b/lib/episodes/episodedownload.dart index bf994fc..d7a0d6d 100644 --- a/lib/episodes/episodedownload.dart +++ b/lib/episodes/episodedownload.dart @@ -8,8 +8,9 @@ import 'package:path_provider/path_provider.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:tsacdop/class/downloadstate.dart'; import 'package:tsacdop/class/episodebrief.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; class DownloadButton extends StatefulWidget { final EpisodeBrief episodeBrief; @@ -150,9 +151,9 @@ class _DownloadButtonState extends State { final tasks = await FlutterDownloader.loadTasks(); _task = _TaskInfo( - name: widget.episodeBrief.title, - link: widget.episodeBrief.enclosureUrl); - + name: widget.episodeBrief.title, + link: widget.episodeBrief.enclosureUrl, + ); tasks?.forEach((task) { if (_task.link == task.url) { _task.taskId = task.taskId; @@ -203,13 +204,28 @@ class _DownloadButtonState extends State { @override Widget build(BuildContext context) { - return _downloadButton(_task); + return _isLoading + ? Center() + : Row( + children: [ + _downloadButton(_task), + AnimatedContainer( + duration: Duration(seconds: 1), + decoration: BoxDecoration( + color: Colors.cyan[300], + borderRadius: BorderRadius.all(Radius.circular(15.0))), + height: 20.0, + width: + (_task.status == DownloadTaskStatus.running) ? 50.0 : 0, + alignment: Alignment.center, + child: Text('${_task.progress}%', + style: TextStyle(color: Colors.white))), + ], + ); } Widget _downloadButton(_TaskInfo task) { - if (_isLoading) - return Center(); - else if (task.status == DownloadTaskStatus.undefined) { + if (task.status == DownloadTaskStatus.undefined) { return _buttonOnMenu( Icon( Icons.arrow_downward, @@ -217,33 +233,28 @@ class _DownloadButtonState extends State { ), () => _requestDownload(task)); } else if (task.status == DownloadTaskStatus.running) { - return Row( - children: [ - Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - _pauseDownload(task); - }, - child: Container( - height: 50.0, - alignment: Alignment.center, - padding: EdgeInsets.symmetric(horizontal: 18.0), - child: SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - backgroundColor: Colors.grey[500], - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.blue), - value: task.progress / 100, - ), - ), + return Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + (task.progress > 0) ? _pauseDownload(task) : null; + }, + child: Container( + height: 50.0, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 18.0), + child: SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + backgroundColor: Colors.grey[500], + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.cyan[300]), + value: task.progress / 100, ), ), ), - Text('${task.progress}%', style: TextStyle(color: Colors.blue,),), - ], + ), ); } else if (task.status == DownloadTaskStatus.paused) { return Material( diff --git a/lib/home/appbar/addpodcast.dart b/lib/home/appbar/addpodcast.dart index 9b0e8b4..c5d4a00 100644 --- a/lib/home/appbar/addpodcast.dart +++ b/lib/home/appbar/addpodcast.dart @@ -11,9 +11,11 @@ import 'package:image/image.dart' as img; import 'package:fluttertoast/fluttertoast.dart'; import 'package:tsacdop/class/importompl.dart'; +import 'package:tsacdop/class/podcast_group.dart'; import 'package:tsacdop/class/searchpodcast.dart'; import 'package:tsacdop/class/podcastlocal.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/class/settingstate.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/home/home.dart'; import 'package:tsacdop/home/appbar/popupmenu.dart'; import 'package:tsacdop/webfeed/webfeed.dart'; @@ -211,7 +213,8 @@ class _SearchResultState extends State { @override Widget build(BuildContext context) { ImportOmpl importOmpl = Provider.of(context); - + var groupList = Provider.of(context); + var _settingState = Provider.of(context); savePodcast(String rss) async { print(rss); if (mounted) setState(() => _adding = true); @@ -240,17 +243,19 @@ class _SearchResultState extends State { PodcastLocal podcastLocal = PodcastLocal( _p.title, _p.itunes.image.href, rss, _primaryColor, _p.author); podcastLocal.description = _p.description; - var dbHelper = DBHelper(); - await dbHelper.savePodcastLocal(podcastLocal); + groupList.subscribe(podcastLocal); importOmpl.importState = ImportState.parse; - + var dbHelper = DBHelper(); await dbHelper.savePodcastRss(_p); importOmpl.importState = ImportState.complete; - importOmpl.importState = ImportState.stop; + + _settingState.subscribeUpdate = Update.backhome; + print('fatch data'); } catch (e) { + importOmpl.importState = ImportState.error; Fluttertoast.showToast( msg: 'Network error, Subscribe failed', gravity: ToastGravity.BOTTOM, diff --git a/lib/home/appbar/popupmenu.dart b/lib/home/appbar/popupmenu.dart index 2ab3401..ca71fc3 100644 --- a/lib/home/appbar/popupmenu.dart +++ b/lib/home/appbar/popupmenu.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:provider/provider.dart'; +import 'package:tsacdop/class/settingstate.dart'; import 'package:xml/xml.dart' as xml; import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; @@ -13,7 +14,7 @@ import 'package:image/image.dart' as img; import 'about.dart'; import 'package:tsacdop/class/podcastlocal.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/class/importompl.dart'; import 'package:tsacdop/webfeed/webfeed.dart'; @@ -43,10 +44,11 @@ class PopupMenu extends StatelessWidget { @override Widget build(BuildContext context) { ImportOmpl importOmpl = Provider.of(context); - + var _settingState = Provider.of(context); _refreshAll() async { var dbHelper = DBHelper(); - List podcastList = await dbHelper.getPodcastLocal(); + List podcastList = + await dbHelper.getPodcastLocalAll(); await Future.forEach(podcastList, (podcastLocal) async { importOmpl.rssTitle = podcastLocal.title; importOmpl.importState = ImportState.parse; @@ -83,12 +85,16 @@ class PopupMenu extends StatelessWidget { _p.title, _p.itunes.image.href, rss, _primaryColor, _p.author); podcastLocal.description = _p.description; - print('_p.description'); + await dbHelper.savePodcastLocal(podcastLocal); importOmpl.importState = ImportState.parse; await dbHelper.savePodcastRss(_p); + + importOmpl.importState = ImportState.complete; + + _settingState.subscribeUpdate = Update.backhome; } catch (e) { importOmpl.importState = ImportState.error; } @@ -110,8 +116,6 @@ class PopupMenu extends StatelessWidget { print(total[i].text); } } - importOmpl.importState = ImportState.complete; - importOmpl.importState = ImportState.stop; print('Import fisnished'); } catch (e) { importOmpl.importState = ImportState.error; diff --git a/lib/home/audio_player.dart b/lib/home/audio_player.dart index d28bd53..f957ee4 100644 --- a/lib/home/audio_player.dart +++ b/lib/home/audio_player.dart @@ -15,7 +15,7 @@ import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/episodebrief.dart'; import 'package:tsacdop/episodes/episodedetail.dart'; import 'package:tsacdop/home/audiopanel.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/util/pageroute.dart'; final Logger _logger = Logger('audiofileplayer'); @@ -84,7 +84,9 @@ class _PlayerWidgetState extends State { _remoteAudioLoading = true; Provider.of(context, listen: false).audioState = AudioState.load; - if (_backgroundAudioPlaying == true) _backgroundAudio?.pause(); + if (_backgroundAudioPlaying == true) + { _backgroundAudio?.pause(); + AudioSystem.instance.stopBackgroundDisplay();} _backgroundAudio?.dispose(); _backgroundAudio = Audio.loadFromRemoteUrl(url, onDuration: (double durationSeconds) { @@ -135,7 +137,9 @@ class _PlayerWidgetState extends State { _remoteAudioLoading = true; ByteData audio = getAudio(path); Provider.of(context, listen: false).audioState = AudioState.load; - if (_backgroundAudioPlaying == true) _backgroundAudio?.pause(); + if (_backgroundAudioPlaying == true) + {_backgroundAudio?.pause(); + AudioSystem.instance.stopBackgroundDisplay();} _backgroundAudio?.dispose(); _backgroundAudio = Audio.loadFromByteData(audio, onDuration: (double durationSeconds) { diff --git a/lib/home/home.dart b/lib/home/home.dart index 0938def..4faeae6 100644 --- a/lib/home/home.dart +++ b/lib/home/home.dart @@ -1,13 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:tsacdop/podcasts/podcastlist.dart'; import 'hometab.dart'; import 'package:tsacdop/home/appbar/importompl.dart'; import 'package:tsacdop/home/audio_player.dart'; import 'homescroll.dart'; -import 'package:tsacdop/util/pageroute.dart'; -import 'package:tsacdop/podcasts/podcastmanage.dart'; class Home extends StatelessWidget { @@ -19,28 +16,6 @@ class Home extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Import(), - Container( - height: 30, - padding: EdgeInsets.symmetric(horizontal: 15), - alignment: Alignment.bottomRight, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - SlideLeftRoute(page: PodcastManage()), - ); - }, - child: Container( - height: 30, - padding: EdgeInsets.all(5.0), - child: Text('See All', - style: TextStyle( - color: Colors.red[300], - fontWeight: FontWeight.bold, - )), - ), - ), - ), Container(child: ScrollPodcasts()), Expanded( child: MainTab(), diff --git a/lib/home/homescroll.dart b/lib/home/homescroll.dart index a06e705..9b48409 100644 --- a/lib/home/homescroll.dart +++ b/lib/home/homescroll.dart @@ -6,15 +6,17 @@ 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/class/sqflite_localpodcast.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/podcastlist.dart'; +import 'package:tsacdop/podcasts/podcastmanage.dart'; import 'package:tsacdop/util/pageroute.dart'; import 'package:tsacdop/class/settingstate.dart'; @@ -25,101 +27,184 @@ class ScrollPodcasts extends StatefulWidget { class _ScrollPodcastsState extends State { var dir; - bool _loading; - List podcastList; - - getPodcastLocal() async { - var dbHelper = DBHelper(); - podcastList = await dbHelper.getPodcastLocal(); - dir = await getApplicationDocumentsDirectory(); - setState(() { - _loading = true; - }); - } + int _groupIndex; + bool _loaded; ImportState importState; - Setting subscribeUpdate; + Update subscribeUpdate; @override void didChangeDependencies() { super.didChangeDependencies(); - final importState = Provider.of(context).importState; - final subscribeUpdate = Provider.of(context).subscribeupdate; - if (importState == ImportState.complete || - subscribeUpdate == Setting.start) { + subscribeUpdate = Provider.of(context).subscribeupdate; + if (subscribeUpdate == Update.backhome) { setState(() { - getPodcastLocal(); + _groupIndex = 0; }); + } else if (subscribeUpdate == Update.justupdate) { + setState(() {}); } } @override void initState() { super.initState(); - _loading = false; - getPodcastLocal(); + _loaded = false; + _groupIndex = 0; + getApplicationDocumentsDirectory().then((value) { + dir = value.path; + setState(() => _loaded = true); + }); } @override Widget build(BuildContext context) { double _width = MediaQuery.of(context).size.width; - return !_loading - ? Container( - height: (_width - 20) / 3 + 110, - ) - : DefaultTabController( - length: podcastList.length, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 70, - alignment: Alignment.centerLeft, - child: TabBar( - labelPadding: EdgeInsets.only( - top: 5.0, bottom: 10.0, left: 6.0, right: 6.0), - indicator: - CircleTabIndicator(color: Colors.blue, radius: 3), - isScrollable: true, - tabs: podcastList.map((PodcastLocal podcastLocal) { - return Tab( - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(25.0)), - child: LimitedBox( - maxHeight: 50, - maxWidth: 50, - child: Image.file( - File("${dir.path}/${podcastLocal.title}.png")), + return Consumer(builder: (_, groupList, __) { + var groups = groupList.groups; + bool isLoading = groupList.isLoading; + return isLoading + ? Container( + height: (_width - 20) / 3 + 110, + child: SizedBox( + height: 20.0, + width: 20.0, + child: CircularProgressIndicator()), + ) + : DefaultTabController( + length: groups[_groupIndex].podcastList.length, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onVerticalDragEnd: (event) { + if (event.primaryVelocity > 200) { + if (groups.length == 1) { + Fluttertoast.showToast( + msg: 'Add some groups', + gravity: ToastGravity.BOTTOM, + ); + } else { + if (mounted) + setState(() { + (_groupIndex != 0) + ? _groupIndex-- + : _groupIndex = groups.length - 1; + }); + } + } else if (event.primaryVelocity < -200) { + if (groups.length == 1) { + Fluttertoast.showToast( + msg: 'Add some groups', + gravity: ToastGravity.BOTTOM, + ); + } else { + setState(() { + (_groupIndex < groups.length - 1) + ? _groupIndex++ + : _groupIndex = 0; + }); + } + } + }, + child: Column( + children: [ + Container( + child: Row( + children: [ + Container( + padding: + EdgeInsets.symmetric(horizontal: 15.0), + child: Text( + groups[_groupIndex].name, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red[300]), + )), + Spacer(), + Container( + height: 30, + padding: EdgeInsets.symmetric(horizontal: 15), + alignment: Alignment.bottomRight, + child: InkWell( + onTap: () { + Navigator.push( + context, + SlideLeftRoute(page: PodcastManage()), + ); + }, + child: Container( + height: 30, + padding: EdgeInsets.all(5.0), + child: Text('See All', + style: TextStyle( + color: Colors.red[300], + fontWeight: FontWeight.bold, + )), + ), + ), + ), + ], ), ), - ); - }).toList(), - ), - ), - Container( - height: (_width - 20) / 3 + 40, - margin: EdgeInsets.only(left: 10, right: 10), - decoration: BoxDecoration( - color: Colors.white, - ), - child: TabBarView( - children: - podcastList.map((PodcastLocal podcastLocal) { - return Container( - decoration: BoxDecoration(color: Colors.grey[100]), - margin: EdgeInsets.symmetric(horizontal: 5.0), - key: ObjectKey(podcastLocal.title), - child: PodcastPreview( - podcastLocal: podcastLocal, + Container( + color: Colors.white10, + height: 70, + width: _width, + alignment: Alignment.centerLeft, + child: TabBar( + labelPadding: EdgeInsets.only( + top: 5.0, bottom: 10.0, left: 6.0, right: 6.0), + indicator: CircleTabIndicator( + color: Colors.blue, radius: 3), + isScrollable: true, + tabs: groups[_groupIndex].podcasts + .map((PodcastLocal podcastLocal) { + return Tab( + child: ClipRRect( + borderRadius: + BorderRadius.all(Radius.circular(25.0)), + child: LimitedBox( + maxHeight: 50, + maxWidth: 50, + child: !_loaded + ? CircularProgressIndicator() + : Image.file(File( + "$dir/${podcastLocal.title}.png")), + ), + ), + ); + }).toList(), + ), ), - ); - }).toList(), + ], + ), ), - ), - ], - ), - ); + Container( + height: (_width - 20) / 3 + 40, + margin: EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + color: Colors.white, + ), + child: TabBarView( + children: groups[_groupIndex].podcasts + .map((PodcastLocal podcastLocal) { + return Container( + decoration: BoxDecoration(color: Colors.grey[100]), + margin: EdgeInsets.symmetric(horizontal: 5.0), + key: ObjectKey(podcastLocal.title), + child: PodcastPreview( + podcastLocal: podcastLocal, + ), + ); + }).toList(), + ), + ), + ], + ), + ); + }); } } @@ -250,6 +335,7 @@ class ShowEpisode extends StatelessWidget { episodeItem: podcast[index], heroTag: 'scroll', //unique hero tag + path: path, )), ); }, diff --git a/lib/home/hometab.dart b/lib/home/hometab.dart index 09cd168..0b92536 100644 --- a/lib/home/hometab.dart +++ b/lib/home/hometab.dart @@ -2,7 +2,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:tsacdop/class/episodebrief.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/util/episodegrid.dart'; class MainTab extends StatefulWidget { diff --git a/lib/local_storage/key_value_storage.dart b/lib/local_storage/key_value_storage.dart new file mode 100644 index 0000000..35bdace --- /dev/null +++ b/lib/local_storage/key_value_storage.dart @@ -0,0 +1,34 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tsacdop/class/podcast_group.dart'; + +class KeyValueStorage { + final String key; + KeyValueStorage(this.key); + + Future> getGroups() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (prefs.getString(key) == null) { + PodcastGroup home = PodcastGroup('Home'); + await prefs.setString( + key, + json.encode({ + 'groups': [home.toEntity().toJson()] + }));} + print(prefs.getString(key)); + return json + .decode(prefs.getString(key))['groups'] + .cast>() + .map(GroupEntity.fromJson) + .toList(growable: false); + } + + Future saveGroup(List groupList) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.setString( + key, + json.encode( + {'groups': groupList.map((group) => group.toJson()).toList()})); + } +} diff --git a/lib/class/sqflite_localpodcast.dart b/lib/local_storage/sqflite_localpodcast.dart similarity index 86% rename from lib/class/sqflite_localpodcast.dart rename to lib/local_storage/sqflite_localpodcast.dart index c925650..c2af400 100644 --- a/lib/class/sqflite_localpodcast.dart +++ b/lib/local_storage/sqflite_localpodcast.dart @@ -5,8 +5,8 @@ import 'package:path/path.dart'; import 'package:intl/intl.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:path_provider/path_provider.dart'; -import 'podcastlocal.dart'; -import 'episodebrief.dart'; +import 'package:tsacdop/class/podcastlocal.dart'; +import 'package:tsacdop/class/episodebrief.dart'; import 'package:tsacdop/webfeed/webfeed.dart'; class DBHelper { @@ -25,72 +25,72 @@ class DBHelper { } void _onCreate(Database db, int version) async { - await db.execute( - """CREATE TABLE PodcastLocal(id INTEGER PRIMARY KEY,title TEXT, + await db + .execute("""CREATE TABLE PodcastLocal(id TEXT PRIMARY KEY,title TEXT, imageUrl TEXT,rssUrl TEXT UNIQUE,primaryColor TEXT,author TEXT, - description TEXT, add_date INTEGER, order_id INTEGER default 0)"""); + description TEXT, add_date INTEGER, order_id INTEGER DEFAULT 0)"""); await db .execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT, enclosure_url TEXT UNIQUE, enclosure_length INTEGER, pubDate TEXT, description TEXT, feed_title TEXT, feed_link TEXT, milliseconds INTEGER, duration INTEGER DEFAULT 0, explicit INTEGER DEFAULT 0, liked INTEGER DEFAULT 0, downloaded TEXT DEFAULT 'ND', download_date INTEGER DEFAULT 0)"""); - await db.execute( - """CREATE TABLE Setting(id INTEGER PRIMARY KEY, setting TEXT, setting_value INTEGER DEFAULT 0)"""); - await db - .execute("""INSERT INTO Setting (setting) VALUES('podcasts_order') """); } - Future> getPodcastLocal() async { + Future> getPodcastLocal( + List podcasts, int podcastsOrder) async { var dbClient = await database; - //query podcasts order setting - List setting = await dbClient.rawQuery("SELECT setting_value FROM Setting WHERE setting = 'podcasts_order'"); - int podcastsOrder = setting.first['setting_value']; - List list; - if (podcastsOrder == 0) - { list = await dbClient.rawQuery( + List podcastLocal = List(); + + await Future.forEach(podcasts, (s) async { + List list; + if (podcastsOrder == 0) { + list = await dbClient.rawQuery( + 'SELECT id, title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal WHERE id = ? ORDER BY add_date DESC', + [s]); + } else if (podcastsOrder == 1) { + list = await dbClient.rawQuery( + 'SELECT id, title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal WHERE id = ? ORDER BY add_date', + [s]); + } else if (podcastsOrder == 2) { + list = await dbClient.rawQuery( + 'SELECT id, title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal WHERE id = ? ORDER BY order_id , add_date DESC', + [s]); + print('Get podcasts list Ordered by 2'); + } + podcastLocal.add(PodcastLocal( + list.first['title'], + list.first['imageUrl'], + list.first['rssUrl'], + list.first['primaryColor'], + list.first['author'], + id: list.first['id'])); + }); + return podcastLocal; + } + + Future> getPodcastLocalAll() async { + var dbClient = await database; + List list = await dbClient.rawQuery( 'SELECT title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal ORDER BY add_date DESC'); - print('Get podcasts list Ordered by 0');} - else if (podcastsOrder == 1) - { list = await dbClient.rawQuery( - 'SELECT title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal ORDER BY add_date');} - else if (podcastsOrder ==2) - { list = await dbClient.rawQuery( - 'SELECT title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal ORDER BY order_id'); - print('Get podcasts list Ordered by 2');} - List podcastLocal = List(); for (int i = 0; i < list.length; i++) { - podcastLocal.add(PodcastLocal( - list[i]['title'], - list[i]['imageUrl'], - list[i]['rssUrl'], - list[i]['primaryColor'], - list[i]['author'], - )); + podcastLocal.add(PodcastLocal(list[i]['title'], list[i]['imageUrl'], + list[i]['rssUrl'], list[i]['primaryColor'], list[i]['author'], + id: list[i]['id'])); } return podcastLocal; } - //save podcast order adter user save + //save podcast order adter user save saveOrder(List podcastList) async { var dbClient = await database; - for (int i = 0; i < podcastList.length; i++){ + for (int i = 0; i < podcastList.length; i++) { await dbClient.rawUpdate( "UPDATE OR IGNORE PodcastLocal SET order_id = ? WHERE title = ?", [i, podcastList[i].title]); print(podcastList[i].title); } - await dbClient.rawUpdate( - "UPDATE OR IGNORE Setting SET setting_value = 2 WHERE setting = 'podcasts_order' "); - print('Changed order'); - } - - updateOrderSetting(int value) async{ - var dbClient = await database; - await dbClient.rawUpdate( - "UPDATE OR IGNORE Setting SET setting_value = ? WHERE setting = 'podcasts_order'",[value]); - } Future savePodcastLocal(PodcastLocal podcastLocal) async { @@ -99,9 +99,10 @@ class DBHelper { var dbClient = await database; await dbClient.transaction((txn) async { return await txn.rawInsert( - """INSERT OR IGNORE INTO PodcastLocal (title, imageUrl, rssUrl, - primaryColor, author, description, add_date) VALUES(?, ?, ?, ?, ?, ?, ?)""", + """INSERT OR IGNORE INTO PodcastLocal (id, title, imageUrl, rssUrl, + primaryColor, author, description, add_date) VALUES(?, ?, ?, ?, ?, ?, ?, ?)""", [ + podcastLocal.id, podcastLocal.title, podcastLocal.imageUrl, podcastLocal.rssUrl, @@ -188,22 +189,22 @@ class DBHelper { } else { for (int i = 0; i < (_result - _count); i++) { print(_p.items[i].title); - _p.items[i].itunes.title != null - ? _title = _p.items[i].itunes.title - : _title = _p.items[i].title; - _p.items[i].itunes.summary != null + _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; + isXimalaya(_p.items[i].enclosure.url) ? _url = _p.items[i].enclosure.url.split('=').last : _url = _p.items[i].enclosure.url; + final _length = _p.items[i].enclosure.length; final _pubDate = _p.items[i].pubDate; final _date = _parsePubDate(_pubDate); final _milliseconds = _date.millisecondsSinceEpoch; - (_p.items[i].itunes.duration != null) - ? _duration = _p.items[i].itunes.duration.inMinutes - : _duration = 0; + + _duration = _p.items[i].itunes.duration?.inMinutes ?? 0; + final _explicit = getExplicit(_p.items[i].itunes.explicit); if (_p.items[i].enclosure.url != null) { await dbClient.transaction((txn) { @@ -384,7 +385,6 @@ class DBHelper { int count = await dbClient.rawUpdate( "UPDATE Episodes SET downloaded = ?, download_date = ? WHERE enclosure_url = ?", [id, _milliseconds, url]); - print('Downloaded ' + url); return count; } diff --git a/lib/main.dart b/lib/main.dart index 54cce98..d6f645e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,12 @@ +import 'dart:io'; + 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'; +import 'package:path_provider/path_provider.dart'; +import 'package:tsacdop/class/podcast_group.dart'; import 'package:tsacdop/home/appbar/addpodcast.dart'; import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/importompl.dart'; @@ -15,6 +19,7 @@ void main() async { ChangeNotifierProvider(create: (context) => Urlchange()), ChangeNotifierProvider(create: (context) => ImportOmpl()), ChangeNotifierProvider(create: (context) => SettingState()), + ChangeNotifierProvider(create: (context) => GroupList()), ], child: MyApp(), ), @@ -23,22 +28,13 @@ void main() async { await FlutterDownloader.initialize(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); await FlutterStatusbarcolor.setStatusBarColor(Colors.grey[100]); - if (useWhiteForeground(Colors.grey[100])) { - FlutterStatusbarcolor.setStatusBarWhiteForeground(true); -} else { - FlutterStatusbarcolor.setStatusBarWhiteForeground(false); -} await FlutterStatusbarcolor.setNavigationBarColor(Colors.grey[100]); - if (useWhiteForeground(Colors.grey[100])) { - FlutterStatusbarcolor.setNavigationBarWhiteForeground(true); -} else { - FlutterStatusbarcolor.setNavigationBarWhiteForeground(false); -} } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + return MaterialApp( debugShowCheckedModeBanner: false, title: 'TsacDop', diff --git a/lib/podcasts/podcastdetail.dart b/lib/podcasts/podcastdetail.dart index e081e31..44aeb2b 100644 --- a/lib/podcasts/podcastdetail.dart +++ b/lib/podcasts/podcastdetail.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'package:tsacdop/class/podcastlocal.dart'; import 'package:tsacdop/class/episodebrief.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/util/episodegrid.dart'; import 'package:tsacdop/webfeed/webfeed.dart'; diff --git a/lib/podcasts/podcastgroup.dart b/lib/podcasts/podcastgroup.dart new file mode 100644 index 0000000..65080ec --- /dev/null +++ b/lib/podcasts/podcastgroup.dart @@ -0,0 +1,398 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:tsacdop/class/podcast_group.dart'; +import 'package:tsacdop/class/podcastlocal.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; +import 'package:tsacdop/class/settingstate.dart'; + +class PodcastGroupList extends StatefulWidget { + final PodcastGroup group; + PodcastGroupList({this.group, Key key}) : super(key: key); + @override + _PodcastGroupListState createState() => _PodcastGroupListState(); +} + +class _PodcastGroupListState extends State { + bool _loading; + bool _loadSave; + String dir; + + @override + void initState() { + super.initState(); + _loading = false; + _loadSave = false; + getApplicationDocumentsDirectory().then((value) { + dir = value.path; + setState(() { + _loading = true; + }); + }); + } + + Widget _saveButton(BuildContext context) { + var _settingState = Provider.of(context); + _saveOrder(List podcastList) async { + var dbHelper = DBHelper(); + await dbHelper.saveOrder(podcastList); + } + + var podcastList = widget.group.podcasts; + return Container( + child: InkWell( + child: AnimatedContainer( + duration: Duration(milliseconds: 800), + width: _loadSave ? 70 : 0, + height: 40, + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.all(Radius.circular(5.0))), + alignment: Alignment.center, + child: Text( + 'Save', + style: TextStyle(color: Colors.white), + maxLines: 1, + )), + onTap: () async { + await _saveOrder(podcastList); + Fluttertoast.showToast( + msg: 'Setting Saved', + gravity: ToastGravity.BOTTOM, + ); + _settingState.subscribeUpdate = Update.justupdate; + setState(() { + _loadSave = false; + }); + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return widget.group.podcastList.length == 0 + ? Center() + : Container( + color: Colors.grey[100], + child: Stack( + children: [ + ReorderableListView( + onReorder: (int oldIndex, int newIndex) { + setState(() { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final PodcastLocal podcast = + widget.group.podcasts.removeAt(oldIndex); + widget.group.podcasts.insert(newIndex, podcast); + _loadSave = true; + }); + }, + children: + widget.group.podcasts.map((PodcastLocal podcastLocal) { + return Container( + decoration: BoxDecoration(color: Colors.grey[100]), + key: ObjectKey(podcastLocal.title), + child: !_loading + ? CircularProgressIndicator() + : PodcastCard( + path: dir, + podcastLocal: podcastLocal, + group: widget.group.name, + ), + ); + }).toList(), + ), + AnimatedPositioned( + duration: Duration(seconds: 1), + bottom: 30, + right: _loadSave ? 50 : 0, + child: Center(), + //_saveButton(context), + ), + ], + ), + ); + } +} + +class PodcastCard extends StatefulWidget { + final PodcastLocal podcastLocal; + final String path; + final String group; + PodcastCard({this.podcastLocal, this.path, this.group, Key key}) + : super(key: key); + @override + _PodcastCardState createState() => _PodcastCardState(); +} + +class _PodcastCardState extends State { + bool _loadMenu; + bool _remove; + bool _addGroup; + bool _loadGroup; + List _selectedGroups; + List _belongGroups; + Color _c; + + _unSubscribe(String title) async { + var dbHelper = DBHelper(); + await dbHelper.delPodcastLocal(title); + } + + @override + void initState() { + super.initState(); + _loadMenu = false; + _remove = false; + _addGroup = false; + _loadGroup = false; + } + + Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + child: Container( + height: 50.0, + padding: EdgeInsets.symmetric(horizontal: 15.0), + child: widget), + ), + ); + + @override + Widget build(BuildContext context) { + var color = json.decode(widget.podcastLocal.primaryColor); + (color[0] > 200 && color[1] > 200 && color[2] > 200) + ? _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); + double _width = MediaQuery.of(context).size.width; + var _settingState = Provider.of(context); + var _groupList = Provider.of(context); + _selectedGroups = _groupList.groups.map((e) => e.name).toList(); + _belongGroups = _groupList + .getPodcastGroup(widget.podcastLocal.id) + .map((e) => e.name) + .toList(); + return _remove + ? Center() + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => setState(() => _loadMenu = !_loadMenu), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12), + height: 100, + child: Row(children: [ + Container( + child: Icon( + Icons.unfold_more, + color: _c, + ), + ), + Container( + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + height: 60, + width: 60, + child: Image.file(File( + "${widget.path}/${widget.podcastLocal.title}.png")), + ), + ), + ), + Container( + width: _width / 2, + padding: EdgeInsets.symmetric(horizontal: 10), + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + alignment: Alignment.centerLeft, + child: Text( + widget.podcastLocal.title, + maxLines: 2, + overflow: TextOverflow.fade, + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 15), + ), + ), + !_loadGroup + ? Center() + : Row( + children: _belongGroups.map((group) { + return Container( + padding: EdgeInsets.only(right: 5.0), + child: Text(group)); + }).toList(), + ), + ], + )), + Spacer(), + Icon(_loadMenu + ? Icons.keyboard_arrow_up + : Icons.keyboard_arrow_down), + Padding( + padding: EdgeInsets.symmetric(horizontal: 5.0), + ), + OutlineButton( + child: Text('Remove'), + onPressed: () { + showDialog( + context: context, + child: AlertDialog( + title: Text('Remove confirm'), + content: Text( + '${widget.podcastLocal.title} will be removed from device.'), + actions: [ + FlatButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('CANCEL'), + ), + FlatButton( + onPressed: () { + _unSubscribe(widget.podcastLocal.title); + _settingState.subscribeUpdate = + Update.justupdate; + Navigator.of(context).pop(); + }, + child: Text( + 'CONFIRM', + style: TextStyle(color: Colors.red), + ), + ) + ], + )); + }, + ), + ]), + ), + ), + !_loadMenu + ? Center() + : Container( + child: Container( + decoration: BoxDecoration( + color: Colors.grey[100], + border: Border( + bottom: BorderSide(color: Colors.grey[300]), + top: BorderSide(color: Colors.grey[300]))), + height: 50, + child: _addGroup + ? Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + flex: 4, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Consumer( + builder: (_, groupList, __) => Row( + children: groupList.groups + .map( + (PodcastGroup group) { + return Container( + padding: EdgeInsets.only(left: 5.0), + child: FilterChip( + key: ValueKey(group.name), + label: Text(group.name), + selected: _belongGroups + .contains(group.name) && + _selectedGroups + .contains(group.name), + onSelected: (bool value) { + setState(() { + if (!value) { + _selectedGroups + .remove(group.name); + } else { + _selectedGroups + .add(group.name); + } + }); + }, + ), + ); + }).toList()), + ), + ), + ), + Expanded( + flex: 1, + child: Row( + children: [ + IconButton( + icon: Icon(Icons.clear), + onPressed: () => setState(() { + _addGroup = false; + }), + ), + IconButton( + onPressed: () async { + print(_selectedGroups); + if (_selectedGroups.length > 0) { + setState(() { + _addGroup = false; + }); + await _groupList.changeGroup( + widget.podcastLocal.id, + _selectedGroups, + ); + _settingState.subscribeUpdate = + Update.justupdate; + Fluttertoast.showToast( + msg: 'Setting Saved', + gravity: ToastGravity.BOTTOM, + ); + if (!_selectedGroups + .contains(widget.group)) { + print(widget.group); + setState(() { + _remove = true; + }); + } + } else + Fluttertoast.showToast( + msg: + 'At least select one group', + gravity: ToastGravity.BOTTOM, + ); + }, + icon: Icon(Icons.done), + ), + ], + ), + ) + ], + ) + : Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + _buttonOnMenu(Icon(Icons.fullscreen), () {}), + _buttonOnMenu(Icon(Icons.add), () { + setState(() { + _addGroup = true; + }); + }), + _buttonOnMenu( + Icon(Icons.notifications), () {}) + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/podcasts/podcastlist.dart b/lib/podcasts/podcastlist.dart index ae0ff9a..9c44d81 100644 --- a/lib/podcasts/podcastlist.dart +++ b/lib/podcasts/podcastlist.dart @@ -6,9 +6,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; import 'package:tsacdop/class/podcastlocal.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; +import 'package:tsacdop/class/settingstate.dart'; +import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/podcasts/podcastdetail.dart'; import 'package:tsacdop/util/pageroute.dart'; @@ -48,12 +50,14 @@ class _AboutPodcastState extends State { @override Widget build(BuildContext context) { + var _settingState = Provider.of(context); return AlertDialog( actions: [ FlatButton( padding: EdgeInsets.all(10.0), onPressed: () { _unSubscribe(widget.podcastLocal.title); + _settingState.subscribeUpdate = Update.justupdate; Navigator.of(context).pop(); }, color: Colors.grey[200], @@ -88,95 +92,104 @@ class PodcastList extends StatefulWidget { class _PodcastListState extends State { var dir; + Future> getPodcastLocal() async { dir = await getApplicationDocumentsDirectory(); var dbHelper = DBHelper(); - var podcastList = await dbHelper.getPodcastLocal(); + var podcastList = await dbHelper.getPodcastLocalAll(); return podcastList; } @override Widget build(BuildContext context) { double _width = MediaQuery.of(context).size.width; - return Container( - color: Colors.grey[100], - child: FutureBuilder>( - future: getPodcastLocal(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return CustomScrollView( - primary: false, - slivers: [ - SliverPadding( - padding: const EdgeInsets.all(10.0), - sliver: SliverGrid( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - childAspectRatio: 0.8, - crossAxisCount: 3, - ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return InkWell( - onTap: () { - Navigator.push( - context, - ScaleRoute( - page: PodcastDetail( - podcastLocal: snapshot.data[index], - )), - ); - }, - onLongPress: () { - showDialog( - context: context, - builder: (BuildContext context) => AboutPodcast( - podcastLocal: snapshot.data[index]), - ).then((_) => setState(() {})); - }, - child: Container( - alignment: Alignment.center, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - height: 10.0, - ), - ClipRRect( - borderRadius: - BorderRadius.all(Radius.circular(_width/8)), - child: Container( - height: _width/4, - width: _width/4, - child: Image.file(File( - "${dir.path}/${snapshot.data[index].title}.png")), + return Scaffold( + appBar: AppBar( + title: Text('Podcasts'), + centerTitle: true, + backgroundColor: Colors.grey[100], + elevation: 0, + ), + body: Container( + color: Colors.grey[100], + child: FutureBuilder>( + future: getPodcastLocal(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return CustomScrollView( + primary: false, + slivers: [ + SliverPadding( + padding: const EdgeInsets.all(10.0), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 0.8, + crossAxisCount: 3, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return InkWell( + onTap: () { + Navigator.push( + context, + ScaleRoute( + page: PodcastDetail( + podcastLocal: snapshot.data[index], + )), + ); + }, + onLongPress: () { + showDialog( + context: context, + builder: (BuildContext context) => AboutPodcast( + podcastLocal: snapshot.data[index]), + ).then((_) => setState(() {})); + }, + child: Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 10.0, ), - ), - Container( - padding: EdgeInsets.all(4.0), - child: Text( - snapshot.data[index].title, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16.0, - color: Colors.black.withOpacity(0.5), + ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(_width / 8)), + child: Container( + height: _width / 4, + width: _width / 4, + child: Image.file(File( + "${dir.path}/${snapshot.data[index].title}.png")), ), - maxLines: 2, ), - ), - ], + Container( + padding: EdgeInsets.all(4.0), + child: Text( + snapshot.data[index].title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.0, + color: Colors.black.withOpacity(0.5), + ), + maxLines: 2, + ), + ), + ], + ), ), - ), - ); - }, - childCount: snapshot.data.length, + ); + }, + childCount: snapshot.data.length, + ), ), ), - ), - ], - ); - } - return Text('NoData'); - }, + ], + ); + } + return Text('NoData'); + }, + ), ), ); } diff --git a/lib/podcasts/podcastmanage.dart b/lib/podcasts/podcastmanage.dart index c896acf..49dfceb 100644 --- a/lib/podcasts/podcastmanage.dart +++ b/lib/podcasts/podcastmanage.dart @@ -1,13 +1,9 @@ -import 'dart:convert'; -import 'dart:io'; - import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; -import 'package:tsacdop/class/podcastlocal.dart'; -import 'package:tsacdop/class/sqflite_localpodcast.dart'; -import 'package:tsacdop/class/settingstate.dart'; +import 'package:tsacdop/class/podcast_group.dart'; +import 'package:tsacdop/podcasts/podcastgroup.dart'; +import 'package:tsacdop/podcasts/podcastlist.dart'; +import 'package:tsacdop/util/pageroute.dart'; class PodcastManage extends StatefulWidget { @override @@ -15,152 +11,208 @@ class PodcastManage extends StatefulWidget { } class _PodcastManageState extends State { - var dir; - bool _loading; - bool _loadSave; - Color _c; - double _width; - List podcastList; - getPodcastLocal() async { - dir = await getApplicationDocumentsDirectory(); - var dbHelper = DBHelper(); - podcastList = await dbHelper.getPodcastLocal(); - setState(() { - _loading = true; - }); - } - - _unSubscribe(String title) async { - var dbHelper = DBHelper(); - await dbHelper.delPodcastLocal(title); - print('Unsubscribe'); + Decoration getIndicator() { + return const UnderlineTabIndicator( + borderSide: BorderSide(color: Colors.red, width: 2), + insets: EdgeInsets.only( + top: 10.0, + )); } @override void initState() { super.initState(); - _loading = false; - _loadSave = false; - getPodcastLocal(); - } - - void _onReorder(int oldIndex, int newIndex) { - setState(() { - if (newIndex > oldIndex) { - newIndex -= 1; - } - final PodcastLocal podcast = podcastList.removeAt(oldIndex); - podcastList.insert(newIndex, podcast); - _loadSave = true; - }); - } - - _saveOrder(List podcastList) async { - var dbHelper = DBHelper(); - await dbHelper.saveOrder(podcastList); - } - - Widget _podcastCard(BuildContext context, PodcastLocal podcastLocal) { - var _settingState = Provider.of(context); - return Container( - padding: EdgeInsets.symmetric(horizontal: 12), - height: 100, - child: Row(children: [ - Container( - child: Icon( - Icons.unfold_more, - color: _c, - ), - ), - Container( - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Container( - height: 60, - width: 60, - child: Image.file(File("${dir.path}/${podcastLocal.title}.png")), - ), - ), - ), - Container( - width: _width / 2, - padding: EdgeInsets.symmetric(horizontal: 10), - child: Text( - podcastLocal.title, - maxLines: 2, - overflow: TextOverflow.fade, - )), - Spacer(), - OutlineButton( - child: Text('Unsubscribe'), - onPressed: () { - _unSubscribe(podcastLocal.title); - _settingState.subscribeUpdate = Setting.start; - setState(() { - getPodcastLocal(); - }); - }, - ), - ]), - ); } @override Widget build(BuildContext context) { - _width = MediaQuery.of(context).size.width; - var _settingState = Provider.of(context); return Scaffold( appBar: AppBar( - title: Text('Podcasts'), - backgroundColor: Colors.grey[100], elevation: 0, centerTitle: true, + backgroundColor: Colors.grey[100], + title: Text('Groups'), actions: [ - !_loadSave - ? Center() - : InkWell( - child: Container( - padding: EdgeInsets.all(20.0), - alignment: Alignment.center, - child: Text('Save')), - onTap: () async{ - await _saveOrder(podcastList); - Fluttertoast.showToast( - msg: 'Saved', - gravity: ToastGravity.BOTTOM, - ); - _settingState.subscribeUpdate = Setting.start; - setState(() { - _loadSave = false; - }); - }, - ), - IconButton( - icon: Icon(Icons.menu), - onPressed: () {}, - ), + OrderMenu(), ], ), - body: Container( - color: Colors.grey[100], - child: !_loading - ? CircularProgressIndicator() - : ReorderableListView( - onReorder: _onReorder, - children: podcastList.map((PodcastLocal podcastLocal) { - var color = json.decode(podcastLocal.primaryColor); - (color[0] > 200 && color[1] > 200 && color[2] > 200) - ? _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 Container( - decoration: BoxDecoration(color: Colors.grey[100]), - margin: EdgeInsets.symmetric(horizontal: 5.0), - key: ObjectKey(podcastLocal.title), - child: _podcastCard(context, podcastLocal), - ); - }).toList(), + body: Consumer(builder: (_, groupList, __) { + bool _isLoading = groupList.isLoading; + List _groups = groupList.groups; + return _isLoading + ? Center() + : DefaultTabController( + length: _groups.length, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + 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), + indicator: getIndicator(), + isScrollable: true, + tabs: _groups.map((group) { + return Tab( + child: Text(group.name), + ); + }).toList(), + ), + ), + ), + Container( + child: FlatButton( + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) => + AddGroup()), + child: Icon(Icons.add)), + ), + ], + ), + Expanded( + child: Container( + child: TabBarView( + children: _groups.map((group) { + return Container( + key: ObjectKey(group), + child: PodcastGroupList(group: group)); + }).toList(),), + ), + ) + ], + )); + }), + ); + } +} + +class OrderMenu extends StatelessWidget { + @override + Widget build(BuildContext context) { + return PopupMenuButton( + elevation: 3, + tooltip: 'Menu', + itemBuilder: (context) => [ + PopupMenuItem( + value: 1, + child: Text('All Podcasts'), + ), + PopupMenuItem( + value: 2, + child: Text('New group'), + ) + ], + onSelected: (value) { + if (value == 1) { + Navigator.push(context, ScaleRoute(page: PodcastList())); + } + if (value == 2) { + showDialog( + context: context, builder: (BuildContext context) => AddGroup()); + } + }, + ); + } +} + +class AddGroup extends StatefulWidget { + @override + _AddGroupState createState() => _AddGroupState(); +} + +class _AddGroupState extends State { + TextEditingController _controller; + String _newGroup; + int _error; + + @override + void initState() { + super.initState(); + _error = 0; + _controller = TextEditingController(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + var groupList = Provider.of(context); + List list = groupList.groups.map((e) => e.name).toList(); + return AlertDialog( + elevation: 1, + contentPadding: EdgeInsets.symmetric(horizontal: 20), + titlePadding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20), + actionsPadding: EdgeInsets.all(0), + actions: [ + FlatButton( + onPressed: () => Navigator.of(context).pop(), + child: Text( + 'CANCEL', + style: TextStyle(color: Colors.grey[700]), + ), + ), + FlatButton( + onPressed: () async { + if (list.contains(_newGroup)) { + setState(() => _error = 1); + } else { + groupList.addGroup(PodcastGroup(_newGroup)); + Navigator.of(context).pop(); + } + }, + child: Text('DONE'), + ) + ], + title: Text('Create new group'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric(horizontal: 10), + hintText: 'New Group', + hintStyle: TextStyle(fontSize: 18), + filled: true, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.white, width: 2.0), ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.blue, width: 2.0), + ), + ), + cursorRadius: Radius.circular(2), + autofocus: true, + maxLines: 1, + controller: _controller, + onChanged: (value) { + _newGroup = value; + }, + ), + Container( + alignment: Alignment.centerLeft, + child: (_error == 1) + ? Text( + 'Group existed', + style: TextStyle(color: Colors.red[400]), + ) + : Center(), + ), + ], ), ); } diff --git a/lib/util/episodegrid.dart b/lib/util/episodegrid.dart index be22186..2ed2a03 100644 --- a/lib/util/episodegrid.dart +++ b/lib/util/episodegrid.dart @@ -25,7 +25,6 @@ class EpisodeGrid extends StatelessWidget { this.showNumber, this.heroTag}) : super(key: key); - double _width; Future _getPath() async { var dir = await getApplicationDocumentsDirectory(); return dir.path; @@ -33,7 +32,7 @@ class EpisodeGrid extends StatelessWidget { @override Widget build(BuildContext context) { - _width = MediaQuery.of(context).size.width; + double _width = MediaQuery.of(context).size.width; return FutureBuilder( future: _getPath(), builder: (context, snapshot) { @@ -67,7 +66,8 @@ class EpisodeGrid extends StatelessWidget { ScaleRoute( page: EpisodeDetail( episodeItem: podcast[index], - heroTag: heroTag)), + heroTag: heroTag, + path: snapshot.data,)), ); }, child: Container( diff --git a/lib/webfeed/domain/rss_item.dart b/lib/webfeed/domain/rss_item.dart index 16350ad..f01e411 100644 --- a/lib/webfeed/domain/rss_item.dart +++ b/lib/webfeed/domain/rss_item.dart @@ -46,14 +46,14 @@ class RssItem { factory RssItem.parse(XmlElement element) { return RssItem( title: findElementOrNull(element, "title")?.text, - description: findElementOrNull(element, "description")?.text, - link: findElementOrNull(element, "link")?.text, + description: findElementOrNull(element, "description")?.text?.trim() ?? 'No shownote provided for this episode', + link: findElementOrNull(element, "link")?.text?.trim(), categories: element.findElements("category").map((element) { return RssCategory.parse(element); }).toList(), - guid: findElementOrNull(element, "guid")?.text, - pubDate: findElementOrNull(element, "pubDate")?.text, - author: findElementOrNull(element, "author")?.text, + //guid: findElementOrNull(element, "guid")?.text, + pubDate: findElementOrNull(element, "pubDate")?.text?.trim(), + author: findElementOrNull(element, "author")?.text?.trim(), // comments: findElementOrNull(element, "comments")?.text, // source: RssSource.parse(findElementOrNull(element, "source")), // content: RssContent.parse(findElementOrNull(element, "content:encoded")), diff --git a/lib/webfeed/domain/rss_itunes.dart b/lib/webfeed/domain/rss_itunes.dart index c5fa60a..8737701 100644 --- a/lib/webfeed/domain/rss_itunes.dart +++ b/lib/webfeed/domain/rss_itunes.dart @@ -45,7 +45,7 @@ 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(), diff --git a/pubspec.lock b/pubspec.lock index e72bb2e..4c09c74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -36,13 +36,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" - cached_network_image: - dependency: "direct dev" - description: - name: cached_network_image - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.0" charcode: dependency: transitive description: @@ -111,13 +104,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.3" flutter_downloader: dependency: "direct dev" description: @@ -240,13 +226,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.0.4" - network_image_to_byte: - dependency: "direct dev" - description: - name: network_image_to_byte - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.0.1" path: dependency: transitive description: @@ -317,6 +296,34 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.0.5" + shared_preferences: + dependency: "direct dev" + description: + name: shared_preferences + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.5.6+1" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1+5" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.2+3" sky_engine: dependency: transitive description: flutter @@ -414,7 +421,7 @@ packages: source: hosted version: "0.1.1" uuid: - dependency: transitive + dependency: "direct dev" description: name: uuid url: "https://pub.flutter-io.cn" @@ -443,4 +450,4 @@ packages: version: "3.5.0" sdks: dart: ">=2.6.0 <3.0.0" - flutter: ">=1.12.13 <2.0.0" + flutter: ">=1.12.13+hotfix.4 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8d04fa2..f8bd79a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,16 +29,14 @@ dev_dependencies: sdk: flutter flutter_statusbarcolor: ^0.2.3 json_annotation: any - cached_network_image: any sqflite: any - flutter_html: any + flutter_html: ^0.11.1 webfeed: any path_provider: any color_thief_flutter: ^1.0.1 provider: ^4.0.1 google_fonts: ^0.3.2 dio: ^3.0.8 - network_image_to_byte: ^0.0.1 file_picker: ^1.2.0 xml: ^3.5.0 marquee: ^1.3.1 @@ -49,6 +47,10 @@ dev_dependencies: intl: ^0.16.1 url_launcher: ^5.4.1 image: ^2.1.4 + shared_preferences: ^0.5.6+1 + uuid: ^2.0.4 + + # For information on the generic Dart part of this file, see the