From 6086db0f8c0b4f98d47010c2461c7eb559e046c9 Mon Sep 17 00:00:00 2001 From: stonegate Date: Sat, 26 Sep 2020 22:26:25 +0800 Subject: [PATCH] Minor change --- README.md | 4 +- lib/home/about.dart | 2 +- lib/home/home.dart | 594 ++++++++++---------- lib/home/home_groups.dart | 68 +-- lib/home/home_menu.dart | 34 +- lib/home/playlist.dart | 344 ++++++------ lib/local_storage/sqflite_localpodcast.dart | 59 +- lib/podcasts/podcast_settings.dart | 93 +-- lib/settings/data_backup.dart | 234 +++++++- lib/state/audio_state.dart | 59 +- lib/state/podcast_group.dart | 22 +- lib/state/refresh_podcast.dart | 46 +- lib/state/setting_state.dart | 2 +- pubspec.yaml | 19 +- 14 files changed, 945 insertions(+), 635 deletions(-) diff --git a/README.md b/README.md index bbe7a31..ff473c6 100644 --- a/README.md +++ b/README.md @@ -173,8 +173,8 @@ For help getting started with Flutter, view our [English]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=English&query=%24.languages%5B3%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% [Chinese Simplified]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=Chinese%20Simplified&query=%24.languages%5B2%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% [French]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=French(ppp)&query=%24.languages%5B5%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% -[Spanish]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=Spanish(Joel)&query=%24.languages%5B8%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% -[Portuguese]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=portuguese(Bruno)&query=%24.languages%5B10%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% +[Spanish]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=Spanish(Joel)&query=%24.languages%5B7%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% +[Portuguese]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=%2323CCC6&label=portuguese(Bruno)&query=%24.languages%5B9%5D.reviewedProgress&url=https%3A%2F%2Fapi.localizely.com%2Fv1%2Fprojects%2Fbde4e9bd-4cb2-449b-9de2-18f231ddb47d%2Fstatus&suffix=% [localizely - website]: https://localizely.com/ [google play - icon]: https://img.shields.io/badge/google-playStore-%2323CCC6 [google play]: https://play.google.com/store/apps/details?id=com.stonegate.tsacdop diff --git a/lib/home/about.dart b/lib/home/about.dart index 47d43b7..3505915 100644 --- a/lib/home/about.dart +++ b/lib/home/about.dart @@ -5,7 +5,7 @@ import 'package:line_icons/line_icons.dart'; import '../util/custom_widget.dart'; import '../util/extension_helper.dart'; -const String version = '0.4.17'; +const String version = '0.4.18'; class AboutApp extends StatelessWidget { Widget _listItem( diff --git a/lib/home/home.dart b/lib/home/home.dart index cd7849f..174286f 100644 --- a/lib/home/home.dart +++ b/lib/home/home.dart @@ -526,12 +526,13 @@ class _PlaylistButton extends StatefulWidget { class __PlaylistButtonState extends State<_PlaylistButton> { bool _loadPlay; - _getPlaylist() async { - await Provider.of(context, listen: false) - .loadPlaylist(); - setState(() { - _loadPlay = true; - }); + Future _getPlaylist() async { + await context.read().loadPlaylist(); + if (mounted) { + setState(() { + _loadPlay = true; + }); + } } @override @@ -545,157 +546,158 @@ class __PlaylistButtonState extends State<_PlaylistButton> { Widget build(BuildContext context) { var audio = context.watch(); final s = context.s; - return MyPopupMenuButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - elevation: 1, - icon: Icon(Icons.playlist_play), - tooltip: s.menu, - itemBuilder: (context) => [ - MyPopupMenuItem( - height: 50, - value: 1, - child: Container( - decoration: BoxDecoration( - // color: Theme.of(context).accentColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10.0), - topRight: Radius.circular(10.0)), - ), - child: Selector>( - selector: (_, audio) => - Tuple3(audio.playerRunning, audio.queue, audio.lastPositin), - builder: (_, data, __) => !_loadPlay - ? Container( - height: 8.0, - ) - : data.item1 || data.item2.playlist.length == 0 - ? Container( - height: 8.0, - ) - : InkWell( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10.0), - topRight: Radius.circular(10.0)), - onTap: () { - audio.playlistLoad(); - Navigator.pop(context); - }, - child: Column( - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 5), - ), - Stack( - alignment: Alignment.center, - children: [ - CircleAvatar( - radius: 20, - backgroundImage: data - .item2.playlist.first.avatarImage), - Container( - height: 40.0, - width: 40.0, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.black12), - child: Icon( - Icons.play_arrow, - color: Colors.white, - ), - ), - ], - ), - Padding( - padding: EdgeInsets.symmetric(vertical: 2), - ), - Container( - height: 70, - width: 140, - child: Column( + return Material( + child: MyPopupMenuButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10))), + elevation: 1, + icon: Icon(Icons.playlist_play), + tooltip: s.menu, + itemBuilder: (context) => [ + MyPopupMenuItem( + height: 50, + value: 1, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), + topRight: Radius.circular(10.0)), + ), + child: Selector>( + selector: (_, audio) => + Tuple3(audio.playerRunning, audio.queue, audio.lastPositin), + builder: (_, data, __) => !_loadPlay + ? SizedBox( + height: 8.0, + ) + : data.item1 || data.item2.playlist.length == 0 + ? SizedBox( + height: 8.0, + ) + : InkWell( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), + topRight: Radius.circular(10.0)), + onTap: () { + audio.playlistLoad(); + Navigator.pop(context); + }, + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 5), + ), + Stack( + alignment: Alignment.center, children: [ - Text( - (data.item3 ~/ 1000).toTime, - // style: - // TextStyle(color: Colors.white) - ), - Text( - data.item2.playlist.first.title, - maxLines: 2, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - // style: TextStyle(color: Colors.white), + CircleAvatar( + radius: 20, + backgroundImage: data + .item2.playlist.first.avatarImage), + Container( + height: 40.0, + width: 40.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.black12), + child: Icon( + Icons.play_arrow, + color: Colors.white, + ), ), ], ), - ), - Divider( - height: 1, - ), - ], + Padding( + padding: EdgeInsets.symmetric(vertical: 2), + ), + Container( + height: 70, + width: 140, + child: Column( + children: [ + Text( + (data.item3 ~/ 1000).toTime, + // style: + // TextStyle(color: Colors.white) + ), + Text( + data.item2.playlist.first.title, + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + // style: TextStyle(color: Colors.white), + ), + ], + ), + ), + Divider( + height: 1, + ), + ], + ), ), - ), - ), - ), - ), - PopupMenuItem( - value: 0, - child: Container( - padding: EdgeInsets.only(left: 10), - child: Row( - children: [ - Icon(Icons.playlist_play), - Padding( - padding: EdgeInsets.symmetric(horizontal: 5.0), - ), - Text(s.homeMenuPlaylist), - ], - ), - ), - ), - PopupMenuDivider( - height: 1, - ), - PopupMenuItem( - value: 2, - child: Container( - padding: EdgeInsets.only(left: 10), - child: Row( - children: [ - Icon(Icons.history), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 5.0), - ), - Text(s.settingsHistory), - ], - ), - ), - ), - PopupMenuDivider( - height: 1, - ), - ], - onSelected: (value) { - if (value == 0) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PlaylistPage( - initPage: InitPage.playlist, ), ), - ); - } else if (value == 2) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PlaylistPage( - initPage: InitPage.history, + ), + PopupMenuItem( + value: 0, + child: Container( + padding: EdgeInsets.only(left: 10), + child: Row( + children: [ + Icon(Icons.playlist_play), + Padding( + padding: EdgeInsets.symmetric(horizontal: 5.0), + ), + Text(s.homeMenuPlaylist), + ], ), ), - ); - } - }, + ), + PopupMenuDivider( + height: 1, + ), + PopupMenuItem( + value: 2, + child: Container( + padding: EdgeInsets.only(left: 10), + child: Row( + children: [ + Icon(Icons.history), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5.0), + ), + Text(s.settingsHistory), + ], + ), + ), + ), + PopupMenuDivider( + height: 1, + ), + ], + onSelected: (value) { + if (value == 0) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PlaylistPage( + initPage: InitPage.playlist, + ), + ), + ); + } else if (value == 2) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PlaylistPage( + initPage: InitPage.history, + ), + ), + ); + } + }, + ), ); } } @@ -707,6 +709,19 @@ class _RecentUpdate extends StatefulWidget { class _RecentUpdateState extends State<_RecentUpdate> with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { + final GlobalKey _refreshIndicatorKey = + GlobalKey(); + + Future _updateRssItem() async { + final refreshWorker = context.read(); + refreshWorker.start(_group); + await Future.delayed(Duration(seconds: 1)); + Fluttertoast.showToast( + msg: 'Refresh started', + gravity: ToastGravity.BOTTOM, + ); + } + Future> _getRssItem(int top, List group, {bool hideListened}) async { var storage = KeyValueStorage(recentLayoutKey); @@ -718,7 +733,7 @@ class _RecentUpdateState extends State<_RecentUpdate> } var dbHelper = DBHelper(); List episodes; - if (group.first == 'All') { + if (group.isEmpty) { episodes = await dbHelper.getRecentRssItem(top, hideListened: _hideListened); } else { @@ -731,7 +746,7 @@ class _RecentUpdateState extends State<_RecentUpdate> Future _getUpdateCounts(List group) async { var dbHelper = DBHelper(); var episodes = []; - if (group.first == 'All') { + if (group.isEmpty) { episodes = await dbHelper.getRecentNewRssItem(); } else { episodes = await dbHelper.getGroupNewRssItem(group); @@ -768,7 +783,7 @@ class _RecentUpdateState extends State<_RecentUpdate> super.initState(); _loadMore = false; _groupName = 'All'; - _group = ['All']; + _group = []; _scroll = false; } @@ -820,24 +835,28 @@ class _RecentUpdateState extends State<_RecentUpdate> } return true; }, - child: CustomScrollView( - key: PageStorageKey('update'), - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: Container( - height: 40, - color: context.primaryColor, - child: Material( - color: Colors.transparent, - child: Row( - children: [ - Consumer( - builder: (context, groupList, - child) => - Material( - color: Colors.transparent, - child: + child: RefreshIndicator( + key: _refreshIndicatorKey, + color: context.accentColor, + onRefresh: () async { + await _updateRssItem(); + }, + child: CustomScrollView( + key: PageStorageKey('update'), + physics: + const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Container( + height: 40, + color: context.primaryColor, + child: Material( + color: Colors.transparent, + child: Row( + children: [ + Consumer( + builder: (context, groupList, + child) => PopupMenuButton( shape: RoundedRectangleBorder( borderRadius: @@ -903,7 +922,7 @@ class _RecentUpdateState extends State<_RecentUpdate> if (value == 'All') { setState(() { _groupName = 'All'; - _group = ['All']; + _group = []; }); } else { for (var group @@ -923,129 +942,128 @@ class _RecentUpdateState extends State<_RecentUpdate> }, ), ), - ), - Spacer(), - FutureBuilder( - future: - _getUpdateCounts(_group), - initialData: 0, - builder: (context, snapshot) { - return snapshot.data != 0 - ? Material( - color: Colors - .transparent, - child: IconButton( - tooltip: s - .addNewEpisodeTooltip, - icon: SizedBox( - height: 15, - width: 20, - child: CustomPaint( - painter: AddToPlaylistPainter( - context - .textTheme.bodyText1.color, - Colors - .red))), - onPressed: - () async { - await audio - .addNewEpisode( - _group); - if (mounted) { - setState( - () {}); - } - Fluttertoast - .showToast( - msg: _groupName == - 'All' - ? s.addNewEpisodeAll(snapshot - .data) - : s.addEpisodeGroup( - _groupName, - snapshot.data), - gravity: - ToastGravity - .BOTTOM, - ); - }), - ) - : Material( - color: Colors - .transparent, - child: IconButton( - tooltip: s - .addNewEpisodeTooltip, - icon: SizedBox( - height: 15, - width: 20, - child: - CustomPaint( - painter: - AddToPlaylistPainter( - context - .textColor, - context - .textColor, - ))), - onPressed: - () {}), - ); - }), - Material( - color: Colors.transparent, - child: IconButton( - tooltip: - s.hideListenedSetting, - icon: SizedBox( - width: 30, - height: 15, - child: HideListened( - hideListened: - _hideListened ?? - false, + Spacer(), + FutureBuilder( + future: _getUpdateCounts( + _group), + initialData: 0, + builder: + (context, snapshot) { + return snapshot.data != 0 + ? Material( + color: Colors + .transparent, + child: IconButton( + tooltip: s + .addNewEpisodeTooltip, + icon: SizedBox( + height: + 15, + width: 20, + child: CustomPaint( + painter: AddToPlaylistPainter( + context + .textTheme.bodyText1.color, + Colors + .red))), + onPressed: + () async { + await audio + .addNewEpisode( + _group); + if (mounted) { + setState( + () {}); + } + Fluttertoast + .showToast( + msg: _groupName == + 'All' + ? s.addNewEpisodeAll(snapshot + .data) + : s.addEpisodeGroup( + _groupName, + snapshot.data), + gravity: + ToastGravity + .BOTTOM, + ); + }), + ) + : Material( + color: Colors + .transparent, + child: IconButton( + tooltip: s + .addNewEpisodeTooltip, + icon: SizedBox( + height: 15, + width: 20, + child: CustomPaint( + painter: AddToPlaylistPainter( + context + .textColor, + context + .textColor, + ))), + onPressed: () {}), + ); + }), + Material( + color: Colors.transparent, + child: IconButton( + tooltip: + s.hideListenedSetting, + icon: SizedBox( + width: 30, + height: 15, + child: HideListened( + hideListened: + _hideListened ?? + false, + ), ), + onPressed: () { + setState(() => + _hideListened = + !_hideListened); + }, ), - onPressed: () { - setState(() => - _hideListened = - !_hideListened); - }, ), - ), - Material( - color: Colors.transparent, - child: LayoutButton( - layout: _layout, - onPressed: (layout) => - setState(() { - _layout = layout; - }), + Material( + color: Colors.transparent, + child: LayoutButton( + layout: _layout, + onPressed: (layout) => + setState(() { + _layout = layout; + }), + ), ), - ), - ], - ), - )), - ), - EpisodeGrid( - episodes: snapshot.data, - layout: _layout, - initNum: _scroll ? 0 : 12, - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return _loadMore - ? Container( - height: 2, - child: - LinearProgressIndicator()) - : Center(); - }, - childCount: 1, + ], + ), + )), ), - ), - ])) + EpisodeGrid( + episodes: snapshot.data, + layout: _layout, + initNum: _scroll ? 0 : 12, + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return _loadMore + ? Container( + height: 2, + child: + LinearProgressIndicator()) + : Center(); + }, + childCount: 1, + ), + ), + ]), + )) : Center(); }, ); diff --git a/lib/home/home_groups.dart b/lib/home/home_groups.dart index 5dc5261..0c5ab24 100644 --- a/lib/home/home_groups.dart +++ b/lib/home/home_groups.dart @@ -361,11 +361,8 @@ class _ScrollPodcastsState extends State alignment: Alignment.centerLeft, color: context.scaffoldBackgroundColor, child: TabBar( - labelPadding: EdgeInsets.only( - top: 5.0, - bottom: 10.0, - left: 6.0, - right: 6.0), + labelPadding: + EdgeInsets.fromLTRB(6.0, 5.0, 6.0, 10.0), indicator: CircleTabIndicator( color: context.accentColor, radius: 3), isScrollable: true, @@ -439,17 +436,30 @@ class _ScrollPodcastsState extends State .podcasts .map((podcastLocal) { return Container( - decoration: BoxDecoration( - color: Theme.of(context).brightness == - Brightness.light - ? Theme.of(context).primaryColor - : Colors.black12), - margin: EdgeInsets.symmetric(horizontal: 5.0), - key: ObjectKey(podcastLocal.title), - child: PodcastPreview( - podcastLocal: podcastLocal, - ), - ); + decoration: BoxDecoration( + color: context.brightness == + Brightness.light + ? context.primaryColor + : Colors.black12), + margin: + EdgeInsets.symmetric(horizontal: 5.0), + key: ObjectKey(podcastLocal.title), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.push( + context, + SlideLeftRoute( + page: PodcastDetail( + podcastLocal: podcastLocal, + )), + ); + }, + child: PodcastPreview( + podcastLocal: podcastLocal, + ), + ))); }).toList(), ), ), @@ -513,27 +523,11 @@ class PodcastPreview extends StatelessWidget { Expanded( flex: 1, child: Align( - alignment: Alignment.centerRight, - child: Material( - color: Colors.transparent, - child: Selector( - selector: (_, audio) => audio.playerRunning, - builder: (_, playerRunning, __) => IconButton( - icon: Icon(Icons.arrow_forward), - tooltip: context.s.homeGroupsSeeAll, - onPressed: () { - Navigator.push( - context, - SlideLeftRoute( - page: PodcastDetail( - podcastLocal: podcastLocal, - )), - ); - }, - ), - ), - ), - ), + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Icon(Icons.arrow_forward), + )), ), ], ), diff --git a/lib/home/home_menu.dart b/lib/home/home_menu.dart index 053dea5..6131f0e 100644 --- a/lib/home/home_menu.dart +++ b/lib/home/home_menu.dart @@ -6,7 +6,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:intl/intl.dart'; import 'package:line_icons/line_icons.dart'; import 'package:provider/provider.dart'; @@ -26,7 +25,6 @@ class PopupMenu extends StatefulWidget { class _PopupMenuState extends State { Future _getRefreshDate(BuildContext context) async { int refreshDate; - final s = context.s; var refreshstorage = KeyValueStorage('refreshdate'); var i = await refreshstorage.getInt(); if (i == 0) { @@ -36,20 +34,21 @@ class _PopupMenuState extends State { } else { refreshDate = i; } - var date = DateTime.fromMillisecondsSinceEpoch(refreshDate); - var difference = DateTime.now().difference(date); - if (difference.inSeconds < 60) { - return s.secondsAgo(difference.inSeconds); - } else if (difference.inMinutes < 60) { - return s.minsAgo(difference.inMinutes); - } else if (difference.inHours < 24) { - return s.hoursAgo(difference.inHours); - } else if (difference.inDays < 7) { - return s.daysAgo(difference.inDays); - } else { - return DateFormat.yMMMd() - .format(DateTime.fromMillisecondsSinceEpoch(refreshDate)); - } + return refreshDate.toDate(context); + // var date = DateTime.fromMillisecondsSinceEpoch(refreshDate); + // var difference = DateTime.now().difference(date); + // if (difference.inSeconds < 60) { + // return s.secondsAgo(difference.inSeconds); + // } else if (difference.inMinutes < 60) { + // return s.minsAgo(difference.inMinutes); + // } else if (difference.inHours < 24) { + // return s.hoursAgo(difference.inHours); + // } else if (difference.inDays < 7) { + // return s.daysAgo(difference.inDays); + // } else { + // return DateFormat.yMMMd() + // .format(DateTime.fromMillisecondsSinceEpoch(refreshDate)); + // } } void _saveOmpl(String path) async { @@ -198,8 +197,7 @@ class _PopupMenuState extends State { } else if (value == 2) { _getFilePath(); } else if (value == 1) { - //_refreshAll(); - refreshWorker.start(); + refreshWorker.start([]); } else if (value == 3) { // setting.theme != 2 ? setting.setTheme(2) : setting.setTheme(1); } else if (value == 4) { diff --git a/lib/home/playlist.dart b/lib/home/playlist.dart index a12745d..b4ba730 100644 --- a/lib/home/playlist.dart +++ b/lib/home/playlist.dart @@ -69,7 +69,7 @@ class _PlaylistPageState extends State { systemNavigationBarColor: Theme.of(context).primaryColor, ), child: Scaffold( - backgroundColor: Theme.of(context).primaryColor, + backgroundColor: context.primaryColor, appBar: AppBar( elevation: 0, backgroundColor: context.accentColor.withAlpha(70), @@ -264,9 +264,12 @@ class _PlaylistPageState extends State { ), ), Expanded( - child: AnimatedSwitcher( - duration: Duration(milliseconds: 300), - child: _loadList)), + child: Container( + color: context.primaryColor, + child: AnimatedSwitcher( + duration: Duration(milliseconds: 300), + child: _loadList), + )), ], ); }, @@ -586,171 +589,192 @@ class __HistoryListState extends State<_HistoryList> { final date = snapshot .data[index].playdate.millisecondsSinceEpoch; final episode = snapshot.data[index].episode; - final c = episode.backgroudColor(context); - return SizedBox( - height: 90.0, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: Center( - child: ListTile( - contentPadding: - EdgeInsets.fromLTRB(24, 8, 20, 8), - onTap: () => audio.episodeLoad(episode), - leading: CircleAvatar( - backgroundColor: c.withOpacity(0.5), - backgroundImage: episode.avatarImage), - title: Padding( - padding: - EdgeInsets.symmetric(vertical: 5.0), - child: Text( - snapshot.data[index].title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - subtitle: Container( - height: 35, - child: Row( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - if (seekValue < 0.9) - Padding( - padding: - const EdgeInsets.symmetric( - vertical: 5.0), - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () async { - audio.episodeLoad(episode, - startPosition: - (seconds * 1000) - .toInt()); - }, - child: Stack(children: [ - ShaderMask( - shaderCallback: - (bounds) { - return LinearGradient( - begin: Alignment - .centerLeft, - colors: [ - Colors.cyan[600] - .withOpacity( - 0.8), - Colors.white70 - ], - stops: [ - seekValue, - seekValue - ], - tileMode: - TileMode.mirror, - ).createShader( - bounds); - }, - child: Container( - height: 25, - alignment: - Alignment.center, - padding: EdgeInsets + final c = episode?.backgroudColor(context); + return episode == null + ? Center() + : SizedBox( + height: 90.0, + child: Column( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: Center( + child: ListTile( + contentPadding: EdgeInsets.fromLTRB( + 24, 8, 20, 8), + onTap: () => + audio.episodeLoad(episode), + leading: CircleAvatar( + backgroundColor: + c?.withOpacity(0.5), + backgroundImage: + episode.avatarImage), + title: Padding( + padding: EdgeInsets.symmetric( + vertical: 5.0), + child: Text( + snapshot.data[index].title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + subtitle: Container( + height: 35, + child: Row( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + if (seekValue < 0.9) + Padding( + padding: const EdgeInsets .symmetric( - horizontal: - 20), - decoration: - BoxDecoration( - borderRadius: - BorderRadius.all( - Radius.circular( - 20.0)), - color: context - .accentColor, - ), - child: Text( - seconds.toTime, - style: TextStyle( - color: Colors - .white), - ), + vertical: 5.0), + child: Material( + color: + Colors.transparent, + child: InkWell( + onTap: () async { + audio.episodeLoad( + episode, + startPosition: + (seconds * + 1000) + .toInt()); + }, + child: Stack( + children: [ + ShaderMask( + shaderCallback: + (bounds) { + return LinearGradient( + begin: Alignment + .centerLeft, + colors: < + Color>[ + Colors + .cyan[600] + .withOpacity(0.8), + Colors + .white70 + ], + stops: [ + seekValue, + seekValue + ], + tileMode: + TileMode + .mirror, + ).createShader( + bounds); + }, + child: + Container( + height: 25, + alignment: + Alignment + .center, + padding: EdgeInsets.symmetric( + horizontal: + 20), + decoration: + BoxDecoration( + borderRadius: + BorderRadius.all( + Radius.circular(20.0)), + color: context + .accentColor, + ), + child: Text( + seconds + .toTime, + style: TextStyle( + color: + Colors.white), + ), + ), + ), + ]), ), ), - ]), + ), + SizedBox( + child: Selector< + AudioPlayerNotifier, + Tuple2< + List, + bool>>( + selector: (_, audio) => + Tuple2( + audio.queue + .playlist, + audio + .queueUpdate), + builder: (_, data, __) { + return data.item1 + .contains( + episode) + ? IconButton( + icon: Icon( + Icons + .playlist_add_check, + color: context + .accentColor), + onPressed: + () async { + audio.delFromPlaylist( + episode); + Fluttertoast + .showToast( + msg: s + .toastRemovePlaylist, + gravity: + ToastGravity + .BOTTOM, + ); + }) + : IconButton( + icon: Icon( + Icons + .playlist_add, + color: Colors + .grey[ + 700]), + onPressed: + () async { + audio.addToPlaylist( + episode); + Fluttertoast + .showToast( + msg: s + .toastAddPlaylist, + gravity: + ToastGravity + .BOTTOM, + ); + }); + }, + ), ), - ), - ), - SizedBox( - child: Selector< - AudioPlayerNotifier, - Tuple2, - bool>>( - selector: (_, audio) => Tuple2( - audio.queue.playlist, - audio.queueUpdate), - builder: (_, data, __) { - return data.item1 - .contains(episode) - ? IconButton( - icon: Icon( - Icons - .playlist_add_check, - color: context - .accentColor), - onPressed: () async { - audio - .delFromPlaylist( - episode); - Fluttertoast - .showToast( - msg: s - .toastRemovePlaylist, - gravity: - ToastGravity - .BOTTOM, - ); - }) - : IconButton( - icon: Icon( - Icons - .playlist_add, - color: Colors - .grey[700]), - onPressed: () async { - audio.addToPlaylist( - episode); - Fluttertoast - .showToast( - msg: s - .toastAddPlaylist, - gravity: - ToastGravity - .BOTTOM, - ); - }); - }, + Spacer(), + Text( + date.toDate(context), + style: TextStyle( + fontSize: 15, + ), + ), + ], ), ), - Spacer(), - Text( - date.toDate(context), - style: TextStyle( - fontSize: 15, - ), - ), - ], + ), ), ), - ), + Divider(height: 1) + ], ), - ), - Divider(height: 1) - ], - ), - ); + ); } }), ) diff --git a/lib/local_storage/sqflite_localpodcast.dart b/lib/local_storage/sqflite_localpodcast.dart index d4d207d..5812687 100644 --- a/lib/local_storage/sqflite_localpodcast.dart +++ b/lib/local_storage/sqflite_localpodcast.dart @@ -38,7 +38,8 @@ class DBHelper { description TEXT, add_date INTEGER, imagePath TEXT, provider TEXT, link TEXT, background_image TEXT DEFAULT '', hosts TEXT DEFAULT '',update_count INTEGER DEFAULT 0, episode_count INTEGER DEFAULT 0, skip_seconds INTEGER DEFAULT 0, - auto_download INTEGER DEFAULT 0, skip_seconds_end INTEGER DEFAULT 0)"""); + auto_download INTEGER DEFAULT 0, skip_seconds_end INTEGER DEFAULT 0, + never_update INTEGER DEFAULT 0)"""); await db .execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT, enclosure_url TEXT UNIQUE, enclosure_length INTEGER, pubDate TEXT, @@ -62,27 +63,41 @@ class DBHelper { "ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0"); await db.execute( "ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0 "); + await db.execute( + "ALTER TABLE PodcastLocal ADD never_update INTEGER DEFAULT 0 "); } else if (oldVersion == 2) { await db.execute( "ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0"); await db.execute( "ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0 "); + await db.execute( + "ALTER TABLE PodcastLocal ADD never_update INTEGER DEFAULT 0 "); } else if (oldVersion == 3) { await db.execute( - "ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0 "); + "ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0"); + await db.execute( + "ALTER TABLE PodcastLocal ADD never_update INTEGER DEFAULT 0 "); } } - Future> getPodcastLocal(List podcasts) async { + Future> getPodcastLocal(List podcasts, + {bool updateOnly = false}) async { var dbClient = await database; var podcastLocal = []; for (var s in podcasts) { List list; - list = await dbClient.rawQuery( - """SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, + if (updateOnly) { + list = await dbClient.rawQuery( + """SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, + link ,update_count, episode_count FROM PodcastLocal WHERE id = ? AND + never_update = 0""", [s]); + } else { + list = await dbClient.rawQuery( + """SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, link ,update_count, episode_count FROM PodcastLocal WHERE id = ?""", - [s]); + [s]); + } if (list.length > 0) { podcastLocal.add(PodcastLocal( list.first['title'], @@ -101,10 +116,21 @@ class DBHelper { return podcastLocal; } - Future> getPodcastLocalAll() async { + Future> getPodcastLocalAll( + {bool updateOnly = false}) async { var dbClient = await database; - List list = await dbClient.rawQuery( - 'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, provider, link FROM PodcastLocal ORDER BY add_date DESC'); + + List list; + if (updateOnly) { + list = await dbClient.rawQuery( + """SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, + provider, link FROM PodcastLocal WHERE never_update = 0 ORDER BY + add_date DESC"""); + } else { + list = await dbClient.rawQuery( + """SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, + provider, link FROM PodcastLocal ORDER BY add_date DESC"""); + } var podcastLocal = []; @@ -131,6 +157,21 @@ class DBHelper { return 0; } + Future getNeverUpdate(String id) async { + var dbClient = await database; + List list = await dbClient + .rawQuery('SELECT never_update FROM PodcastLocal WHERE id = ?', [id]); + if (list.isNotEmpty) return list.first['never_update'] == 1; + return false; + } + + Future saveNeverUpdate(String id, {bool boo}) async { + var dbClient = await database; + return await dbClient.rawUpdate( + "UPDATE PodcastLocal SET never_update = ? WHERE id = ?", + [boo ? 1 : 0, id]); + } + Future getPodcastUpdateCounts(String id) async { var dbClient = await database; List list = await dbClient.rawQuery( diff --git a/lib/podcasts/podcast_settings.dart b/lib/podcasts/podcast_settings.dart index 926b4dc..84ae8bb 100644 --- a/lib/podcasts/podcast_settings.dart +++ b/lib/podcasts/podcast_settings.dart @@ -58,6 +58,11 @@ class _PodcastSettingState extends State { if (mounted) setState(() {}); } + Future _setNeverUpdate(bool boo) async { + await _dbHelper.saveNeverUpdate(widget.podcastLocal.id, boo: boo); + if (mounted) setState(() {}); + } + Future _saveSkipSecondsStart(int seconds) async { await _dbHelper.saveSkipSecondsStart(widget.podcastLocal.id, seconds); } @@ -70,6 +75,10 @@ class _PodcastSettingState extends State { return await _dbHelper.getAutoDownload(id); } + Future _getNeverUpdate(String id) async { + return await _dbHelper.getNeverUpdate(id); + } + Future _getSkipSecondStart(String id) async { return await _dbHelper.getSkipSecondsStart(id); } @@ -217,6 +226,21 @@ class _PodcastSettingState extends State { ), ); }), + FutureBuilder( + future: _getNeverUpdate(widget.podcastLocal.id), + initialData: false, + builder: (context, snapshot) { + return ListTile( + onTap: () => _setNeverUpdate(!snapshot.data), + leading: Icon(Icons.lock), + title: Text('Never update'), + trailing: Transform.scale( + scale: 0.9, + child: + Switch(value: snapshot.data, onChanged: _setNeverUpdate), + ), + ); + }), FutureBuilder( future: _getSkipSecondStart(widget.podcastLocal.id), initialData: 0, @@ -246,42 +270,42 @@ class _PodcastSettingState extends State { }, onConfirm: () async { await _saveSkipSecondsStart(_secondsStart); - setState(() => _showStartTimePicker = false); + if (mounted) setState(() => _showStartTimePicker = false); }, onChange: (value) => _secondsStart = value.inSeconds), - FutureBuilder( - future: _getSkipSecondEnd(widget.podcastLocal.id), - initialData: 0, - builder: (context, snapshot) => ListTile( - onTap: () { - _secondsEnd = 0; - setState(() { - _removeConfirm = false; - _markConfirm = false; - _showStartTimePicker = false; - _showEndTimePicker = !_showEndTimePicker; - }); - }, - leading: Icon(Icons.fast_rewind), - title: Text(s.skipSecondsAtEnd), - trailing: Padding( - padding: const EdgeInsets.only(right: 10.0), - child: Text(snapshot.data.toTime), - ), - ), - ), - if (_showEndTimePicker) - _TimePicker( - onCancel: () { - _secondsEnd = 0; - setState(() => _showEndTimePicker = false); - }, - onConfirm: () async { - await _saveSkipSecondsEnd(_secondsEnd); - setState(() => _showEndTimePicker = false); - }, - onChange: (value) => _secondsEnd = value.inSeconds, - ), + // FutureBuilder( + // future: _getSkipSecondEnd(widget.podcastLocal.id), + // initialData: 0, + // builder: (context, snapshot) => ListTile( + // onTap: () { + // _secondsEnd = 0; + // setState(() { + // _removeConfirm = false; + // _markConfirm = false; + // _showStartTimePicker = false; + // _showEndTimePicker = !_showEndTimePicker; + // }); + // }, + // leading: Icon(Icons.fast_rewind), + // title: Text(s.skipSecondsAtEnd), + // trailing: Padding( + // padding: const EdgeInsets.only(right: 10.0), + // child: Text(snapshot.data.toTime), + // ), + // ), + // ), + // if (_showEndTimePicker) + // _TimePicker( + // onCancel: () { + // _secondsEnd = 0; + // setState(() => _showEndTimePicker = false); + // }, + // onConfirm: () async { + // await _saveSkipSecondsEnd(_secondsEnd); + // setState(() => _showEndTimePicker = false); + // }, + // onChange: (value) => _secondsEnd = value.inSeconds, + // ), ListTile( onTap: () { if (_coverStatus != RefreshCoverStatus.start) { @@ -419,7 +443,6 @@ class _TimePicker extends StatelessWidget { children: [ SizedBox(height: 10), DurationPicker( - key: key, onChange: onChange, ), Row( diff --git a/lib/settings/data_backup.dart b/lib/settings/data_backup.dart index a9ff7ab..cd60062 100644 --- a/lib/settings/data_backup.dart +++ b/lib/settings/data_backup.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:developer' as developer; import 'dart:io'; +import 'package:device_info/device_info.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -9,6 +10,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_file_dialog/flutter_file_dialog.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:line_icons/line_icons.dart'; +import 'package:confetti/confetti.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; @@ -299,23 +301,37 @@ class _DataBackupState extends State { final loginInfo = snapshot.data; if (loginInfo.isNotEmpty) { return ListTile( - contentPadding: - const EdgeInsets.only(left: 70.0, right: 20), + contentPadding: const EdgeInsets.only( + left: 70.0, right: 20, top: 10, bottom: 10), onTap: _syncNow, title: Text(s.syncNow), + trailing: IconButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => _GpodderInfo())); + }, + icon: Icon(LineIcons.info_circle_solid), + ), subtitle: FutureBuilder>( future: _getSyncStatus(), initialData: [0, 0], builder: (context, snapshot) { final dateTime = snapshot.data[0]; final status = snapshot.data[1]; - return Wrap( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${s.lastUpdate}: ${dateTime.toDate(context)}'), SizedBox(width: 8), - Text('${s.status}: '), - _syncStauts(status), + Row( + children: [ + Text('${s.status}: '), + _syncStauts(status), + ], + ), ], ); }), @@ -323,6 +339,7 @@ class _DataBackupState extends State { } return Center(); }), + // ListTile( // onTap: () async { // final subscribeWorker = context.read(); @@ -402,7 +419,7 @@ class _DataBackupState extends State { ], ), ), - Divider(), + Divider(height: 1), Container( height: 30.0, padding: EdgeInsets.symmetric(horizontal: 70), @@ -568,13 +585,21 @@ class __LoginGpodderState extends State<_LoginGpodder> { var _username = ''; var _password = ''; LoginStatus _loginStatus; + ConfettiController _controller; @override void initState() { _loginStatus = LoginStatus.none; + _controller = ConfettiController(duration: Duration(seconds: 3)); super.initState(); } + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + final GlobalKey> _passwordFieldKey = GlobalKey>(); final GlobalKey _formKey = GlobalKey(); @@ -601,6 +626,7 @@ class __LoginGpodderState extends State<_LoginGpodder> { if (mounted) { setState(() { _loginStatus = LoginStatus.complete; + _controller.play(); }); } } @@ -635,9 +661,11 @@ class __LoginGpodderState extends State<_LoginGpodder> { var rssLink = rssExp.stringMatch(rss.xmlUrl); if (rssLink != null) { final dbHelper = DBHelper(); - final exist = dbHelper.checkPodcast(rssLink); + final exist = await dbHelper.checkPodcast(rssLink); if (exist == '') { - var item = SubscribeItem(rssLink, rss.text, group: 'Home'); + var item = SubscribeItem( + rssLink, rss.text == '' ? rssLink : rss.text, + group: 'Home'); await subscribeWorker.setSubscribeItem(item, syncGpodder: false); await Future.delayed(Duration(milliseconds: 200)); } @@ -757,14 +785,37 @@ class __LoginGpodderState extends State<_LoginGpodder> { _loginStatus == LoginStatus.complete ? SliverList( delegate: SliverChildListDelegate([ - Padding( - padding: const EdgeInsets.fromLTRB(40.0, 50, 40, 100), - child: Text( - s.gpodderLoginDes, - textAlign: TextAlign.center, - style: - context.textTheme.subtitle1.copyWith(height: 2), - ), + Stack( + children: [ + Padding( + padding: + const EdgeInsets.fromLTRB(40.0, 50, 40, 100), + child: Text( + s.gpodderLoginDes, + textAlign: TextAlign.center, + style: context.textTheme.subtitle1 + .copyWith(height: 2), + ), + ), + Align( + alignment: Alignment.center, + child: ConfettiWidget( + confettiController: _controller, + blastDirectionality: + BlastDirectionality.explosive, + emissionFrequency: 0.05, + maximumSize: Size(20, 10), + shouldLoop: false, + colors: const [ + Colors.green, + Colors.blue, + Colors.pink, + Colors.orange, + Colors.purple + ], + ), + ), + ], ), Center( child: OutlineButton( @@ -773,7 +824,7 @@ class __LoginGpodderState extends State<_LoginGpodder> { }, highlightedBorderColor: context.accentColor, child: Text(s.back)), - ) + ), ]), ) : Form( @@ -922,3 +973,152 @@ class _PasswordFieldState extends State { ); } } + +class _GpodderInfo extends StatefulWidget { + _GpodderInfo({Key key}) : super(key: key); + + @override + __GpodderInfoState createState() => __GpodderInfoState(); +} + +class __GpodderInfoState extends State<_GpodderInfo> { + final _gpodder = Gpodder(); + var _syncing = false; + + Future> _getLoginInfo() async { + final storage = KeyValueStorage(gpodderApiKey); + final androidInfo = await DeviceInfoPlugin().androidInfo; + final deviceInfo = await storage.getStringList(); + deviceInfo.add("Tsacdop on ${androidInfo.model}"); + return deviceInfo; + } + + Future _fullSync() async { + if (mounted) { + setState(() { + _syncing = true; + }); + } + final uploadStatus = await _gpodder.uploadSubscriptions(); + if (uploadStatus == 200) { + var subscribeWorker = context.read(); + var rssExp = RegExp(r'^(https?):\/\/(.*)'); + final opml = await _gpodder.getAllPodcast(); + if (opml != '') { + Map> data = PodcastsBackup.parseOMPL(opml); + for (var entry in data.entries) { + var list = entry.value.reversed; + for (var rss in list) { + var rssLink = rssExp.stringMatch(rss.xmlUrl); + if (rssLink != null) { + final dbHelper = DBHelper(); + final exist = await dbHelper.checkPodcast(rssLink); + if (exist == '') { + var item = SubscribeItem( + rssLink, rss.text == '' ? rssLink : rss.text, + group: 'Home'); + await subscribeWorker.setSubscribeItem(item, + syncGpodder: false); + await Future.delayed(Duration(milliseconds: 200)); + } + } + } + } + } + } + //await _syncNow(); + if (mounted) { + setState(() { + _syncing = false; + }); + } + } + + @override + Widget build(BuildContext context) { + final s = context.s; + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + systemNavigationBarColor: Theme.of(context).primaryColor, + systemNavigationBarIconBrightness: + Theme.of(context).accentColorBrightness, + ), + child: Scaffold( + resizeToAvoidBottomInset: true, + body: SafeArea( + top: false, + child: CustomScrollView( + slivers: [ + SliverAppBar( + brightness: Brightness.dark, + iconTheme: IconThemeData( + color: Colors.white, + ), + elevation: 0, + backgroundColor: context.accentColor, + expandedHeight: 200, + flexibleSpace: Container( + height: 200, + width: double.infinity, + color: context.accentColor, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + CircleAvatar( + minRadius: 50, + backgroundColor: context.primaryColor.withOpacity(0.3), + child: SizedBox( + height: 80, + width: 80, + child: Image.asset('assets/gpodder.png')), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(s.intergateWith('gpodder.net'), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold)), + ), + ], + ), + ), + ), + SliverList( + delegate: SliverChildListDelegate([ + FutureBuilder>( + future: _getLoginInfo(), + initialData: [], + builder: (context, snapshot) { + final deviceId = + snapshot.data.isNotEmpty ? snapshot.data[1] : ''; + final deviceName = + snapshot.data.isNotEmpty ? snapshot.data[3] : ''; + return Column( + children: [ + ListTile( + title: Text('Divice id'), + subtitle: Text(deviceId), + ), + ListTile( + title: Text('Divice name'), + subtitle: Text(deviceName), + ), + ], + ); + }), + ListTile( + onTap: _fullSync, + // contentPadding: + // const EdgeInsets.only(left: 70.0, right: 20), + title: Text('Full sync'), + subtitle: Text('If sync have error')), + ]), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/state/audio_state.dart b/lib/state/audio_state.dart index 818aec5..6de229c 100644 --- a/lib/state/audio_state.dart +++ b/lib/state/audio_state.dart @@ -218,7 +218,7 @@ class AudioPlayerNotifier extends ChangeNotifier { volumeGainStorage.saveInt(volumeGain); } - Future _initAudioData() async { + Future _initAudioData() async { var index = await playerHeightStorage.getInt(defaultValue: 0); _playerHeight = PlayerHeight.values[index]; _currentSpeed = await speedStorage.getDoubel(defaultValue: 1.0); @@ -250,8 +250,6 @@ class AudioPlayerNotifier extends ChangeNotifier { void addListener(VoidCallback listener) { super.addListener(listener); _initAudioData(); - // _queueUpdate = false; - // _getAutoSleepTimer(); AudioService.connect(); var running = AudioService.running; if (running) {} @@ -265,7 +263,7 @@ class AudioPlayerNotifier extends ChangeNotifier { if (_lastPostion > 0 && _queue.playlist.length > 0) { final episode = _queue.playlist.first; final duration = episode.duration * 1000; - final seekValue = duration != 0 ? _lastPostion / duration : 1; + final seekValue = duration != 0 ? _lastPostion / duration : 1.0; final history = PlayHistory( episode.title, episode.enclosureUrl, _lastPostion ~/ 1000, seekValue); await dbHelper.saveHistory(history); @@ -319,7 +317,7 @@ class AudioPlayerNotifier extends ChangeNotifier { } } - _startAudioService(int position, String url) async { + Future _startAudioService(int position, String url) async { _stopOnComplete = false; _sleepTimerMode = SleepTimerMode.undefined; _switchValue = 0; @@ -439,27 +437,26 @@ class AudioPlayerNotifier extends ChangeNotifier { }); AudioService.customEventStream.distinct().listen((event) async { - if (event is String && _episode.title == event) { + if (event is String && + _queue.playlist.isNotEmpty && + _queue.playlist.first.title == event) { _queue.delFromPlaylist(_episode); _lastPostion = 0; notifyListeners(); await positionStorage.saveInt(_lastPostion); - if (_lastPostion == 0) { + final history = PlayHistory(_episode.title, _episode.enclosureUrl, + _backgroundAudioPosition ~/ 1000, _seekSliderValue); + await dbHelper.saveHistory(history); + } + if (event is Map && event['playerRunning'] == false && _playerRunning) { + _playerRunning = false; + notifyListeners(); + if (_lastPostion > 0) { final history = PlayHistory(_episode.title, _episode.enclosureUrl, - _backgroundAudioPosition ~/ 1000, _seekSliderValue); + _lastPostion ~/ 1000, _seekSliderValue); await dbHelper.saveHistory(history); } - } - if (event is Map && event['playerRunning'] == false) { - if (_playerRunning) { - _playerRunning = false; - notifyListeners(); - if (_lastPostion > 0) { - final history = PlayHistory(_episode.title, _episode.enclosureUrl, - _lastPostion ~/ 1000, _seekSliderValue); - await dbHelper.saveHistory(history); - } - } + _episode = null; } }); @@ -531,7 +528,7 @@ class AudioPlayerNotifier extends ChangeNotifier { Future addNewEpisode(List group) async { var newEpisodes = []; - if (group.first == 'All') { + if (group.isEmpty) { newEpisodes = await dbHelper.getRecentNewRssItem(); } else { newEpisodes = await dbHelper.getGroupNewRssItem(group); @@ -541,14 +538,14 @@ class AudioPlayerNotifier extends ChangeNotifier { await addToPlaylist(episode); } } - if (group.first == 'All') { + if (group.isEmpty) { await dbHelper.removeAllNewMark(); } else { await dbHelper.removeGroupNewMark(group); } } - updateMediaItem(EpisodeBrief episode) async { + Future updateMediaItem(EpisodeBrief episode) async { if (episode.enclosureUrl == episode.mediaId) { var index = _queue.playlist .indexWhere((item) => item.enclosureUrl == episode.enclosureUrl); @@ -867,7 +864,8 @@ class AudioPlayerTask extends BackgroundAudioTask { await AudioServiceBackground.setQueue(_queue); if (_queue.length == 0 || _stopAtEnd) { _skipState = null; - onStop(); + await Future.delayed(Duration(milliseconds: 200)); + await onStop(); } else { await AudioServiceBackground.setQueue(_queue); await AudioServiceBackground.setMediaItem(mediaItem); @@ -878,13 +876,7 @@ class AudioPlayerTask extends BackgroundAudioTask { mediaItem.copyWith(duration: duration)); } _skipState = null; - // Resume playback if we were playing - // if (_playing) { - //onPlay(); _playFromStart(); - // } else { - // _setState(state: BasicPlaybackState.paused); - // } } } @@ -922,10 +914,11 @@ class AudioPlayerTask extends BackgroundAudioTask { _session.setActive(true); if (mediaItem.extras['skipSecondsStart'] > 0 || mediaItem.extras['skipSecondsEnd'] > 0) { - //_audioPlayer.seek(Duration(seconds: mediaItem.extras['skip'])); - _audioPlayer.setClip( - start: Duration(seconds: mediaItem.extras['skipSecondsStart']), - end: Duration(seconds: mediaItem.extras['skipSecondsEnd'])); + _audioPlayer + .seek(Duration(seconds: mediaItem.extras['skipSecondsStart'])); + // await _audioPlayer.setClip( + // start: Duration(seconds: mediaItem.extras['skipSecondsStart']), + // ); } if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting || _audioPlayer.playbackEvent.state != AudioPlaybackState.none) { diff --git a/lib/state/podcast_group.dart b/lib/state/podcast_group.dart index 6cfaa7a..63e3614 100644 --- a/lib/state/podcast_group.dart +++ b/lib/state/podcast_group.dart @@ -152,14 +152,14 @@ class SubscribeItem { ///Podcast group, default Home. String group; - ///sync to gpodder - bool syncWithGpodder; - SubscribeItem(this.url, this.title, - {this.subscribeState = SubscribeState.none, - this.id = '', - this.imgUrl = '', - this.group = '', - this.syncWithGpodder = true}); + SubscribeItem( + this.url, + this.title, { + this.subscribeState = SubscribeState.none, + this.id = '', + this.imgUrl = '', + this.group = '', + }); } class GroupList extends ChangeNotifier { @@ -219,7 +219,7 @@ class GroupList extends ChangeNotifier { } Future _start() async { - if (_created == false) { + if (!_created) { await _createIsolate(); _created = true; listen(); @@ -229,7 +229,6 @@ class GroupList extends ChangeNotifier { _subscribeItem.title, _subscribeItem.imgUrl, _subscribeItem.group, - _subscribeItem.syncWithGpodder ]); } } @@ -250,7 +249,6 @@ class GroupList extends ChangeNotifier { _subscribeItem.title, _subscribeItem.imgUrl, _subscribeItem.group, - _subscribeItem.syncWithGpodder ]); } else if (message is List) { _setCurrentSubscribeItem(SubscribeItem( @@ -717,7 +715,7 @@ Future subIsolateEntryPoint(SendPort sendPort) async { subReceivePort.distinct().listen((message) { if (message is List) { items.add(SubscribeItem(message[0], message[1], - imgUrl: message[2], group: message[3], syncWithGpodder: message[4])); + imgUrl: message[2], group: message[3])); if (!_running) { _subscribe(items.first); _running = true; diff --git a/lib/state/refresh_podcast.dart b/lib/state/refresh_podcast.dart index 4fa002b..438cceb 100644 --- a/lib/state/refresh_podcast.dart +++ b/lib/state/refresh_podcast.dart @@ -34,8 +34,12 @@ class RefreshWorker extends ChangeNotifier { refreshIsolateEntryPoint, receivePort.sendPort); } - void _listen() { + void _listen(List podcasts) { receivePort.distinct().listen((message) { + if (message is SendPort) { + refreshSendPort = message; + refreshSendPort.send(podcasts); + } if (message is List) { _currentRefreshItem = RefreshItem(message[0], RefreshState.values[message[1]]); @@ -51,11 +55,11 @@ class RefreshWorker extends ChangeNotifier { }); } - Future start() async { + Future start(List podcasts) async { if (!_created) { _complete = false; - _createIsolate(); - _listen(); + await _createIsolate(); + _listen(podcasts); _created = true; } } @@ -68,14 +72,30 @@ class RefreshWorker extends ChangeNotifier { } Future refreshIsolateEntryPoint(SendPort sendPort) async { - var refreshstorage = KeyValueStorage(refreshdateKey); - await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch); - var dbHelper = DBHelper(); - var podcastList = await dbHelper.getPodcastLocalAll(); - for (var podcastLocal in podcastList) { - sendPort.send([podcastLocal.title, 1]); - var updateCount = await dbHelper.updatePodcastRss(podcastLocal); - developer.log('Refresh ${podcastLocal.title}$updateCount'); + var refreshReceivePort = ReceivePort(); + sendPort.send(refreshReceivePort.sendPort); + var _dbHelper = DBHelper(); + + Future _refreshAll(List podcasts) async { + var podcastList; + if (podcasts.isEmpty) { + var refreshstorage = KeyValueStorage(refreshdateKey); + await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch); + podcastList = await _dbHelper.getPodcastLocalAll(updateOnly: true); + } else { + podcastList = await _dbHelper.getPodcastLocal(podcasts, updateOnly: true); + } + for (var podcastLocal in podcastList) { + sendPort.send([podcastLocal.title, 1]); + var updateCount = await _dbHelper.updatePodcastRss(podcastLocal); + developer.log('Refresh ${podcastLocal.title}$updateCount'); + } + sendPort.send("done"); } - sendPort.send("done"); + + refreshReceivePort.distinct().listen((message) { + if (message is List) { + _refreshAll(message); + } + }); } diff --git a/lib/state/setting_state.dart b/lib/state/setting_state.dart index c68a066..6274b55 100644 --- a/lib/state/setting_state.dart +++ b/lib/state/setting_state.dart @@ -21,7 +21,7 @@ void callbackDispatcher() { if (Platform.isAndroid) { Workmanager.executeTask((task, inputData) async { var dbHelper = DBHelper(); - var podcastList = await dbHelper.getPodcastLocalAll(); + var podcastList = await dbHelper.getPodcastLocalAll(updateOnly: false); //lastWork is a indicator for if the app was opened since last backgroundwork //if the app wes opend,then the old marked new episode would be marked not new. var lastWorkStorage = KeyValueStorage(lastWorkKey); diff --git a/pubspec.yaml b/pubspec.yaml index 37fc948..3b6ca70 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tsacdop description: An open source podacasts player. -version: 0.4.17+34 +version: 0.4.18+35 environment: sdk: ">=2.6.0 <3.0.0" @@ -15,7 +15,8 @@ dependencies: audio_session: ^0.0.7 cached_network_image: ^2.3.2+1 color_thief_flutter: ^1.0.2 - cookie_jar: ^1.0.0 + confetti: ^0.5.4+1 + cookie_jar: ^1.0.1 cupertino_icons: ^1.0.0 connectivity: ^0.4.9 device_info: ^0.4.2+7 @@ -25,7 +26,7 @@ dependencies: effective_dart: ^1.2.4 equatable: ^1.2.5 feature_discovery: ^0.10.0 - file_picker: ^2.0.0 + file_picker: ^2.0.1+2 flutter_html: ^0.11.1 flutter_downloader: ^1.5.0 fluttertoast: ^4.0.0 @@ -36,18 +37,18 @@ dependencies: fl_chart: ^0.11.1 marquee: ^1.3.1 google_fonts: ^1.1.0 - image: ^2.1.14 + image: ^2.1.17 intl: ^0.16.1 - json_serializable: ^3.4.1 - json_annotation: ^3.0.1 - path_provider: ^1.6.16 + json_serializable: ^3.5.0 + json_annotation: ^3.1.0 + path_provider: ^1.6.18 permission_handler: ^5.0.1 provider: ^4.3.2 rxdart: ^0.24.1 sqflite: ^1.3.1 - shared_preferences: ^0.5.10 + shared_preferences: ^0.5.12 tuple: ^1.0.3 - url_launcher: ^5.6.0 + url_launcher: ^5.7.1 uuid: ^2.2.2 xml: ^4.2.0 workmanager: ^0.2.3