mirror of
https://github.com/stonega/tsacdop
synced 2025-02-18 04:20:37 +01:00
Popup menu setting
Auto download on work Add rewind when using headset Fixed audio auto play when notification come
This commit is contained in:
parent
1a497a78ed
commit
935566b304
@ -49,7 +49,7 @@ android {
|
|||||||
applicationId "com.stonegate.tsacdop"
|
applicationId "com.stonegate.tsacdop"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 16
|
versionCode 17
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
@ -55,10 +55,12 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_showMenu = true;
|
_showMenu = true;
|
||||||
});
|
});
|
||||||
} else
|
} else if (_controller.offset <
|
||||||
|
_controller.position.maxScrollExtent * 0.8) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showMenu = false;
|
_showMenu = false;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_launchUrl(String url) async {
|
_launchUrl(String url) async {
|
||||||
@ -113,7 +115,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
elevation: 2,
|
elevation: 1,
|
||||||
tooltip: 'Menu',
|
tooltip: 'Menu',
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
@ -152,6 +154,8 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -323,8 +327,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
'Still no shownote received\n for this episode.',
|
'Still no shownote received\n for this episode.',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.textTheme
|
color: context.textColor
|
||||||
.bodyText1.color
|
|
||||||
.withOpacity(0.5))),
|
.withOpacity(0.5))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -381,7 +384,7 @@ class MenuBar extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MenuBarState extends State<MenuBar> {
|
class _MenuBarState extends State<MenuBar> {
|
||||||
bool _liked;
|
bool _liked = false;
|
||||||
|
|
||||||
Future<PlayHistory> getPosition(EpisodeBrief episode) async {
|
Future<PlayHistory> getPosition(EpisodeBrief episode) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
@ -401,7 +404,6 @@ class _MenuBarState extends State<MenuBar> {
|
|||||||
if (result == 1 && mounted)
|
if (result == 1 && mounted)
|
||||||
setState(() {
|
setState(() {
|
||||||
_liked = false;
|
_liked = false;
|
||||||
// _like = 0;
|
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -526,12 +528,10 @@ class _MenuBarState extends State<MenuBar> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
DownloadButton(episode: widget.episodeItem),
|
DownloadButton(episode: widget.episodeItem),
|
||||||
Selector<AudioPlayerNotifier, List<String>>(
|
Selector<AudioPlayerNotifier, List<EpisodeBrief>>(
|
||||||
selector: (_, audio) => audio.queue.playlist
|
selector: (_, audio) => audio.queue.playlist,
|
||||||
.map((e) => e.enclosureUrl)
|
|
||||||
.toList(),
|
|
||||||
builder: (_, data, __) {
|
builder: (_, data, __) {
|
||||||
return data.contains(widget.episodeItem.enclosureUrl)
|
return data.contains(widget.episodeItem)
|
||||||
? _buttonOnMenu(
|
? _buttonOnMenu(
|
||||||
Icon(Icons.playlist_add_check,
|
Icon(Icons.playlist_add_check,
|
||||||
color: Theme.of(context).accentColor), () {
|
color: Theme.of(context).accentColor), () {
|
||||||
|
@ -2,7 +2,6 @@ import 'dart:ui';
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
@ -13,6 +12,7 @@ import '../state/download_state.dart';
|
|||||||
import '../state/audiostate.dart';
|
import '../state/audiostate.dart';
|
||||||
import '../state/settingstate.dart';
|
import '../state/settingstate.dart';
|
||||||
import '../type/episodebrief.dart';
|
import '../type/episodebrief.dart';
|
||||||
|
import '../util/general_dialog.dart';
|
||||||
|
|
||||||
class DownloadButton extends StatefulWidget {
|
class DownloadButton extends StatefulWidget {
|
||||||
final EpisodeBrief episode;
|
final EpisodeBrief episode;
|
||||||
@ -107,54 +107,31 @@ class _DownloadButtonState extends State<DownloadButton> {
|
|||||||
|
|
||||||
Future<bool> _useDataConfirem() async {
|
Future<bool> _useDataConfirem() async {
|
||||||
bool ifUseData = false;
|
bool ifUseData = false;
|
||||||
await showGeneralDialog(
|
await generalDialog(
|
||||||
context: context,
|
context,
|
||||||
barrierDismissible: true,
|
title: Text('Cellular data warn'),
|
||||||
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
content: Text('Are you sure you want to use cellular data to download?'),
|
||||||
barrierColor: Colors.black54,
|
actions: <Widget>[
|
||||||
transitionDuration: const Duration(milliseconds: 200),
|
FlatButton(
|
||||||
pageBuilder: (BuildContext context, Animation animaiton,
|
onPressed: () {
|
||||||
Animation secondaryAnimation) =>
|
Navigator.of(context).pop();
|
||||||
AnnotatedRegion<SystemUiOverlayStyle>(
|
},
|
||||||
value: SystemUiOverlayStyle(
|
child: Text(
|
||||||
statusBarIconBrightness: Brightness.light,
|
'CANCEL',
|
||||||
systemNavigationBarColor:
|
style: TextStyle(color: Colors.grey[600]),
|
||||||
Theme.of(context).brightness == Brightness.light
|
),
|
||||||
? Color.fromRGBO(113, 113, 113, 1)
|
|
||||||
: Color.fromRGBO(15, 15, 15, 1),
|
|
||||||
),
|
),
|
||||||
child: AlertDialog(
|
FlatButton(
|
||||||
elevation: 1,
|
onPressed: () {
|
||||||
shape: RoundedRectangleBorder(
|
ifUseData = true;
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10.0))),
|
Navigator.of(context).pop();
|
||||||
titlePadding:
|
},
|
||||||
EdgeInsets.only(top: 20, left: 20, right: 100, bottom: 20),
|
child: Text(
|
||||||
title: Text('Cellular data warn'),
|
'CONFIRM',
|
||||||
content:
|
style: TextStyle(color: Colors.red),
|
||||||
Text('Are you sure you want to use cellular data to download?'),
|
),
|
||||||
actions: <Widget>[
|
)
|
||||||
FlatButton(
|
],
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'CANCEL',
|
|
||||||
style: TextStyle(color: Colors.grey[600]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () {
|
|
||||||
ifUseData = true;
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'CONFIRM',
|
|
||||||
style: TextStyle(color: Colors.red),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return ifUseData;
|
return ifUseData;
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,13 @@ import 'intl/messages_all.dart';
|
|||||||
// Made by Localizely
|
// Made by Localizely
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
|
||||||
|
|
||||||
class S {
|
class S {
|
||||||
S();
|
S();
|
||||||
|
|
||||||
|
static S current;
|
||||||
|
|
||||||
static const AppLocalizationDelegate delegate =
|
static const AppLocalizationDelegate delegate =
|
||||||
AppLocalizationDelegate();
|
AppLocalizationDelegate();
|
||||||
|
|
||||||
@ -19,7 +23,9 @@ class S {
|
|||||||
final localeName = Intl.canonicalizedLocale(name);
|
final localeName = Intl.canonicalizedLocale(name);
|
||||||
return initializeMessages(localeName).then((_) {
|
return initializeMessages(localeName).then((_) {
|
||||||
Intl.defaultLocale = localeName;
|
Intl.defaultLocale = localeName;
|
||||||
return S();
|
S.current = S();
|
||||||
|
|
||||||
|
return S.current;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import 'package:line_icons/line_icons.dart';
|
|||||||
|
|
||||||
import '../util/context_extension.dart';
|
import '../util/context_extension.dart';
|
||||||
|
|
||||||
const String version = '0.3.3';
|
const String version = '0.3.4';
|
||||||
|
|
||||||
class AboutApp extends StatelessWidget {
|
class AboutApp extends StatelessWidget {
|
||||||
_launchUrl(String url) async {
|
_launchUrl(String url) async {
|
||||||
|
@ -238,7 +238,7 @@ class _SearchListState extends State<SearchList> {
|
|||||||
Future<List> _getList(String searchText, int nextOffset) async {
|
Future<List> _getList(String searchText, int nextOffset) async {
|
||||||
String apiKey = environment['apiKey'];
|
String apiKey = environment['apiKey'];
|
||||||
String url = "https://listen-api.listennotes.com/api/v2/search?q=" +
|
String url = "https://listen-api.listennotes.com/api/v2/search?q=" +
|
||||||
searchText +
|
Uri.encodeComponent(searchText) +
|
||||||
"&sort_by_date=0&type=podcast&offset=$nextOffset";
|
"&sort_by_date=0&type=podcast&offset=$nextOffset";
|
||||||
Response response = await Dio().get(url,
|
Response response = await Dio().get(url,
|
||||||
options: Options(headers: {
|
options: Options(headers: {
|
||||||
|
@ -1050,168 +1050,183 @@ class _MyFavoriteState extends State<_MyFavorite>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
return FutureBuilder<List<EpisodeBrief>>(
|
return Selector<AudioPlayerNotifier, bool>(
|
||||||
future: _getLikedRssItem(_top, _sortBy),
|
selector: (_, audio) => audio.episodeState,
|
||||||
builder: (context, snapshot) {
|
builder: (context, episodeState, child) {
|
||||||
if (snapshot.hasError) print(snapshot.error);
|
return FutureBuilder<List<EpisodeBrief>>(
|
||||||
return (snapshot.hasData)
|
future: _getLikedRssItem(_top, _sortBy),
|
||||||
? snapshot.data.length == 0
|
builder: (context, snapshot) {
|
||||||
? Padding(
|
if (snapshot.hasError) print(snapshot.error);
|
||||||
padding: EdgeInsets.only(top: 150),
|
return (snapshot.hasData)
|
||||||
child: Column(
|
? snapshot.data.length == 0
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
? Padding(
|
||||||
children: [
|
padding: EdgeInsets.only(top: 150),
|
||||||
Icon(LineIcons.heartbeat_solid,
|
child: Column(
|
||||||
size: 80, color: Colors.grey[500]),
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
Padding(padding: EdgeInsets.symmetric(vertical: 10)),
|
children: [
|
||||||
Text(
|
Icon(LineIcons.heartbeat_solid,
|
||||||
'No episode collected yet',
|
size: 80, color: Colors.grey[500]),
|
||||||
style: TextStyle(color: Colors.grey[500]),
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10)),
|
||||||
|
Text(
|
||||||
|
'No episode collected yet',
|
||||||
|
style: TextStyle(color: Colors.grey[500]),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
: NotificationListener<ScrollNotification>(
|
||||||
),
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
)
|
if (scrollInfo.metrics.pixels ==
|
||||||
: NotificationListener<ScrollNotification>(
|
scrollInfo.metrics.maxScrollExtent &&
|
||||||
onNotification: (ScrollNotification scrollInfo) {
|
snapshot.data.length == _top)
|
||||||
if (scrollInfo.metrics.pixels ==
|
_loadMoreEpisode();
|
||||||
scrollInfo.metrics.maxScrollExtent &&
|
return true;
|
||||||
snapshot.data.length == _top) _loadMoreEpisode();
|
},
|
||||||
return true;
|
child: CustomScrollView(
|
||||||
},
|
key: PageStorageKey<String>('favorite'),
|
||||||
child: CustomScrollView(
|
slivers: <Widget>[
|
||||||
key: PageStorageKey<String>('favorite'),
|
SliverToBoxAdapter(
|
||||||
slivers: <Widget>[
|
child: Container(
|
||||||
SliverToBoxAdapter(
|
height: 40,
|
||||||
child: Container(
|
color: context.primaryColor,
|
||||||
height: 40,
|
child: Row(
|
||||||
color: context.primaryColor,
|
children: <Widget>[
|
||||||
child: Row(
|
Material(
|
||||||
children: <Widget>[
|
color: Colors.transparent,
|
||||||
Material(
|
child: PopupMenuButton<int>(
|
||||||
color: Colors.transparent,
|
shape: RoundedRectangleBorder(
|
||||||
child: PopupMenuButton<int>(
|
borderRadius: BorderRadius.all(
|
||||||
shape: RoundedRectangleBorder(
|
Radius.circular(10))),
|
||||||
borderRadius: BorderRadius.all(
|
elevation: 1,
|
||||||
Radius.circular(10))),
|
tooltip: 'Sort By',
|
||||||
elevation: 1,
|
child: Container(
|
||||||
tooltip: 'Sort By',
|
height: 50,
|
||||||
child: Container(
|
|
||||||
height: 50,
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 20),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Text('Sory by'),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: 5),
|
horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Sory by'),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(
|
||||||
|
horizontal: 5),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
_sortBy == 0
|
||||||
|
? LineIcons
|
||||||
|
.cloud_download_alt_solid
|
||||||
|
: LineIcons
|
||||||
|
.heartbeat_solid,
|
||||||
|
size: 18,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 0,
|
||||||
|
child: Text('Update Date'),
|
||||||
),
|
),
|
||||||
Icon(
|
PopupMenuItem(
|
||||||
_sortBy == 0
|
value: 1,
|
||||||
? LineIcons
|
child: Text('Like Date'),
|
||||||
.cloud_download_alt_solid
|
|
||||||
: LineIcons.heartbeat_solid,
|
|
||||||
size: 18,
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)),
|
onSelected: (value) {
|
||||||
itemBuilder: (context) => [
|
if (value == 0)
|
||||||
PopupMenuItem(
|
setState(() => _sortBy = 0);
|
||||||
value: 0,
|
else if (value == 1)
|
||||||
child: Text('Update Date'),
|
setState(() => _sortBy = 1);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
onPressed: () {
|
||||||
|
if (_layout == Layout.three)
|
||||||
|
setState(() {
|
||||||
|
_layout = Layout.one;
|
||||||
|
});
|
||||||
|
else if (_layout == Layout.two)
|
||||||
|
setState(() {
|
||||||
|
_layout = Layout.three;
|
||||||
|
});
|
||||||
|
else
|
||||||
|
setState(() {
|
||||||
|
_layout = Layout.two;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: _layout == Layout.three
|
||||||
|
? SizedBox(
|
||||||
|
height: 10,
|
||||||
|
width: 30,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: LayoutPainter(
|
||||||
|
0,
|
||||||
|
context
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.color),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: _layout == Layout.two
|
||||||
|
? SizedBox(
|
||||||
|
height: 10,
|
||||||
|
width: 30,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: LayoutPainter(
|
||||||
|
1,
|
||||||
|
context
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.color),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
height: 10,
|
||||||
|
width: 30,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: LayoutPainter(
|
||||||
|
4,
|
||||||
|
context
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.color),
|
||||||
|
),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
|
||||||
value: 1,
|
|
||||||
child: Text('Like Date'),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
onSelected: (value) {
|
)),
|
||||||
if (value == 0)
|
),
|
||||||
setState(() => _sortBy = 0);
|
EpisodeGrid(
|
||||||
else if (value == 1)
|
episodes: snapshot.data,
|
||||||
setState(() => _sortBy = 1);
|
layout: _layout,
|
||||||
},
|
initNum: 0,
|
||||||
),
|
),
|
||||||
),
|
SliverList(
|
||||||
Spacer(),
|
delegate: SliverChildBuilderDelegate(
|
||||||
Material(
|
(BuildContext context, int index) {
|
||||||
color: Colors.transparent,
|
return _loadMore
|
||||||
child: IconButton(
|
? Container(
|
||||||
padding: EdgeInsets.zero,
|
height: 2,
|
||||||
onPressed: () {
|
child: LinearProgressIndicator())
|
||||||
if (_layout == Layout.three)
|
: Center();
|
||||||
setState(() {
|
},
|
||||||
_layout = Layout.one;
|
childCount: 1,
|
||||||
});
|
),
|
||||||
else if (_layout == Layout.two)
|
),
|
||||||
setState(() {
|
],
|
||||||
_layout = Layout.three;
|
|
||||||
});
|
|
||||||
else
|
|
||||||
setState(() {
|
|
||||||
_layout = Layout.two;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: _layout == Layout.three
|
|
||||||
? SizedBox(
|
|
||||||
height: 10,
|
|
||||||
width: 30,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: LayoutPainter(
|
|
||||||
0,
|
|
||||||
context.textTheme
|
|
||||||
.bodyText1.color),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: _layout == Layout.two
|
|
||||||
? SizedBox(
|
|
||||||
height: 10,
|
|
||||||
width: 30,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: LayoutPainter(
|
|
||||||
1,
|
|
||||||
context.textTheme
|
|
||||||
.bodyText1.color),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
height: 10,
|
|
||||||
width: 30,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: LayoutPainter(
|
|
||||||
4,
|
|
||||||
context.textTheme
|
|
||||||
.bodyText1.color),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
EpisodeGrid(
|
|
||||||
episodes: snapshot.data,
|
|
||||||
layout: _layout,
|
|
||||||
initNum: 0,
|
|
||||||
),
|
|
||||||
SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(BuildContext context, int index) {
|
|
||||||
return _loadMore
|
|
||||||
? Container(
|
|
||||||
height: 2,
|
|
||||||
child: LinearProgressIndicator())
|
|
||||||
: Center();
|
|
||||||
},
|
|
||||||
childCount: 1,
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
],
|
: Center();
|
||||||
),
|
},
|
||||||
)
|
);
|
||||||
: Center();
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -12,6 +12,7 @@ import 'package:feature_discovery/feature_discovery.dart';
|
|||||||
import '../type/episodebrief.dart';
|
import '../type/episodebrief.dart';
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
import '../state/subscribe_podcast.dart';
|
import '../state/subscribe_podcast.dart';
|
||||||
|
import '../state/download_state.dart';
|
||||||
import '../type/podcastlocal.dart';
|
import '../type/podcastlocal.dart';
|
||||||
import '../state/audiostate.dart';
|
import '../state/audiostate.dart';
|
||||||
import '../util/custompaint.dart';
|
import '../util/custompaint.dart';
|
||||||
@ -19,6 +20,7 @@ import '../util/pageroute.dart';
|
|||||||
import '../util/colorize.dart';
|
import '../util/colorize.dart';
|
||||||
import '../util/context_extension.dart';
|
import '../util/context_extension.dart';
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
|
import '../local_storage/key_value_storage.dart';
|
||||||
import '../episodes/episodedetail.dart';
|
import '../episodes/episodedetail.dart';
|
||||||
import '../podcasts/podcastdetail.dart';
|
import '../podcasts/podcastdetail.dart';
|
||||||
import '../podcasts/podcastmanage.dart';
|
import '../podcasts/podcastmanage.dart';
|
||||||
@ -478,11 +480,61 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> _isListened(EpisodeBrief episode) async {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
return await dbHelper.isListened(episode.enclosureUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _isLiked(EpisodeBrief episode) async {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
return await dbHelper.isLiked(episode.enclosureUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<int>> _getEpisodeMenu() async {
|
||||||
|
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
|
||||||
|
List<int> list = await popupMenuStorage.getMenu();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _isDownloaded(EpisodeBrief episode) async {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
return await dbHelper.isDownloaded(episode.enclosureUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
_markListened(EpisodeBrief episode) async {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
bool marked = await dbHelper.checkMarked(episode);
|
||||||
|
if (!marked) {
|
||||||
|
final PlayHistory history =
|
||||||
|
PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
|
||||||
|
await dbHelper.saveHistory(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _saveLiked(String url) async {
|
||||||
|
var dbHelper = DBHelper();
|
||||||
|
int result = await dbHelper.setLiked(url);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _setUnliked(String url) async {
|
||||||
|
var dbHelper = DBHelper();
|
||||||
|
int result = await dbHelper.setUniked(url);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
_showPopupMenu(Offset offset, EpisodeBrief episode, BuildContext context,
|
_showPopupMenu(Offset offset, EpisodeBrief episode, BuildContext context,
|
||||||
bool isPlaying, bool isInPlaylist) async {
|
bool isPlaying, bool isInPlaylist) async {
|
||||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||||
double left = offset.dx;
|
double left = offset.dx;
|
||||||
double top = offset.dy;
|
double top = offset.dy;
|
||||||
|
bool isLiked, isDownload;
|
||||||
|
int isListened;
|
||||||
|
var downloader = Provider.of<DownloadState>(context, listen: false);
|
||||||
|
List<int> menuList = await _getEpisodeMenu();
|
||||||
|
if (menuList.contains(3)) isListened = await _isListened(episode);
|
||||||
|
if (menuList.contains(2)) isLiked = await _isLiked(episode);
|
||||||
|
if (menuList.contains(4)) isDownload = await _isDownloaded(episode);
|
||||||
await showMenu<int>(
|
await showMenu<int>(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
@ -506,39 +558,134 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
menuList.contains(1)
|
||||||
value: 1,
|
? PopupMenuItem(
|
||||||
child: Row(
|
value: 1,
|
||||||
children: <Widget>[
|
child: Row(
|
||||||
Icon(
|
children: <Widget>[
|
||||||
LineIcons.clock_solid,
|
Icon(
|
||||||
color: Colors.red,
|
LineIcons.clock_solid,
|
||||||
),
|
color: Colors.red,
|
||||||
Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
Padding(
|
||||||
),
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
!isInPlaylist ? Text('Later') : Text('Remove')
|
),
|
||||||
],
|
!isInPlaylist ? Text('Later') : Text('Remove')
|
||||||
)),
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
menuList.contains(2)
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: 2,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(LineIcons.heart, color: Colors.red, size: 21),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
),
|
||||||
|
isLiked
|
||||||
|
? Text(
|
||||||
|
'Unlike',
|
||||||
|
)
|
||||||
|
: Text('Like')
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
menuList.contains(3)
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: 3,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 23,
|
||||||
|
height: 23,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter:
|
||||||
|
ListenedAllPainter(Colors.blue, stroke: 1.5)),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
),
|
||||||
|
isListened > 0.95
|
||||||
|
? Text('Listened',
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.textColor.withOpacity(0.5)))
|
||||||
|
: Text('Mark\nListened')
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
menuList.contains(4)
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: 4,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(LineIcons.download_solid, color: Colors.green),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
),
|
||||||
|
isDownload
|
||||||
|
? Text('Downloaded',
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.textColor.withOpacity(0.5)))
|
||||||
|
: Text('Download')
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
],
|
],
|
||||||
elevation: 5.0,
|
elevation: 5.0,
|
||||||
).then((value) {
|
).then((value) async {
|
||||||
if (value == 0) {
|
switch (value) {
|
||||||
if (!isPlaying) audio.episodeLoad(episode);
|
case 0:
|
||||||
} else if (value == 1) {
|
if (!isPlaying) audio.episodeLoad(episode);
|
||||||
if (!isInPlaylist) {
|
break;
|
||||||
audio.addToPlaylist(episode);
|
case 1:
|
||||||
Fluttertoast.showToast(
|
if (!isInPlaylist) {
|
||||||
msg: 'Added to playlist',
|
audio.addToPlaylist(episode);
|
||||||
gravity: ToastGravity.BOTTOM,
|
Fluttertoast.showToast(
|
||||||
);
|
msg: 'Added to playlist',
|
||||||
} else {
|
gravity: ToastGravity.BOTTOM,
|
||||||
audio.delFromPlaylist(episode);
|
);
|
||||||
Fluttertoast.showToast(
|
} else {
|
||||||
msg: 'Removed from playlist',
|
audio.delFromPlaylist(episode);
|
||||||
gravity: ToastGravity.BOTTOM,
|
Fluttertoast.showToast(
|
||||||
);
|
msg: 'Removed from playlist',
|
||||||
}
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (isLiked) {
|
||||||
|
await _setUnliked(episode.enclosureUrl);
|
||||||
|
audio.setEpisodeState = true;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Unliked',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await _saveLiked(episode.enclosureUrl);
|
||||||
|
audio.setEpisodeState = true;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Liked',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (isListened < 0.95) {
|
||||||
|
await _markListened(episode);
|
||||||
|
audio.setEpisodeState = true;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Mark listened',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
if (!isDownload) downloader.startTask(episode);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ const String podcastLayoutKey = 'podcastLayoutKey';
|
|||||||
const String recentLayoutKey = 'recentLayoutKey';
|
const String recentLayoutKey = 'recentLayoutKey';
|
||||||
const String favLayoutKey = 'favLayoutKey';
|
const String favLayoutKey = 'favLayoutKey';
|
||||||
const String downloadLayoutKey = 'downloadLayoutKey';
|
const String downloadLayoutKey = 'downloadLayoutKey';
|
||||||
|
const String autoDownloadNetworkKey = 'autoDownloadNetwork';
|
||||||
|
const String episodePopupMenuKey = 'episodePopupMenuKey';
|
||||||
|
|
||||||
class KeyValueStorage {
|
class KeyValueStorage {
|
||||||
final String key;
|
final String key;
|
||||||
@ -88,4 +90,18 @@ class KeyValueStorage {
|
|||||||
}
|
}
|
||||||
return prefs.getString(key);
|
return prefs.getString(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveMenu(List<int> list) async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setStringList(key, list.map((e) => e.toString()).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<int>> getMenu() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.getStringList(key) == null) {
|
||||||
|
await prefs.setStringList(key, ['0', '1', '12', '13', '14']);
|
||||||
|
}
|
||||||
|
List<String> list = prefs.getStringList(key);
|
||||||
|
return list.map((e) => int.parse(e)).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -382,7 +382,6 @@ class DBHelper {
|
|||||||
String month = mmDd.stringMatch(pubDate);
|
String month = mmDd.stringMatch(pubDate);
|
||||||
date = DateFormat('yyyy-MM-dd HH:mm', 'en_US')
|
date = DateFormat('yyyy-MM-dd HH:mm', 'en_US')
|
||||||
.parse(month + ' ' + time);
|
.parse(month + ' ' + time);
|
||||||
print(month);
|
|
||||||
print(date.toString());
|
print(date.toString());
|
||||||
} else {
|
} else {
|
||||||
date = DateTime.now();
|
date = DateTime.now();
|
||||||
@ -832,36 +831,36 @@ class DBHelper {
|
|||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> gettNewRssItem(String id) async {
|
//Future<List<EpisodeBrief>> getNewRssItem(String id) async {
|
||||||
var dbClient = await database;
|
// var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = [];
|
// List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient.rawQuery(
|
// List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
// """SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
// E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
// E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
// FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE is_new = 1 AND downloaded != 'ND' AND P.id = ?ORDER BY E.milliseconds DESC """,
|
// WHERE is_new = 1 AND downloaded != 'ND' AND P.id = ?ORDER BY E.milliseconds DESC """,
|
||||||
[id],
|
// [id],
|
||||||
);
|
// );
|
||||||
for (int x = 0; x < list.length; x++) {
|
// for (int x = 0; x < list.length; x++) {
|
||||||
episodes.add(EpisodeBrief(
|
// episodes.add(EpisodeBrief(
|
||||||
list[x]['title'],
|
// list[x]['title'],
|
||||||
list[x]['enclosure_url'],
|
// list[x]['enclosure_url'],
|
||||||
list[x]['enclosure_length'],
|
// list[x]['enclosure_length'],
|
||||||
list[x]['milliseconds'],
|
// list[x]['milliseconds'],
|
||||||
list[x]['feed_title'],
|
// list[x]['feed_title'],
|
||||||
list[x]['primaryColor'],
|
// list[x]['primaryColor'],
|
||||||
list[x]['liked'],
|
// list[x]['liked'],
|
||||||
list[x]['downloaded'],
|
// list[x]['downloaded'],
|
||||||
list[x]['duration'],
|
// list[x]['duration'],
|
||||||
list[x]['explicit'],
|
// list[x]['explicit'],
|
||||||
list[x]['imagePath'],
|
// list[x]['imagePath'],
|
||||||
list[x]['media_id'],
|
// list[x]['media_id'],
|
||||||
list[x]['is_new'],
|
// list[x]['is_new'],
|
||||||
list[x]['skip_seconds']));
|
// list[x]['skip_seconds']));
|
||||||
}
|
// }
|
||||||
return episodes;
|
// return episodes;
|
||||||
}
|
//}
|
||||||
|
|
||||||
Future<int> removeAllNewMark() async {
|
Future<int> removeAllNewMark() async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
@ -994,6 +993,13 @@ class DBHelper {
|
|||||||
return list.first['liked'] == 0 ? false : true;
|
return list.first['liked'] == 0 ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isDownloaded(String url) async {
|
||||||
|
var dbClient = await database;
|
||||||
|
List<Map> list = await dbClient
|
||||||
|
.rawQuery("SELECT downloaded FROM Episodes WHERE enclosure_url = ?", [url]);
|
||||||
|
return list.first['downloaded'] == 'ND' ? false: true;
|
||||||
|
}
|
||||||
|
|
||||||
Future<int> saveDownloaded(String url, String id) async {
|
Future<int> saveDownloaded(String url, String id) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
int milliseconds = DateTime.now().millisecondsSinceEpoch;
|
int milliseconds = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:connectivity/connectivity.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:tsacdop/state/download_state.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||||
@ -56,6 +58,26 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
msg: 'Updated $result Episodes',
|
msg: 'Updated $result Episodes',
|
||||||
gravity: ToastGravity.TOP,
|
gravity: ToastGravity.TOP,
|
||||||
);
|
);
|
||||||
|
bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
|
||||||
|
if (autoDownload) {
|
||||||
|
var result = await Connectivity().checkConnectivity();
|
||||||
|
KeyValueStorage autoDownloadStorage =
|
||||||
|
KeyValueStorage(autoDownloadNetworkKey);
|
||||||
|
int autoDownloadNetwork = await autoDownloadStorage.getInt();
|
||||||
|
if (autoDownloadNetwork == 1) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||||
|
episodes.forEach((episode) {
|
||||||
|
DownloadState().startTask(episode, showNotification: false);
|
||||||
|
});
|
||||||
|
} else if (result == ConnectivityResult.wifi) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||||
|
episodes.forEach((episode) {
|
||||||
|
DownloadState().startTask(episode, showNotification: false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
// Provider.of<GroupList>(context, listen: false)
|
// Provider.of<GroupList>(context, listen: false)
|
||||||
// .updatePodcast(podcastLocal.id);
|
// .updatePodcast(podcastLocal.id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,14 +5,12 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
|
||||||
|
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
import '../type/podcastlocal.dart';
|
import '../type/podcastlocal.dart';
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
import '../podcasts/podcastdetail.dart';
|
|
||||||
import '../util/pageroute.dart';
|
|
||||||
import '../util/colorize.dart';
|
import '../util/colorize.dart';
|
||||||
import '../util/duraiton_picker.dart';
|
import '../util/duraiton_picker.dart';
|
||||||
import '../util/context_extension.dart';
|
import '../util/context_extension.dart';
|
||||||
@ -98,8 +96,11 @@ class _PodcastCardState extends State<PodcastCard>
|
|||||||
}
|
}
|
||||||
|
|
||||||
_setAutoDownload(String id, bool boo) async {
|
_setAutoDownload(String id, bool boo) async {
|
||||||
DBHelper dbHelper = DBHelper();
|
bool permission = await _checkPermmison();
|
||||||
await dbHelper.saveAutoDownload(id, boo);
|
if (permission) {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
await dbHelper.saveAutoDownload(id, boo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _getAutoDownload(String id) async {
|
Future<bool> _getAutoDownload(String id) async {
|
||||||
@ -107,6 +108,21 @@ class _PodcastCardState extends State<PodcastCard>
|
|||||||
return await dbHelper.getAutoDownload(id);
|
return await dbHelper.getAutoDownload(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _checkPermmison() async {
|
||||||
|
PermissionStatus permission = await Permission.storage.status;
|
||||||
|
if (permission != PermissionStatus.granted) {
|
||||||
|
Map<Permission, PermissionStatus> permissions =
|
||||||
|
await [Permission.storage].request();
|
||||||
|
if (permissions[Permission.storage] == PermissionStatus.granted) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _stringForSeconds(double seconds) {
|
String _stringForSeconds(double seconds) {
|
||||||
if (seconds == null) return null;
|
if (seconds == null) return null;
|
||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
@ -344,13 +360,27 @@ class _PodcastCardState extends State<PodcastCard>
|
|||||||
initialData: false,
|
initialData: false,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return _buttonOnMenu(
|
return _buttonOnMenu(
|
||||||
icon: Icon(
|
icon: Container(
|
||||||
LineIcons.cloud_download_alt_solid,
|
child: Icon(Icons.done_all,
|
||||||
size: _value == 0 ? 1 : 20 * _value,
|
size: _value * 15,
|
||||||
color: snapshot.data
|
color: snapshot.data
|
||||||
? context.accentColor
|
? Colors.white
|
||||||
: null),
|
: null),
|
||||||
tooltip: 'AutoDownload',
|
height: _value == 0 ? 1 : 18 * _value,
|
||||||
|
width: _value == 0 ? 1 : 18 * _value,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: snapshot.data
|
||||||
|
? context.accentColor
|
||||||
|
: context.textTheme.subtitle1
|
||||||
|
.color),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: snapshot.data
|
||||||
|
? context.accentColor
|
||||||
|
: null),
|
||||||
|
),
|
||||||
|
tooltip: 'Auto Download',
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_setAutoDownload(widget.podcastLocal.id,
|
_setAutoDownload(widget.podcastLocal.id,
|
||||||
!snapshot.data);
|
!snapshot.data);
|
||||||
|
@ -58,7 +58,6 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
|||||||
_delSelectedEpisodes() async {
|
_delSelectedEpisodes() async {
|
||||||
setState(() => _clearing = true);
|
setState(() => _clearing = true);
|
||||||
await Future.forEach(_selectedList, (EpisodeBrief episode) async {
|
await Future.forEach(_selectedList, (EpisodeBrief episode) async {
|
||||||
print(episode.downloaded);
|
|
||||||
await FlutterDownloader.remove(
|
await FlutterDownloader.remove(
|
||||||
taskId: episode.downloaded, shouldDeleteContent: true);
|
taskId: episode.downloaded, shouldDeleteContent: true);
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
@ -212,7 +211,6 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
|||||||
value: _selectedList
|
value: _selectedList
|
||||||
.contains(_episodes[index]),
|
.contains(_episodes[index]),
|
||||||
onChanged: (bool boo) {
|
onChanged: (bool boo) {
|
||||||
print(boo);
|
|
||||||
if (boo) {
|
if (boo) {
|
||||||
setState(() => _selectedList
|
setState(() => _selectedList
|
||||||
.add(_episodes[index]));
|
.add(_episodes[index]));
|
||||||
|
@ -117,7 +117,6 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
|||||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
systemNavigationBarIconBrightness:
|
systemNavigationBarIconBrightness:
|
||||||
Theme.of(context).accentColorBrightness,
|
Theme.of(context).accentColorBrightness,
|
||||||
//statusBarColor: Theme.of(context).primaryColor,
|
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
@ -5,6 +5,7 @@ import '../util/context_extension.dart';
|
|||||||
import '../util/episodegrid.dart';
|
import '../util/episodegrid.dart';
|
||||||
import '../util/custompaint.dart';
|
import '../util/custompaint.dart';
|
||||||
import '../local_storage/key_value_storage.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
|
import 'popup_menu.dart';
|
||||||
|
|
||||||
class LayoutSetting extends StatefulWidget {
|
class LayoutSetting extends StatefulWidget {
|
||||||
const LayoutSetting({Key key}) : super(key: key);
|
const LayoutSetting({Key key}) : super(key: key);
|
||||||
@ -23,7 +24,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||||||
Widget _gridOptions(BuildContext context,
|
Widget _gridOptions(BuildContext context,
|
||||||
{String key, Layout layout, Layout option, double scale}) =>
|
{String key, Layout layout, Layout option, double scale}) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 10.0, bottom: 10.0, left: 20.0),
|
padding: const EdgeInsets.only(top: 10.0, bottom: 10.0),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
KeyValueStorage storage = KeyValueStorage(key);
|
KeyValueStorage storage = KeyValueStorage(key);
|
||||||
@ -34,7 +35,9 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
height: 30,
|
height: 30,
|
||||||
width: 50,
|
width: 50,
|
||||||
color: layout == option ? context.accentColor : Colors.transparent,
|
color: layout == option
|
||||||
|
? context.accentColor
|
||||||
|
: context.primaryColorDark,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
@ -80,6 +83,32 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _setDefaultGridView(BuildContext context, {String text, String key}) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(left: 80.0, right: 20, bottom: 10),
|
||||||
|
child: context.width > 360
|
||||||
|
? Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
_setDefaultGrid(context, key: key),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
),
|
||||||
|
_setDefaultGrid(context, key: key),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
@ -106,7 +135,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||||||
height: 30.0,
|
height: 30.0,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 80),
|
padding: EdgeInsets.symmetric(horizontal: 80),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text('Default grid view',
|
child: Text('Episode popup menu',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyText1
|
.bodyText1
|
||||||
@ -118,44 +147,42 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding:
|
onTap: () => Navigator.push(
|
||||||
EdgeInsets.only(left: 80.0, right: 20, bottom: 10),
|
context,
|
||||||
// leading: Icon(Icons.colorize),
|
MaterialPageRoute(
|
||||||
title: Text(
|
builder: (context) => PopupMenuSetting())),
|
||||||
'Podcast page',
|
contentPadding: EdgeInsets.only(left: 80.0, right: 20),
|
||||||
),
|
title: Text('Episode popup menu'),
|
||||||
subtitle:
|
subtitle: Text('Change the menu when long tap episode'),
|
||||||
_setDefaultGrid(context, key: podcastLayoutKey),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.only(left: 80.0, right: 20, bottom: 10),
|
|
||||||
// leading: Icon(Icons.colorize),
|
|
||||||
title: Text(
|
|
||||||
'Recent tab in homepage',
|
|
||||||
),
|
|
||||||
subtitle:
|
|
||||||
_setDefaultGrid(context, key: recentLayoutKey),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.only(left: 80.0, right: 20, bottom: 10),
|
|
||||||
// leading: Icon(Icons.colorize),
|
|
||||||
title: Text(
|
|
||||||
'Favorite tab in homepage',
|
|
||||||
),
|
|
||||||
subtitle: _setDefaultGrid(context, key: favLayoutKey),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.only(left: 80.0, right: 20, bottom: 10),
|
|
||||||
// leading: Icon(Icons.colorize),
|
|
||||||
title: Text(
|
|
||||||
'Download tab in homepage',
|
|
||||||
),
|
|
||||||
subtitle: _setDefaultGrid(context, key: downloadLayoutKey),
|
|
||||||
),
|
),
|
||||||
Divider(height: 2),
|
Divider(height: 2),
|
||||||
|
Container(
|
||||||
|
height: 30.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 80),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text('Default grid view',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context).accentColor)),
|
||||||
|
),
|
||||||
|
ListView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
children: <Widget>[
|
||||||
|
_setDefaultGridView(context,
|
||||||
|
text: 'Podcast page', key: podcastLayoutKey),
|
||||||
|
_setDefaultGridView(context,
|
||||||
|
text: 'Recent tab', key: recentLayoutKey),
|
||||||
|
_setDefaultGridView(context,
|
||||||
|
text: 'Favorite tab', key: favLayoutKey),
|
||||||
|
_setDefaultGridView(context,
|
||||||
|
text: 'Downlaod tab', key: downloadLayoutKey),
|
||||||
|
Divider(height: 2),
|
||||||
|
]),
|
||||||
|
Divider(height: 2)
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
176
lib/settings/popup_menu.dart
Normal file
176
lib/settings/popup_menu.dart
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
import 'package:flare_flutter/flare_actor.dart';
|
||||||
|
|
||||||
|
import '../util/context_extension.dart';
|
||||||
|
import '../util/custompaint.dart';
|
||||||
|
import '../local_storage/key_value_storage.dart';
|
||||||
|
|
||||||
|
class PopupMenuSetting extends StatefulWidget {
|
||||||
|
const PopupMenuSetting({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PopupMenuSettingState createState() => _PopupMenuSettingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PopupMenuSettingState extends State<PopupMenuSetting> {
|
||||||
|
Future<List<int>> _getEpisodeMenu() async {
|
||||||
|
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
|
||||||
|
List<int> list = await popupMenuStorage.getMenu();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveEpisodeMene(List<int> list) async {
|
||||||
|
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
|
||||||
|
await popupMenuStorage.saveMenu(list);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _popupMenuItem(List<int> menu, int e,
|
||||||
|
{Widget icon,
|
||||||
|
String text,
|
||||||
|
String description = '',
|
||||||
|
bool enable = false}) {
|
||||||
|
return Padding(
|
||||||
|
key: ObjectKey(text),
|
||||||
|
padding: EdgeInsets.only(left: 60.0, right: 20),
|
||||||
|
child: ListTile(
|
||||||
|
leading: icon,
|
||||||
|
title: Text(text),
|
||||||
|
subtitle: Text(description),
|
||||||
|
trailing: Checkbox(
|
||||||
|
value: e < 10,
|
||||||
|
onChanged: e == 0
|
||||||
|
? null
|
||||||
|
: (bool boo) {
|
||||||
|
if (boo && e >= 10) {
|
||||||
|
int index = menu.indexOf(e);
|
||||||
|
menu.remove(e);
|
||||||
|
menu.insert(index, e - 10);
|
||||||
|
_saveEpisodeMene(menu);
|
||||||
|
} else if (e < 10) {
|
||||||
|
int index = menu.indexOf(e);
|
||||||
|
menu.remove(e);
|
||||||
|
menu.insert(index, e + 10);
|
||||||
|
_saveEpisodeMene(menu);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
systemNavigationBarColor: context.primaryColor,
|
||||||
|
systemNavigationBarIconBrightness:
|
||||||
|
Theme.of(context).accentColorBrightness,
|
||||||
|
),
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: context.primaryColor,
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
color: context.primaryColor,
|
||||||
|
height: 200,
|
||||||
|
// color: Colors.red,
|
||||||
|
child: FlareActor(
|
||||||
|
'assets/longtap.flr',
|
||||||
|
alignment: Alignment.center,
|
||||||
|
animation: 'longtap',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)),
|
||||||
|
Divider(height: 2),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 30.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 80),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text('Episode popup menu',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(color: Theme.of(context).accentColor)),
|
||||||
|
),
|
||||||
|
FutureBuilder<List<int>>(
|
||||||
|
future: _getEpisodeMenu(),
|
||||||
|
initialData: [0, 1, 12, 13, 14],
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
List<int> menu = snapshot.data;
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
children: menu.map<Widget>((int e) {
|
||||||
|
int i = e % 10;
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
return _popupMenuItem(menu, e,
|
||||||
|
icon: Icon(
|
||||||
|
LineIcons.play_circle_solid,
|
||||||
|
color: context.accentColor,
|
||||||
|
),
|
||||||
|
text: 'Play',
|
||||||
|
description: 'Play the episode');
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
return _popupMenuItem(menu, e,
|
||||||
|
icon: Icon(
|
||||||
|
LineIcons.clock_solid,
|
||||||
|
color: Colors.cyan,
|
||||||
|
),
|
||||||
|
text: 'Later',
|
||||||
|
description: 'Add episode to playlist');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
return _popupMenuItem(menu, e,
|
||||||
|
icon: Icon(
|
||||||
|
LineIcons.heart,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 21
|
||||||
|
),
|
||||||
|
text: 'Like',
|
||||||
|
description: 'Add episode to favorite');
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
return _popupMenuItem(menu, e,
|
||||||
|
icon: SizedBox(
|
||||||
|
width: 23,
|
||||||
|
height: 23,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: ListenedAllPainter(Colors.blue,
|
||||||
|
stroke: 1.5)),
|
||||||
|
),
|
||||||
|
text: 'Mark Listened',
|
||||||
|
description: 'Mark episode as listened');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return _popupMenuItem(menu, e,
|
||||||
|
icon: Icon(
|
||||||
|
LineIcons.download_solid,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
text: 'Download',
|
||||||
|
description: 'Download episode');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Text('Text');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ class _StorageSettingState extends State<StorageSetting>
|
|||||||
Animation<double> _animation;
|
Animation<double> _animation;
|
||||||
_getCacheMax() async {
|
_getCacheMax() async {
|
||||||
int cache = await cacheStorage.getInt();
|
int cache = await cacheStorage.getInt();
|
||||||
int value = cache == 0 ? 500 : cache ~/ (1024 * 1024);
|
int value = cache == 0 ? 200 : cache ~/ (1024 * 1024);
|
||||||
if (value > 100) {
|
if (value > 100) {
|
||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
vsync: this, duration: Duration(milliseconds: value * 2));
|
vsync: this, duration: Duration(milliseconds: value * 2));
|
||||||
@ -38,6 +38,17 @@ class _StorageSettingState extends State<StorageSetting>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _getAutoDownloadNetwork() async {
|
||||||
|
KeyValueStorage storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||||
|
int value = await storage.getInt();
|
||||||
|
return value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setAudtDownloadNetwork(bool boo) async {
|
||||||
|
KeyValueStorage storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||||
|
await storage.saveInt(boo ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
double _value;
|
double _value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -96,29 +107,50 @@ class _StorageSettingState extends State<StorageSetting>
|
|||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
Selector<SettingState, bool>(
|
||||||
onTap: () => Navigator.push(
|
selector: (_, settings) => settings.downloadUsingData,
|
||||||
context,
|
builder: (_, data, __) {
|
||||||
MaterialPageRoute(
|
return ListTile(
|
||||||
builder: (context) => DownloadsManage())),
|
onTap: () => settings.downloadUsingData = !data,
|
||||||
contentPadding: EdgeInsets.only(
|
contentPadding: EdgeInsets.only(
|
||||||
left: 80.0, right: 25, bottom: 10, top: 10),
|
left: 80.0, right: 25, bottom: 10, top: 10),
|
||||||
title: Text('Ask before using cellular data'),
|
title: Text('Ask before using cellular data'),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'Ask to confirm when using cellular data to download episodes.'),
|
'Ask to confirm when using cellular data to download episodes.'),
|
||||||
trailing: Selector<SettingState, bool>(
|
trailing: Switch(
|
||||||
selector: (_, settings) =>
|
|
||||||
settings.downloadUsingData,
|
|
||||||
builder: (_, data, __) {
|
|
||||||
return Switch(
|
|
||||||
value: data,
|
value: data,
|
||||||
onChanged: (value) =>
|
onChanged: (value) =>
|
||||||
settings.downloadUsingData = value,
|
settings.downloadUsingData = value,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
Divider(height: 2),
|
Divider(height: 2),
|
||||||
|
FutureBuilder<bool>(
|
||||||
|
future: _getAutoDownloadNetwork(),
|
||||||
|
initialData: false,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return ListTile(
|
||||||
|
onTap: () async {
|
||||||
|
_setAudtDownloadNetwork(!snapshot.data);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
contentPadding: EdgeInsets.only(
|
||||||
|
left: 80.0, right: 25, bottom: 10, top: 10),
|
||||||
|
title:
|
||||||
|
Text('Auto download using cellular data'),
|
||||||
|
subtitle: Text(
|
||||||
|
'You can set podcast auto download in group manage page.'),
|
||||||
|
trailing: Switch(
|
||||||
|
value: snapshot.data,
|
||||||
|
onChanged: (value) async {
|
||||||
|
await _setAudtDownloadNetwork(value);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(height: 2),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@ -151,7 +183,7 @@ class _StorageSettingState extends State<StorageSetting>
|
|||||||
builder: (context) => DownloadsManage())),
|
builder: (context) => DownloadsManage())),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 80.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 80.0),
|
||||||
title: Text('Downloads'),
|
title: Text('Downloads'),
|
||||||
subtitle: Text('Manage doanloaded audio files'),
|
subtitle: Text('Manage downloaded audio files'),
|
||||||
),
|
),
|
||||||
Divider(height: 2),
|
Divider(height: 2),
|
||||||
ListTile(
|
ListTile(
|
||||||
@ -175,8 +207,10 @@ class _StorageSettingState extends State<StorageSetting>
|
|||||||
left: 60.0, right: 20.0, bottom: 10.0),
|
left: 60.0, right: 20.0, bottom: 10.0),
|
||||||
child: SliderTheme(
|
child: SliderTheme(
|
||||||
data: Theme.of(context).sliderTheme.copyWith(
|
data: Theme.of(context).sliderTheme.copyWith(
|
||||||
showValueIndicator: ShowValueIndicator.always,
|
showValueIndicator: ShowValueIndicator.always,
|
||||||
),
|
trackHeight: 2,
|
||||||
|
thumbShape:
|
||||||
|
RoundSliderThumbShape(enabledThumbRadius: 6)),
|
||||||
child: Slider(
|
child: Slider(
|
||||||
label: '${_value ~/ 100 * 100} Mb',
|
label: '${_value ~/ 100 * 100} Mb',
|
||||||
activeColor: context.accentColor,
|
activeColor: context.accentColor,
|
||||||
|
@ -155,6 +155,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
int _currentPosition;
|
int _currentPosition;
|
||||||
double _currentSpeed = 1;
|
double _currentSpeed = 1;
|
||||||
BehaviorSubject<List<MediaItem>> queueSubject;
|
BehaviorSubject<List<MediaItem>> queueSubject;
|
||||||
|
//Update episode card when setting changed
|
||||||
|
bool _episodeState = false;
|
||||||
|
|
||||||
BasicPlaybackState get audioState => _audioState;
|
BasicPlaybackState get audioState => _audioState;
|
||||||
|
|
||||||
@ -176,6 +178,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
int get timeLeft => _timeLeft;
|
int get timeLeft => _timeLeft;
|
||||||
double get switchValue => _switchValue;
|
double get switchValue => _switchValue;
|
||||||
double get currentSpeed => _currentSpeed;
|
double get currentSpeed => _currentSpeed;
|
||||||
|
bool get episodeState => _episodeState;
|
||||||
|
|
||||||
set setSwitchValue(double value) {
|
set setSwitchValue(double value) {
|
||||||
_switchValue = value;
|
_switchValue = value;
|
||||||
@ -193,6 +196,11 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set setEpisodeState(bool boo) {
|
||||||
|
_episodeState = !_episodeState;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Future _getAutoPlay() async {
|
Future _getAutoPlay() async {
|
||||||
int i = await autoPlayStorage.getInt();
|
int i = await autoPlayStorage.getInt();
|
||||||
_autoPlay = i == 0 ? true : false;
|
_autoPlay = i == 0 ? true : false;
|
||||||
@ -315,7 +323,6 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
queueSubject.addStream(
|
queueSubject.addStream(
|
||||||
AudioService.queueStream.distinct().where((event) => event != null));
|
AudioService.queueStream.distinct().where((event) => event != null));
|
||||||
queueSubject.stream.listen((event) {
|
queueSubject.stream.listen((event) {
|
||||||
print(event.length);
|
|
||||||
if (event.length == _queue.playlist.length - 1 &&
|
if (event.length == _queue.playlist.length - 1 &&
|
||||||
_audioState == BasicPlaybackState.skippingToNext) {
|
_audioState == BasicPlaybackState.skippingToNext) {
|
||||||
if (event.length == 0 || _stopOnComplete) {
|
if (event.length == 0 || _stopOnComplete) {
|
||||||
@ -506,7 +513,6 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sliderSeek(double val) async {
|
sliderSeek(double val) async {
|
||||||
print(val.toString());
|
|
||||||
if (_audioState != BasicPlaybackState.connecting &&
|
if (_audioState != BasicPlaybackState.connecting &&
|
||||||
_audioState != BasicPlaybackState.none) {
|
_audioState != BasicPlaybackState.none) {
|
||||||
_noSlide = false;
|
_noSlide = false;
|
||||||
@ -633,6 +639,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||||||
AudioPlayer _audioPlayer = AudioPlayer();
|
AudioPlayer _audioPlayer = AudioPlayer();
|
||||||
Completer _completer = Completer();
|
Completer _completer = Completer();
|
||||||
BasicPlaybackState _skipState;
|
BasicPlaybackState _skipState;
|
||||||
|
bool _lostFocus;
|
||||||
bool _playing;
|
bool _playing;
|
||||||
bool _stopAtEnd;
|
bool _stopAtEnd;
|
||||||
int cacheMax;
|
int cacheMax;
|
||||||
@ -662,7 +669,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||||||
@override
|
@override
|
||||||
Future<void> onStart() async {
|
Future<void> onStart() async {
|
||||||
_stopAtEnd = false;
|
_stopAtEnd = false;
|
||||||
|
_lostFocus = false;
|
||||||
var playerStateSubscription = _audioPlayer.playbackStateStream
|
var playerStateSubscription = _audioPlayer.playbackStateStream
|
||||||
.where((state) => state == AudioPlaybackState.completed)
|
.where((state) => state == AudioPlaybackState.completed)
|
||||||
.listen((state) {
|
.listen((state) {
|
||||||
@ -792,9 +799,12 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||||||
@override
|
@override
|
||||||
void onPause() {
|
void onPause() {
|
||||||
if (_skipState == null) {
|
if (_skipState == null) {
|
||||||
if (_playing == null) {}
|
if (_playing == null) {
|
||||||
_playing = false;
|
} else if (_audioPlayer.playbackEvent.state ==
|
||||||
_audioPlayer.pause();
|
AudioPlaybackState.playing) {
|
||||||
|
_playing = false;
|
||||||
|
_audioPlayer.pause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -857,32 +867,45 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||||||
milliseconds: AudioServiceBackground.state.position + 30 * 1000));
|
milliseconds: AudioServiceBackground.state.position + 30 * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRewind() {
|
||||||
|
_audioPlayer.seek(Duration(
|
||||||
|
milliseconds: AudioServiceBackground.state.position - 10 * 1000));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAudioFocusLost() {
|
void onAudioFocusLost() {
|
||||||
if (_skipState == null) {
|
if (_skipState == null) {
|
||||||
if (_playing == null ||
|
if (_playing == null) {
|
||||||
_audioPlayer.playbackState == AudioPlaybackState.none ||
|
} else if (_audioPlayer.playbackEvent.state ==
|
||||||
_audioPlayer.playbackState == AudioPlaybackState.connecting) {}
|
AudioPlaybackState.playing) {
|
||||||
_playing = false;
|
_playing = false;
|
||||||
_audioPlayer.pause();
|
_lostFocus = true;
|
||||||
|
_audioPlayer.pause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAudioBecomingNoisy() {
|
void onAudioBecomingNoisy() {
|
||||||
if (_skipState == null) {
|
if (_skipState == null) {
|
||||||
if (_playing == null) {}
|
if (_playing == null) {
|
||||||
_playing = false;
|
} else if (_audioPlayer.playbackEvent.state ==
|
||||||
_audioPlayer.pause();
|
AudioPlaybackState.playing) {
|
||||||
|
_playing = false;
|
||||||
|
_audioPlayer.pause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAudioFocusGained() {
|
void onAudioFocusGained() {
|
||||||
if (_skipState == null) {
|
if (_skipState == null) {
|
||||||
if (_playing == null) {}
|
if (_lostFocus) {
|
||||||
_playing = true;
|
_lostFocus = false;
|
||||||
_audioPlayer.play();
|
_playing = true;
|
||||||
|
_audioPlayer.play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,8 @@ class DownloadState extends ChangeNotifier {
|
|||||||
final completeTask = await FlutterDownloader.loadTasksWithRawQuery(
|
final completeTask = await FlutterDownloader.loadTasksWithRawQuery(
|
||||||
query: "SELECT * FROM task WHERE task_id = '${episodeTask.taskId}'");
|
query: "SELECT * FROM task WHERE task_id = '${episodeTask.taskId}'");
|
||||||
String filePath = 'file://' +
|
String filePath = 'file://' +
|
||||||
path.join(completeTask.first.savedDir, completeTask.first.filename);
|
path.join(completeTask.first.savedDir,
|
||||||
|
Uri.encodeComponent(completeTask.first.filename));
|
||||||
dbHelper.saveMediaId(
|
dbHelper.saveMediaId(
|
||||||
episodeTask.episode.enclosureUrl, filePath, episodeTask.taskId);
|
episodeTask.episode.enclosureUrl, filePath, episodeTask.taskId);
|
||||||
EpisodeBrief episode =
|
EpisodeBrief episode =
|
||||||
@ -124,7 +125,7 @@ class DownloadState extends ChangeNotifier {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future startTask(EpisodeBrief episode) async {
|
Future startTask(EpisodeBrief episode, {bool showNotification = true}) async {
|
||||||
final dir = await getExternalStorageDirectory();
|
final dir = await getExternalStorageDirectory();
|
||||||
String localPath = path.join(dir.path, episode.feedTitle);
|
String localPath = path.join(dir.path, episode.feedTitle);
|
||||||
final saveDir = Directory(localPath);
|
final saveDir = Directory(localPath);
|
||||||
@ -137,10 +138,7 @@ class DownloadState extends ChangeNotifier {
|
|||||||
now.month.toString() +
|
now.month.toString() +
|
||||||
now.day.toString() +
|
now.day.toString() +
|
||||||
now.second.toString();
|
now.second.toString();
|
||||||
String title = episode.title.trim().substring(0, 1) == '#'
|
String fileName = episode.title +
|
||||||
? episode.title.trim().substring(1)
|
|
||||||
: episode.title.trim();
|
|
||||||
String fileName = title +
|
|
||||||
datePlus +
|
datePlus +
|
||||||
'.' +
|
'.' +
|
||||||
episode.enclosureUrl.split('/').last.split('.').last;
|
episode.enclosureUrl.split('/').last.split('.').last;
|
||||||
@ -148,12 +146,12 @@ class DownloadState extends ChangeNotifier {
|
|||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
url: episode.enclosureUrl,
|
url: episode.enclosureUrl,
|
||||||
savedDir: localPath,
|
savedDir: localPath,
|
||||||
showNotification: true,
|
showNotification: showNotification,
|
||||||
openFileFromNotification: false,
|
openFileFromNotification: false,
|
||||||
);
|
);
|
||||||
_episodeTasks.add(EpisodeTask(episode, taskId));
|
_episodeTasks.add(EpisodeTask(episode, taskId));
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
await dbHelper.saveDownloaded(taskId, episode.enclosureUrl);
|
await dbHelper.saveDownloaded(episode.enclosureUrl, taskId);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,13 @@ import 'dart:isolate';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_isolate/flutter_isolate.dart';
|
import 'package:flutter_isolate/flutter_isolate.dart';
|
||||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
import 'package:connectivity/connectivity.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
|
||||||
|
|
||||||
|
import '../local_storage/key_value_storage.dart';
|
||||||
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
import '../type/podcastlocal.dart';
|
import '../type/podcastlocal.dart';
|
||||||
|
import '../type/episodebrief.dart';
|
||||||
|
import 'download_state.dart';
|
||||||
|
|
||||||
enum RefreshState { none, fetch, error }
|
enum RefreshState { none, fetch, error }
|
||||||
|
|
||||||
@ -68,13 +71,32 @@ class RefreshWorker extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> refreshIsolateEntryPoint(SendPort sendPort) async {
|
Future<void> refreshIsolateEntryPoint(SendPort sendPort) async {
|
||||||
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
|
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
|
||||||
|
KeyValueStorage autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
|
||||||
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
|
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||||
//int i = 0;
|
//int i = 0;
|
||||||
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
|
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
|
||||||
sendPort.send([podcastLocal.title, 1]);
|
sendPort.send([podcastLocal.title, 1]);
|
||||||
await dbHelper.updatePodcastRss(podcastLocal);
|
int updateCount = await dbHelper.updatePodcastRss(podcastLocal);
|
||||||
|
bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
|
||||||
|
if (autoDownload && updateCount > 0) {
|
||||||
|
var result = await Connectivity().checkConnectivity();
|
||||||
|
int autoDownloadNetwork = await autoDownloadStorage.getInt();
|
||||||
|
if (autoDownloadNetwork == 1) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||||
|
episodes.forEach((episode) {
|
||||||
|
DownloadState().startTask(episode, showNotification: false);
|
||||||
|
});
|
||||||
|
} else if (result == ConnectivityResult.wifi) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||||
|
episodes.forEach((episode) {
|
||||||
|
DownloadState().startTask(episode, showNotification: false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
print('Refresh ' + podcastLocal.title);
|
print('Refresh ' + podcastLocal.title);
|
||||||
});
|
});
|
||||||
// KeyValueStorage refreshcountstorage = KeyValueStorage('refreshcount');
|
// KeyValueStorage refreshcountstorage = KeyValueStorage('refreshcount');
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:connectivity/connectivity.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:workmanager/workmanager.dart';
|
import 'package:workmanager/workmanager.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
|
||||||
|
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
import '../local_storage/key_value_storage.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
@ -24,11 +24,23 @@ void callbackDispatcher() {
|
|||||||
await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
|
await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
|
||||||
bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
|
bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
|
||||||
if (autoDownload && updateCount > 0) {
|
if (autoDownload && updateCount > 0) {
|
||||||
List<EpisodeBrief> episodes =
|
var result = await Connectivity().checkConnectivity();
|
||||||
await dbHelper.getNewEpisodes(podcastLocal.id);
|
KeyValueStorage autoDownloadStorage =
|
||||||
episodes.forEach((episode) {
|
KeyValueStorage(autoDownloadNetworkKey);
|
||||||
DownloadState().startTask(episode);
|
int autoDownloadNetwork = await autoDownloadStorage.getInt();
|
||||||
});
|
if (autoDownloadNetwork == 1) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||||
|
episodes.forEach((episode) {
|
||||||
|
DownloadState().startTask(episode, showNotification: false);
|
||||||
|
});
|
||||||
|
} else if (result == ConnectivityResult.wifi) {
|
||||||
|
List<EpisodeBrief> episodes =
|
||||||
|
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||||
|
episodes.forEach((episode) {
|
||||||
|
DownloadState().startTask(episode, showNotification: false);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
print('Refresh ' + podcastLocal.title);
|
print('Refresh ' + podcastLocal.title);
|
||||||
});
|
});
|
||||||
|
@ -5,6 +5,7 @@ extension ContextExtension on BuildContext{
|
|||||||
Color get accentColor => Theme.of(this).accentColor;
|
Color get accentColor => Theme.of(this).accentColor;
|
||||||
Color get scaffoldBackgroundColor => Theme.of(this).scaffoldBackgroundColor;
|
Color get scaffoldBackgroundColor => Theme.of(this).scaffoldBackgroundColor;
|
||||||
Color get primaryColorDark => Theme.of(this).primaryColorDark;
|
Color get primaryColorDark => Theme.of(this).primaryColorDark;
|
||||||
|
Color get textColor => Theme.of(this).textTheme.bodyText1.color;
|
||||||
Brightness get brightness => Theme.of(this).brightness;
|
Brightness get brightness => Theme.of(this).brightness;
|
||||||
double get width => MediaQuery.of(this).size.width;
|
double get width => MediaQuery.of(this).size.width;
|
||||||
double get height => MediaQuery.of(this).size.width;
|
double get height => MediaQuery.of(this).size.width;
|
||||||
|
@ -11,9 +11,11 @@ import 'package:auto_animated/auto_animated.dart';
|
|||||||
import 'open_container.dart';
|
import 'open_container.dart';
|
||||||
|
|
||||||
import '../state/audiostate.dart';
|
import '../state/audiostate.dart';
|
||||||
|
import '../state/download_state.dart';
|
||||||
import '../type/episodebrief.dart';
|
import '../type/episodebrief.dart';
|
||||||
import '../episodes/episodedetail.dart';
|
import '../episodes/episodedetail.dart';
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
|
import '../local_storage/key_value_storage.dart';
|
||||||
import 'colorize.dart';
|
import 'colorize.dart';
|
||||||
import 'context_extension.dart';
|
import 'context_extension.dart';
|
||||||
import 'custompaint.dart';
|
import 'custompaint.dart';
|
||||||
@ -51,6 +53,39 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
return await dbHelper.isLiked(episode.enclosureUrl);
|
return await dbHelper.isLiked(episode.enclosureUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<int>> _getEpisodeMenu() async {
|
||||||
|
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
|
||||||
|
List<int> list = await popupMenuStorage.getMenu();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _isDownloaded(EpisodeBrief episode) async {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
return await dbHelper.isDownloaded(episode.enclosureUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
_markListened(EpisodeBrief episode) async {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
bool marked = await dbHelper.checkMarked(episode);
|
||||||
|
if (!marked) {
|
||||||
|
final PlayHistory history =
|
||||||
|
PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
|
||||||
|
await dbHelper.saveHistory(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _saveLiked(String url) async {
|
||||||
|
var dbHelper = DBHelper();
|
||||||
|
int result = await dbHelper.setLiked(url);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _setUnliked(String url) async {
|
||||||
|
var dbHelper = DBHelper();
|
||||||
|
int result = await dbHelper.setUniked(url);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
String _stringForSeconds(double seconds) {
|
String _stringForSeconds(double seconds) {
|
||||||
if (seconds == null) return null;
|
if (seconds == null) return null;
|
||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
@ -171,15 +206,23 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
color: color,
|
color: color,
|
||||||
fontStyle: FontStyle.italic),
|
fontStyle: FontStyle.italic),
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double _width = MediaQuery.of(context).size.width;
|
double _width = context.width;
|
||||||
Offset _offset;
|
Offset _offset;
|
||||||
_showPopupMenu(Offset offset, EpisodeBrief episode, BuildContext context,
|
_showPopupMenu(Offset offset, EpisodeBrief episode, BuildContext context,
|
||||||
bool isPlaying, bool isInPlaylist) async {
|
{bool isPlaying, bool isInPlaylist}) async {
|
||||||
|
bool isLiked, isDownload;
|
||||||
|
int isListened;
|
||||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||||
|
var downloader = Provider.of<DownloadState>(context, listen: false);
|
||||||
double left = offset.dx;
|
double left = offset.dx;
|
||||||
double top = offset.dy;
|
double top = offset.dy;
|
||||||
|
List<int> menuList = await _getEpisodeMenu();
|
||||||
|
if (menuList.contains(3)) isListened = await _isListened(episode);
|
||||||
|
if (menuList.contains(2)) isLiked = await _isLiked(episode);
|
||||||
|
if (menuList.contains(4)) isDownload = await _isDownloaded(episode);
|
||||||
await showMenu<int>(
|
await showMenu<int>(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
@ -203,39 +246,134 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
menuList.contains(1)
|
||||||
value: 1,
|
? PopupMenuItem(
|
||||||
child: Row(
|
value: 1,
|
||||||
children: <Widget>[
|
child: Row(
|
||||||
Icon(
|
children: <Widget>[
|
||||||
LineIcons.clock_solid,
|
Icon(
|
||||||
color: Colors.red,
|
LineIcons.clock_solid,
|
||||||
),
|
color: Colors.cyan,
|
||||||
Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
Padding(
|
||||||
),
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
!isInPlaylist ? Text('Later') : Text('Remove')
|
),
|
||||||
],
|
!isInPlaylist ? Text('Later') : Text('Remove')
|
||||||
)),
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
menuList.contains(2)
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: 2,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(LineIcons.heart, color: Colors.red, size: 21),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
),
|
||||||
|
isLiked
|
||||||
|
? Text(
|
||||||
|
'Unlike',
|
||||||
|
)
|
||||||
|
: Text('Like')
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
menuList.contains(3)
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: 3,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 23,
|
||||||
|
height: 23,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter:
|
||||||
|
ListenedAllPainter(Colors.blue, stroke: 1.5)),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
),
|
||||||
|
isListened > 0.95
|
||||||
|
? Text('Listened',
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.textColor.withOpacity(0.5)))
|
||||||
|
: Text('Mark\nListened')
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
menuList.contains(4)
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: 4,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(LineIcons.download_solid, color: Colors.green),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
),
|
||||||
|
isDownload
|
||||||
|
? Text('Downloaded',
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.textColor.withOpacity(0.5)))
|
||||||
|
: Text('Download')
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: null,
|
||||||
],
|
],
|
||||||
elevation: 5.0,
|
elevation: 5.0,
|
||||||
).then((value) {
|
).then((value) async {
|
||||||
if (value == 0) {
|
switch (value) {
|
||||||
if (!isPlaying) audio.episodeLoad(episode);
|
case 0:
|
||||||
} else if (value == 1) {
|
if (!isPlaying) audio.episodeLoad(episode);
|
||||||
if (!isInPlaylist) {
|
break;
|
||||||
audio.addToPlaylist(episode);
|
case 1:
|
||||||
Fluttertoast.showToast(
|
if (!isInPlaylist) {
|
||||||
msg: 'Added to playlist',
|
audio.addToPlaylist(episode);
|
||||||
gravity: ToastGravity.BOTTOM,
|
Fluttertoast.showToast(
|
||||||
);
|
msg: 'Added to playlist',
|
||||||
} else {
|
gravity: ToastGravity.BOTTOM,
|
||||||
audio.delFromPlaylist(episode);
|
);
|
||||||
Fluttertoast.showToast(
|
} else {
|
||||||
msg: 'Removed from playlist',
|
audio.delFromPlaylist(episode);
|
||||||
gravity: ToastGravity.BOTTOM,
|
Fluttertoast.showToast(
|
||||||
);
|
msg: 'Removed from playlist',
|
||||||
}
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (isLiked) {
|
||||||
|
await _setUnliked(episode.enclosureUrl);
|
||||||
|
audio.setEpisodeState = true;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Unliked',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await _saveLiked(episode.enclosureUrl);
|
||||||
|
audio.setEpisodeState = true;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Liked',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (isListened < 0.95) {
|
||||||
|
await _markListened(episode);
|
||||||
|
audio.setEpisodeState = true;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Mark listened',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
if (!isDownload) downloader.startTask(episode);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -273,9 +411,11 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
opacity: Tween<double>(begin: index < initNum ? 0 : 1, end: 1)
|
opacity: Tween<double>(begin: index < initNum ? 0 : 1, end: 1)
|
||||||
.animate(animation),
|
.animate(animation),
|
||||||
child: Selector<AudioPlayerNotifier,
|
child: Selector<AudioPlayerNotifier,
|
||||||
Tuple2<EpisodeBrief, List<String>>>(
|
Tuple3<EpisodeBrief, List<String>, bool>>(
|
||||||
selector: (_, audio) => Tuple2(audio?.episode,
|
selector: (_, audio) => Tuple3(
|
||||||
audio.queue.playlist.map((e) => e.enclosureUrl).toList()),
|
audio?.episode,
|
||||||
|
audio.queue.playlist.map((e) => e.enclosureUrl).toList(),
|
||||||
|
audio.episodeState),
|
||||||
builder: (_, data, __) => OpenContainerWrapper(
|
builder: (_, data, __) => OpenContainerWrapper(
|
||||||
episode: episodes[index],
|
episode: episodes[index],
|
||||||
closedBuilder: (context, action, boo) => FutureBuilder<int>(
|
closedBuilder: (context, action, boo) => FutureBuilder<int>(
|
||||||
@ -310,12 +450,13 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
details.globalPosition.dx,
|
details.globalPosition.dx,
|
||||||
details.globalPosition.dy),
|
details.globalPosition.dy),
|
||||||
onLongPress: () => _showPopupMenu(
|
onLongPress: () => _showPopupMenu(
|
||||||
_offset,
|
_offset,
|
||||||
episodes[index],
|
episodes[index],
|
||||||
context,
|
context,
|
||||||
data.item1 == episodes[index],
|
isPlaying: data.item1 == episodes[index],
|
||||||
data.item2
|
isInPlaylist: data.item2
|
||||||
.contains(episodes[index].enclosureUrl)),
|
.contains(episodes[index].enclosureUrl),
|
||||||
|
),
|
||||||
onTap: action,
|
onTap: action,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: tsacdop
|
name: tsacdop
|
||||||
description: An easy-use podacasts player.
|
description: An easy-use podacasts player.
|
||||||
|
|
||||||
version: 0.3.3
|
version: 0.3.4
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.6.0 <3.0.0"
|
sdk: ">=2.6.0 <3.0.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user