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"),
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("Show notes font"),
"size" : MessageLookupByLibrary.simpleMessage("Size"),
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("Skip seconds at end"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Skip seconds at start"),
"skipSilence" : MessageLookupByLibrary.simpleMessage("Skip silence"),
"skipToNext" : MessageLookupByLibrary.simpleMessage("Skip to next"),

View File

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

View File

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

View File

@ -308,6 +308,7 @@ class MessageLookup extends MessageLookupByLibrary {
"share" : MessageLookupByLibrary.simpleMessage("分享"),
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("节目简介字体"),
"size" : MessageLookupByLibrary.simpleMessage("大小"),
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("结束跳过秒数"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("开头跳过秒数"),
"skipSilence" : 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`
String get skipSecondsAtStart {
return Intl.message(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,12 +3,14 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.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 '../local_storage/sqflite_localpodcast.dart';
import '../state/podcast_group.dart';
import '../type/play_histroy.dart';
import '../type/podcastlocal.dart';
import '../util/custom_widget.dart';
@ -28,73 +30,74 @@ class PodcastSetting extends StatefulWidget {
}
class _PodcastSettingState extends State<PodcastSetting> {
final _dbHelper = DBHelper();
MarkStatus _markStatus = MarkStatus.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 {
var permission = await _checkPermmison();
if (permission) {
var dbHelper = DBHelper();
await dbHelper.saveAutoDownload(widget.podcastLocal.id, boo: boo);
await _dbHelper.saveAutoDownload(widget.podcastLocal.id, boo: boo);
}
if (mounted) setState(() {});
}
Future<void> _saveSkipSeconds(int seconds) async {
var dbHelper = DBHelper();
await dbHelper.saveSkipSeconds(widget.podcastLocal.id, seconds);
Future<void> _saveSkipSecondsStart(int seconds) async {
await _dbHelper.saveSkipSecondsStart(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 {
setState(() {
_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) {
var marked = await dbHelper.checkMarked(episode);
final marked = await _dbHelper.checkMarked(episode);
if (!marked) {
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
if (mounted) {
setState(() {
_markStatus = MarkStatus.complete;
});
}
await _dbHelper.saveHistory(history);
}
}
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 {
setState(() => _coverStatus = RefreshCoverStatus.start);
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) {
switch (status) {
case RefreshCoverStatus.none:
@ -194,130 +186,265 @@ class _PodcastSettingState extends State<PodcastSetting> {
@override
Widget build(BuildContext context) {
final s = context.s;
final groupList = context.watch<GroupList>();
return Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FutureBuilder<bool>(
future: _getAutoDownload(widget.podcastLocal.id),
initialData: false,
builder: (context, snapshot) {
return ListTile(
onTap: () => _setAutoDownload(!snapshot.data),
leading: SizedBox(
height: 18,
width: 18,
child: CustomPaint(
painter: DownloadPainter(
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
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FutureBuilder<bool>(
future: _getAutoDownload(widget.podcastLocal.id),
initialData: false,
builder: (context, snapshot) {
return ListTile(
onTap: () => _setAutoDownload(!snapshot.data),
leading: SizedBox(
height: 22,
width: 24,
child: CustomPaint(
painter: DownloadPainter(
color: context.brightness == Brightness.light
? Colors.grey[600]
: Colors.white,
stroke: 2),
fraction: 0,
progressColor: context.accentColor,
),
),
),
),
trailing: Padding(
padding: const EdgeInsets.only(right: 10.0),
title: Text(s.autoDownload),
trailing: Transform.scale(
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(
height: 20,
width: 20,
child: _markStatus == MarkStatus.none
? Center()
: _markStatus == MarkStatus.start
? CircularProgressIndicator(strokeWidth: 2)
: Icon(Icons.done)),
)),
Divider(height: 1),
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(
height: 20,
width: 20,
child: _getRefreshStatusIcon(_coverStatus)))),
Divider(height: 1),
]);
child: _getRefreshStatusIcon(_coverStatus)))),
Divider(height: 1),
ListTile(
onTap: () {
setState(() {
_removeConfirm = false;
_showStartTimePicker = false;
_showEndTimePicker = false;
_markConfirm = !_markConfirm;
});
},
title: Text(s.menuMarkAllListened,
style: TextStyle(
color: context.accentColor, fontWeight: FontWeight.bold)),
leading: SizedBox(
height: 22,
width: 24,
child: CustomPaint(
painter: ListenedAllPainter(
context.brightness == Brightness.light
? Colors.grey[600]
: 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 {
_playing = 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 ||
_audioPlayer.playbackEvent.state != AudioPlaybackState.none) {
try {
@ -928,9 +935,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
_setState(processingState: AudioProcessingState.error);
}
}
if (mediaItem.extras['skip'] > 0) {
_audioPlayer.seek(Duration(seconds: mediaItem.extras['skip']));
}
}
@override

View File

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

View File

@ -635,24 +635,20 @@ class DurationPicker extends StatelessWidget {
@required this.onChange,
this.snapToMins,
this.width,
this.height});
this.height,
Key key})
: super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: width ?? _kDurationPickerWidthPortrait / 1.5,
height: height ?? _kDurationPickerHeightPortrait / 1.5,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: _Dial(
duration: duration,
onChanged: onChange,
snapToMins: snapToMins,
),
),
]));
width: width ?? _kDurationPickerWidthPortrait / 1.5,
height: height ?? _kDurationPickerHeightPortrait / 1.5,
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,
context: context,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 2.0),
child: Container(
height: 4,
width: 25,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2.0),
color: context.primaryColorDark),
),
return SafeArea(
child: SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 2.0),
child: Container(
height: 4,
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,
],
),
);
});