Update podcast setting menu, add skip seconds at end.

This commit is contained in:
stonegate 2020-09-18 23:01:00 +08:00
parent fb31758f50
commit b271965009
17 changed files with 425 additions and 235 deletions

View File

@ -308,6 +308,7 @@ class MessageLookup extends MessageLookupByLibrary {
"share" : MessageLookupByLibrary.simpleMessage("Share"), "share" : MessageLookupByLibrary.simpleMessage("Share"),
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"), "showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"),
"size" : MessageLookupByLibrary.simpleMessage("Size"), "size" : MessageLookupByLibrary.simpleMessage("Size"),
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("Skip seconds at end"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Skip seconds at start"), "skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Skip seconds at start"),
"skipSilence" : MessageLookupByLibrary.simpleMessage("Skip silence"), "skipSilence" : MessageLookupByLibrary.simpleMessage("Skip silence"),
"skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"), "skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"),

View File

@ -308,6 +308,7 @@ class MessageLookup extends MessageLookupByLibrary {
"share" : MessageLookupByLibrary.simpleMessage("Compartir"), "share" : MessageLookupByLibrary.simpleMessage("Compartir"),
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"), "showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"),
"size" : MessageLookupByLibrary.simpleMessage("Tamaño"), "size" : MessageLookupByLibrary.simpleMessage("Tamaño"),
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("Skip seconds at end"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Saltar segundos al inicio"), "skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Saltar segundos al inicio"),
"skipSilence" : MessageLookupByLibrary.simpleMessage("Saltar silencios"), "skipSilence" : MessageLookupByLibrary.simpleMessage("Saltar silencios"),
"skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"), "skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"),

View File

@ -308,6 +308,7 @@ class MessageLookup extends MessageLookupByLibrary {
"share" : MessageLookupByLibrary.simpleMessage("Partager"), "share" : MessageLookupByLibrary.simpleMessage("Partager"),
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"), "showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"),
"size" : MessageLookupByLibrary.simpleMessage("Taille"), "size" : MessageLookupByLibrary.simpleMessage("Taille"),
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("Skip seconds at end"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Passer les premières secondes du début"), "skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Passer les premières secondes du début"),
"skipSilence" : MessageLookupByLibrary.simpleMessage("Skip silence"), "skipSilence" : MessageLookupByLibrary.simpleMessage("Skip silence"),
"skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"), "skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"),

View File

@ -308,6 +308,7 @@ class MessageLookup extends MessageLookupByLibrary {
"share" : MessageLookupByLibrary.simpleMessage("分享"), "share" : MessageLookupByLibrary.simpleMessage("分享"),
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("节目简介字体"), "showNotesFonts" : MessageLookupByLibrary.simpleMessage("节目简介字体"),
"size" : MessageLookupByLibrary.simpleMessage("大小"), "size" : MessageLookupByLibrary.simpleMessage("大小"),
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("结束跳过秒数"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("开头跳过秒数"), "skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("开头跳过秒数"),
"skipSilence" : MessageLookupByLibrary.simpleMessage("跳过无声"), "skipSilence" : MessageLookupByLibrary.simpleMessage("跳过无声"),
"skipToNext" : MessageLookupByLibrary.simpleMessage("下一首"), "skipToNext" : MessageLookupByLibrary.simpleMessage("下一首"),

View File

@ -2362,6 +2362,16 @@ class S {
); );
} }
/// `Skip seconds at end`
String get skipSecondsAtEnd {
return Intl.message(
'Skip seconds at end',
name: 'skipSecondsAtEnd',
desc: '',
args: [],
);
}
/// `Skip seconds at start` /// `Skip seconds at start`
String get skipSecondsAtStart { String get skipSecondsAtStart {
return Intl.message( return Intl.message(

View File

@ -535,6 +535,8 @@
"@showNotesFonts": {}, "@showNotesFonts": {},
"size": "Size", "size": "Size",
"@size": {}, "@size": {},
"skipSecondsAtEnd": "Skip seconds at end",
"@skipSecondsAtEnd": {},
"skipSecondsAtStart": "Skip seconds at start", "skipSecondsAtStart": "Skip seconds at start",
"@skipSecondsAtStart": {}, "@skipSecondsAtStart": {},
"skipSilence": "Skip silence", "skipSilence": "Skip silence",

View File

@ -535,6 +535,8 @@
"@showNotesFonts": {}, "@showNotesFonts": {},
"size": "Tamaño", "size": "Tamaño",
"@size": {}, "@size": {},
"skipSecondsAtEnd": "Skip seconds at end",
"@skipSecondsAtEnd": {},
"skipSecondsAtStart": "Saltar segundos al inicio", "skipSecondsAtStart": "Saltar segundos al inicio",
"@skipSecondsAtStart": {}, "@skipSecondsAtStart": {},
"skipSilence": "Saltar silencios", "skipSilence": "Saltar silencios",

View File

@ -535,6 +535,8 @@
"@showNotesFonts": {}, "@showNotesFonts": {},
"size": "Taille", "size": "Taille",
"@size": {}, "@size": {},
"skipSecondsAtEnd": "Skip seconds at end",
"@skipSecondsAtEnd": {},
"skipSecondsAtStart": "Passer les premières secondes du début", "skipSecondsAtStart": "Passer les premières secondes du début",
"@skipSecondsAtStart": {}, "@skipSecondsAtStart": {},
"skipSilence": "Skip silence", "skipSilence": "Skip silence",

View File

@ -535,6 +535,8 @@
"@showNotesFonts": {}, "@showNotesFonts": {},
"size": "大小", "size": "大小",
"@size": {}, "@size": {},
"skipSecondsAtEnd": "结束跳过秒数",
"@skipSecondsAtEnd": {},
"skipSecondsAtStart": "开头跳过秒数", "skipSecondsAtStart": "开头跳过秒数",
"@skipSecondsAtStart": {}, "@skipSecondsAtStart": {},
"skipSilence": "跳过无声", "skipSilence": "跳过无声",

View File

@ -27,7 +27,7 @@ class DBHelper {
var documentsDirectory = await getDatabasesPath(); var documentsDirectory = await getDatabasesPath();
var path = join(documentsDirectory, "podcasts.db"); var path = join(documentsDirectory, "podcasts.db");
var theDb = await openDatabase(path, var theDb = await openDatabase(path,
version: 3, onCreate: _onCreate, onUpgrade: _onUpgrade); version: 4, onCreate: _onCreate, onUpgrade: _onUpgrade);
return theDb; return theDb;
} }
@ -37,14 +37,15 @@ class DBHelper {
imageUrl TEXT,rssUrl TEXT UNIQUE, primaryColor TEXT, author TEXT, imageUrl TEXT,rssUrl TEXT UNIQUE, primaryColor TEXT, author TEXT,
description TEXT, add_date INTEGER, imagePath TEXT, provider TEXT, link TEXT, description TEXT, add_date INTEGER, imagePath TEXT, provider TEXT, link TEXT,
background_image TEXT DEFAULT '', hosts TEXT DEFAULT '',update_count INTEGER DEFAULT 0, 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)"""); episode_count INTEGER DEFAULT 0, skip_seconds INTEGER DEFAULT 0,
auto_download INTEGER DEFAULT 0, skip_seconds_end INTEGER DEFAULT 0)""");
await db await db
.execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT, .execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT,
enclosure_url TEXT UNIQUE, enclosure_length INTEGER, pubDate TEXT, enclosure_url TEXT UNIQUE, enclosure_length INTEGER, pubDate TEXT,
description TEXT, feed_id TEXT, feed_link TEXT, milliseconds INTEGER, description TEXT, feed_id TEXT, feed_link TEXT, milliseconds INTEGER,
duration INTEGER DEFAULT 0, explicit INTEGER DEFAULT 0, liked INTEGER DEFAULT 0, duration INTEGER DEFAULT 0, explicit INTEGER DEFAULT 0, liked INTEGER DEFAULT 0,
liked_date INTEGER DEFAULT 0, downloaded TEXT DEFAULT 'ND', download_date INTEGER DEFAULT 0, media_id TEXT, liked_date INTEGER DEFAULT 0, downloaded TEXT DEFAULT 'ND',
is_new INTEGER DEFAULT 0)"""); download_date INTEGER DEFAULT 0, media_id TEXT, is_new INTEGER DEFAULT 0)""");
await db.execute( await db.execute(
"""CREATE TABLE PlayHistory(id INTEGER PRIMARY KEY, title TEXT, enclosure_url TEXT, """CREATE TABLE PlayHistory(id INTEGER PRIMARY KEY, title TEXT, enclosure_url TEXT,
seconds REAL, seek_value REAL, add_date INTEGER, listen_time INTEGER DEFAULT 0)"""); seconds REAL, seek_value REAL, add_date INTEGER, listen_time INTEGER DEFAULT 0)""");
@ -59,9 +60,16 @@ class DBHelper {
"ALTER TABLE PodcastLocal ADD skip_seconds INTEGER DEFAULT 0 "); "ALTER TABLE PodcastLocal ADD skip_seconds INTEGER DEFAULT 0 ");
await db.execute( await db.execute(
"ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0"); "ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0");
await db.execute(
"ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0 ");
} else if (oldVersion == 2) { } else if (oldVersion == 2) {
await db.execute( await db.execute(
"ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0"); "ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0");
await db.execute(
"ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0 ");
} else if (oldVersion == 3) {
await db.execute(
"ALTER TABLE PodcastLocal ADD skip_seconds_end INTEGER DEFAULT 0 ");
} }
} }
@ -130,19 +138,33 @@ class DBHelper {
return list.first['count']; return list.first['count'];
} }
Future<int> getSkipSeconds(String id) async { Future<int> getSkipSecondsStart(String id) async {
var dbClient = await database; var dbClient = await database;
List<Map> list = await dbClient List<Map> list = await dbClient
.rawQuery('SELECT skip_seconds FROM PodcastLocal WHERE id = ?', [id]); .rawQuery('SELECT skip_seconds FROM PodcastLocal WHERE id = ?', [id]);
return list.first['skip_seconds']; return list.first['skip_seconds'];
} }
Future<int> saveSkipSeconds(String id, int seconds) async { Future<int> saveSkipSecondsStart(String id, int seconds) async {
var dbClient = await database; var dbClient = await database;
return await dbClient.rawUpdate( return await dbClient.rawUpdate(
"UPDATE PodcastLocal SET skip_seconds = ? WHERE id = ?", [seconds, id]); "UPDATE PodcastLocal SET skip_seconds = ? WHERE id = ?", [seconds, id]);
} }
Future<int> getSkipSecondsEnd(String id) async {
var dbClient = await database;
List<Map> list = await dbClient.rawQuery(
'SELECT skip_seconds_end FROM PodcastLocal WHERE id = ?', [id]);
return list.first['skip_seconds_end'];
}
Future<int> saveSkipSecondsEnd(String id, int seconds) async {
var dbClient = await database;
return await dbClient.rawUpdate(
"UPDATE PodcastLocal SET skip_seconds_end = ? WHERE id = ?",
[seconds, id]);
}
Future<bool> getAutoDownload(String id) async { Future<bool> getAutoDownload(String id) async {
var dbClient = await database; var dbClient = await database;
List<Map> list = await dbClient List<Map> list = await dbClient
@ -1336,8 +1358,8 @@ class DBHelper {
EpisodeBrief episode; EpisodeBrief episode;
List<Map> list = await dbClient.rawQuery( List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,E.is_new, P.title as feed_title, E.duration, E.explicit, P.skip_seconds, P.skip_seconds_end,
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id E.is_new, P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.enclosure_url = ?""", [url]); WHERE E.enclosure_url = ?""", [url]);
if (list.isEmpty) { if (list.isEmpty) {
return null; return null;
@ -1354,7 +1376,8 @@ class DBHelper {
list.first['imagePath'], list.first['imagePath'],
list.first['is_new'], list.first['is_new'],
mediaId: list.first['media_id'], mediaId: list.first['media_id'],
skipSeconds: list.first['skip_seconds']); skipSecondsStart: list.first['skip_seconds'],
skipSecondsEnd: list.first['skip_seconds_end']);
return episode; return episode;
} }
} }
@ -1364,9 +1387,9 @@ class DBHelper {
EpisodeBrief episode; EpisodeBrief episode;
List<Map> list = await dbClient.rawQuery( List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,E.is_new, P.title as feed_title, E.duration, E.explicit, P.skip_seconds, P.skip_seconds_end,
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id E.is_new, P.primaryColor, E.media_id FROM Episodes E INNER JOIN
WHERE E.media_id = ?""", [id]); PodcastLocal P ON E.feed_id = P.id WHERE E.media_id = ?""", [id]);
if (list.isEmpty) { if (list.isEmpty) {
return null; return null;
} else { } else {
@ -1382,7 +1405,8 @@ class DBHelper {
list.first['imagePath'], list.first['imagePath'],
list.first['is_new'], list.first['is_new'],
mediaId: list.first['media_id'], mediaId: list.first['media_id'],
skipSeconds: list.first['skip_seconds']); skipSecondsStart: list.first['skip_seconds'],
skipSecondsEnd: list.first['skip_seconds_end']);
return episode; return episode;
} }
} }

View File

@ -354,7 +354,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
case 2: case 2:
generalSheet( generalSheet(
context, context,
title: s.settings, title: widget.podcastLocal.title,
child: PodcastSetting(podcastLocal: widget.podcastLocal), child: PodcastSetting(podcastLocal: widget.podcastLocal),
).then((value) => setState(() {})); ).then((value) => setState(() {}));
break; break;
@ -674,7 +674,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var _color = widget.podcastLocal.primaryColor.colorizedark(); final color = widget.podcastLocal.primaryColor.colorizedark();
final s = context.s; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
@ -745,7 +745,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
color: Colors.white, color: Colors.white,
), ),
expandedHeight: 150 + context.paddingTop, expandedHeight: 150 + context.paddingTop,
backgroundColor: _color, backgroundColor: color,
floating: true, floating: true,
pinned: true, pinned: true,
flexibleSpace: LayoutBuilder( flexibleSpace: LayoutBuilder(

View File

@ -82,14 +82,14 @@ class __PodcastCardState extends State<_PodcastCard>
Future<int> _getSkipSecond(String id) async { Future<int> _getSkipSecond(String id) async {
var dbHelper = DBHelper(); var dbHelper = DBHelper();
var seconds = await dbHelper.getSkipSeconds(id); var seconds = await dbHelper.getSkipSecondsStart(id);
_skipSeconds = seconds; _skipSeconds = seconds;
return seconds; return seconds;
} }
_saveSkipSeconds(String id, int seconds) async { _saveSkipSeconds(String id, int seconds) async {
var dbHelper = DBHelper(); var dbHelper = DBHelper();
await dbHelper.saveSkipSeconds(id, seconds); await dbHelper.saveSkipSecondsStart(id, seconds);
} }
_setAutoDownload(String id, bool boo) async { _setAutoDownload(String id, bool boo) async {
@ -463,8 +463,7 @@ class __PodcastCardState extends State<_PodcastCard>
), ),
), ),
FlatButton( FlatButton(
splashColor: splashColor: Colors.red.withAlpha(70),
context.accentColor.withAlpha(70),
onPressed: () { onPressed: () {
groupList.removePodcast( groupList.removePodcast(
widget.podcastLocal.id); widget.podcastLocal.id);

View File

@ -3,12 +3,14 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:image/image.dart' as img; import 'package:provider/provider.dart';
import 'package:webfeed/webfeed.dart'; import 'package:webfeed/webfeed.dart';
import '../local_storage/sqflite_localpodcast.dart'; import '../local_storage/sqflite_localpodcast.dart';
import '../state/podcast_group.dart';
import '../type/play_histroy.dart'; import '../type/play_histroy.dart';
import '../type/podcastlocal.dart'; import '../type/podcastlocal.dart';
import '../util/custom_widget.dart'; import '../util/custom_widget.dart';
@ -28,73 +30,74 @@ class PodcastSetting extends StatefulWidget {
} }
class _PodcastSettingState extends State<PodcastSetting> { class _PodcastSettingState extends State<PodcastSetting> {
final _dbHelper = DBHelper();
MarkStatus _markStatus = MarkStatus.none; MarkStatus _markStatus = MarkStatus.none;
RefreshCoverStatus _coverStatus = RefreshCoverStatus.none; RefreshCoverStatus _coverStatus = RefreshCoverStatus.none;
int _seconds = 0; int _secondsStart;
int _secondsEnd;
bool _markConfirm;
bool _removeConfirm;
bool _showStartTimePicker;
bool _showEndTimePicker;
@override
void initState() {
super.initState();
_secondsStart = 0;
_secondsEnd = 0;
_markConfirm = false;
_removeConfirm = false;
_showStartTimePicker = false;
_showEndTimePicker = false;
}
Future<void> _setAutoDownload(bool boo) async { Future<void> _setAutoDownload(bool boo) async {
var permission = await _checkPermmison(); var permission = await _checkPermmison();
if (permission) { if (permission) {
var dbHelper = DBHelper(); await _dbHelper.saveAutoDownload(widget.podcastLocal.id, boo: boo);
await dbHelper.saveAutoDownload(widget.podcastLocal.id, boo: boo);
} }
if (mounted) setState(() {}); if (mounted) setState(() {});
} }
Future<void> _saveSkipSeconds(int seconds) async { Future<void> _saveSkipSecondsStart(int seconds) async {
var dbHelper = DBHelper(); await _dbHelper.saveSkipSecondsStart(widget.podcastLocal.id, seconds);
await dbHelper.saveSkipSeconds(widget.podcastLocal.id, seconds); }
Future<void> _saveSkipSecondsEnd(int seconds) async {
await _dbHelper.saveSkipSecondsEnd(widget.podcastLocal.id, seconds);
}
Future<bool> _getAutoDownload(String id) async {
return await _dbHelper.getAutoDownload(id);
}
Future<int> _getSkipSecondStart(String id) async {
return await _dbHelper.getSkipSecondsStart(id);
}
Future<int> _getSkipSecondEnd(String id) async {
return await _dbHelper.getSkipSecondsEnd(id);
} }
Future<void> _markListened(String podcastId) async { Future<void> _markListened(String podcastId) async {
setState(() { setState(() {
_markStatus = MarkStatus.start; _markStatus = MarkStatus.start;
}); });
var dbHelper = DBHelper(); var episodes = await _dbHelper.getRssItem(podcastId, -1, reverse: true);
var episodes = await dbHelper.getRssItem(podcastId, -1, reverse: true);
for (var episode in episodes) { for (var episode in episodes) {
var marked = await dbHelper.checkMarked(episode); final marked = await _dbHelper.checkMarked(episode);
if (!marked) { if (!marked) {
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1); final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history); await _dbHelper.saveHistory(history);
if (mounted) {
setState(() {
_markStatus = MarkStatus.complete;
});
}
} }
} }
if (mounted) {
setState(() {
_markStatus = MarkStatus.complete;
});
}
} }
void _confirmMarkListened(BuildContext context) => generalDialog(
context,
title: Text(context.s.markConfirm),
content: Text(context.s.markConfirmContent),
actions: <Widget>[
FlatButton(
splashColor: context.accentColor.withAlpha(70),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
context.s.cancel,
style: TextStyle(color: Colors.grey[600]),
),
),
FlatButton(
splashColor: context.accentColor.withAlpha(70),
onPressed: () async {
Navigator.of(context).pop();
await _markListened(widget.podcastLocal.id);
},
child: Text(
context.s.confirm,
style: TextStyle(color: context.accentColor),
),
)
],
);
Future<void> _refreshArtWork() async { Future<void> _refreshArtWork() async {
setState(() => _coverStatus = RefreshCoverStatus.start); setState(() => _coverStatus = RefreshCoverStatus.start);
var options = BaseOptions( var options = BaseOptions(
@ -161,17 +164,6 @@ class _PodcastSettingState extends State<PodcastSetting> {
} }
} }
Future<bool> _getAutoDownload(String id) async {
var dbHelper = DBHelper();
return await dbHelper.getAutoDownload(id);
}
Future<int> _getSkipSecond(String id) async {
var dbHelper = DBHelper();
var seconds = await dbHelper.getSkipSeconds(id);
return seconds;
}
Widget _getRefreshStatusIcon(RefreshCoverStatus status) { Widget _getRefreshStatusIcon(RefreshCoverStatus status) {
switch (status) { switch (status) {
case RefreshCoverStatus.none: case RefreshCoverStatus.none:
@ -194,130 +186,265 @@ class _PodcastSettingState extends State<PodcastSetting> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s; final s = context.s;
final groupList = context.watch<GroupList>();
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
FutureBuilder<bool>( FutureBuilder<bool>(
future: _getAutoDownload(widget.podcastLocal.id), future: _getAutoDownload(widget.podcastLocal.id),
initialData: false, initialData: false,
builder: (context, snapshot) { builder: (context, snapshot) {
return ListTile( return ListTile(
onTap: () => _setAutoDownload(!snapshot.data), onTap: () => _setAutoDownload(!snapshot.data),
leading: SizedBox( leading: SizedBox(
height: 18, height: 22,
width: 18, width: 24,
child: CustomPaint( child: CustomPaint(
painter: DownloadPainter( painter: DownloadPainter(
color: context.brightness == Brightness.light color: context.brightness == Brightness.light
? Colors.grey[600]
: Colors.white,
fraction: 0,
progressColor: context.accentColor,
),
),
),
title: Text(s.autoDownload),
trailing: Transform.scale(
scale: 0.9,
child: Switch(
value: snapshot.data, onChanged: _setAutoDownload),
),
);
}),
Divider(height: 1),
FutureBuilder<int>(
future: _getSkipSecond(widget.podcastLocal.id),
initialData: 0,
builder: (context, snapshot) => ListTile(
onTap: () {
generalDialog(
context,
title: Text(s.skipSecondsAtStart, maxLines: 2),
content: DurationPicker(
duration: Duration(seconds: snapshot.data),
onChange: (value) => _seconds = value.inSeconds,
),
actions: <Widget>[
FlatButton(
splashColor: context.accentColor.withAlpha(70),
onPressed: () {
Navigator.of(context).pop();
_seconds = 0;
},
child: Text(
s.cancel,
style: TextStyle(color: Colors.grey[600]),
),
),
FlatButton(
splashColor: context.accentColor.withAlpha(70),
onPressed: () {
Navigator.of(context).pop();
_saveSkipSeconds(_seconds);
},
child: Text(
s.confirm,
style: TextStyle(color: context.accentColor),
),
)
],
).then((value) => setState(() {}));
},
leading: Icon(Icons.fast_forward),
title: Text(s.skipSecondsAtStart),
trailing: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(snapshot.data.toTime),
),
),
),
Divider(height: 1),
ListTile(
onTap: () {
if (_markStatus != MarkStatus.start) {
_confirmMarkListened(context);
}
},
title: Text(s.menuMarkAllListened),
leading: SizedBox(
height: 20,
width: 20,
child: CustomPaint(
painter: ListenedAllPainter(
context.brightness == Brightness.light
? Colors.grey[600] ? Colors.grey[600]
: Colors.white, : Colors.white,
stroke: 2), fraction: 0,
progressColor: context.accentColor,
),
),
), ),
), title: Text(s.autoDownload),
trailing: Padding( trailing: Transform.scale(
padding: const EdgeInsets.only(right: 10.0), scale: 0.9,
child:
Switch(value: snapshot.data, onChanged: _setAutoDownload),
),
);
}),
FutureBuilder<int>(
future: _getSkipSecondStart(widget.podcastLocal.id),
initialData: 0,
builder: (context, snapshot) => ListTile(
onTap: () {
_secondsStart = 0;
setState(() {
_removeConfirm = false;
_markConfirm = false;
_showEndTimePicker = false;
_showStartTimePicker = !_showStartTimePicker;
});
},
leading: Icon(Icons.fast_forward),
title: Text(s.skipSecondsAtStart),
trailing: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(snapshot.data.toTime),
),
),
),
if (_showStartTimePicker)
_TimePicker(
onCancel: () {
_secondsStart = 0;
setState(() => _showStartTimePicker = false);
},
onConfirm: () async {
await _saveSkipSecondsStart(_secondsStart);
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,
),
ListTile(
onTap: () {
if (_coverStatus != RefreshCoverStatus.start) {
_refreshArtWork();
}
},
title: Text(s.refreshArtwork),
leading: Icon(Icons.refresh),
trailing: Padding(
padding: const EdgeInsets.only(right: 15.0),
child: SizedBox( child: SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: _markStatus == MarkStatus.none child: _getRefreshStatusIcon(_coverStatus)))),
? Center() Divider(height: 1),
: _markStatus == MarkStatus.start ListTile(
? CircularProgressIndicator(strokeWidth: 2) onTap: () {
: Icon(Icons.done)), setState(() {
)), _removeConfirm = false;
Divider(height: 1), _showStartTimePicker = false;
ListTile( _showEndTimePicker = false;
onTap: () { _markConfirm = !_markConfirm;
if (_coverStatus != RefreshCoverStatus.start) { });
_refreshArtWork(); },
} title: Text(s.menuMarkAllListened,
}, style: TextStyle(
title: Text(s.refreshArtwork), color: context.accentColor, fontWeight: FontWeight.bold)),
leading: Icon(Icons.refresh), leading: SizedBox(
trailing: Padding( height: 22,
padding: const EdgeInsets.only(right: 15.0), width: 24,
child: SizedBox( child: CustomPaint(
height: 20, painter: ListenedAllPainter(
width: 20, context.brightness == Brightness.light
child: _getRefreshStatusIcon(_coverStatus)))), ? Colors.grey[600]
Divider(height: 1), : Colors.white,
]); stroke: 2),
),
),
trailing: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: SizedBox(
height: 20,
width: 20,
child: _markStatus == MarkStatus.none
? Center()
: _markStatus == MarkStatus.start
? CircularProgressIndicator(strokeWidth: 2)
: Icon(Icons.done)),
)),
if (_markConfirm)
Container(
width: double.infinity,
color: context.primaryColorDark,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FlatButton(
onPressed: () => setState(() {
_markConfirm = false;
}),
child: Text(
s.cancel,
style: TextStyle(color: Colors.grey[600]),
)),
FlatButton(
onPressed: () {
if (_markStatus != MarkStatus.start) {
_markListened(widget.podcastLocal.id);
}
setState(() {
_markConfirm = false;
});
},
child: Text(s.confirm,
style: TextStyle(color: context.accentColor))),
],
),
),
ListTile(
onTap: () {
setState(() {
_markConfirm = false;
_showStartTimePicker = false;
_showEndTimePicker = false;
_removeConfirm = !_removeConfirm;
});
},
title: Text(s.remove,
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold)),
leading: Icon(Icons.delete, color: Colors.red),
),
if (_removeConfirm)
Container(
width: double.infinity,
color: context.primaryColorDark,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FlatButton(
onPressed: () => setState(() {
_removeConfirm = false;
}),
child:
Text(s.cancel, style: TextStyle(color: Colors.grey[600])),
),
FlatButton(
splashColor: Colors.red.withAlpha(70),
onPressed: () {
groupList.removePodcast(widget.podcastLocal.id);
Navigator.of(context).pop();
},
child:
Text(s.confirm, style: TextStyle(color: Colors.red))),
],
),
),
],
);
}
}
class _TimePicker extends StatelessWidget {
const _TimePicker({this.onConfirm, this.onCancel, this.onChange, Key key})
: super(key: key);
final VoidCallback onConfirm;
final VoidCallback onCancel;
final ValueChanged<Duration> onChange;
@override
Widget build(BuildContext context) {
final s = context.s;
return Container(
color: context.primaryColorDark,
child: Column(
children: [
SizedBox(height: 10),
DurationPicker(
key: key,
onChange: onChange,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FlatButton(
onPressed: onCancel,
child: Text(
s.cancel,
style: TextStyle(color: Colors.grey[600]),
),
),
FlatButton(
splashColor: context.accentColor.withAlpha(70),
onPressed: onConfirm,
child: Text(
s.confirm,
style: TextStyle(color: context.accentColor),
),
)
],
)
],
),
);
} }
} }

View File

@ -920,6 +920,13 @@ class AudioPlayerTask extends BackgroundAudioTask {
Future<void> _playFromStart() async { Future<void> _playFromStart() async {
_playing = true; _playing = true;
_session.setActive(true); _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']));
}
if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting || if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting ||
_audioPlayer.playbackEvent.state != AudioPlaybackState.none) { _audioPlayer.playbackEvent.state != AudioPlaybackState.none) {
try { try {
@ -928,9 +935,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
_setState(processingState: AudioProcessingState.error); _setState(processingState: AudioProcessingState.error);
} }
} }
if (mediaItem.extras['skip'] > 0) {
_audioPlayer.seek(Duration(seconds: mediaItem.extras['skip']));
}
} }
@override @override

View File

@ -21,7 +21,8 @@ class EpisodeBrief extends Equatable {
final String imagePath; final String imagePath;
final String mediaId; final String mediaId;
final int isNew; final int isNew;
final int skipSeconds; final int skipSecondsStart;
final int skipSecondsEnd;
final int downloadDate; final int downloadDate;
EpisodeBrief( EpisodeBrief(
this.title, this.title,
@ -37,7 +38,8 @@ class EpisodeBrief extends Equatable {
{this.mediaId, {this.mediaId,
this.liked, this.liked,
this.downloaded, this.downloaded,
this.skipSeconds, this.skipSecondsStart,
this.skipSecondsEnd,
this.description = '', this.description = '',
this.downloadDate = 0}) this.downloadDate = 0})
: assert(enclosureUrl != null); : assert(enclosureUrl != null);
@ -50,7 +52,10 @@ class EpisodeBrief extends Equatable {
album: feedTitle, album: feedTitle,
duration: Duration.zero, duration: Duration.zero,
artUri: 'file://$imagePath', artUri: 'file://$imagePath',
extras: {'skip': skipSeconds}); extras: {
'skipSecondsStart': skipSecondsStart,
'skipSecondsEnd': skipSecondsEnd
});
} }
ImageProvider get avatarImage { ImageProvider get avatarImage {
@ -72,7 +77,8 @@ class EpisodeBrief extends Equatable {
primaryColor, duration, explicit, imagePath, isNew, primaryColor, duration, explicit, imagePath, isNew,
mediaId: mediaId ?? this.mediaId, mediaId: mediaId ?? this.mediaId,
downloaded: downloaded, downloaded: downloaded,
skipSeconds: skipSeconds, skipSecondsStart: skipSecondsStart,
skipSecondsEnd: skipSecondsEnd,
description: description, description: description,
downloadDate: downloadDate); downloadDate: downloadDate);

View File

@ -635,24 +635,20 @@ class DurationPicker extends StatelessWidget {
@required this.onChange, @required this.onChange,
this.snapToMins, this.snapToMins,
this.width, this.width,
this.height}); this.height,
Key key})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
width: width ?? _kDurationPickerWidthPortrait / 1.5, width: width ?? _kDurationPickerWidthPortrait / 1.5,
height: height ?? _kDurationPickerHeightPortrait / 1.5, height: height ?? _kDurationPickerHeightPortrait / 1.5,
child: Column( child: _Dial(
mainAxisSize: MainAxisSize.min, duration: duration,
crossAxisAlignment: CrossAxisAlignment.stretch, onChanged: onChange,
children: <Widget>[ snapToMins: snapToMins,
Expanded( ),
child: _Dial( );
duration: duration,
onChanged: onChange,
snapToMins: snapToMins,
),
),
]));
} }
} }

View File

@ -41,25 +41,37 @@ Future generalSheet(BuildContext context, {Widget child, String title}) async =>
elevation: 2, elevation: 2,
context: context, context: context,
builder: (context) { builder: (context) {
return Column( return SafeArea(
mainAxisSize: MainAxisSize.min, child: SingleChildScrollView(
children: [ physics: NeverScrollableScrollPhysics(),
Padding( child: Column(
padding: EdgeInsets.only(top: 10.0, bottom: 2.0), mainAxisSize: MainAxisSize.min,
child: Container( children: [
height: 4, Padding(
width: 25, padding: EdgeInsets.only(top: 10.0, bottom: 2.0),
decoration: BoxDecoration( child: Container(
borderRadius: BorderRadius.circular(2.0), height: 4,
color: context.primaryColorDark), width: 25,
), decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2.0),
color: context.primaryColorDark),
),
),
Padding(
padding: EdgeInsets.only(
left: 50, right: 50, top: 6.0, bottom: 15),
child: Text(
title,
style: context.textTheme.headline6,
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.clip,
),
),
Divider(height: 1),
child,
],
), ),
Padding( ),
padding: EdgeInsets.symmetric(vertical: 6.0),
child: Text(title, style: context.textTheme.headline6),
),
Divider(height: 1),
child,
],
); );
}); });