From 60e066d2a9f95eaaec55fb9f733b5c8df3d631a7 Mon Sep 17 00:00:00 2001 From: stonegate Date: Fri, 24 Apr 2020 01:46:36 +0800 Subject: [PATCH] Change add new episode to playlist icon Add duration picker initial duration --- android/build.gradle | 2 +- lib/class/audiostate.dart | 44 ++++++++++------- lib/class/episodebrief.dart | 1 + lib/class/podcast_group.dart | 5 +- lib/class/subscribe_podcast.dart | 8 ++-- lib/home/appbar/about.dart | 4 +- lib/home/appbar/importompl.dart | 2 +- lib/home/audioplayer.dart | 52 +++++++++++++-------- lib/home/home_groups.dart | 7 ++- lib/home/nested_home.dart | 52 +++++++++++++++------ lib/local_storage/sqflite_localpodcast.dart | 23 +++++++-- lib/podcasts/podcastdetail.dart | 33 +++++++------ lib/podcasts/podcastgroup.dart | 12 +++-- lib/util/custompaint.dart | 46 ++++++++++++++++++ pubspec.yaml | 10 ++-- 15 files changed, 205 insertions(+), 96 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 9a8f207..b3db53a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' + classpath 'com.android.tools.build:gradle:3.6.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/lib/class/audiostate.dart b/lib/class/audiostate.dart index 8b64c38..c81f2f7 100644 --- a/lib/class/audiostate.dart +++ b/lib/class/audiostate.dart @@ -91,11 +91,13 @@ class Playlist { addToPlayList(EpisodeBrief episodeBrief) async { _playlist.add(episodeBrief); await savePlaylist(); + dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl); } addToPlayListAt(EpisodeBrief episodeBrief, int index) async { _playlist.insert(index, episodeBrief); await savePlaylist(); + dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl); } Future delFromPlaylist(EpisodeBrief episodeBrief) async { @@ -188,10 +190,10 @@ class AudioPlayerNotifier extends ChangeNotifier { _setAutoAdd(); } - Future _getAutoAdd() async { - int i = await autoAddStorage.getInt(); - _autoAdd = i == 0 ? false : true; - } + //Future _getAutoAdd() async { + // int i = await autoAddStorage.getInt(); + // _autoAdd = i == 0 ? false : true; + //} Future _setAutoAdd() async { await autoAddStorage.saveInt(_autoAdd ? 1 : 0); @@ -255,6 +257,8 @@ class AudioPlayerNotifier extends ChangeNotifier { notifyListeners(); //await _queue.savePlaylist(); _startAudioService(startPosition); + if (episodeNew.isNew == 1) + dbHelper.removeEpisodeNewMark(episodeNew.enclosureUrl); } } @@ -571,7 +575,7 @@ class AudioPlayerTask extends BackgroundAudioTask { bool get hasNext => _queue.length > 0; - MediaItem get mediaItem => _queue.first; + MediaItem get mediaItem => _queue.length > 0 ? _queue.first : null; BasicPlaybackState _stateToBasicState(AudioPlaybackState state) { switch (state) { @@ -644,19 +648,21 @@ class AudioPlayerTask extends BackgroundAudioTask { Future onSkipToNext() async { _skipState = BasicPlaybackState.skippingToNext; await _audioPlayer.stop(); - _queue.removeAt(0); + if (_queue.length > 0) _queue.removeAt(0); await AudioServiceBackground.setQueue(_queue); // } if (_queue.length == 0 || _stopAtEnd) { _skipState = null; onStop(); } else { - AudioServiceBackground.setQueue(_queue); - AudioServiceBackground.setMediaItem(mediaItem); - await _audioPlayer.setUrl(mediaItem.id); - Duration duration = await _audioPlayer.durationFuture ?? Duration.zero; - AudioServiceBackground.setMediaItem( - mediaItem.copyWith(duration: duration.inMilliseconds)); + await AudioServiceBackground.setQueue(_queue); + await AudioServiceBackground.setMediaItem(mediaItem); + await _audioPlayer.setUrl(mediaItem.id); + print(mediaItem.title); + Duration duration = await _audioPlayer.durationFuture; + if(duration != null) + await AudioServiceBackground.setMediaItem( + mediaItem.copyWith(duration: duration.inMilliseconds)); _skipState = null; // Resume playback if we were playing // if (_playing) { @@ -674,9 +680,10 @@ class AudioPlayerTask extends BackgroundAudioTask { _playing = true; // await AudioServiceBackground.setQueue(_queue); await _audioPlayer.setUrl(mediaItem.id); - Duration duration = await _audioPlayer.durationFuture; - AudioServiceBackground.setMediaItem( - mediaItem.copyWith(duration: duration.inMilliseconds)); + var duration = await _audioPlayer.durationFuture; + if (duration != null) + await AudioServiceBackground.setMediaItem( + mediaItem.copyWith(duration: duration.inMilliseconds)); } // if (mediaItem.extras['skip'] > 0) { // await _audioPlayer.setClip( @@ -684,8 +691,9 @@ class AudioPlayerTask extends BackgroundAudioTask { // print(mediaItem.extras['skip']); // print('set clip success'); // } - _playing = true; - _audioPlayer.play(); + else + _playing = true; + await _audioPlayer.play(); if (mediaItem.extras['skip'] > 0) { _audioPlayer.seek(Duration(seconds: mediaItem.extras['skip'])); } @@ -716,7 +724,7 @@ class AudioPlayerTask extends BackgroundAudioTask { await _audioPlayer.stop(); _setState(state: BasicPlaybackState.stopped); await Future.delayed(Duration(milliseconds: 500)); - _completer.complete(); + _completer?.complete(); } @override diff --git a/lib/class/episodebrief.dart b/lib/class/episodebrief.dart index e7b52e7..19d3af4 100644 --- a/lib/class/episodebrief.dart +++ b/lib/class/episodebrief.dart @@ -56,6 +56,7 @@ class EpisodeBrief { title: title, artist: feedTitle, album: feedTitle, + // duration: 0, artUri: 'file://$imagePath', extras: {'skip': skipSeconds}); } diff --git a/lib/class/podcast_group.dart b/lib/class/podcast_group.dart index 643fd8c..76200c6 100644 --- a/lib/class/podcast_group.dart +++ b/lib/class/podcast_group.dart @@ -182,12 +182,11 @@ class GroupList extends ChangeNotifier { } Future updatePodcast(String id) async { - List counts = await dbHelper.getPodcastCounts(id); + int counts = await dbHelper.getPodcastCounts(id); _groups.forEach((group) { if (group.podcastList.contains(id)) { group.podcasts.firstWhere((podcast) => podcast.id == id) - ..upateCount = counts[0] - ..episodeCount = counts[1]; + ..episodeCount = counts; notifyListeners(); } }); diff --git a/lib/class/subscribe_podcast.dart b/lib/class/subscribe_podcast.dart index 136cee2..16fb05b 100644 --- a/lib/class/subscribe_podcast.dart +++ b/lib/class/subscribe_podcast.dart @@ -34,7 +34,7 @@ class SubscribeWorker extends ChangeNotifier { SubscribeItem _currentSubscribeItem = SubscribeItem('', ''); bool _created = false; - setSubscribeItem(SubscribeItem item) async{ + setSubscribeItem(SubscribeItem item) async { _subscribeItem = item; await _start(); } @@ -155,7 +155,7 @@ Future subIsolateEntryPoint(SendPort sendPort) async { sendPort.send([item.title, item.url, 3, uuid]); - await Future.delayed(Duration(seconds: 5)); + await Future.delayed(Duration(seconds: 2)); sendPort.send([item.title, item.url, 4]); items.removeWhere((element) => element.url == item.url); @@ -165,7 +165,7 @@ Future subIsolateEntryPoint(SendPort sendPort) async { sendPort.send("done"); } else { sendPort.send([item.title, item.url, 5]); - await Future.delayed(Duration(seconds: 5)); + await Future.delayed(Duration(seconds: 2)); sendPort.send([item.title, item.url, 4]); items.removeWhere((element) => element.url == item.url); if (items.length > 0) { @@ -175,7 +175,7 @@ Future subIsolateEntryPoint(SendPort sendPort) async { } } else { sendPort.send([item.title, item.url, 6]); - await Future.delayed(Duration(seconds: 5)); + await Future.delayed(Duration(seconds: 2)); sendPort.send([item.title, item.url, 4]); items.removeWhere((element) => element.url == item.url); if (items.length > 0) { diff --git a/lib/home/appbar/about.dart b/lib/home/appbar/about.dart index 1ba5425..ebfa11f 100644 --- a/lib/home/appbar/about.dart +++ b/lib/home/appbar/about.dart @@ -72,14 +72,14 @@ class AboutApp extends StatelessWidget { image: AssetImage('assets/logo.png'), height: 80, ), - Text('Version: 0.1.9'), + Text('Version: 0.2.0'), ], ), ), Container( padding: EdgeInsets.symmetric(horizontal: 50), child: Text( - 'Tsacdop is a podcast player developed in flutter, a simply beautiful and friendly app.', + 'Tsacdop is a podcast player developed in flutter, a clean, simply beautiful and friendly app.', textAlign: TextAlign.center, ), ), diff --git a/lib/home/appbar/importompl.dart b/lib/home/appbar/importompl.dart index c8895f0..b50e1c2 100644 --- a/lib/home/appbar/importompl.dart +++ b/lib/home/appbar/importompl.dart @@ -34,7 +34,7 @@ class Import extends StatelessWidget { SubscribeItem item = subscribeWorker.currentSubscribeItem; switch (item.subscribeState) { case SubscribeState.start: - return importColumn("Subscribe: ${item.title}", context); + return importColumn("Subscribe ${item.title}", context); case SubscribeState.subscribe: groupList.subscribeNewPodcast(item.id); return importColumn("Fetch data ${item.title}", context); diff --git a/lib/home/audioplayer.dart b/lib/home/audioplayer.dart index f6c2d17..cd23a99 100644 --- a/lib/home/audioplayer.dart +++ b/lib/home/audioplayer.dart @@ -507,33 +507,42 @@ class _PlayerWidgetState extends State { Expanded( flex: 2, child: Selector>( - selector: (_, audio) => Tuple2( + Tuple3>( + selector: (_, audio) => Tuple3( audio.audioState, (audio.backgroundAudioDuration - audio.backgroundAudioPosition) / - 1000), + 1000, + audio.remoteErrorMessage), builder: (_, data, __) { return Container( padding: EdgeInsets.symmetric(horizontal: 10), alignment: Alignment.center, - child: data.item1 == BasicPlaybackState.buffering || - data.item1 == BasicPlaybackState.connecting - ? Text( - 'Buffring...', - style: TextStyle( - color: Theme.of(context).accentColor), - ) - : Row( - children: [ - Text( - _stringForSeconds(data.item2) ?? '', + child: data.item3 != null + ? Text(data.item3, + style: const TextStyle( + color: const Color(0xFFFF0000))) + : data.item1 == BasicPlaybackState.buffering || + data.item1 == + BasicPlaybackState.connecting || + data.item1 == + BasicPlaybackState.skippingToNext || + data.item1 == BasicPlaybackState.stopped + ? Text( + 'Buffring...', + style: TextStyle( + color: Theme.of(context).accentColor), + ) + : Row( + children: [ + Text( + _stringForSeconds(data.item2) ?? '', + ), + Text( + ' Left', + ), + ], ), - Text( - ' Left', - ), - ], - ), ); }, ), @@ -1278,7 +1287,10 @@ class _ControlPanelState extends State BasicPlaybackState .connecting || data.audioState == - BasicPlaybackState.none + BasicPlaybackState.none || + data.audioState == + BasicPlaybackState + .skippingToNext ? 'Buffring...' : '', style: TextStyle( diff --git a/lib/home/home_groups.dart b/lib/home/home_groups.dart index e70ef77..e22b909 100644 --- a/lib/home/home_groups.dart +++ b/lib/home/home_groups.dart @@ -29,10 +29,9 @@ class ScrollPodcasts extends StatefulWidget { class _ScrollPodcastsState extends State { int _groupIndex; - Future getPodcastCounts(String id) async { + Future getPodcastUpdateCounts(String id) async { var dbHelper = DBHelper(); - List list = await dbHelper.getPodcastCounts(id); - return list.first; + return await dbHelper.getPodcastUpdateCounts(id); } @override @@ -268,7 +267,7 @@ class _ScrollPodcastsState extends State { "${podcastLocal.imagePath}")), ), FutureBuilder( - future: getPodcastCounts( + future: getPodcastUpdateCounts( podcastLocal.id), initialData: 0, builder: (context, snapshot) { diff --git a/lib/home/nested_home.dart b/lib/home/nested_home.dart index e622177..efea0c0 100644 --- a/lib/home/nested_home.dart +++ b/lib/home/nested_home.dart @@ -11,16 +11,16 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:line_icons/line_icons.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:tsacdop/class/audiostate.dart'; -import 'package:tsacdop/class/episodebrief.dart'; -import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; -import 'package:tsacdop/util/episodegrid.dart'; -import 'package:tsacdop/util/mypopupmenu.dart'; -import 'package:tsacdop/util/context_extension.dart'; -import 'package:tsacdop/util/custompaint.dart'; +import '../class/audiostate.dart'; +import '../class/episodebrief.dart'; +import '../local_storage/sqflite_localpodcast.dart'; +import '../util/episodegrid.dart'; +import '../util/mypopupmenu.dart'; +import '../util/context_extension.dart'; +import '../util/custompaint.dart'; -import 'package:tsacdop/home/appbar/importompl.dart'; -import 'package:tsacdop/home/audioplayer.dart'; +import '../home/appbar/importompl.dart'; +import '../home/audioplayer.dart'; import 'home_groups.dart'; import 'download_list.dart'; @@ -466,14 +466,24 @@ class _RecentUpdateState extends State<_RecentUpdate> future: _getUpdateCounts(_group), initialData: 0, builder: (context, snapshot) { - return snapshot.data > 0 + return snapshot.data != 0 ? Material( color: Colors.transparent, child: IconButton( tooltip: 'Add new episodes to playlist', - icon: Icon( - LineIcons.tasks_solid), + icon: + // Icon(Icons.playlist_add), + SizedBox( + height: 16, + width: 21, + child: CustomPaint( + painter: AddToPlaylistPainter( + context + .textTheme + .bodyText1 + .color, + Colors.red))), onPressed: () async { await audio .addNewEpisode(_group); @@ -488,7 +498,23 @@ class _RecentUpdateState extends State<_RecentUpdate> ); }), ) - : Center(); + : IconButton( + tooltip: + 'Add new episodes to playlist', + icon: + // Icon(Icons.playlist_add), + SizedBox( + height: 16, + width: 21, + child: CustomPaint( + painter: + AddToPlaylistPainter( + context.textTheme + .bodyText1.color, + context.textTheme + .bodyText1.color, + ))), + onPressed: () {}); }), Material( color: Colors.transparent, diff --git a/lib/local_storage/sqflite_localpodcast.dart b/lib/local_storage/sqflite_localpodcast.dart index ef05fc5..ca4b389 100644 --- a/lib/local_storage/sqflite_localpodcast.dart +++ b/lib/local_storage/sqflite_localpodcast.dart @@ -102,12 +102,20 @@ class DBHelper { return podcastLocal; } - Future> getPodcastCounts(String id) async { + Future getPodcastCounts(String id) async { var dbClient = await database; List list = await dbClient.rawQuery( - 'SELECT update_count, episode_count FROM PodcastLocal WHERE id = ?', + 'SELECT episode_count FROM PodcastLocal WHERE id = ?', [id]); - return [list.first['update_count'], list.first['episode_count']]; + return list.first['episode_count']; + } + + Future getPodcastUpdateCounts(String id) async { + var dbClient = await database; + List list = await dbClient.rawQuery( + 'SELECt count(*) as count FROM Episodes WHERE feed_id = ? AND is_new = 1', + [id]); + return list.first['count']; } Future getSkipSeconds(String id) async { @@ -518,7 +526,7 @@ class DBHelper { return 0; } catch (e) { print(e); - return 0; + return -1; } } @@ -577,7 +585,6 @@ class DBHelper { return episodes; } - Future> getNewEpisodes(String id) async { var dbClient = await database; List episodes = []; @@ -820,6 +827,12 @@ class DBHelper { return 0; } + Future removeEpisodeNewMark(String url) async { + var dbClient = await database; + return await dbClient.rawUpdate( + "UPDATE Episodes SET is_new = 0 WHERE enclosure_url = ?", [url]); + } + Future> getLikedRssItem(int i, int sortBy) async { var dbClient = await database; List episodes = List(); diff --git a/lib/podcasts/podcastdetail.dart b/lib/podcasts/podcastdetail.dart index f781f89..bd685e6 100644 --- a/lib/podcasts/podcastdetail.dart +++ b/lib/podcasts/podcastdetail.dart @@ -38,22 +38,21 @@ class _PodcastDetailState extends State { List hosts; Future _updateRssItem(PodcastLocal podcastLocal) async { var dbHelper = DBHelper(); - try { - final result = await dbHelper.updatePodcastRss(podcastLocal); - if (result == 0) { - Fluttertoast.showToast( - msg: 'No Update', - gravity: ToastGravity.TOP, - ); - } else { - Fluttertoast.showToast( - msg: 'Updated $result Episodes', - gravity: ToastGravity.TOP, - ); - Provider.of(context, listen: false) - .updatePodcast(podcastLocal.id); - } - } catch (e) { + + final result = await dbHelper.updatePodcastRss(podcastLocal); + if (result == 0) { + Fluttertoast.showToast( + msg: 'No Update', + gravity: ToastGravity.TOP, + ); + } else if (result > 0) { + Fluttertoast.showToast( + msg: 'Updated $result Episodes', + gravity: ToastGravity.TOP, + ); + Provider.of(context, listen: false) + .updatePodcast(podcastLocal.id); + } else { Fluttertoast.showToast( msg: 'Update failed, network error', gravity: ToastGravity.TOP, @@ -217,7 +216,7 @@ class _PodcastDetailState extends State { color: Theme.of(context).accentColor, onRefresh: () async { await _updateRssItem(widget.podcastLocal); - // audio.addNewEpisode(widget.podcastLocal.id); + // audio.addNewEpisode(widget.podcastLocal.id); }, child: Stack( children: [ diff --git a/lib/podcasts/podcastgroup.dart b/lib/podcasts/podcastgroup.dart index b349f6b..24ff809 100644 --- a/lib/podcasts/podcastgroup.dart +++ b/lib/podcasts/podcastgroup.dart @@ -81,10 +81,12 @@ class _PodcastCardState extends State Animation _animation; double _value; int _seconds; + int _skipSeconds; Future getSkipSecond(String id) async { var dbHelper = DBHelper(); int seconds = await dbHelper.getSkipSeconds(id); + _skipSeconds = seconds; return seconds; } @@ -373,12 +375,15 @@ class _PodcastCardState extends State left: 20, right: 100, bottom: 20), - title: Text('Skip seconds at the beginning'), + title: + Text('Skip seconds at the beginning'), content: DurationPicker( - duration: Duration.zero, + duration: Duration( + seconds: _skipSeconds ?? 0), onChange: (value) => _seconds = value.inSeconds, ), + // content: Text('test'), actionsPadding: EdgeInsets.all(10), actions: [ @@ -402,7 +407,8 @@ class _PodcastCardState extends State }, child: Text( 'CONFIRM', - style: TextStyle(color: context.accentColor), + style: TextStyle( + color: context.accentColor), ), ) ], diff --git a/lib/util/custompaint.dart b/lib/util/custompaint.dart index 820bb95..e96998f 100644 --- a/lib/util/custompaint.dart +++ b/lib/util/custompaint.dart @@ -154,6 +154,52 @@ class ListenedAllPainter extends CustomPainter { } } +//Add new episode to palylist +class AddToPlaylistPainter extends CustomPainter { + Color _color; + Color _textColor; + AddToPlaylistPainter(this._color, this._textColor); + @override + void paint(Canvas canvas, Size size) { + Paint _paint = Paint() + ..color = _color + ..strokeWidth = 1 + ..strokeCap = StrokeCap.round + ..style = PaintingStyle.stroke; + Path _path = Path(); + _path.moveTo(0, 0); + _path.lineTo(size.width * 4 / 7, 0); + _path.moveTo(0, size.height / 3); + _path.lineTo(size.width * 4 / 7, size.height / 3); + _path.moveTo(0, size.height * 2 / 3); + _path.lineTo(size.width * 3 / 7, size.height * 2 / 3); + //_path.moveTo(size.width * 3 / 7, size.height * 2 / 3); + //_path.lineTo(size.width, size.height * 2 / 3); + //_path.moveTo(size.width * 5 / 7, size.height / 3); + //_path.lineTo(size.width * 5 / 7, size.height); + // _path.moveTo(size.width*5/7, size.height/4); + // _path.lineTo(size.width*11/14, 0); + // _path.lineTo(size.width*13/14, size.height/4); + // _path.lineTo(size.width, 0); + var textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + text: TextSpan( + text: 'N', + style: TextStyle( + fontStyle: FontStyle.italic, color: _textColor, fontSize: 10), + )) + ..layout(); + textPainter.paint(canvas, Offset(size.width * 4 / 7, size.height / 3)); + canvas.drawPath(_path, _paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} + //Wave play indicator class WavePainter extends CustomPainter { double _fraction; diff --git a/pubspec.yaml b/pubspec.yaml index 083353f..713691f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,7 +33,7 @@ dev_dependencies: path_provider: ^1.6.5 color_thief_flutter: ^1.0.2 provider: ^4.0.5 - google_fonts: ^0.5.0+1 + google_fonts: ^1.0.0 dio: ^3.0.9 file_picker: ^1.6.3+1 xml: ^3.5.0 @@ -44,14 +44,14 @@ dev_dependencies: intl: ^0.16.1 url_launcher: ^5.4.2 image: ^2.1.12 - shared_preferences: ^0.5.6+1 + shared_preferences: ^0.5.7 uuid: ^2.0.4 tuple: ^1.0.3 cached_network_image: ^2.1.0+1 workmanager: ^0.2.2 - flutter_colorpicker: ^0.3.2 + flutter_colorpicker: ^0.3.4 app_settings: ^3.0.1 - fl_chart: ^0.9.0 + fl_chart: ^0.9.3 audio_service: ^0.7.2 just_audio: git: @@ -63,7 +63,7 @@ dev_dependencies: flutter_linkify: ^3.1.0 extended_nested_scroll_view: ^0.4.0 connectivity: ^0.4.8+2 - flare_flutter: ^2.0.1 + flare_flutter: ^2.0.3 rxdart: ^0.24.0 flutter_isolate: ^1.0.0+11