Update podcast setting menu, add skip seconds at end.
This commit is contained in:
parent
fb31758f50
commit
b271965009
|
@ -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"),
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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("下一首"),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -535,6 +535,8 @@
|
|||
"@showNotesFonts": {},
|
||||
"size": "Size",
|
||||
"@size": {},
|
||||
"skipSecondsAtEnd": "Skip seconds at end",
|
||||
"@skipSecondsAtEnd": {},
|
||||
"skipSecondsAtStart": "Skip seconds at start",
|
||||
"@skipSecondsAtStart": {},
|
||||
"skipSilence": "Skip silence",
|
||||
|
|
|
@ -535,6 +535,8 @@
|
|||
"@showNotesFonts": {},
|
||||
"size": "Tamaño",
|
||||
"@size": {},
|
||||
"skipSecondsAtEnd": "Skip seconds at end",
|
||||
"@skipSecondsAtEnd": {},
|
||||
"skipSecondsAtStart": "Saltar segundos al inicio",
|
||||
"@skipSecondsAtStart": {},
|
||||
"skipSilence": "Saltar silencios",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -535,6 +535,8 @@
|
|||
"@showNotesFonts": {},
|
||||
"size": "大小",
|
||||
"@size": {},
|
||||
"skipSecondsAtEnd": "结束跳过秒数",
|
||||
"@skipSecondsAtEnd": {},
|
||||
"skipSecondsAtStart": "开头跳过秒数",
|
||||
"@skipSecondsAtStart": {},
|
||||
"skipSilence": "跳过无声",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue