Change add new episode to playlist icon

Add duration picker initial duration
This commit is contained in:
stonegate 2020-04-24 01:46:36 +08:00
parent 0040513380
commit 60e066d2a9
15 changed files with 205 additions and 96 deletions

View File

@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.android.tools.build:gradle:3.6.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -91,11 +91,13 @@ class Playlist {
addToPlayList(EpisodeBrief episodeBrief) async {
_playlist.add(episodeBrief);
await savePlaylist();
dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl);
}
addToPlayListAt(EpisodeBrief episodeBrief, int index) async {
_playlist.insert(index, episodeBrief);
await savePlaylist();
dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl);
}
Future<int> delFromPlaylist(EpisodeBrief episodeBrief) async {
@ -188,10 +190,10 @@ class AudioPlayerNotifier extends ChangeNotifier {
_setAutoAdd();
}
Future _getAutoAdd() async {
int i = await autoAddStorage.getInt();
_autoAdd = i == 0 ? false : true;
}
//Future _getAutoAdd() async {
// int i = await autoAddStorage.getInt();
// _autoAdd = i == 0 ? false : true;
//}
Future _setAutoAdd() async {
await autoAddStorage.saveInt(_autoAdd ? 1 : 0);
@ -255,6 +257,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
notifyListeners();
//await _queue.savePlaylist();
_startAudioService(startPosition);
if (episodeNew.isNew == 1)
dbHelper.removeEpisodeNewMark(episodeNew.enclosureUrl);
}
}
@ -571,7 +575,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
bool get hasNext => _queue.length > 0;
MediaItem get mediaItem => _queue.first;
MediaItem get mediaItem => _queue.length > 0 ? _queue.first : null;
BasicPlaybackState _stateToBasicState(AudioPlaybackState state) {
switch (state) {
@ -644,19 +648,21 @@ class AudioPlayerTask extends BackgroundAudioTask {
Future<void> onSkipToNext() async {
_skipState = BasicPlaybackState.skippingToNext;
await _audioPlayer.stop();
_queue.removeAt(0);
if (_queue.length > 0) _queue.removeAt(0);
await AudioServiceBackground.setQueue(_queue);
// }
if (_queue.length == 0 || _stopAtEnd) {
_skipState = null;
onStop();
} else {
AudioServiceBackground.setQueue(_queue);
AudioServiceBackground.setMediaItem(mediaItem);
await _audioPlayer.setUrl(mediaItem.id);
Duration duration = await _audioPlayer.durationFuture ?? Duration.zero;
AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration.inMilliseconds));
await AudioServiceBackground.setQueue(_queue);
await AudioServiceBackground.setMediaItem(mediaItem);
await _audioPlayer.setUrl(mediaItem.id);
print(mediaItem.title);
Duration duration = await _audioPlayer.durationFuture;
if(duration != null)
await AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration.inMilliseconds));
_skipState = null;
// Resume playback if we were playing
// if (_playing) {
@ -674,9 +680,10 @@ class AudioPlayerTask extends BackgroundAudioTask {
_playing = true;
// await AudioServiceBackground.setQueue(_queue);
await _audioPlayer.setUrl(mediaItem.id);
Duration duration = await _audioPlayer.durationFuture;
AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration.inMilliseconds));
var duration = await _audioPlayer.durationFuture;
if (duration != null)
await AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration.inMilliseconds));
}
// if (mediaItem.extras['skip'] > 0) {
// await _audioPlayer.setClip(
@ -684,8 +691,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
// print(mediaItem.extras['skip']);
// print('set clip success');
// }
_playing = true;
_audioPlayer.play();
else
_playing = true;
await _audioPlayer.play();
if (mediaItem.extras['skip'] > 0) {
_audioPlayer.seek(Duration(seconds: mediaItem.extras['skip']));
}
@ -716,7 +724,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
await _audioPlayer.stop();
_setState(state: BasicPlaybackState.stopped);
await Future.delayed(Duration(milliseconds: 500));
_completer.complete();
_completer?.complete();
}
@override

View File

@ -56,6 +56,7 @@ class EpisodeBrief {
title: title,
artist: feedTitle,
album: feedTitle,
// duration: 0,
artUri: 'file://$imagePath',
extras: {'skip': skipSeconds});
}

View File

@ -182,12 +182,11 @@ class GroupList extends ChangeNotifier {
}
Future updatePodcast(String id) async {
List<int> counts = await dbHelper.getPodcastCounts(id);
int counts = await dbHelper.getPodcastCounts(id);
_groups.forEach((group) {
if (group.podcastList.contains(id)) {
group.podcasts.firstWhere((podcast) => podcast.id == id)
..upateCount = counts[0]
..episodeCount = counts[1];
..episodeCount = counts;
notifyListeners();
}
});

View File

@ -34,7 +34,7 @@ class SubscribeWorker extends ChangeNotifier {
SubscribeItem _currentSubscribeItem = SubscribeItem('', '');
bool _created = false;
setSubscribeItem(SubscribeItem item) async{
setSubscribeItem(SubscribeItem item) async {
_subscribeItem = item;
await _start();
}
@ -155,7 +155,7 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
sendPort.send([item.title, item.url, 3, uuid]);
await Future.delayed(Duration(seconds: 5));
await Future.delayed(Duration(seconds: 2));
sendPort.send([item.title, item.url, 4]);
items.removeWhere((element) => element.url == item.url);
@ -165,7 +165,7 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
sendPort.send("done");
} else {
sendPort.send([item.title, item.url, 5]);
await Future.delayed(Duration(seconds: 5));
await Future.delayed(Duration(seconds: 2));
sendPort.send([item.title, item.url, 4]);
items.removeWhere((element) => element.url == item.url);
if (items.length > 0) {
@ -175,7 +175,7 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
}
} else {
sendPort.send([item.title, item.url, 6]);
await Future.delayed(Duration(seconds: 5));
await Future.delayed(Duration(seconds: 2));
sendPort.send([item.title, item.url, 4]);
items.removeWhere((element) => element.url == item.url);
if (items.length > 0) {

View File

@ -72,14 +72,14 @@ class AboutApp extends StatelessWidget {
image: AssetImage('assets/logo.png'),
height: 80,
),
Text('Version: 0.1.9'),
Text('Version: 0.2.0'),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 50),
child: Text(
'Tsacdop is a podcast player developed in flutter, a simply beautiful and friendly app.',
'Tsacdop is a podcast player developed in flutter, a clean, simply beautiful and friendly app.',
textAlign: TextAlign.center,
),
),

View File

@ -34,7 +34,7 @@ class Import extends StatelessWidget {
SubscribeItem item = subscribeWorker.currentSubscribeItem;
switch (item.subscribeState) {
case SubscribeState.start:
return importColumn("Subscribe: ${item.title}", context);
return importColumn("Subscribe ${item.title}", context);
case SubscribeState.subscribe:
groupList.subscribeNewPodcast(item.id);
return importColumn("Fetch data ${item.title}", context);

View File

@ -507,33 +507,42 @@ class _PlayerWidgetState extends State<PlayerWidget> {
Expanded(
flex: 2,
child: Selector<AudioPlayerNotifier,
Tuple2<BasicPlaybackState, double>>(
selector: (_, audio) => Tuple2(
Tuple3<BasicPlaybackState, double, String>>(
selector: (_, audio) => Tuple3(
audio.audioState,
(audio.backgroundAudioDuration -
audio.backgroundAudioPosition) /
1000),
1000,
audio.remoteErrorMessage),
builder: (_, data, __) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: data.item1 == BasicPlaybackState.buffering ||
data.item1 == BasicPlaybackState.connecting
? Text(
'Buffring...',
style: TextStyle(
color: Theme.of(context).accentColor),
)
: Row(
children: <Widget>[
Text(
_stringForSeconds(data.item2) ?? '',
child: data.item3 != null
? Text(data.item3,
style: const TextStyle(
color: const Color(0xFFFF0000)))
: data.item1 == BasicPlaybackState.buffering ||
data.item1 ==
BasicPlaybackState.connecting ||
data.item1 ==
BasicPlaybackState.skippingToNext ||
data.item1 == BasicPlaybackState.stopped
? Text(
'Buffring...',
style: TextStyle(
color: Theme.of(context).accentColor),
)
: Row(
children: <Widget>[
Text(
_stringForSeconds(data.item2) ?? '',
),
Text(
' Left',
),
],
),
Text(
' Left',
),
],
),
);
},
),
@ -1278,7 +1287,10 @@ class _ControlPanelState extends State<ControlPanel>
BasicPlaybackState
.connecting ||
data.audioState ==
BasicPlaybackState.none
BasicPlaybackState.none ||
data.audioState ==
BasicPlaybackState
.skippingToNext
? 'Buffring...'
: '',
style: TextStyle(

View File

@ -29,10 +29,9 @@ class ScrollPodcasts extends StatefulWidget {
class _ScrollPodcastsState extends State<ScrollPodcasts> {
int _groupIndex;
Future<int> getPodcastCounts(String id) async {
Future<int> getPodcastUpdateCounts(String id) async {
var dbHelper = DBHelper();
List<int> list = await dbHelper.getPodcastCounts(id);
return list.first;
return await dbHelper.getPodcastUpdateCounts(id);
}
@override
@ -268,7 +267,7 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
"${podcastLocal.imagePath}")),
),
FutureBuilder<int>(
future: getPodcastCounts(
future: getPodcastUpdateCounts(
podcastLocal.id),
initialData: 0,
builder: (context, snapshot) {

View File

@ -11,16 +11,16 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:line_icons/line_icons.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tsacdop/class/audiostate.dart';
import 'package:tsacdop/class/episodebrief.dart';
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
import 'package:tsacdop/util/episodegrid.dart';
import 'package:tsacdop/util/mypopupmenu.dart';
import 'package:tsacdop/util/context_extension.dart';
import 'package:tsacdop/util/custompaint.dart';
import '../class/audiostate.dart';
import '../class/episodebrief.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../util/episodegrid.dart';
import '../util/mypopupmenu.dart';
import '../util/context_extension.dart';
import '../util/custompaint.dart';
import 'package:tsacdop/home/appbar/importompl.dart';
import 'package:tsacdop/home/audioplayer.dart';
import '../home/appbar/importompl.dart';
import '../home/audioplayer.dart';
import 'home_groups.dart';
import 'download_list.dart';
@ -466,14 +466,24 @@ class _RecentUpdateState extends State<_RecentUpdate>
future: _getUpdateCounts(_group),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data > 0
return snapshot.data != 0
? Material(
color: Colors.transparent,
child: IconButton(
tooltip:
'Add new episodes to playlist',
icon: Icon(
LineIcons.tasks_solid),
icon:
// Icon(Icons.playlist_add),
SizedBox(
height: 16,
width: 21,
child: CustomPaint(
painter: AddToPlaylistPainter(
context
.textTheme
.bodyText1
.color,
Colors.red))),
onPressed: () async {
await audio
.addNewEpisode(_group);
@ -488,7 +498,23 @@ class _RecentUpdateState extends State<_RecentUpdate>
);
}),
)
: Center();
: IconButton(
tooltip:
'Add new episodes to playlist',
icon:
// Icon(Icons.playlist_add),
SizedBox(
height: 16,
width: 21,
child: CustomPaint(
painter:
AddToPlaylistPainter(
context.textTheme
.bodyText1.color,
context.textTheme
.bodyText1.color,
))),
onPressed: () {});
}),
Material(
color: Colors.transparent,

View File

@ -102,12 +102,20 @@ class DBHelper {
return podcastLocal;
}
Future<List<int>> getPodcastCounts(String id) async {
Future<int> getPodcastCounts(String id) async {
var dbClient = await database;
List<Map> list = await dbClient.rawQuery(
'SELECT update_count, episode_count FROM PodcastLocal WHERE id = ?',
'SELECT episode_count FROM PodcastLocal WHERE id = ?',
[id]);
return [list.first['update_count'], list.first['episode_count']];
return list.first['episode_count'];
}
Future<int> getPodcastUpdateCounts(String id) async {
var dbClient = await database;
List<Map> list = await dbClient.rawQuery(
'SELECt count(*) as count FROM Episodes WHERE feed_id = ? AND is_new = 1',
[id]);
return list.first['count'];
}
Future<int> getSkipSeconds(String id) async {
@ -518,7 +526,7 @@ class DBHelper {
return 0;
} catch (e) {
print(e);
return 0;
return -1;
}
}
@ -577,7 +585,6 @@ class DBHelper {
return episodes;
}
Future<List<EpisodeBrief>> getNewEpisodes(String id) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
@ -820,6 +827,12 @@ class DBHelper {
return 0;
}
Future<int> removeEpisodeNewMark(String url) async {
var dbClient = await database;
return await dbClient.rawUpdate(
"UPDATE Episodes SET is_new = 0 WHERE enclosure_url = ?", [url]);
}
Future<List<EpisodeBrief>> getLikedRssItem(int i, int sortBy) async {
var dbClient = await database;
List<EpisodeBrief> episodes = List();

View File

@ -38,22 +38,21 @@ class _PodcastDetailState extends State<PodcastDetail> {
List<PodcastHost> hosts;
Future _updateRssItem(PodcastLocal podcastLocal) async {
var dbHelper = DBHelper();
try {
final result = await dbHelper.updatePodcastRss(podcastLocal);
if (result == 0) {
Fluttertoast.showToast(
msg: 'No Update',
gravity: ToastGravity.TOP,
);
} else {
Fluttertoast.showToast(
msg: 'Updated $result Episodes',
gravity: ToastGravity.TOP,
);
Provider.of<GroupList>(context, listen: false)
.updatePodcast(podcastLocal.id);
}
} catch (e) {
final result = await dbHelper.updatePodcastRss(podcastLocal);
if (result == 0) {
Fluttertoast.showToast(
msg: 'No Update',
gravity: ToastGravity.TOP,
);
} else if (result > 0) {
Fluttertoast.showToast(
msg: 'Updated $result Episodes',
gravity: ToastGravity.TOP,
);
Provider.of<GroupList>(context, listen: false)
.updatePodcast(podcastLocal.id);
} else {
Fluttertoast.showToast(
msg: 'Update failed, network error',
gravity: ToastGravity.TOP,
@ -217,7 +216,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
color: Theme.of(context).accentColor,
onRefresh: () async {
await _updateRssItem(widget.podcastLocal);
// audio.addNewEpisode(widget.podcastLocal.id);
// audio.addNewEpisode(widget.podcastLocal.id);
},
child: Stack(
children: <Widget>[

View File

@ -81,10 +81,12 @@ class _PodcastCardState extends State<PodcastCard>
Animation _animation;
double _value;
int _seconds;
int _skipSeconds;
Future<int> getSkipSecond(String id) async {
var dbHelper = DBHelper();
int seconds = await dbHelper.getSkipSeconds(id);
_skipSeconds = seconds;
return seconds;
}
@ -373,12 +375,15 @@ class _PodcastCardState extends State<PodcastCard>
left: 20,
right: 100,
bottom: 20),
title: Text('Skip seconds at the beginning'),
title:
Text('Skip seconds at the beginning'),
content: DurationPicker(
duration: Duration.zero,
duration: Duration(
seconds: _skipSeconds ?? 0),
onChange: (value) =>
_seconds = value.inSeconds,
),
// content: Text('test'),
actionsPadding: EdgeInsets.all(10),
actions: <Widget>[
@ -402,7 +407,8 @@ class _PodcastCardState extends State<PodcastCard>
},
child: Text(
'CONFIRM',
style: TextStyle(color: context.accentColor),
style: TextStyle(
color: context.accentColor),
),
)
],

View File

@ -154,6 +154,52 @@ class ListenedAllPainter extends CustomPainter {
}
}
//Add new episode to palylist
class AddToPlaylistPainter extends CustomPainter {
Color _color;
Color _textColor;
AddToPlaylistPainter(this._color, this._textColor);
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
..color = _color
..strokeWidth = 1
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Path _path = Path();
_path.moveTo(0, 0);
_path.lineTo(size.width * 4 / 7, 0);
_path.moveTo(0, size.height / 3);
_path.lineTo(size.width * 4 / 7, size.height / 3);
_path.moveTo(0, size.height * 2 / 3);
_path.lineTo(size.width * 3 / 7, size.height * 2 / 3);
//_path.moveTo(size.width * 3 / 7, size.height * 2 / 3);
//_path.lineTo(size.width, size.height * 2 / 3);
//_path.moveTo(size.width * 5 / 7, size.height / 3);
//_path.lineTo(size.width * 5 / 7, size.height);
// _path.moveTo(size.width*5/7, size.height/4);
// _path.lineTo(size.width*11/14, 0);
// _path.lineTo(size.width*13/14, size.height/4);
// _path.lineTo(size.width, 0);
var textPainter = TextPainter(
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
text: TextSpan(
text: 'N',
style: TextStyle(
fontStyle: FontStyle.italic, color: _textColor, fontSize: 10),
))
..layout();
textPainter.paint(canvas, Offset(size.width * 4 / 7, size.height / 3));
canvas.drawPath(_path, _paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
//Wave play indicator
class WavePainter extends CustomPainter {
double _fraction;

View File

@ -33,7 +33,7 @@ dev_dependencies:
path_provider: ^1.6.5
color_thief_flutter: ^1.0.2
provider: ^4.0.5
google_fonts: ^0.5.0+1
google_fonts: ^1.0.0
dio: ^3.0.9
file_picker: ^1.6.3+1
xml: ^3.5.0
@ -44,14 +44,14 @@ dev_dependencies:
intl: ^0.16.1
url_launcher: ^5.4.2
image: ^2.1.12
shared_preferences: ^0.5.6+1
shared_preferences: ^0.5.7
uuid: ^2.0.4
tuple: ^1.0.3
cached_network_image: ^2.1.0+1
workmanager: ^0.2.2
flutter_colorpicker: ^0.3.2
flutter_colorpicker: ^0.3.4
app_settings: ^3.0.1
fl_chart: ^0.9.0
fl_chart: ^0.9.3
audio_service: ^0.7.2
just_audio:
git:
@ -63,7 +63,7 @@ dev_dependencies:
flutter_linkify: ^3.1.0
extended_nested_scroll_view: ^0.4.0
connectivity: ^0.4.8+2
flare_flutter: ^2.0.1
flare_flutter: ^2.0.3
rxdart: ^0.24.0
flutter_isolate: ^1.0.0+11