Minor change

This commit is contained in:
stonegate 2020-09-26 22:26:25 +08:00
parent 93ed9d3513
commit 6086db0f8c
14 changed files with 945 additions and 635 deletions

View File

@ -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

View File

@ -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(

View File

@ -526,12 +526,13 @@ class _PlaylistButton extends StatefulWidget {
class __PlaylistButtonState extends State<_PlaylistButton> {
bool _loadPlay;
_getPlaylist() async {
await Provider.of<AudioPlayerNotifier>(context, listen: false)
.loadPlaylist();
setState(() {
_loadPlay = true;
});
Future<void> _getPlaylist() async {
await context.read<AudioPlayerNotifier>().loadPlaylist();
if (mounted) {
setState(() {
_loadPlay = true;
});
}
}
@override
@ -545,157 +546,158 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Widget build(BuildContext context) {
var audio = context.watch<AudioPlayerNotifier>();
final s = context.s;
return MyPopupMenuButton<int>(
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<AudioPlayerNotifier, Tuple3<bool, Playlist, int>>(
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<int>(context);
},
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
),
Stack(
alignment: Alignment.center,
children: <Widget>[
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<int>(
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<AudioPlayerNotifier, Tuple3<bool, Playlist, int>>(
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<int>(context);
},
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
),
Stack(
alignment: Alignment.center,
children: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
Future _updateRssItem() async {
final refreshWorker = context.read<RefreshWorker>();
refreshWorker.start(_group);
await Future.delayed(Duration(seconds: 1));
Fluttertoast.showToast(
msg: 'Refresh started',
gravity: ToastGravity.BOTTOM,
);
}
Future<List<EpisodeBrief>> _getRssItem(int top, List<String> group,
{bool hideListened}) async {
var storage = KeyValueStorage(recentLayoutKey);
@ -718,7 +733,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
}
var dbHelper = DBHelper();
List<EpisodeBrief> 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<int> _getUpdateCounts(List<String> group) async {
var dbHelper = DBHelper();
var episodes = <EpisodeBrief>[];
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<String>('update'),
physics: const AlwaysScrollableScrollPhysics(),
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 40,
color: context.primaryColor,
child: Material(
color: Colors.transparent,
child: Row(
children: <Widget>[
Consumer<GroupList>(
builder: (context, groupList,
child) =>
Material(
color: Colors.transparent,
child:
child: RefreshIndicator(
key: _refreshIndicatorKey,
color: context.accentColor,
onRefresh: () async {
await _updateRssItem();
},
child: CustomScrollView(
key: PageStorageKey<String>('update'),
physics:
const AlwaysScrollableScrollPhysics(),
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 40,
color: context.primaryColor,
child: Material(
color: Colors.transparent,
child: Row(
children: <Widget>[
Consumer<GroupList>(
builder: (context, groupList,
child) =>
PopupMenuButton<String>(
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<int>(
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<int>(
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();
},
);

View File

@ -361,11 +361,8 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
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<ScrollPodcasts>
.podcasts
.map<Widget>((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<AudioPlayerNotifier, bool>(
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),
)),
),
],
),

View File

@ -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<PopupMenu> {
Future<String> _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<PopupMenu> {
} 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<PopupMenu> {
} 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) {

View File

@ -69,7 +69,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
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<PlaylistPage> {
),
),
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: <Widget>[
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: <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
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: <Widget>[
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<EpisodeBrief>,
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<List<EpisodeBrief>,
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)
],
),
);
);
}
}),
)

View File

@ -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<List<PodcastLocal>> getPodcastLocal(List<String> podcasts) async {
Future<List<PodcastLocal>> getPodcastLocal(List<String> podcasts,
{bool updateOnly = false}) async {
var dbClient = await database;
var podcastLocal = <PodcastLocal>[];
for (var s in podcasts) {
List<Map> 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<List<PodcastLocal>> getPodcastLocalAll() async {
Future<List<PodcastLocal>> getPodcastLocalAll(
{bool updateOnly = false}) async {
var dbClient = await database;
List<Map> list = await dbClient.rawQuery(
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, provider, link FROM PodcastLocal ORDER BY add_date DESC');
List<Map> 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 = <PodcastLocal>[];
@ -131,6 +157,21 @@ class DBHelper {
return 0;
}
Future<bool> getNeverUpdate(String id) async {
var dbClient = await database;
List<Map> list = await dbClient
.rawQuery('SELECT never_update FROM PodcastLocal WHERE id = ?', [id]);
if (list.isNotEmpty) return list.first['never_update'] == 1;
return false;
}
Future<int> 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<int> getPodcastUpdateCounts(String id) async {
var dbClient = await database;
List<Map> list = await dbClient.rawQuery(

View File

@ -58,6 +58,11 @@ class _PodcastSettingState extends State<PodcastSetting> {
if (mounted) setState(() {});
}
Future<void> _setNeverUpdate(bool boo) async {
await _dbHelper.saveNeverUpdate(widget.podcastLocal.id, boo: boo);
if (mounted) setState(() {});
}
Future<void> _saveSkipSecondsStart(int seconds) async {
await _dbHelper.saveSkipSecondsStart(widget.podcastLocal.id, seconds);
}
@ -70,6 +75,10 @@ class _PodcastSettingState extends State<PodcastSetting> {
return await _dbHelper.getAutoDownload(id);
}
Future<bool> _getNeverUpdate(String id) async {
return await _dbHelper.getNeverUpdate(id);
}
Future<int> _getSkipSecondStart(String id) async {
return await _dbHelper.getSkipSecondsStart(id);
}
@ -217,6 +226,21 @@ class _PodcastSettingState extends State<PodcastSetting> {
),
);
}),
FutureBuilder<bool>(
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<int>(
future: _getSkipSecondStart(widget.podcastLocal.id),
initialData: 0,
@ -246,42 +270,42 @@ class _PodcastSettingState extends State<PodcastSetting> {
},
onConfirm: () async {
await _saveSkipSecondsStart(_secondsStart);
setState(() => _showStartTimePicker = false);
if (mounted) setState(() => _showStartTimePicker = false);
},
onChange: (value) => _secondsStart = value.inSeconds),
FutureBuilder<int>(
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<int>(
// 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(

View File

@ -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<DataBackup> {
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<List<int>>(
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<DataBackup> {
}
return Center();
}),
// ListTile(
// onTap: () async {
// final subscribeWorker = context.read<GroupList>();
@ -402,7 +419,7 @@ class _DataBackupState extends State<DataBackup> {
],
),
),
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<FormFieldState<String>> _passwordFieldKey =
GlobalKey<FormFieldState<String>>();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@ -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<PasswordField> {
);
}
}
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<List<String>> _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<void> _fullSync() async {
if (mounted) {
setState(() {
_syncing = true;
});
}
final uploadStatus = await _gpodder.uploadSubscriptions();
if (uploadStatus == 200) {
var subscribeWorker = context.read<GroupList>();
var rssExp = RegExp(r'^(https?):\/\/(.*)');
final opml = await _gpodder.getAllPodcast();
if (opml != '') {
Map<String, List<OmplOutline>> 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<SystemUiOverlayStyle>(
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<List<String>>(
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')),
]),
),
],
),
),
),
);
}
}

View File

@ -218,7 +218,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
volumeGainStorage.saveInt(volumeGain);
}
Future _initAudioData() async {
Future<void> _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<void> _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<void> addNewEpisode(List<String> group) async {
var newEpisodes = <EpisodeBrief>[];
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<void> 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) {

View File

@ -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<void> subIsolateEntryPoint(SendPort sendPort) async {
subReceivePort.distinct().listen((message) {
if (message is List<dynamic>) {
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;

View File

@ -34,8 +34,12 @@ class RefreshWorker extends ChangeNotifier {
refreshIsolateEntryPoint, receivePort.sendPort);
}
void _listen() {
void _listen(List<String> 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<void> start() async {
Future<void> start(List<String> podcasts) async {
if (!_created) {
_complete = false;
_createIsolate();
_listen();
await _createIsolate();
_listen(podcasts);
_created = true;
}
}
@ -68,14 +72,30 @@ class RefreshWorker extends ChangeNotifier {
}
Future<void> 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<void> _refreshAll(List<String> 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<dynamic>) {
_refreshAll(message);
}
});
}

View File

@ -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);

View File

@ -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