Effective dart.

This commit is contained in:
stonegate 2020-07-26 18:20:42 +08:00
parent 913358ede4
commit 54120848bb
61 changed files with 1753 additions and 1653 deletions

View File

@ -9,7 +9,7 @@ Release date 2020/7/25
* Filter in podcast detail page, you can also hide listened episodes.
* Search result ui improved, you can see more info for result.
* Update audio service to latest version.
* Support fast forward seconds and rewind seconds costomize.
* Support fast forward seconds and rewind seconds customize.
* Add Franch language support(beta).
* Add translators in about page.

View File

@ -4,6 +4,7 @@
[![GitHub Release][]][github release - recent]
[![Github Downloads][]][github release - recent]
[![Localizely][]][localizely - website]
[![style: effective dart][]][effective dart pub]
## About
@ -140,3 +141,5 @@ For help getting started with Flutter, view our
[Podcast Screenshot]: https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893877702.png
[Episode Screenshot]: https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585896237809.png
[Darkmode Screenshot]: https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893920721.png
[style: effective dart]: https://img.shields.io/badge/style-effective_dart-40c4ff.svg
[effective dart pub]: https://pub.dev/packages/effective_dart

View File

@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:tsacdop/home/audioplayer.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
@ -12,12 +11,13 @@ import 'package:tuple/tuple.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:google_fonts/google_fonts.dart';
import '../home/audioplayer.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../util/extension_helper.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
import 'episode_download.dart';
class EpisodeDetail extends StatefulWidget {
@ -50,25 +50,28 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
_description = (await dbHelper.getDescription(url))
.replaceAll(RegExp(r'\s?<p>(<br>)?</p>\s?'), '')
.replaceAll('\r', '');
if (mounted)
if (mounted) {
setState(() {
_loaddes = true;
});
}
}
ScrollController _controller;
_scrollListener() {
if (_controller.offset > _controller.position.maxScrollExtent * 0.8) {
if (!_showMenu)
if (!_showMenu) {
setState(() {
_showMenu = true;
});
}
} else if (_controller.offset <
_controller.position.maxScrollExtent * 0.8) {
if (_showMenu)
if (_showMenu) {
setState(() {
_showMenu = false;
});
}
}
if (_controller.offset > context.textTheme.headline5.fontSize) {
if (!_showTitle) setState(() => _showTitle = true);
@ -84,11 +87,10 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
}
_markListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
bool marked = await dbHelper.checkMarked(episode);
var dbHelper = DBHelper();
var marked = await dbHelper.checkMarked(episode);
if (!marked) {
final PlayHistory history =
PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
}
}
@ -164,7 +166,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
),
),
],
onSelected: (int value) async {
onSelected: (value) async {
switch (value) {
case 0:
await _markListened(widget.episodeItem);
@ -255,10 +257,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
((widget.episodeItem.enclosureLength) ~/
1000000)
.toString() +
'MB',
'${(widget.episodeItem.enclosureLength) ~/ 1000000}MB',
style: textstyle),
),
],
@ -397,15 +396,10 @@ class _MenuBarState extends State<MenuBar> {
}
Future<bool> _isLiked(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
static String _stringForSeconds(double seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material(
color: Colors.transparent,
child: InkWell(
@ -477,7 +471,7 @@ class _MenuBarState extends State<MenuBar> {
FutureBuilder<bool>(
future: _isLiked(widget.episodeItem),
initialData: false,
builder: (BuildContext context, AsyncSnapshot snapshot) {
builder: (context, snapshot) {
return (!snapshot.data)
? _buttonOnMenu(
Icon(
@ -589,8 +583,8 @@ class _MenuBarState extends State<MenuBar> {
color: context.accentColor,
),
child: Text(
_stringForSeconds(
snapshot.data.seconds),
snapshot
.data.seconds.toTime,
style: TextStyle(
color: Colors.white),
),

View File

@ -1,19 +1,18 @@
import 'dart:ui';
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:connectivity/connectivity.dart';
import 'package:tsacdop/util/custom_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import '../state/download_state.dart';
import '../state/audio_state.dart';
import '../state/download_state.dart';
import '../state/setting_state.dart';
import '../type/episodebrief.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
import '../util/general_dialog.dart';
@ -46,7 +45,7 @@ class _DownloadButtonState extends State<DownloadButton> {
void _requestDownload(EpisodeBrief episode, bool downloadUsingData) async {
_permissionReady = await _checkPermmison();
bool _dataConfirm = true;
var _dataConfirm = true;
if (_permissionReady) {
if (downloadUsingData && _usingData) {
_dataConfirm = await _useDataConfirem();
@ -78,10 +77,9 @@ class _DownloadButtonState extends State<DownloadButton> {
}
Future<bool> _checkPermmison() async {
PermissionStatus permission = await Permission.storage.status;
var permission = await Permission.storage.status;
if (permission != PermissionStatus.granted) {
Map<Permission, PermissionStatus> permissions =
await [Permission.storage].request();
var permissions = await [Permission.storage].request();
if (permissions[Permission.storage] == PermissionStatus.granted) {
return true;
} else {
@ -93,7 +91,7 @@ class _DownloadButtonState extends State<DownloadButton> {
}
Future<bool> _useDataConfirem() async {
bool ifUseData = false;
var ifUseData = false;
final s = context.s;
await generalDialog(
context,
@ -138,7 +136,7 @@ class _DownloadButtonState extends State<DownloadButton> {
@override
Widget build(BuildContext context) {
return Consumer<DownloadState>(builder: (_, downloader, __) {
EpisodeTask _task = Provider.of<DownloadState>(context, listen: false)
var _task = Provider.of<DownloadState>(context, listen: false)
.episodeToTask(widget.episode);
return Row(
children: <Widget>[

View File

@ -42,9 +42,8 @@ MessageLookupByLibrary _findExact(String localeName) {
/// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) async {
var availableLocale = Intl.verifiedLocale(
localeName,
(locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null);
localeName, (locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null);
if (availableLocale == null) {
return new Future.value(false);
}
@ -58,14 +57,15 @@ Future<bool> initializeMessages(String localeName) async {
bool _messagesExistFor(String locale) {
try {
return _findExact(locale) != null;
// ignore: avoid_catches_without_on_clauses
} catch (e) {
return false;
}
}
MessageLookupByLibrary _findGeneratedMessagesFor(String locale) {
var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
onFailure: (_) => null);
var actualLocale =
Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
if (actualLocale == null) return null;
return _findExact(actualLocale);
}

View File

@ -19,9 +19,11 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'zh_Hans';
static m0(groupName, count) => "{count, plural, zero{} other{{group Name}分组${count}集节目添加到播放列表}}";
static m0(groupName, count) =>
"{count, plural, zero{} other{{group Name}分组${count}集节目添加到播放列表}}";
static m1(count) => "${Intl.plural(count, zero: '', other: '${count}集节目添加到播放列表')}";
static m1(count) =>
"${Intl.plural(count, zero: '', other: '${count}集节目添加到播放列表')}";
static m2(count) => "${Intl.plural(count, zero: '今天', other: '${count}天前')}";
@ -37,11 +39,14 @@ class MessageLookup extends MessageLookupByLibrary {
static m8(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}小时前')}";
static m9(count) => "${Intl.plural(count, zero: '0小时', other: '${count} 小时')}";
static m9(count) =>
"${Intl.plural(count, zero: '0小时', other: '${count} 小时')}";
static m10(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}分钟前')}";
static m10(count) =>
"${Intl.plural(count, zero: '刚刚', other: '${count}分钟前')}";
static m11(count) => "${Intl.plural(count, zero: '0分钟', other: '${count}分钟')}";
static m11(count) =>
"${Intl.plural(count, zero: '0分钟', other: '${count}分钟')}";
static m12(title) => "获取数据 ${title}";
@ -63,7 +68,8 @@ class MessageLookup extends MessageLookupByLibrary {
static m21(date) => "${date}移除";
static m22(count) => "${Intl.plural(count, zero: '0 秒', other: '${count} 秒')}";
static m22(count) =>
"${Intl.plural(count, zero: '0 秒', other: '${count} 秒')}";
static m23(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}秒前')}";
@ -73,251 +79,301 @@ class MessageLookup extends MessageLookupByLibrary {
static m26(time) => "${time}";
static m27(count) => "${Intl.plural(count, zero: '未有更新', other: '更新 ${count} 集节目')}";
static m27(count) =>
"${Intl.plural(count, zero: '未有更新', other: '更新 ${count} 集节目')}";
static m28(version) => "版本:${version}";
final messages = _notInlinedMessages(_notInlinedMessages);
static _notInlinedMessages(_) => <String, Function> {
"add" : MessageLookupByLibrary.simpleMessage("订阅"),
"addEpisodeGroup" : m0,
"addNewEpisodeAll" : m1,
"addNewEpisodeTooltip" : MessageLookupByLibrary.simpleMessage("添加更新节目到播放列表"),
"addSomeGroups" : MessageLookupByLibrary.simpleMessage("请添加分组"),
"all" : MessageLookupByLibrary.simpleMessage("全部"),
"autoDownload" : MessageLookupByLibrary.simpleMessage("自动下载"),
"back" : MessageLookupByLibrary.simpleMessage("返回"),
"buffering" : MessageLookupByLibrary.simpleMessage("缓冲"),
"cancel" : MessageLookupByLibrary.simpleMessage("取消"),
"cellularConfirm" : MessageLookupByLibrary.simpleMessage("流量确认"),
"cellularConfirmDes" : MessageLookupByLibrary.simpleMessage("您确定使用流量下载吗"),
"changeLayout" : MessageLookupByLibrary.simpleMessage("修改布局"),
"changelog" : MessageLookupByLibrary.simpleMessage("更新日志"),
"chooseA" : MessageLookupByLibrary.simpleMessage("选择"),
"clear" : MessageLookupByLibrary.simpleMessage("清除"),
"color" : MessageLookupByLibrary.simpleMessage("颜色"),
"confirm" : MessageLookupByLibrary.simpleMessage("确认"),
"darkMode" : MessageLookupByLibrary.simpleMessage("夜晚模式"),
"daysAgo" : m2,
"daysCount" : m3,
"delete" : MessageLookupByLibrary.simpleMessage("删除"),
"developer" : MessageLookupByLibrary.simpleMessage("关于我"),
"dismiss" : MessageLookupByLibrary.simpleMessage("忽略"),
"done" : MessageLookupByLibrary.simpleMessage("完成"),
"download" : MessageLookupByLibrary.simpleMessage("下载"),
"downloaded" : MessageLookupByLibrary.simpleMessage("已下载"),
"editGroupName" : MessageLookupByLibrary.simpleMessage("修改组名"),
"endOfEpisode" : MessageLookupByLibrary.simpleMessage("节目结束"),
"episode" : m4,
"featureDiscoveryEditGroup" : MessageLookupByLibrary.simpleMessage("点击修改分组"),
"featureDiscoveryEditGroupDes" : MessageLookupByLibrary.simpleMessage("您可以修改分组名或者删除分组,注意 Home 分组无法修改,也不能被删除。"),
"featureDiscoveryEpisode" : MessageLookupByLibrary.simpleMessage("节目界面"),
"featureDiscoveryEpisodeDes" : MessageLookupByLibrary.simpleMessage("您可以长按播放节目或者添加节目到播放列表。"),
"featureDiscoveryEpisodeTitle" : MessageLookupByLibrary.simpleMessage("您可以长按快速播放节目"),
"featureDiscoveryGroup" : MessageLookupByLibrary.simpleMessage("点击添加分组"),
"featureDiscoveryGroupDes" : MessageLookupByLibrary.simpleMessage("新订阅播客默认分组为 Home您可以添加新的分组移动播客到新的分组每个播客可以被添加到多个分组。"),
"featureDiscoveryGroupPodcast" : MessageLookupByLibrary.simpleMessage("长按可以移动播客位置"),
"featureDiscoveryGroupPodcastDes" : MessageLookupByLibrary.simpleMessage("您可以点击对播客进行设置,或者长按重新排序。"),
"featureDiscoveryOMPL" : MessageLookupByLibrary.simpleMessage("点击导入 OMPL"),
"featureDiscoveryOMPLDes" : MessageLookupByLibrary.simpleMessage("在这里您可以导入OMPL文件打开设置页面或者刷新所有播客。"),
"featureDiscoveryPlaylist" : MessageLookupByLibrary.simpleMessage("点击打开播放列表"),
"featureDiscoveryPlaylistDes" : MessageLookupByLibrary.simpleMessage("您可以添加节目到播放列表,节目在播放后将会从播放列表自动移除。"),
"featureDiscoveryPodcast" : MessageLookupByLibrary.simpleMessage("播客界面"),
"featureDiscoveryPodcastDes" : MessageLookupByLibrary.simpleMessage("您可以点击“查看所有”新增或管理分组。"),
"featureDiscoveryPodcastTitle" : MessageLookupByLibrary.simpleMessage("您可以通过上下滑动切换分组"),
"featureDiscoverySearch" : MessageLookupByLibrary.simpleMessage("点击搜索播客"),
"featureDiscoverySearchDes" : MessageLookupByLibrary.simpleMessage("您可以通过搜索播客名称、关键字或者RSS链接订阅播客。"),
"feedbackEmail" : MessageLookupByLibrary.simpleMessage("发送邮件"),
"feedbackGithub" : MessageLookupByLibrary.simpleMessage("提交Issue"),
"feedbackPlay" : MessageLookupByLibrary.simpleMessage("Play评价"),
"feedbackTelegram" : MessageLookupByLibrary.simpleMessage("加入小组"),
"filter" : MessageLookupByLibrary.simpleMessage("过滤"),
"fonts" : MessageLookupByLibrary.simpleMessage("字体"),
"from" : m5,
"goodNight" : MessageLookupByLibrary.simpleMessage("晚安"),
"groupExisted" : MessageLookupByLibrary.simpleMessage("组名已使用"),
"groupFilter" : MessageLookupByLibrary.simpleMessage("分组"),
"groupRemoveConfirm" : MessageLookupByLibrary.simpleMessage("您确认要移除该分组吗?播客将被移动到 Home 分组。"),
"groups" : m6,
"homeGroupsSeeAll" : MessageLookupByLibrary.simpleMessage("查看全部"),
"homeMenuPlaylist" : MessageLookupByLibrary.simpleMessage("播放列表"),
"homeSubMenuSortBy" : MessageLookupByLibrary.simpleMessage("排序"),
"homeTabMenuFavotite" : MessageLookupByLibrary.simpleMessage("收藏"),
"homeTabMenuRecent" : MessageLookupByLibrary.simpleMessage("最近更新"),
"homeToprightMenuAbout" : MessageLookupByLibrary.simpleMessage("关于"),
"homeToprightMenuImportOMPL" : MessageLookupByLibrary.simpleMessage("导入OMPL"),
"homeToprightMenuRefreshAll" : MessageLookupByLibrary.simpleMessage("全部刷新"),
"hostedOn" : m7,
"hoursAgo" : m8,
"hoursCount" : m9,
"import" : MessageLookupByLibrary.simpleMessage("导入"),
"introFourthPage" : MessageLookupByLibrary.simpleMessage("您可以长按节目打开快捷菜单。"),
"introSecondPage" : MessageLookupByLibrary.simpleMessage("您可以通过搜索订阅播客也可以直接导入OMPL文件。"),
"introThirdPage" : MessageLookupByLibrary.simpleMessage("您可以创建分组,上下滑动切换分组。"),
"later" : MessageLookupByLibrary.simpleMessage("稍后"),
"lightMode" : MessageLookupByLibrary.simpleMessage("明亮模式"),
"like" : MessageLookupByLibrary.simpleMessage("喜欢"),
"likeDate" : MessageLookupByLibrary.simpleMessage("收藏日期"),
"liked" : MessageLookupByLibrary.simpleMessage("已收藏"),
"listen" : MessageLookupByLibrary.simpleMessage("收听"),
"listened" : MessageLookupByLibrary.simpleMessage("已收听"),
"loadMore" : MessageLookupByLibrary.simpleMessage("加载更多"),
"mark" : MessageLookupByLibrary.simpleMessage("标记"),
"markConfirm" : MessageLookupByLibrary.simpleMessage("确认标记"),
"markConfirmContent" : MessageLookupByLibrary.simpleMessage("是否确认标记全部节目为已收听?"),
"markListened" : MessageLookupByLibrary.simpleMessage("标记已收听"),
"menu" : MessageLookupByLibrary.simpleMessage("菜单"),
"menuAllPodcasts" : MessageLookupByLibrary.simpleMessage("所有订阅"),
"menuMarkAllListened" : MessageLookupByLibrary.simpleMessage("标记所有已收听"),
"menuViewRSS" : MessageLookupByLibrary.simpleMessage("查看 RSS"),
"menuVisitSite" : MessageLookupByLibrary.simpleMessage("访问网站"),
"minsAgo" : m10,
"minsCount" : m11,
"network" : MessageLookupByLibrary.simpleMessage("网络"),
"newGroup" : MessageLookupByLibrary.simpleMessage("创建分组"),
"newestFirst" : MessageLookupByLibrary.simpleMessage("由新到旧"),
"next" : MessageLookupByLibrary.simpleMessage("下一步"),
"noEpisodeDownload" : MessageLookupByLibrary.simpleMessage("暂无下载节目"),
"noEpisodeFavorite" : MessageLookupByLibrary.simpleMessage("暂无收藏节目"),
"noEpisodeRecent" : MessageLookupByLibrary.simpleMessage("暂无节目"),
"noPodcastGroup" : MessageLookupByLibrary.simpleMessage("分组无播客"),
"noShownote" : MessageLookupByLibrary.simpleMessage("节目简介暂未收到。"),
"notificaitonFatch" : m12,
"notificationNetworkError" : m13,
"notificationSubscribe" : m14,
"notificationSubscribeExisted" : m15,
"notificationSuccess" : m16,
"notificationUpdate" : m17,
"notificationUpdateError" : m18,
"oldestFirst" : MessageLookupByLibrary.simpleMessage("由旧到新"),
"play" : MessageLookupByLibrary.simpleMessage("播放"),
"playback" : MessageLookupByLibrary.simpleMessage("播放控制"),
"playing" : MessageLookupByLibrary.simpleMessage("正在播放"),
"plugins" : MessageLookupByLibrary.simpleMessage("插件"),
"podcast" : m19,
"podcastSubscribed" : MessageLookupByLibrary.simpleMessage("播客已订阅"),
"popupMenuDownloadDes" : MessageLookupByLibrary.simpleMessage("下载节目"),
"popupMenuLaterDes" : MessageLookupByLibrary.simpleMessage("添加到播放列表"),
"popupMenuLikeDes" : MessageLookupByLibrary.simpleMessage("添加到收藏"),
"popupMenuMarkDes" : MessageLookupByLibrary.simpleMessage("设置为已收听"),
"popupMenuPlayDes" : MessageLookupByLibrary.simpleMessage("播放节目"),
"privacyPolicy" : MessageLookupByLibrary.simpleMessage("隐私条款"),
"published" : m20,
"publishedDaily" : MessageLookupByLibrary.simpleMessage("每日更新"),
"publishedMonthly" : MessageLookupByLibrary.simpleMessage("每月更新"),
"publishedWeekly" : MessageLookupByLibrary.simpleMessage("每周更新"),
"publishedYearly" : MessageLookupByLibrary.simpleMessage("每年更新"),
"recoverSubscribe" : MessageLookupByLibrary.simpleMessage("恢复订阅"),
"refreshArtwork" : MessageLookupByLibrary.simpleMessage("更新头像"),
"remove" : MessageLookupByLibrary.simpleMessage("移除"),
"removeConfirm" : MessageLookupByLibrary.simpleMessage("取消订阅"),
"removePodcastDes" : MessageLookupByLibrary.simpleMessage("您确认要取消订阅吗?"),
"removedAt" : m21,
"save" : MessageLookupByLibrary.simpleMessage("保存"),
"schedule" : MessageLookupByLibrary.simpleMessage("定时"),
"search" : MessageLookupByLibrary.simpleMessage("搜索"),
"searchEpisode" : MessageLookupByLibrary.simpleMessage("搜索节目"),
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("RSS 链接错误"),
"searchPodcast" : MessageLookupByLibrary.simpleMessage("搜索播客"),
"secCount" : m22,
"secondsAgo" : m23,
"settingStorage" : MessageLookupByLibrary.simpleMessage("储存空间"),
"settings" : MessageLookupByLibrary.simpleMessage("设置"),
"settingsAccentColor" : MessageLookupByLibrary.simpleMessage("次要颜色"),
"settingsAccentColorDes" : MessageLookupByLibrary.simpleMessage("包括溢出颜色"),
"settingsAppIntro" : MessageLookupByLibrary.simpleMessage("引导页"),
"settingsAppearance" : MessageLookupByLibrary.simpleMessage("界面"),
"settingsAppearanceDes" : MessageLookupByLibrary.simpleMessage("颜色与主题"),
"settingsAudioCache" : MessageLookupByLibrary.simpleMessage("播放缓存"),
"settingsAudioCacheDes" : MessageLookupByLibrary.simpleMessage("播放缓存设置"),
"settingsAutoDelete" : MessageLookupByLibrary.simpleMessage("自动删除下载节目"),
"settingsAutoDeleteDes" : MessageLookupByLibrary.simpleMessage("默认 30 天"),
"settingsAutoPlayDes" : MessageLookupByLibrary.simpleMessage("自动播放下一节目"),
"settingsBackup" : MessageLookupByLibrary.simpleMessage("备份"),
"settingsBackupDes" : MessageLookupByLibrary.simpleMessage("备份应用数据"),
"settingsDefaultGrid" : MessageLookupByLibrary.simpleMessage("默认布局"),
"settingsDefaultGridDownload" : MessageLookupByLibrary.simpleMessage("下载页"),
"settingsDefaultGridFavorite" : MessageLookupByLibrary.simpleMessage("收藏页"),
"settingsDefaultGridPodcast" : MessageLookupByLibrary.simpleMessage("播客页"),
"settingsDefaultGridRecent" : MessageLookupByLibrary.simpleMessage("最近页"),
"settingsDiscovery" : MessageLookupByLibrary.simpleMessage("再次功能介绍"),
"settingsEnableSyncing" : MessageLookupByLibrary.simpleMessage("开启自动更新"),
"settingsEnableSyncingDes" : MessageLookupByLibrary.simpleMessage("在后台更新所有订阅播客"),
"settingsExportDes" : MessageLookupByLibrary.simpleMessage("导出及恢复所有设置项"),
"settingsFastForwardSec" : MessageLookupByLibrary.simpleMessage("快进时间"),
"settingsFastForwardSecDes" : MessageLookupByLibrary.simpleMessage("修改播放器快进时间"),
"settingsFeedback" : MessageLookupByLibrary.simpleMessage("反馈"),
"settingsFeedbackDes" : MessageLookupByLibrary.simpleMessage("意见与建议"),
"settingsHistory" : MessageLookupByLibrary.simpleMessage("历史记录"),
"settingsHistoryDes" : MessageLookupByLibrary.simpleMessage("收听记录"),
"settingsInfo" : MessageLookupByLibrary.simpleMessage("信息"),
"settingsInterface" : MessageLookupByLibrary.simpleMessage("界面"),
"settingsLanguages" : MessageLookupByLibrary.simpleMessage("语言"),
"settingsLanguagesDes" : MessageLookupByLibrary.simpleMessage("设置语言"),
"settingsLayout" : MessageLookupByLibrary.simpleMessage("布局"),
"settingsLayoutDes" : MessageLookupByLibrary.simpleMessage("应用布局"),
"settingsLibraries" : MessageLookupByLibrary.simpleMessage("开源"),
"settingsLibrariesDes" : MessageLookupByLibrary.simpleMessage("开源项目使用"),
"settingsManageDownload" : MessageLookupByLibrary.simpleMessage("下载管理"),
"settingsManageDownloadDes" : MessageLookupByLibrary.simpleMessage("管理下载节目文件"),
"settingsMenuAutoPlay" : MessageLookupByLibrary.simpleMessage("自动播放下一节目"),
"settingsNetworkCellular" : MessageLookupByLibrary.simpleMessage("蜂窝数据确认"),
"settingsNetworkCellularAuto" : MessageLookupByLibrary.simpleMessage("是否用蜂窝数据自动下载"),
"settingsNetworkCellularAutoDes" : MessageLookupByLibrary.simpleMessage("你可以在分组管理页面设置自动下载"),
"settingsNetworkCellularDes" : MessageLookupByLibrary.simpleMessage("在使用蜂窝数据下载前确认"),
"settingsPlayDes" : MessageLookupByLibrary.simpleMessage("播放列表和播放器"),
"settingsPopupMenu" : MessageLookupByLibrary.simpleMessage("节目弹出菜单"),
"settingsPopupMenuDes" : MessageLookupByLibrary.simpleMessage("修改节目弹出菜单"),
"settingsPrefrence" : MessageLookupByLibrary.simpleMessage("首选项"),
"settingsRealDark" : MessageLookupByLibrary.simpleMessage("极黑"),
"settingsRealDarkDes" : MessageLookupByLibrary.simpleMessage("如果夜不够黑,请开启"),
"settingsRewindSec" : MessageLookupByLibrary.simpleMessage("快退时间"),
"settingsRewindSecDes" : MessageLookupByLibrary.simpleMessage("修改播放器快退时间"),
"settingsSTAuto" : MessageLookupByLibrary.simpleMessage("自动睡眠模式"),
"settingsSTAutoDes" : MessageLookupByLibrary.simpleMessage("定期开启睡眠模式"),
"settingsSTDefaultTime" : MessageLookupByLibrary.simpleMessage("默认时长"),
"settingsSTDefautTimeDes" : MessageLookupByLibrary.simpleMessage("睡眠模式默认时长"),
"settingsSTMode" : MessageLookupByLibrary.simpleMessage("自动睡眠模式默认时长"),
"settingsStorageDes" : MessageLookupByLibrary.simpleMessage("管理缓存和下载空间"),
"settingsSyncing" : MessageLookupByLibrary.simpleMessage("同步"),
"settingsSyncingDes" : MessageLookupByLibrary.simpleMessage("在后台更新播客"),
"settingsTapToOpenPopupMenu" : MessageLookupByLibrary.simpleMessage("轻点打开弹出菜单"),
"settingsTapToOpenPopupMenuDes" : MessageLookupByLibrary.simpleMessage("开启后您需长按打开节目页"),
"settingsTheme" : MessageLookupByLibrary.simpleMessage("主题"),
"settingsUpdateInterval" : MessageLookupByLibrary.simpleMessage("更新频率"),
"settingsUpdateIntervalDes" : MessageLookupByLibrary.simpleMessage("默认 24 小时"),
"share" : MessageLookupByLibrary.simpleMessage("分享"),
"size" : MessageLookupByLibrary.simpleMessage("大小"),
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("开头跳过秒数"),
"sleepTimer" : MessageLookupByLibrary.simpleMessage("睡眠模式"),
"subscribe" : MessageLookupByLibrary.simpleMessage("订阅"),
"subscribeExportDes" : MessageLookupByLibrary.simpleMessage("导出 OMPL 文件"),
"systemDefault" : MessageLookupByLibrary.simpleMessage("系统默认"),
"timeLastPlayed" : m24,
"timeLeft" : m25,
"to" : m26,
"toastAddPlaylist" : MessageLookupByLibrary.simpleMessage("添加到播放列表"),
"toastDiscovery" : MessageLookupByLibrary.simpleMessage("重启应用后可查看"),
"toastFileError" : MessageLookupByLibrary.simpleMessage("文件错误,导入失败"),
"toastFileNotValid" : MessageLookupByLibrary.simpleMessage("文件错误"),
"toastHomeGroupNotSupport" : MessageLookupByLibrary.simpleMessage("Home 分组不支持此功能"),
"toastImportSettingsSuccess" : MessageLookupByLibrary.simpleMessage("导入设置成功"),
"toastOneGroup" : MessageLookupByLibrary.simpleMessage("请至少选择一个分组"),
"toastPodcastRecovering" : MessageLookupByLibrary.simpleMessage("恢复中,请稍后"),
"toastReadFile" : MessageLookupByLibrary.simpleMessage("读取文件成功"),
"toastRecoverFailed" : MessageLookupByLibrary.simpleMessage("恢复订阅失败"),
"toastRemovePlaylist" : MessageLookupByLibrary.simpleMessage("从播放列表移除"),
"toastSettingSaved" : MessageLookupByLibrary.simpleMessage("设置已保存"),
"toastTimeEqualEnd" : MessageLookupByLibrary.simpleMessage("与结束时刻相同"),
"toastTimeEqualStart" : MessageLookupByLibrary.simpleMessage("与起始时刻相同"),
"translators" : MessageLookupByLibrary.simpleMessage("翻译者"),
"understood" : MessageLookupByLibrary.simpleMessage("了解"),
"undo" : MessageLookupByLibrary.simpleMessage("撤销"),
"unlike" : MessageLookupByLibrary.simpleMessage("取消喜欢"),
"unliked" : MessageLookupByLibrary.simpleMessage("从收藏移除"),
"updateDate" : MessageLookupByLibrary.simpleMessage("更新日期"),
"updateEpisodesCount" : m27,
"updateFailed" : MessageLookupByLibrary.simpleMessage("更新失败"),
"version" : m28
};
static _notInlinedMessages(_) => <String, Function>{
"add": MessageLookupByLibrary.simpleMessage("订阅"),
"addEpisodeGroup": m0,
"addNewEpisodeAll": m1,
"addNewEpisodeTooltip":
MessageLookupByLibrary.simpleMessage("添加更新节目到播放列表"),
"addSomeGroups": MessageLookupByLibrary.simpleMessage("请添加分组"),
"all": MessageLookupByLibrary.simpleMessage("全部"),
"autoDownload": MessageLookupByLibrary.simpleMessage("自动下载"),
"back": MessageLookupByLibrary.simpleMessage("返回"),
"buffering": MessageLookupByLibrary.simpleMessage("缓冲"),
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
"cellularConfirm": MessageLookupByLibrary.simpleMessage("流量确认"),
"cellularConfirmDes":
MessageLookupByLibrary.simpleMessage("您确定使用流量下载吗"),
"changeLayout": MessageLookupByLibrary.simpleMessage("修改布局"),
"changelog": MessageLookupByLibrary.simpleMessage("更新日志"),
"chooseA": MessageLookupByLibrary.simpleMessage("选择"),
"clear": MessageLookupByLibrary.simpleMessage("清除"),
"color": MessageLookupByLibrary.simpleMessage("颜色"),
"confirm": MessageLookupByLibrary.simpleMessage("确认"),
"darkMode": MessageLookupByLibrary.simpleMessage("夜晚模式"),
"daysAgo": m2,
"daysCount": m3,
"delete": MessageLookupByLibrary.simpleMessage("删除"),
"developer": MessageLookupByLibrary.simpleMessage("关于我"),
"dismiss": MessageLookupByLibrary.simpleMessage("忽略"),
"done": MessageLookupByLibrary.simpleMessage("完成"),
"download": MessageLookupByLibrary.simpleMessage("下载"),
"downloaded": MessageLookupByLibrary.simpleMessage("已下载"),
"editGroupName": MessageLookupByLibrary.simpleMessage("修改组名"),
"endOfEpisode": MessageLookupByLibrary.simpleMessage("节目结束"),
"episode": m4,
"featureDiscoveryEditGroup":
MessageLookupByLibrary.simpleMessage("点击修改分组"),
"featureDiscoveryEditGroupDes": MessageLookupByLibrary.simpleMessage(
"您可以修改分组名或者删除分组,注意 Home 分组无法修改,也不能被删除。"),
"featureDiscoveryEpisode": MessageLookupByLibrary.simpleMessage("节目界面"),
"featureDiscoveryEpisodeDes":
MessageLookupByLibrary.simpleMessage("您可以长按播放节目或者添加节目到播放列表。"),
"featureDiscoveryEpisodeTitle":
MessageLookupByLibrary.simpleMessage("您可以长按快速播放节目"),
"featureDiscoveryGroup": MessageLookupByLibrary.simpleMessage("点击添加分组"),
"featureDiscoveryGroupDes": MessageLookupByLibrary.simpleMessage(
"新订阅播客默认分组为 Home您可以添加新的分组移动播客到新的分组每个播客可以被添加到多个分组。"),
"featureDiscoveryGroupPodcast":
MessageLookupByLibrary.simpleMessage("长按可以移动播客位置"),
"featureDiscoveryGroupPodcastDes":
MessageLookupByLibrary.simpleMessage("您可以点击对播客进行设置,或者长按重新排序。"),
"featureDiscoveryOMPL":
MessageLookupByLibrary.simpleMessage("点击导入 OMPL"),
"featureDiscoveryOMPLDes": MessageLookupByLibrary.simpleMessage(
"在这里您可以导入OMPL文件打开设置页面或者刷新所有播客。"),
"featureDiscoveryPlaylist":
MessageLookupByLibrary.simpleMessage("点击打开播放列表"),
"featureDiscoveryPlaylistDes": MessageLookupByLibrary.simpleMessage(
"您可以添加节目到播放列表,节目在播放后将会从播放列表自动移除。"),
"featureDiscoveryPodcast": MessageLookupByLibrary.simpleMessage("播客界面"),
"featureDiscoveryPodcastDes":
MessageLookupByLibrary.simpleMessage("您可以点击“查看所有”新增或管理分组。"),
"featureDiscoveryPodcastTitle":
MessageLookupByLibrary.simpleMessage("您可以通过上下滑动切换分组"),
"featureDiscoverySearch":
MessageLookupByLibrary.simpleMessage("点击搜索播客"),
"featureDiscoverySearchDes":
MessageLookupByLibrary.simpleMessage("您可以通过搜索播客名称、关键字或者RSS链接订阅播客。"),
"feedbackEmail": MessageLookupByLibrary.simpleMessage("发送邮件"),
"feedbackGithub": MessageLookupByLibrary.simpleMessage("提交Issue"),
"feedbackPlay": MessageLookupByLibrary.simpleMessage("Play评价"),
"feedbackTelegram": MessageLookupByLibrary.simpleMessage("加入小组"),
"filter": MessageLookupByLibrary.simpleMessage("过滤"),
"fonts": MessageLookupByLibrary.simpleMessage("字体"),
"from": m5,
"goodNight": MessageLookupByLibrary.simpleMessage("晚安"),
"groupExisted": MessageLookupByLibrary.simpleMessage("组名已使用"),
"groupFilter": MessageLookupByLibrary.simpleMessage("分组"),
"groupRemoveConfirm":
MessageLookupByLibrary.simpleMessage("您确认要移除该分组吗?播客将被移动到 Home 分组。"),
"groups": m6,
"homeGroupsSeeAll": MessageLookupByLibrary.simpleMessage("查看全部"),
"homeMenuPlaylist": MessageLookupByLibrary.simpleMessage("播放列表"),
"homeSubMenuSortBy": MessageLookupByLibrary.simpleMessage("排序"),
"homeTabMenuFavotite": MessageLookupByLibrary.simpleMessage("收藏"),
"homeTabMenuRecent": MessageLookupByLibrary.simpleMessage("最近更新"),
"homeToprightMenuAbout": MessageLookupByLibrary.simpleMessage("关于"),
"homeToprightMenuImportOMPL":
MessageLookupByLibrary.simpleMessage("导入OMPL"),
"homeToprightMenuRefreshAll":
MessageLookupByLibrary.simpleMessage("全部刷新"),
"hostedOn": m7,
"hoursAgo": m8,
"hoursCount": m9,
"import": MessageLookupByLibrary.simpleMessage("导入"),
"introFourthPage":
MessageLookupByLibrary.simpleMessage("您可以长按节目打开快捷菜单。"),
"introSecondPage":
MessageLookupByLibrary.simpleMessage("您可以通过搜索订阅播客也可以直接导入OMPL文件。"),
"introThirdPage":
MessageLookupByLibrary.simpleMessage("您可以创建分组,上下滑动切换分组。"),
"later": MessageLookupByLibrary.simpleMessage("稍后"),
"lightMode": MessageLookupByLibrary.simpleMessage("明亮模式"),
"like": MessageLookupByLibrary.simpleMessage("喜欢"),
"likeDate": MessageLookupByLibrary.simpleMessage("收藏日期"),
"liked": MessageLookupByLibrary.simpleMessage("已收藏"),
"listen": MessageLookupByLibrary.simpleMessage("收听"),
"listened": MessageLookupByLibrary.simpleMessage("已收听"),
"loadMore": MessageLookupByLibrary.simpleMessage("加载更多"),
"mark": MessageLookupByLibrary.simpleMessage("标记"),
"markConfirm": MessageLookupByLibrary.simpleMessage("确认标记"),
"markConfirmContent":
MessageLookupByLibrary.simpleMessage("是否确认标记全部节目为已收听?"),
"markListened": MessageLookupByLibrary.simpleMessage("标记已收听"),
"menu": MessageLookupByLibrary.simpleMessage("菜单"),
"menuAllPodcasts": MessageLookupByLibrary.simpleMessage("所有订阅"),
"menuMarkAllListened": MessageLookupByLibrary.simpleMessage("标记所有已收听"),
"menuViewRSS": MessageLookupByLibrary.simpleMessage("查看 RSS"),
"menuVisitSite": MessageLookupByLibrary.simpleMessage("访问网站"),
"minsAgo": m10,
"minsCount": m11,
"network": MessageLookupByLibrary.simpleMessage("网络"),
"newGroup": MessageLookupByLibrary.simpleMessage("创建分组"),
"newestFirst": MessageLookupByLibrary.simpleMessage("由新到旧"),
"next": MessageLookupByLibrary.simpleMessage("下一步"),
"noEpisodeDownload": MessageLookupByLibrary.simpleMessage("暂无下载节目"),
"noEpisodeFavorite": MessageLookupByLibrary.simpleMessage("暂无收藏节目"),
"noEpisodeRecent": MessageLookupByLibrary.simpleMessage("暂无节目"),
"noPodcastGroup": MessageLookupByLibrary.simpleMessage("分组无播客"),
"noShownote": MessageLookupByLibrary.simpleMessage("节目简介暂未收到。"),
"notificaitonFatch": m12,
"notificationNetworkError": m13,
"notificationSubscribe": m14,
"notificationSubscribeExisted": m15,
"notificationSuccess": m16,
"notificationUpdate": m17,
"notificationUpdateError": m18,
"oldestFirst": MessageLookupByLibrary.simpleMessage("由旧到新"),
"play": MessageLookupByLibrary.simpleMessage("播放"),
"playback": MessageLookupByLibrary.simpleMessage("播放控制"),
"playing": MessageLookupByLibrary.simpleMessage("正在播放"),
"plugins": MessageLookupByLibrary.simpleMessage("插件"),
"podcast": m19,
"podcastSubscribed": MessageLookupByLibrary.simpleMessage("播客已订阅"),
"popupMenuDownloadDes": MessageLookupByLibrary.simpleMessage("下载节目"),
"popupMenuLaterDes": MessageLookupByLibrary.simpleMessage("添加到播放列表"),
"popupMenuLikeDes": MessageLookupByLibrary.simpleMessage("添加到收藏"),
"popupMenuMarkDes": MessageLookupByLibrary.simpleMessage("设置为已收听"),
"popupMenuPlayDes": MessageLookupByLibrary.simpleMessage("播放节目"),
"privacyPolicy": MessageLookupByLibrary.simpleMessage("隐私条款"),
"published": m20,
"publishedDaily": MessageLookupByLibrary.simpleMessage("每日更新"),
"publishedMonthly": MessageLookupByLibrary.simpleMessage("每月更新"),
"publishedWeekly": MessageLookupByLibrary.simpleMessage("每周更新"),
"publishedYearly": MessageLookupByLibrary.simpleMessage("每年更新"),
"recoverSubscribe": MessageLookupByLibrary.simpleMessage("恢复订阅"),
"refreshArtwork": MessageLookupByLibrary.simpleMessage("更新头像"),
"remove": MessageLookupByLibrary.simpleMessage("移除"),
"removeConfirm": MessageLookupByLibrary.simpleMessage("取消订阅"),
"removePodcastDes": MessageLookupByLibrary.simpleMessage("您确认要取消订阅吗?"),
"removedAt": m21,
"save": MessageLookupByLibrary.simpleMessage("保存"),
"schedule": MessageLookupByLibrary.simpleMessage("定时"),
"search": MessageLookupByLibrary.simpleMessage("搜索"),
"searchEpisode": MessageLookupByLibrary.simpleMessage("搜索节目"),
"searchInvalidRss": MessageLookupByLibrary.simpleMessage("RSS 链接错误"),
"searchPodcast": MessageLookupByLibrary.simpleMessage("搜索播客"),
"secCount": m22,
"secondsAgo": m23,
"settingStorage": MessageLookupByLibrary.simpleMessage("储存空间"),
"settings": MessageLookupByLibrary.simpleMessage("设置"),
"settingsAccentColor": MessageLookupByLibrary.simpleMessage("次要颜色"),
"settingsAccentColorDes":
MessageLookupByLibrary.simpleMessage("包括溢出颜色"),
"settingsAppIntro": MessageLookupByLibrary.simpleMessage("引导页"),
"settingsAppearance": MessageLookupByLibrary.simpleMessage("界面"),
"settingsAppearanceDes": MessageLookupByLibrary.simpleMessage("颜色与主题"),
"settingsAudioCache": MessageLookupByLibrary.simpleMessage("播放缓存"),
"settingsAudioCacheDes": MessageLookupByLibrary.simpleMessage("播放缓存设置"),
"settingsAutoDelete": MessageLookupByLibrary.simpleMessage("自动删除下载节目"),
"settingsAutoDeleteDes":
MessageLookupByLibrary.simpleMessage("默认 30 天"),
"settingsAutoPlayDes": MessageLookupByLibrary.simpleMessage("自动播放下一节目"),
"settingsBackup": MessageLookupByLibrary.simpleMessage("备份"),
"settingsBackupDes": MessageLookupByLibrary.simpleMessage("备份应用数据"),
"settingsDefaultGrid": MessageLookupByLibrary.simpleMessage("默认布局"),
"settingsDefaultGridDownload":
MessageLookupByLibrary.simpleMessage("下载页"),
"settingsDefaultGridFavorite":
MessageLookupByLibrary.simpleMessage("收藏页"),
"settingsDefaultGridPodcast":
MessageLookupByLibrary.simpleMessage("播客页"),
"settingsDefaultGridRecent":
MessageLookupByLibrary.simpleMessage("最近页"),
"settingsDiscovery": MessageLookupByLibrary.simpleMessage("再次功能介绍"),
"settingsEnableSyncing": MessageLookupByLibrary.simpleMessage("开启自动更新"),
"settingsEnableSyncingDes":
MessageLookupByLibrary.simpleMessage("在后台更新所有订阅播客"),
"settingsExportDes": MessageLookupByLibrary.simpleMessage("导出及恢复所有设置项"),
"settingsFastForwardSec": MessageLookupByLibrary.simpleMessage("快进时间"),
"settingsFastForwardSecDes":
MessageLookupByLibrary.simpleMessage("修改播放器快进时间"),
"settingsFeedback": MessageLookupByLibrary.simpleMessage("反馈"),
"settingsFeedbackDes": MessageLookupByLibrary.simpleMessage("意见与建议"),
"settingsHistory": MessageLookupByLibrary.simpleMessage("历史记录"),
"settingsHistoryDes": MessageLookupByLibrary.simpleMessage("收听记录"),
"settingsInfo": MessageLookupByLibrary.simpleMessage("信息"),
"settingsInterface": MessageLookupByLibrary.simpleMessage("界面"),
"settingsLanguages": MessageLookupByLibrary.simpleMessage("语言"),
"settingsLanguagesDes": MessageLookupByLibrary.simpleMessage("设置语言"),
"settingsLayout": MessageLookupByLibrary.simpleMessage("布局"),
"settingsLayoutDes": MessageLookupByLibrary.simpleMessage("应用布局"),
"settingsLibraries": MessageLookupByLibrary.simpleMessage("开源"),
"settingsLibrariesDes": MessageLookupByLibrary.simpleMessage("开源项目使用"),
"settingsManageDownload": MessageLookupByLibrary.simpleMessage("下载管理"),
"settingsManageDownloadDes":
MessageLookupByLibrary.simpleMessage("管理下载节目文件"),
"settingsMenuAutoPlay":
MessageLookupByLibrary.simpleMessage("自动播放下一节目"),
"settingsNetworkCellular":
MessageLookupByLibrary.simpleMessage("蜂窝数据确认"),
"settingsNetworkCellularAuto":
MessageLookupByLibrary.simpleMessage("是否用蜂窝数据自动下载"),
"settingsNetworkCellularAutoDes":
MessageLookupByLibrary.simpleMessage("你可以在分组管理页面设置自动下载"),
"settingsNetworkCellularDes":
MessageLookupByLibrary.simpleMessage("在使用蜂窝数据下载前确认"),
"settingsPlayDes": MessageLookupByLibrary.simpleMessage("播放列表和播放器"),
"settingsPopupMenu": MessageLookupByLibrary.simpleMessage("节目弹出菜单"),
"settingsPopupMenuDes":
MessageLookupByLibrary.simpleMessage("修改节目弹出菜单"),
"settingsPrefrence": MessageLookupByLibrary.simpleMessage("首选项"),
"settingsRealDark": MessageLookupByLibrary.simpleMessage("极黑"),
"settingsRealDarkDes":
MessageLookupByLibrary.simpleMessage("如果夜不够黑,请开启"),
"settingsRewindSec": MessageLookupByLibrary.simpleMessage("快退时间"),
"settingsRewindSecDes":
MessageLookupByLibrary.simpleMessage("修改播放器快退时间"),
"settingsSTAuto": MessageLookupByLibrary.simpleMessage("自动睡眠模式"),
"settingsSTAutoDes": MessageLookupByLibrary.simpleMessage("定期开启睡眠模式"),
"settingsSTDefaultTime": MessageLookupByLibrary.simpleMessage("默认时长"),
"settingsSTDefautTimeDes":
MessageLookupByLibrary.simpleMessage("睡眠模式默认时长"),
"settingsSTMode": MessageLookupByLibrary.simpleMessage("自动睡眠模式默认时长"),
"settingsStorageDes": MessageLookupByLibrary.simpleMessage("管理缓存和下载空间"),
"settingsSyncing": MessageLookupByLibrary.simpleMessage("同步"),
"settingsSyncingDes": MessageLookupByLibrary.simpleMessage("在后台更新播客"),
"settingsTapToOpenPopupMenu":
MessageLookupByLibrary.simpleMessage("轻点打开弹出菜单"),
"settingsTapToOpenPopupMenuDes":
MessageLookupByLibrary.simpleMessage("开启后您需长按打开节目页"),
"settingsTheme": MessageLookupByLibrary.simpleMessage("主题"),
"settingsUpdateInterval": MessageLookupByLibrary.simpleMessage("更新频率"),
"settingsUpdateIntervalDes":
MessageLookupByLibrary.simpleMessage("默认 24 小时"),
"share": MessageLookupByLibrary.simpleMessage("分享"),
"size": MessageLookupByLibrary.simpleMessage("大小"),
"skipSecondsAtStart": MessageLookupByLibrary.simpleMessage("开头跳过秒数"),
"sleepTimer": MessageLookupByLibrary.simpleMessage("睡眠模式"),
"subscribe": MessageLookupByLibrary.simpleMessage("订阅"),
"subscribeExportDes":
MessageLookupByLibrary.simpleMessage("导出 OMPL 文件"),
"systemDefault": MessageLookupByLibrary.simpleMessage("系统默认"),
"timeLastPlayed": m24,
"timeLeft": m25,
"to": m26,
"toastAddPlaylist": MessageLookupByLibrary.simpleMessage("添加到播放列表"),
"toastDiscovery": MessageLookupByLibrary.simpleMessage("重启应用后可查看"),
"toastFileError": MessageLookupByLibrary.simpleMessage("文件错误,导入失败"),
"toastFileNotValid": MessageLookupByLibrary.simpleMessage("文件错误"),
"toastHomeGroupNotSupport":
MessageLookupByLibrary.simpleMessage("Home 分组不支持此功能"),
"toastImportSettingsSuccess":
MessageLookupByLibrary.simpleMessage("导入设置成功"),
"toastOneGroup": MessageLookupByLibrary.simpleMessage("请至少选择一个分组"),
"toastPodcastRecovering":
MessageLookupByLibrary.simpleMessage("恢复中,请稍后"),
"toastReadFile": MessageLookupByLibrary.simpleMessage("读取文件成功"),
"toastRecoverFailed": MessageLookupByLibrary.simpleMessage("恢复订阅失败"),
"toastRemovePlaylist": MessageLookupByLibrary.simpleMessage("从播放列表移除"),
"toastSettingSaved": MessageLookupByLibrary.simpleMessage("设置已保存"),
"toastTimeEqualEnd": MessageLookupByLibrary.simpleMessage("与结束时刻相同"),
"toastTimeEqualStart": MessageLookupByLibrary.simpleMessage("与起始时刻相同"),
"translators": MessageLookupByLibrary.simpleMessage("翻译者"),
"understood": MessageLookupByLibrary.simpleMessage("了解"),
"undo": MessageLookupByLibrary.simpleMessage("撤销"),
"unlike": MessageLookupByLibrary.simpleMessage("取消喜欢"),
"unliked": MessageLookupByLibrary.simpleMessage("从收藏移除"),
"updateDate": MessageLookupByLibrary.simpleMessage("更新日期"),
"updateEpisodesCount": m27,
"updateFailed": MessageLookupByLibrary.simpleMessage("更新失败"),
"version": m28
};
}

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tsacdop/util/custom_widget.dart';
import 'package:line_icons/line_icons.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
const String version = '0.4.8';

View File

@ -1,25 +1,25 @@
import 'dart:io';
import 'dart:math' as math;
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:marquee/marquee.dart';
import 'package:tuple/tuple.dart';
import 'package:audio_service/audio_service.dart';
import 'package:line_icons/line_icons.dart';
import 'package:marquee/marquee.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import '../episodes/episode_detail.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../state/audio_state.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../util/pageroute.dart';
import '../util/extension_helper.dart';
import '../util/custom_widget.dart';
import '../util/custom_slider.dart';
import '../episodes/episode_detail.dart';
import '../util/audiopanel.dart';
import '../util/custom_slider.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
import '../util/pageroute.dart';
import 'playlist.dart';
final List<BoxShadow> _customShadow = [
@ -40,7 +40,8 @@ final List<BoxShadow> _customShadowNight = [
String _stringForSeconds(double seconds) {
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')}';
}
const List minsToSelect = [10, 15, 20, 25, 30, 45, 60, 70, 80, 90, 99];
@ -402,7 +403,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
selector: (_, audio) =>
Tuple2(audio.episode?.primaryColor, audio.seekSliderValue),
builder: (_, data, __) {
Color _c = (Theme.of(context).brightness == Brightness.light)
var _c = (Theme.of(context).brightness == Brightness.light)
? data.item1.colorizedark()
: data.item1.colorizeLight();
return SizedBox(
@ -452,8 +453,8 @@ class _PlayerWidgetState extends State<PlayerWidget> {
alignment: Alignment.center,
child: data.item3 != null
? Text(data.item3,
style: const TextStyle(
color: const Color(0xFFFF0000)))
style:
const TextStyle(color: Color(0xFFFF0000)))
: data.item1
? Text(
s.buffering,
@ -563,7 +564,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
@override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
var _width = MediaQuery.of(context).size.width;
return Selector<AudioPlayerNotifier, bool>(
selector: (_, audio) => audio.playerRunning,
builder: (_, playerrunning, __) {
@ -585,11 +586,6 @@ class LastPosition extends StatefulWidget {
}
class _LastPositionState extends State<LastPosition> {
static String _stringForSeconds(double seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
Future<PlayHistory> getPosition(EpisodeBrief episode) async {
var dbHelper = DBHelper();
return await dbHelper.getPosition(episode);
@ -645,8 +641,7 @@ class _LastPositionState extends State<LastPosition> {
borderRadius: BorderRadius.all(
Radius.circular(10.0))),
child: Text(s.timeLastPlayed(
_stringForSeconds(
snapshot.data.seconds))),
snapshot.data.seconds.toTime)),
),
),
)
@ -680,10 +675,11 @@ class _ImageRotateState extends State<ImageRotate>
);
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_value = _animation.value;
});
}
});
_controller.forward();
_controller.addStatusListener((status) {
@ -759,7 +755,7 @@ class _MeteorLoaderState extends State<MeteorLoader>
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
animation = Tween(begin: 0.0, end: 1.0).animate(controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_move = animation.value;
if (animation.value <= 0.5) {
@ -768,6 +764,7 @@ class _MeteorLoaderState extends State<MeteorLoader>
_fraction = 2 - (animation.value) * 2;
}
});
}
});
controller.forward();
// controller.addStatusListener((status) {
@ -812,9 +809,8 @@ class SleepModeState extends State<SleepMode>
Animation<double> _animation;
Future _getDefaultTime() async {
KeyValueStorage defaultSleepTimerStorage =
KeyValueStorage(defaultSleepTimerKey);
int defaultTime = await defaultSleepTimerStorage.getInt(defaultValue: 30);
var defaultSleepTimerStorage = KeyValueStorage(defaultSleepTimerKey);
var defaultTime = await defaultSleepTimerStorage.getInt(defaultValue: 30);
setState(() => _minSelected = defaultTime);
}
@ -869,15 +865,15 @@ class SleepModeState extends State<SleepMode>
@override
Widget build(BuildContext context) {
final s = context.s;
final ColorTween _colorTween =
final _colorTween =
ColorTween(begin: context.primaryColor, end: Colors.black);
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
return Selector<AudioPlayerNotifier, Tuple3<int, double, SleepTimerMode>>(
selector: (_, audio) =>
Tuple3(audio.timeLeft, audio.switchValue, audio.sleepTimerMode),
builder: (_, data, __) {
double fraction = data.item2 < 0.5 ? data.item2 * 2 : 1;
double move = data.item2 > 0.5 ? data.item2 * 2 - 1 : 0;
var fraction = data.item2 < 0.5 ? data.item2 * 2 : 1;
var move = data.item2 > 0.5 ? data.item2 * 2 - 1 : 0;
return Container(
height: 300,
color: _colorTween.transform(move),
@ -1173,7 +1169,7 @@ class _ControlPanelState extends State<ControlPanel>
children: <Widget>[
Consumer<AudioPlayerNotifier>(
builder: (_, data, __) {
Color _c = (context.brightness == Brightness.light)
var _c = (context.brightness == Brightness.light)
? data.episode.primaryColor.colorizedark()
: data.episode.primaryColor.colorizeLight();
return Column(
@ -1204,7 +1200,7 @@ class _ControlPanelState extends State<ControlPanel>
),
child: Slider(
value: data.seekSliderValue,
onChanged: (double val) {
onChanged: (val) {
audio.sliderSeek(val);
}),
),
@ -1226,7 +1222,7 @@ class _ControlPanelState extends State<ControlPanel>
child: data.remoteErrorMessage != null
? Text(data.remoteErrorMessage,
style: const TextStyle(
color: const Color(0xFFFF0000)))
color: Color(0xFFFF0000)))
: Text(
data.audioState ==
AudioProcessingState
@ -1472,10 +1468,11 @@ class _ControlPanelState extends State<ControlPanel>
IconButton(
padding: EdgeInsets.zero,
onPressed: () {
if (_setSpeed == 0)
if (_setSpeed == 0) {
_controller.forward();
else
} else {
_controller.reverse();
}
},
icon: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,

View File

@ -1,10 +1,11 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import '../state/download_state.dart';
import '../episodes/episode_detail.dart';
import '../state/download_state.dart';
import '../util/pageroute.dart';
class DownloadList extends StatefulWidget {
@ -63,7 +64,7 @@ class _DownloadListState extends State<DownloadList> {
padding: EdgeInsets.all(5.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
(context, index) {
return ListTile(
onTap: () => Navigator.push(
context,
@ -101,7 +102,7 @@ class _DownloadListState extends State<DownloadList> {
Radius.circular(6)),
color: Colors.red),
child: Text(
tasks[index].progress.toString() + '%',
'${tasks[index].progress}%',
textAlign: TextAlign.center,
maxLines: 1,
style: TextStyle(color: Colors.white),

View File

@ -1,34 +1,34 @@
import 'dart:async';
import 'dart:io';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:feature_discovery/feature_discovery.dart';
import 'package:flutter/material.dart' hide NestedScrollView;
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:line_icons/line_icons.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:feature_discovery/feature_discovery.dart';
import '../state/audio_state.dart';
import '../type/playlist.dart';
import '../type/episodebrief.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../util/episodegrid.dart';
import '../util/mypopupmenu.dart';
import '../util/extension_helper.dart';
import '../util/custom_widget.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../state/download_state.dart';
import '../state/podcast_group.dart';
import '../state/setting_state.dart';
import 'playlist.dart';
import 'import_ompl.dart';
import '../type/episodebrief.dart';
import '../type/playlist.dart';
import '../util/custom_widget.dart';
import '../util/episodegrid.dart';
import '../util/extension_helper.dart';
import '../util/mypopupmenu.dart';
import 'audioplayer.dart';
import 'search_podcast.dart';
import 'home_menu.dart';
import 'home_groups.dart';
import 'download_list.dart';
import 'home_groups.dart';
import 'home_menu.dart';
import 'import_ompl.dart';
import 'playlist.dart';
import 'search_podcast.dart';
const String addFeature = 'addFeature';
const String menuFeature = 'menuFeature';
@ -55,7 +55,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
));
}
var _androidAppRetain = MethodChannel("android_app_retain");
final _androidAppRetain = MethodChannel("android_app_retain");
var feature1OverflowMode = OverflowMode.clipContent;
var feature1EnablePulsingAnimation = false;
@ -64,7 +64,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
super.initState();
_controller = TabController(length: 3, vsync: this);
FeatureDiscovery.isDisplayed(context, addFeature).then((value) {
if (!value)
if (!value) {
WidgetsBinding.instance.addPostFrameCallback((_) {
FeatureDiscovery.discoverFeatures(
context,
@ -77,6 +77,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
},
);
});
}
});
}
@ -89,8 +90,8 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
double top = 0;
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = (width - 20) / 3 + 140;
var width = MediaQuery.of(context).size.width;
var height = (width - 20) / 3 + 140;
var settings = Provider.of<SettingState>(context, listen: false);
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
@ -119,11 +120,10 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
Expanded(
child: NestedScrollView(
innerScrollPositionKeyBuilder: () {
return Key('tab' + _controller.index.toString());
return Key('tab${_controller.index}');
},
pinnedHeaderSliverHeightBuilder: () => 50,
headerSliverBuilder:
(BuildContext context, bool innerBoxScrolled) {
headerSliverBuilder: (context, innerBoxScrolled) {
return <Widget>[
SliverToBoxAdapter(
child: Column(
@ -276,7 +276,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
(context, index) {
return DescribedFeatureOverlay(
featureId: groupsFeature,
tapTarget: Center(
@ -686,26 +686,28 @@ class _RecentUpdate extends StatefulWidget {
class _RecentUpdateState extends State<_RecentUpdate>
with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin {
Future<List<EpisodeBrief>> _getRssItem(int top, List<String> group) async {
KeyValueStorage storage = KeyValueStorage(recentLayoutKey);
int index = await storage.getInt(defaultValue: 1);
var storage = KeyValueStorage(recentLayoutKey);
var index = await storage.getInt(defaultValue: 1);
if (_layout == null) _layout = Layout.values[index];
var dbHelper = DBHelper();
List<EpisodeBrief> episodes;
if (group.first == 'All')
if (group.first == 'All') {
episodes = await dbHelper.getRecentRssItem(top);
else
} else {
episodes = await dbHelper.getGroupRssItem(top, group);
}
return episodes;
}
Future<int> _getUpdateCounts(List<String> group) async {
var dbHelper = DBHelper();
List<EpisodeBrief> episodes = [];
if (group.first == 'All')
var episodes = <EpisodeBrief>[];
if (group.first == 'All') {
episodes = await dbHelper.getRecentNewRssItem();
else
} else {
episodes = await dbHelper.getGroupNewRssItem(group);
}
return episodes.length;
}
@ -713,11 +715,12 @@ class _RecentUpdateState extends State<_RecentUpdate>
_loadMoreEpisode() async {
if (mounted) setState(() => _loadMore = true);
await Future.delayed(Duration(seconds: 3));
if (mounted)
if (mounted) {
setState(() {
_top = _top + 33;
_loadMore = false;
});
}
}
/// Episodes loaded first time.
@ -771,7 +774,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
),
)
: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
onNotification: (scrollInfo) {
if (scrollInfo is ScrollStartNotification &&
mounted &&
!_scroll) {
@ -779,8 +782,9 @@ class _RecentUpdateState extends State<_RecentUpdate>
}
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top)
snapshot.data.length == _top) {
_loadMoreEpisode();
}
return true;
},
child: CustomScrollView(
@ -912,9 +916,10 @@ class _RecentUpdateState extends State<_RecentUpdate>
await audio
.addNewEpisode(
_group);
if (mounted)
if (mounted) {
setState(
() {});
}
Fluttertoast
.showToast(
msg: _groupName ==
@ -960,19 +965,21 @@ class _RecentUpdateState extends State<_RecentUpdate>
tooltip: s.changeLayout,
padding: EdgeInsets.zero,
onPressed: () {
if (_layout == Layout.three)
if (_layout ==
Layout.three) {
setState(() {
_layout = Layout.one;
});
else if (_layout ==
Layout.two)
} else if (_layout ==
Layout.two) {
setState(() {
_layout = Layout.three;
});
else
} else {
setState(() {
_layout = Layout.two;
});
}
},
icon: _layout == Layout.three
? SizedBox(
@ -1026,7 +1033,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
(context, index) {
return _loadMore
? Container(
height: 2,
@ -1055,22 +1062,23 @@ class _MyFavorite extends StatefulWidget {
class _MyFavoriteState extends State<_MyFavorite>
with AutomaticKeepAliveClientMixin {
Future<List<EpisodeBrief>> _getLikedRssItem(int top, int sortBy) async {
KeyValueStorage storage = KeyValueStorage(favLayoutKey);
int index = await storage.getInt(defaultValue: 1);
var storage = KeyValueStorage(favLayoutKey);
var index = await storage.getInt(defaultValue: 1);
if (_layout == null) _layout = Layout.values[index];
var dbHelper = DBHelper();
List<EpisodeBrief> episodes = await dbHelper.getLikedRssItem(top, sortBy);
var episodes = await dbHelper.getLikedRssItem(top, sortBy);
return episodes;
}
_loadMoreEpisode() async {
if (mounted) setState(() => _loadMore = true);
await Future.delayed(Duration(seconds: 3));
if (mounted)
if (mounted) {
setState(() {
_top = _top + 33;
_loadMore = false;
});
}
}
int _top = 99;
@ -1114,11 +1122,12 @@ class _MyFavoriteState extends State<_MyFavorite>
),
)
: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top)
snapshot.data.length == _top) {
_loadMoreEpisode();
}
return true;
},
child: CustomScrollView(
@ -1184,10 +1193,11 @@ class _MyFavoriteState extends State<_MyFavorite>
)
],
onSelected: (value) {
if (value == 0)
if (value == 0) {
setState(() => _sortBy = 0);
else if (value == 1)
} else if (value == 1) {
setState(() => _sortBy = 1);
}
},
),
),
@ -1197,18 +1207,20 @@ class _MyFavoriteState extends State<_MyFavorite>
child: IconButton(
padding: EdgeInsets.zero,
onPressed: () {
if (_layout == Layout.three)
if (_layout == Layout.three) {
setState(() {
_layout = Layout.one;
});
else if (_layout == Layout.two)
} else if (_layout ==
Layout.two) {
setState(() {
_layout = Layout.three;
});
else
} else {
setState(() {
_layout = Layout.two;
});
}
},
icon: _layout == Layout.three
? SizedBox(
@ -1259,7 +1271,7 @@ class _MyFavoriteState extends State<_MyFavorite>
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
(context, index) {
return _loadMore
? Container(
height: 2,
@ -1291,12 +1303,13 @@ class _MyDownloadState extends State<_MyDownload>
with AutomaticKeepAliveClientMixin {
Layout _layout;
_getLayout() async {
KeyValueStorage keyValueStorage = KeyValueStorage(downloadLayoutKey);
int layout = await keyValueStorage.getInt(defaultValue: 1);
if (_layout == null)
var keyValueStorage = KeyValueStorage(downloadLayoutKey);
var layout = await keyValueStorage.getInt(defaultValue: 1);
if (_layout == null) {
setState(() {
_layout = Layout.values[layout];
});
}
}
@override
@ -1327,48 +1340,11 @@ class _MyDownloadState extends State<_MyDownload>
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),
),
),
child: LayoutButton(
layout: _layout,
onPressed: (layout) => setState(() {
_layout = layout;
}),
),
),
],

View File

@ -1,30 +1,30 @@
import 'dart:io';
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:focused_menu/focused_menu.dart';
import 'package:focused_menu/modals.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tuple/tuple.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../state/podcast_group.dart';
import '../state/download_state.dart';
import '../type/podcastlocal.dart';
import '../state/audio_state.dart';
import '../util/custom_widget.dart';
import '../util/pageroute.dart';
import '../util/extension_helper.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../episodes/episode_detail.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../podcasts/podcast_detail.dart';
import '../podcasts/podcast_manage.dart';
import '../state/audio_state.dart';
import '../state/download_state.dart';
import '../state/podcast_group.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../type/podcastlocal.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
import '../util/pageroute.dart';
class ScrollPodcasts extends StatefulWidget {
@override
@ -55,15 +55,15 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
@override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
var _width = MediaQuery.of(context).size.width;
final s = context.s;
return Selector<GroupList, Tuple3<List<PodcastGroup>, bool, bool>>(
selector: (_, groupList) =>
Tuple3(groupList.groups, groupList.created, groupList.isLoading),
builder: (_, data, __) {
var groups = data.item1;
bool import = data.item2;
bool isLoading = data.item3;
var import = data.item2;
var isLoading = data.item3;
return isLoading
? Container(
height: (_width - 20) / 3 + 140,
@ -84,12 +84,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
gravity: ToastGravity.BOTTOM,
);
} else {
if (mounted)
if (mounted) {
setState(() {
(_groupIndex != 0)
? _groupIndex--
: _groupIndex = groups.length - 1;
});
}
}
} else if (event.primaryVelocity < -200) {
if (groups.length == 1) {
@ -131,12 +132,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
alignment: Alignment.bottomRight,
child: InkWell(
onTap: () {
if (!import)
if (!import) {
Navigator.push(
context,
SlideLeftRoute(
page: PodcastManage()),
);
}
},
child: Container(
height: 30,
@ -196,7 +198,7 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
WidgetSpan(
child:
Icon(Icons.add_circle_outline)),
TextSpan(text: ' to subscribe podcasts')
TextSpan(text: ' to search podcasts')
],
))
: Text(s.noPodcastGroup,
@ -223,12 +225,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
gravity: ToastGravity.BOTTOM,
);
} else {
if (mounted)
if (mounted) {
setState(() {
(_groupIndex != 0)
? _groupIndex--
: _groupIndex = groups.length - 1;
});
}
}
} else if (event.primaryVelocity < -200) {
if (groups.length == 1) {
@ -268,12 +271,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
alignment: Alignment.bottomRight,
child: InkWell(
onTap: () {
if (!import)
if (!import) {
Navigator.push(
context,
SlideLeftRoute(
page: PodcastManage()),
);
}
},
child: Container(
height: 30,
@ -309,14 +313,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
isScrollable: true,
tabs: groups[_groupIndex]
.podcasts
.map<Widget>((PodcastLocal podcastLocal) {
Color color =
(Theme.of(context).brightness ==
Brightness.light)
? podcastLocal.primaryColor
.colorizedark()
: podcastLocal.primaryColor
.colorizeLight();
.map<Widget>((podcastLocal) {
var color = (Theme.of(context).brightness ==
Brightness.light)
? podcastLocal.primaryColor
.colorizedark()
: podcastLocal.primaryColor
.colorizeLight();
return Tab(
child: ClipRRect(
borderRadius: BorderRadius.all(
@ -378,7 +381,7 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
child: TabBarView(
children: groups[_groupIndex]
.podcasts
.map<Widget>((PodcastLocal podcastLocal) {
.map<Widget>((podcastLocal) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).brightness ==
@ -408,13 +411,13 @@ class PodcastPreview extends StatelessWidget {
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
var dbHelper = DBHelper();
List<EpisodeBrief> episodes = await dbHelper.getRssItemTop(podcastLocal.id);
var episodes = await dbHelper.getRssItemTop(podcastLocal.id);
return episodes;
}
@override
Widget build(BuildContext context) {
Color _c = (Theme.of(context).brightness == Brightness.light)
var _c = (Theme.of(context).brightness == Brightness.light)
? podcastLocal.primaryColor.colorizedark()
: podcastLocal.primaryColor.colorizeLight();
return Column(
@ -504,7 +507,7 @@ class ShowEpisode extends StatelessWidget {
String _dateToString(BuildContext context, {int pubDate}) {
final s = context.s;
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
var date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
var difference = DateTime.now().toUtc().difference(date);
if (difference.inHours < 24) {
return s.hoursAgo(difference.inHours);
@ -518,50 +521,48 @@ class ShowEpisode extends StatelessWidget {
Future<Tuple5<int, bool, bool, bool, List<int>>> _initData(
EpisodeBrief episode) async {
List<int> menuList = await _getEpisodeMenu();
bool tapToOpen = await _getTapToOpenPopupMenu();
int listened = await _isListened(episode);
var menuList = await _getEpisodeMenu();
var tapToOpen = await _getTapToOpenPopupMenu();
var listened = await _isListened(episode);
bool liked = await _isLiked(episode);
bool downloaded = await _isDownloaded(episode);
var liked = await _isLiked(episode);
var downloaded = await _isDownloaded(episode);
return Tuple5(listened, liked, downloaded, tapToOpen, menuList);
}
Future<int> _isListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
Future<bool> _isLiked(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
Future<List<int>> _getEpisodeMenu() async {
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
List<int> list = await popupMenuStorage.getMenu();
var popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
var list = await popupMenuStorage.getMenu();
return list;
}
Future<bool> _isDownloaded(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isDownloaded(episode.enclosureUrl);
}
Future<bool> _getTapToOpenPopupMenu() async {
KeyValueStorage tapToOpenPopupMenuStorage =
KeyValueStorage(tapToOpenPopupMenuKey);
int boo = await tapToOpenPopupMenuStorage.getInt(defaultValue: 0);
var tapToOpenPopupMenuStorage = KeyValueStorage(tapToOpenPopupMenuKey);
var boo = await tapToOpenPopupMenuStorage.getInt(defaultValue: 0);
return boo == 1;
}
_markListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
bool marked = await dbHelper.checkMarked(episode);
var dbHelper = DBHelper();
var marked = await dbHelper.checkMarked(episode);
if (!marked) {
final PlayHistory history =
PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
}
}
@ -578,7 +579,7 @@ class ShowEpisode extends StatelessWidget {
@override
Widget build(BuildContext context) {
double _width = context.width;
var _width = context.width;
final s = context.s;
var downloader = Provider.of<DownloadState>(context, listen: false);
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
@ -596,8 +597,8 @@ class ShowEpisode extends StatelessWidget {
crossAxisSpacing: 6.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
Color _c = (Theme.of(context).brightness == Brightness.light)
(context, index) {
var _c = (Theme.of(context).brightness == Brightness.light)
? podcastLocal.primaryColor.colorizedark()
: podcastLocal.primaryColor.colorizeLight();
return Selector<AudioPlayerNotifier,
@ -612,13 +613,12 @@ class ShowEpisode extends StatelessWidget {
Tuple5<int, bool, bool, bool, List<int>>>(
future: _initData(episodes[index]),
initialData: Tuple5(0, false, false, false, []),
builder:
(BuildContext context, AsyncSnapshot snapshot) {
int isListened = snapshot.data.item1;
bool isLiked = snapshot.data.item2;
bool isDownloaded = snapshot.data.item3;
bool tapToOpen = snapshot.data.item4;
List<int> menuList = snapshot.data.item5;
builder: (context, snapshot) {
var isListened = snapshot.data.item1;
var isLiked = snapshot.data.item2;
var isDownloaded = snapshot.data.item3;
var tapToOpen = snapshot.data.item4;
var menuList = snapshot.data.item5;
return Container(
decoration: BoxDecoration(
borderRadius:
@ -658,8 +658,9 @@ class ShowEpisode extends StatelessWidget {
color: context.accentColor,
),
onPressed: () {
if (data.item1 != episodes[index])
if (data.item1 != episodes[index]) {
audio.episodeLoad(episodes[index]);
}
}),
menuList.contains(1)
? FocusedMenuItem(
@ -777,9 +778,10 @@ class ShowEpisode extends StatelessWidget {
LineIcons.download_solid,
color: Colors.green),
onPressed: () {
if (!isDownloaded)
if (!isDownloaded) {
downloader
.startTask(episodes[index]);
}
})
: null
],
@ -804,8 +806,8 @@ class ShowEpisode extends StatelessWidget {
MainAxisAlignment.start,
children: <Widget>[
Hero(
tag: episodes[index].enclosureUrl +
'scroll',
tag:
'${episodes[index].enclosureUrl}scroll',
child: Container(
height: _width / 18,
width: _width / 18,
@ -929,11 +931,7 @@ class ShowEpisode extends StatelessWidget {
? Container(
alignment: Alignment.center,
child: Text(
((episodes[index]
.enclosureLength) ~/
1000000)
.toString() +
'MB',
'${(episodes[index].enclosureLength) ~/ 1000000}MB',
style: TextStyle(
fontSize:
_width / 35),
@ -978,7 +976,7 @@ class _CirclePainter extends BoxPainter {
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
final Offset circleOffset =
final circleOffset =
offset + Offset(cfg.size.width / 2, cfg.size.height - radius);
canvas.drawCircle(circleOffset, radius, _paint);
}

View File

@ -1,19 +1,18 @@
import 'dart:io';
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/local_storage/key_value_storage.dart';
import 'package:tsacdop/service/ompl_build.dart';
import 'package:tsacdop/state/podcast_group.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:line_icons/line_icons.dart';
import 'package:intl/intl.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import '../local_storage/key_value_storage.dart';
import '../service/ompl_build.dart';
import '../settings/settting.dart';
import '../state/podcast_group.dart';
import '../state/refresh_podcast.dart';
import '../util/extension_helper.dart';
import 'about.dart';
@ -27,16 +26,16 @@ class _PopupMenuState extends State<PopupMenu> {
Future<String> _getRefreshDate(BuildContext context) async {
int refreshDate;
final s = context.s;
KeyValueStorage refreshstorage = KeyValueStorage('refreshdate');
int i = await refreshstorage.getInt();
var refreshstorage = KeyValueStorage('refreshdate');
var i = await refreshstorage.getInt();
if (i == 0) {
KeyValueStorage refreshstorage = KeyValueStorage('refreshdate');
var refreshstorage = KeyValueStorage('refreshdate');
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
refreshDate = DateTime.now().millisecondsSinceEpoch;
} else {
refreshDate = i;
}
DateTime date = DateTime.fromMillisecondsSinceEpoch(refreshDate);
var date = DateTime.fromMillisecondsSinceEpoch(refreshDate);
var difference = DateTime.now().difference(date);
if (difference.inSeconds < 60) {
return s.secondsAgo(difference.inSeconds);
@ -54,19 +53,19 @@ class _PopupMenuState extends State<PopupMenu> {
void _saveOmpl(String path) async {
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
RegExp rssExp = RegExp(r'^(https?):\/\/(.*)');
var rssExp = RegExp(r'^(https?):\/\/(.*)');
final s = context.s;
File file = File(path);
var file = File(path);
try {
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOMPL(file);
for (var entry in data.entries) {
String title = entry.key;
var title = entry.key;
print(title);
var list = entry.value.reversed;
for (var rss in list) {
String rssLink = rssExp.stringMatch(rss.xmlUrl);
var rssLink = rssExp.stringMatch(rss.xmlUrl);
if (rssLink != null) {
SubscribeItem item = SubscribeItem(rssLink, rss.text, group: title);
var item = SubscribeItem(rssLink, rss.text, group: title);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 200));
print(rss.text);
@ -85,11 +84,11 @@ class _PopupMenuState extends State<PopupMenu> {
void _getFilePath() async {
final s = context.s;
try {
String filePath = await FilePicker.getFilePath(type: FileType.any);
var filePath = await FilePicker.getFilePath(type: FileType.any);
if (filePath == '') {
return;
}
print('File Path' + filePath);
print('File Path$filePath');
//importOmpl.importState = ImportState.start;
Fluttertoast.showToast(
msg: s.toastReadFile,
@ -131,13 +130,14 @@ class _PopupMenuState extends State<PopupMenu> {
FutureBuilder<String>(
future: _getRefreshDate(context),
builder: (_, snapshot) {
if (snapshot.hasData)
if (snapshot.hasData) {
return Text(
snapshot.data,
style: TextStyle(color: Colors.red, fontSize: 12),
);
else
} else {
return Center();
}
})
],
),

View File

@ -4,10 +4,9 @@ import 'package:provider/provider.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/podcast_group.dart';
import '../state/download_state.dart';
import '../state/podcast_group.dart';
import '../state/refresh_podcast.dart';
import '../type/episodebrief.dart';
import '../util/extension_helper.dart';
class Import extends StatelessWidget {
@ -30,38 +29,39 @@ class Import extends StatelessWidget {
}
_autoDownloadNew(BuildContext context) async {
final DBHelper dbHelper = DBHelper();
final dbHelper = DBHelper();
var downloader = Provider.of<DownloadState>(context, listen: false);
var result = await Connectivity().checkConnectivity();
KeyValueStorage autoDownloadStorage =
KeyValueStorage(autoDownloadNetworkKey);
int autoDownloadNetwork = await autoDownloadStorage.getInt();
var autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
var autoDownloadNetwork = await autoDownloadStorage.getInt();
if (autoDownloadNetwork == 1) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
var episodes = await dbHelper.getNewEpisodes('all');
// For safety
if (episodes.length < 100 && episodes.length > 0)
if (episodes.length < 100 && episodes.length > 0) {
for (var episode in episodes) {
await downloader.startTask(episode, showNotification: true);
}
}
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
var episodes = await dbHelper.getNewEpisodes('all');
//For safety
if (episodes.length < 100 && episodes.length > 0)
if (episodes.length < 100 && episodes.length > 0) {
for (var episode in episodes) {
await downloader.startTask(episode, showNotification: true);
}
}
}
}
@override
Widget build(BuildContext context) {
final s = context.s;
GroupList groupList = Provider.of<GroupList>(context, listen: false);
var groupList = Provider.of<GroupList>(context, listen: false);
return Column(
children: <Widget>[
Consumer<GroupList>(
builder: (_, subscribeWorker, __) {
SubscribeItem item = subscribeWorker.currentSubscribeItem;
var item = subscribeWorker.currentSubscribeItem;
switch (item.subscribeState) {
case SubscribeState.start:
return importColumn(
@ -83,7 +83,7 @@ class Import extends StatelessWidget {
),
Consumer<RefreshWorker>(
builder: (context, refreshWorker, child) {
RefreshItem item = refreshWorker.currentRefreshItem;
var item = refreshWorker.currentRefreshItem;
if (refreshWorker.complete) {
groupList.updateGroups();
_autoDownloadNew(context);

View File

@ -2,17 +2,17 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tuple/tuple.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import '../state/audio_state.dart';
import '../type/episodebrief.dart';
import '../type/playlist.dart';
import '../util/extension_helper.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
class PlaylistPage extends StatefulWidget {
@override
@ -23,7 +23,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
final textstyle = TextStyle(fontSize: 15.0, color: Colors.black);
int _sumPlaylistLength(List<EpisodeBrief> episodes) {
int sum = 0;
var sum = 0;
if (episodes.length == 0) {
return sum;
} else {
@ -36,7 +36,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
ScrollController _controller;
_scrollListener() {
double value = _controller.offset;
var value = _controller.offset;
setState(() => _topHeight = (100 - value) > 60 ? 100 - value : 60);
}
@ -79,7 +79,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
selector: (_, audio) =>
Tuple3(audio.queue, audio.playerRunning, audio.queueUpdate),
builder: (_, data, __) {
final List<EpisodeBrief> episodes = data.item1.playlist;
final episodes = data.item1.playlist;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@ -100,7 +100,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
child: RichText(
text: TextSpan(
text: _topHeight > 90
? s.homeMenuPlaylist + '\n'
? '${s.homeMenuPlaylist}\n'
: '',
style: TextStyle(
color: Theme.of(context)
@ -243,11 +243,11 @@ class _PlaylistPageState extends State<PlaylistPage> {
Expanded(
child: ReorderableListView(
scrollController: _controller,
onReorder: (int oldIndex, int newIndex) {
onReorder: (oldIndex, newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final EpisodeBrief episodeRemove = episodes[oldIndex];
final episodeRemove = episodes[oldIndex];
audio.delFromPlaylist(episodeRemove);
audio.addToPlaylistAt(episodeRemove, newIndex);
setState(() {});
@ -256,15 +256,16 @@ class _PlaylistPageState extends State<PlaylistPage> {
children: data.item2
? episodes.map<Widget>((episode) {
if (episode.enclosureUrl !=
episodes.first.enclosureUrl)
episodes.first.enclosureUrl) {
return DismissibleContainer(
episode: episode,
key: ValueKey(episode.enclosureUrl),
);
else
} else {
return Container(
key: ValueKey('sd'),
);
}
}).toList()
: episodes
.map<Widget>((episode) => DismissibleContainer(
@ -315,7 +316,7 @@ class _DismissibleContainerState extends State<DismissibleContainer> {
Widget build(BuildContext context) {
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
final s = context.s;
Color _c = (Theme.of(context).brightness == Brightness.light)
var _c = (Theme.of(context).brightness == Brightness.light)
? widget.episode.primaryColor.colorizedark()
: widget.episode.primaryColor.colorizeLight();
return AnimatedContainer(
@ -327,7 +328,7 @@ class _DismissibleContainerState extends State<DismissibleContainer> {
color: Colors.transparent,
)
: Dismissible(
key: ValueKey(widget.episode.enclosureUrl + 't'),
key: ValueKey('${widget.episode.enclosureUrl}t'),
background: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
@ -364,7 +365,7 @@ class _DismissibleContainerState extends State<DismissibleContainer> {
setState(() {
_delete = true;
});
int index = await audio.delFromPlaylist(widget.episode);
var index = await audio.delFromPlaylist(widget.episode);
final episodeRemove = widget.episode;
Fluttertoast.showToast(
msg: s.toastRemovePlaylist,
@ -430,9 +431,7 @@ class _DismissibleContainerState extends State<DismissibleContainer> {
: Center(),
widget.episode.enclosureLength != null
? _episodeTag(
((widget.episode.enclosureLength) ~/ 1000000)
.toString() +
'MB',
'${(widget.episode.enclosureLength) ~/ 1000000}MB',
Colors.lightBlue[300])
: Center(),
],

View File

@ -1,19 +1,18 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:tsacdop/state/podcast_group.dart';
import 'package:provider/provider.dart';
import '../type/searchpodcast.dart';
import '../type/searchepisodes.dart';
import '../service/api_search.dart';
import '../state/podcast_group.dart';
import '../type/searchepisodes.dart';
import '../type/searchpodcast.dart';
import '../util/extension_helper.dart';
import '../webfeed/webfeed.dart';
@ -27,14 +26,14 @@ class MyHomePageDelegate extends SearchDelegate<int> {
static Future getRss(String url) async {
try {
BaseOptions options = new BaseOptions(
var options = BaseOptions(
connectTimeout: 10000,
receiveTimeout: 10000,
);
Response response = await Dio(options).get(url);
var response = await Dio(options).get(url);
return RssFeed.parse(response.data);
} catch (e) {
throw e;
rethrow;
}
}
@ -94,7 +93,7 @@ class MyHomePageDelegate extends SearchDelegate<int> {
@override
Widget buildResults(BuildContext context) {
if (query.isEmpty)
if (query.isEmpty) {
return Container(
height: 10,
width: 10,
@ -107,29 +106,31 @@ class MyHomePageDelegate extends SearchDelegate<int> {
),
),
);
else if (rssExp.stringMatch(query) != null)
} else if (rssExp.stringMatch(query) != null) {
return FutureBuilder(
future: getRss(rssExp.stringMatch(query)),
builder: (context, snapshot) {
if (snapshot.hasError)
if (snapshot.hasError) {
return invalidRss(context);
else if (snapshot.hasData)
} else if (snapshot.hasData) {
return RssResult(
url: rssExp.stringMatch(query),
rssFeed: snapshot.data,
);
else
} else {
return Container(
padding: EdgeInsets.only(top: 200),
alignment: Alignment.topCenter,
child: CircularProgressIndicator(),
);
}
},
);
else
} else {
return SearchList(
query: query,
);
}
}
}
@ -165,7 +166,7 @@ class _RssResultState extends State<RssResult> {
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
final s = context.s;
_subscribePodcast(OnlinePodcast podcast) {
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title,
var item = SubscribeItem(podcast.rss, podcast.title,
imgUrl: podcast.image, group: 'Home');
subscribeWorker.setSubscribeItem(item);
}
@ -325,7 +326,7 @@ class _RssResultState extends State<RssResult> {
ListView.builder(
itemCount: math.min(_loadItems + 1, items.length),
itemBuilder: (context, index) {
if (index == _loadItems)
if (index == _loadItems) {
return Container(
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
alignment: Alignment.center,
@ -344,6 +345,7 @@ class _RssResultState extends State<RssResult> {
),
),
);
}
return ListTile(
title: Text(items[index].title),
subtitle: Text('${items[index].pubDate}',
@ -368,12 +370,12 @@ class SearchList extends StatefulWidget {
class _SearchListState extends State<SearchList> {
int _nextOffset = 0;
List<OnlinePodcast> _podcastList = [];
final List<OnlinePodcast> _podcastList = [];
int _offset;
bool _loading;
OnlinePodcast _selectedPodcast;
Future _searchFuture;
List<OnlinePodcast> _subscribed = [];
final List<OnlinePodcast> _subscribed = [];
@override
void initState() {
super.initState();
@ -382,7 +384,7 @@ class _SearchListState extends State<SearchList> {
Future<List<OnlinePodcast>> _getList(
String searchText, int nextOffset) async {
SearchEngine searchEngine = SearchEngine();
var searchEngine = SearchEngine();
var searchResult = await searchEngine.searchPodcasts(
searchText: searchText, nextOffset: nextOffset);
_offset = searchResult.nextOffset;
@ -398,13 +400,14 @@ class _SearchListState extends State<SearchList> {
children: [
FutureBuilder<List>(
future: _searchFuture,
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (!snapshot.hasData && widget.query != null)
builder: (context, snapshot) {
if (!snapshot.hasData && widget.query != null) {
return Container(
padding: EdgeInsets.only(top: 200),
alignment: Alignment.topCenter,
child: CircularProgressIndicator(),
);
}
var content = snapshot.data;
return CustomScrollView(
slivers: [
@ -519,7 +522,7 @@ class SearchResult extends StatelessWidget {
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
final s = context.s;
subscribePodcast(OnlinePodcast podcast) {
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title,
var item = SubscribeItem(podcast.rss, podcast.title,
imgUrl: podcast.image, group: 'Home');
subscribeWorker.setSubscribeItem(item);
onSubscribe(podcast);
@ -647,7 +650,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
int _nextEpisdoeDate = DateTime.now().millisecondsSinceEpoch;
/// Search result.
List<OnlineEpisode> _episodeList = [];
final List<OnlineEpisode> _episodeList = [];
Future _searchFuture;
@ -683,7 +686,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
Future<List<OnlineEpisode>> _getEpisodes(
{String id, int nextEpisodeDate}) async {
SearchEngine searchEngine = SearchEngine();
var searchEngine = SearchEngine();
var searchResult = await searchEngine.fetchEpisode(
id: id, nextEpisodeDate: nextEpisodeDate);
_nextEpisdoeDate = searchResult.nextEpisodeDate;
@ -761,15 +764,15 @@ class _SearchResultDetailState extends State<SearchResultDetail>
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
final s = context.s;
subscribePodcast(OnlinePodcast podcast) {
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title,
var item = SubscribeItem(podcast.rss, podcast.title,
imgUrl: podcast.image, group: 'Home');
subscribeWorker.setSubscribeItem(item);
widget.onSubscribe(podcast);
}
return GestureDetector(
onVerticalDragStart: (event) => _start(event),
onVerticalDragUpdate: (event) => _update(event),
onVerticalDragStart: _start,
onVerticalDragUpdate: _update,
onVerticalDragEnd: (event) => _end(),
child: SingleChildScrollView(
child: Container(
@ -947,7 +950,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
: null,
itemCount: content.length + 1,
itemBuilder: (context, index) {
if (index == content.length)
if (index == content.length) {
return Container(
padding: const EdgeInsets.only(
top: 10.0, bottom: 20.0),
@ -986,6 +989,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
),
),
);
}
return ListTile(
title: Text(content[index].title),
subtitle: Text(

View File

@ -1,14 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../state/setting_state.dart';
import '../home/home.dart';
import '../util/pageroute.dart';
import '../state/setting_state.dart';
import '../util/extension_helper.dart';
import '../util/pageroute.dart';
import 'firstpage.dart';
import 'fourthpage.dart';
import 'secondpage.dart';
import 'thirdpage.dart';
import 'firstpage.dart';
enum Goto { home, settings }
@ -21,7 +22,7 @@ class SlideIntro extends StatefulWidget {
}
class _SlideIntroState extends State<SlideIntro> {
List<BoxShadow> _customShadow = [
final List<BoxShadow> _customShadow = [
BoxShadow(blurRadius: 2, offset: Offset(-2, -2), color: Colors.white54),
BoxShadow(
blurRadius: 8,

View File

@ -38,9 +38,9 @@ class KeyValueStorage {
final String key;
KeyValueStorage(this.key);
Future<List<GroupEntity>> getGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
if (prefs.getString(key) == null) {
PodcastGroup home = PodcastGroup('Home');
var home = PodcastGroup('Home');
await prefs.setString(
key,
json.encode({
@ -56,7 +56,7 @@ class KeyValueStorage {
}
Future<bool> saveGroup(List<GroupEntity> groupList) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
return prefs.setString(
key,
json.encode(
@ -64,23 +64,23 @@ class KeyValueStorage {
}
Future<bool> saveInt(int setting) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
return prefs.setInt(key, setting);
}
Future<int> getInt({int defaultValue = 0}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
if (prefs.getInt(key) == null) await prefs.setInt(key, defaultValue);
return prefs.getInt(key);
}
Future<bool> saveStringList(List<String> playList) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
return prefs.setStringList(key, playList);
}
Future<List<String>> getStringList() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
if (prefs.getStringList(key) == null) {
await prefs.setStringList(key, []);
}
@ -88,12 +88,12 @@ class KeyValueStorage {
}
Future<bool> saveString(String string) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
return prefs.setString(key, string);
}
Future<String> getString() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
if (prefs.getString(key) == null) {
await prefs.setString(key, '');
}
@ -101,34 +101,35 @@ class KeyValueStorage {
}
saveMenu(List<int> list) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
await prefs.setStringList(key, list.map((e) => e.toString()).toList());
}
Future<List<int>> getMenu() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var 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();
var list = prefs.getStringList(key);
return list.map(int.parse).toList();
}
/// Rreverse is used for compatite bool value save before which set true = 0, false = 1
Future<bool> getBool(
{@required bool defaultValue, bool reverse = false}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getInt(key) == null)
var prefs = await SharedPreferences.getInstance();
if (prefs.getInt(key) == null) {
reverse
? await prefs.setInt(key, defaultValue ? 0 : 1)
: await prefs.setInt(key, defaultValue ? 1 : 0);
int i = prefs.getInt(key);
}
var i = prefs.getInt(key);
return reverse ? i == 0 : i == 1;
}
/// Rreverse is used for compatite bool value save before which set true = 0, false = 1
saveBool(bool boo, {bool reverse = false}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
saveBool(boo, {reverse = false}) async {
var prefs = await SharedPreferences.getInstance();
reverse ? prefs.setInt(key, boo ? 0 : 1) : prefs.setInt(key, boo ? 1 : 0);
}
}

View File

@ -1,14 +1,16 @@
import 'package:sqflite/sqflite.dart';
import 'dart:async';
import 'package:path/path.dart';
import 'package:intl/intl.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:dio/dio.dart';
import '../type/podcastlocal.dart';
import '../type/play_histroy.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import '../type/episodebrief.dart';
import '../webfeed/webfeed.dart';
import '../type/play_histroy.dart';
import '../type/podcastlocal.dart';
import '../type/sub_history.dart';
import '../webfeed/webfeed.dart';
enum Filter { downloaded, liked, search, all }
@ -22,8 +24,8 @@ class DBHelper {
initDb() async {
var documentsDirectory = await getDatabasesPath();
String path = join(documentsDirectory, "podcasts.db");
Database theDb = await openDatabase(path,
var path = join(documentsDirectory, "podcasts.db");
var theDb = await openDatabase(path,
version: 3, onCreate: _onCreate, onUpgrade: _onUpgrade);
return theDb;
}
@ -64,7 +66,7 @@ class DBHelper {
Future<List<PodcastLocal>> getPodcastLocal(List<String> podcasts) async {
var dbClient = await database;
List<PodcastLocal> podcastLocal = [];
var podcastLocal = <PodcastLocal>[];
for (var s in podcasts) {
List<Map> list;
@ -72,7 +74,7 @@ class DBHelper {
"""SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider,
link ,update_count, episode_count FROM PodcastLocal WHERE id = ?""",
[s]);
if (list.length > 0)
if (list.length > 0) {
podcastLocal.add(PodcastLocal(
list.first['title'],
list.first['imageUrl'],
@ -85,6 +87,7 @@ class DBHelper {
list.first['link'],
upateCount: list.first['update_count'],
episodeCount: list.first['episode_count']));
}
}
return podcastLocal;
}
@ -94,7 +97,7 @@ class DBHelper {
List<Map> list = await dbClient.rawQuery(
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, provider, link FROM PodcastLocal ORDER BY add_date DESC');
List<PodcastLocal> podcastLocal = [];
var podcastLocal = <PodcastLocal>[];
for (var i in list) {
podcastLocal.add(PodcastLocal(
@ -146,7 +149,7 @@ class DBHelper {
return list.first['auto_download'] == 1;
}
Future<int> saveAutoDownload(String id, bool boo) async {
Future<int> saveAutoDownload(String id, {bool boo}) async {
var dbClient = await database;
return await dbClient.rawUpdate(
"UPDATE PodcastLocal SET auto_download = ? WHERE id = ?",
@ -162,7 +165,7 @@ class DBHelper {
}
Future savePodcastLocal(PodcastLocal podcastLocal) async {
int _milliseconds = DateTime.now().millisecondsSinceEpoch;
var _milliseconds = DateTime.now().millisecondsSinceEpoch;
var dbClient = await database;
await dbClient.transaction((txn) async {
await txn.rawInsert(
@ -194,7 +197,7 @@ class DBHelper {
Future<int> saveFiresideData(List<String> list) async {
var dbClient = await database;
int result = await dbClient.rawUpdate(
var result = await dbClient.rawUpdate(
'UPDATE PodcastLocal SET background_image = ? , hosts = ? WHERE id = ?',
[list[1], list[2], list[0]]);
print('Fireside data save in sqllite');
@ -206,7 +209,7 @@ class DBHelper {
List<Map> list = await dbClient.rawQuery(
'SELECT background_image, hosts FROM PodcastLocal WHERE id = ?', [id]);
if (list.length > 0) {
List<String> data = [list.first['background_image'], list.first['hosts']];
var data = <String>[list.first['background_image'], list.first['hosts']];
return data;
}
return ['', ''];
@ -219,13 +222,14 @@ class DBHelper {
"""SELECT downloaded FROM Episodes WHERE downloaded != 'ND' AND feed_id = ?""",
[id]);
for (var i in list) {
if (i != null)
if (i != null) {
await FlutterDownloader.remove(
taskId: i['downloaded'], shouldDeleteContent: true);
}
print('Removed all download tasks');
}
await dbClient.rawDelete('DELETE FROM Episodes WHERE feed_id=?', [id]);
int _milliseconds = DateTime.now().millisecondsSinceEpoch;
var _milliseconds = DateTime.now().millisecondsSinceEpoch;
await dbClient.rawUpdate(
"""UPDATE SubscribeHistory SET remove_date = ? , status = ? WHERE id = ?""",
[_milliseconds, 1, id]);
@ -233,15 +237,15 @@ class DBHelper {
Future<int> saveHistory(PlayHistory history) async {
var dbClient = await database;
int _milliseconds = DateTime.now().millisecondsSinceEpoch;
List<PlayHistory> recent = await getPlayHistory(1);
var _milliseconds = DateTime.now().millisecondsSinceEpoch;
var recent = await getPlayHistory(1);
if (recent.length == 1) {
if (recent.first.url == history.url) {
await dbClient.rawDelete("DELETE FROM PlayHistory WHERE add_date = ?",
[recent.first.playdate.millisecondsSinceEpoch]);
}
}
int result = await dbClient.transaction((txn) async {
var result = await dbClient.transaction((txn) async {
return await txn.rawInsert(
"""REPLACE INTO PlayHistory (title, enclosure_url, seconds, seek_value, add_date, listen_time)
VALUES (?, ?, ?, ?, ?, ?) """,
@ -263,7 +267,7 @@ class DBHelper {
"""SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory
ORDER BY add_date DESC LIMIT ?
""", [top]);
List<PlayHistory> playHistory = [];
var playHistory = <PlayHistory>[];
for (var record in list) {
playHistory.add(PlayHistory(record['title'], record['enclosure_url'],
record['seconds'], record['seek_value'],
@ -274,12 +278,12 @@ class DBHelper {
Future<int> isListened(String url) async {
var dbClient = await database;
int i = 0;
var i = 0;
List<Map> list = await dbClient.rawQuery(
"SELECT listen_time FROM PlayHistory WHERE enclosure_url = ?", [url]);
if (list.length == 0)
if (list.length == 0) {
return 0;
else {
} else {
for (var element in list) {
i += element['listen_time'];
}
@ -314,11 +318,13 @@ class DBHelper {
List<Map> list = await dbClient.rawQuery(
"SELECT seconds FROM PlayHistory WHERE add_date > ? AND add_date < ?",
[start, end]);
double sum = 0;
var sum = 0;
if (list.isEmpty) {
sum = 0;
} else {
for (var record in list) sum += record['seconds'];
for (var record in list) {
sum += record['seconds'];
}
}
return (sum ~/ 60).toDouble();
}
@ -350,14 +356,14 @@ class DBHelper {
DateTime _parsePubDate(String pubDate) {
if (pubDate == null) return DateTime.now();
DateTime date;
RegExp yyyy = RegExp(r'[1-2][0-9]{3}');
RegExp hhmm = RegExp(r'[0-2][0-9]\:[0-5][0-9]');
RegExp ddmmm = RegExp(r'[0-3][0-9]\s[A-Z][a-z]{2}');
RegExp mmDd = RegExp(r'([1-2][0-9]{3}\-[0-1]|\s)[0-9]\-[0-3][0-9]');
var yyyy = RegExp(r'[1-2][0-9]{3}');
var hhmm = RegExp(r'[0-2][0-9]\:[0-5][0-9]');
var ddmmm = RegExp(r'[0-3][0-9]\s[A-Z][a-z]{2}');
var mmDd = RegExp(r'([1-2][0-9]{3}\-[0-1]|\s)[0-9]\-[0-3][0-9]');
// RegExp timezone
RegExp z = RegExp(r'(\+|\-)[0-1][0-9]00');
String timezone = z.stringMatch(pubDate);
int timezoneInt = 0;
var z = RegExp(r'(\+|\-)[0-1][0-9]00');
var timezone = z.stringMatch(pubDate);
var timezoneInt = 0;
if (timezone != null) {
if (timezone.substring(0, 1) == '-') {
timezoneInt = int.parse(timezone.substring(1, 2));
@ -375,16 +381,16 @@ class DBHelper {
date = DateFormat('EEE, dd MMM yyyy HH:mm Z', 'en_US').parse(pubDate);
} catch (e) {
//parse date using regex, still have issue in parse maonth/day
String year = yyyy.stringMatch(pubDate);
String time = hhmm.stringMatch(pubDate);
String month = ddmmm.stringMatch(pubDate);
var year = yyyy.stringMatch(pubDate);
var time = hhmm.stringMatch(pubDate);
var month = ddmmm.stringMatch(pubDate);
if (year != null && time != null && month != null) {
date = DateFormat('dd MMM yyyy HH:mm', 'en_US')
.parse(month + year + time);
} else if (year != null && time != null && month == null) {
String month = mmDd.stringMatch(pubDate);
date = DateFormat('yyyy-MM-dd HH:mm', 'en_US')
.parse(month + ' ' + time);
var month = mmDd.stringMatch(pubDate);
date =
DateFormat('yyyy-MM-dd HH:mm', 'en_US').parse('$month $time');
print(date.toString());
} else {
date = DateTime.now();
@ -392,7 +398,7 @@ class DBHelper {
}
}
}
DateTime result = date
var result = date
.add(Duration(hours: timezoneInt))
.add(DateTime.now().timeZoneOffset);
return result;
@ -410,7 +416,7 @@ class DBHelper {
}
bool _isXimalaya(String input) {
RegExp ximalaya = RegExp(r"ximalaya.com");
var ximalaya = RegExp(r"ximalaya.com");
return ximalaya.hasMatch(input);
}
@ -430,10 +436,10 @@ class DBHelper {
Future<int> savePodcastRss(RssFeed feed, String id) async {
feed.items.removeWhere((item) => item == null);
int result = feed.items.length;
var result = feed.items.length;
var dbClient = await database;
String description, url;
for (int i = 0; i < result; i++) {
for (var i = 0; i < result; i++) {
print(feed.items[i].title);
description = _getDescription(feed.items[i].content.value ?? '',
feed.items[i].description ?? '', feed.items[i].itunes.summary ?? '');
@ -472,7 +478,7 @@ class DBHelper {
});
}
}
int countUpdate = Sqflite.firstIntValue(await dbClient
var countUpdate = Sqflite.firstIntValue(await dbClient
.rawQuery('SELECT COUNT(*) FROM Episodes WHERE feed_id = ?', [id]));
await dbClient.rawUpdate(
@ -483,25 +489,26 @@ class DBHelper {
Future<int> updatePodcastRss(PodcastLocal podcastLocal,
{int removeMark = 0}) async {
BaseOptions options = BaseOptions(
var options = BaseOptions(
connectTimeout: 20000,
receiveTimeout: 20000,
);
try {
Response response = await Dio(options).get(podcastLocal.rssUrl);
var response = await Dio(options).get(podcastLocal.rssUrl);
if (response.statusCode == 200) {
var feed = RssFeed.parse(response.data);
String url, description;
feed.items.removeWhere((item) => item == null);
var dbClient = await database;
int count = Sqflite.firstIntValue(await dbClient.rawQuery(
var count = Sqflite.firstIntValue(await dbClient.rawQuery(
'SELECT COUNT(*) FROM Episodes WHERE feed_id = ?',
[podcastLocal.id]));
if (removeMark == 0)
if (removeMark == 0) {
await dbClient.rawUpdate(
"UPDATE Episodes SET is_new = 0 WHERE feed_id = ?",
[podcastLocal.id]);
}
for (var item in feed.items) {
print(item.title);
description = _getDescription(item.content.value ?? '',
@ -541,7 +548,7 @@ class DBHelper {
});
}
}
int countUpdate = Sqflite.firstIntValue(await dbClient.rawQuery(
var countUpdate = Sqflite.firstIntValue(await dbClient.rawQuery(
'SELECT COUNT(*) FROM Episodes WHERE feed_id = ?',
[podcastLocal.id]));
@ -557,13 +564,14 @@ class DBHelper {
}
}
Future<List<EpisodeBrief>> getRssItem(String id, int count, bool reverse,
{Filter filter = Filter.all,
Future<List<EpisodeBrief>> getRssItem(String id, int count,
{bool reverse,
Filter filter = Filter.all,
String query = '',
bool hideListened = false}) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
List<Map> list = [];
var episodes = <EpisodeBrief>[];
var list = <Map>[];
if (count == -1) {
switch (filter) {
case Filter.all:
@ -670,7 +678,7 @@ class DBHelper {
}
}
if (list.isNotEmpty) {
if (!hideListened)
if (!hideListened) {
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
@ -684,10 +692,10 @@ class DBHelper {
i['imagePath'],
i['is_new']));
}
else
} else {
for (var i in list) {
int listened = await isListened(i['enclosure_url']);
if (listened == 0)
var listened = await isListened(i['enclosure_url']);
if (listened == 0) {
episodes.add(EpisodeBrief(
i['title'],
i['enclosure_url'],
@ -699,30 +707,33 @@ class DBHelper {
i['explicit'],
i['imagePath'],
i['is_new']));
}
}
}
}
return episodes;
}
Future<List<EpisodeBrief>> getNewEpisodes(String id) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
List<Map> list;
if (id == 'all')
if (id == 'all') {
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND P.auto_download = 1 ORDER BY E.milliseconds ASC""",
);
else
} else {
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND E.feed_id = ? ORDER BY E.milliseconds ASC""",
[id]);
if (list.isNotEmpty)
}
if (list.isNotEmpty) {
for (var i in list) {
episodes.add(EpisodeBrief(
i['title'],
@ -736,12 +747,13 @@ class DBHelper {
i['imagePath'],
i['is_new']));
}
}
return episodes;
}
Future<List<EpisodeBrief>> getRssItemTop(String id) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
@ -765,7 +777,7 @@ class DBHelper {
Future<List<EpisodeBrief>> getRecentRssItem(int top) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -790,9 +802,9 @@ class DBHelper {
Future<List<EpisodeBrief>> getGroupRssItem(
int top, List<String> group) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
if (group.length > 0) {
List<String> s = group.map<String>((e) => "'$e'").toList();
var s = group.map<String>((e) => "'$e'").toList();
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -818,7 +830,7 @@ class DBHelper {
Future<List<EpisodeBrief>> getRecentNewRssItem() async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new, E.media_id,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -844,7 +856,7 @@ class DBHelper {
Future<List<EpisodeBrief>> getOutdatedEpisode(int deadline) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -869,10 +881,10 @@ class DBHelper {
Future<List<EpisodeBrief>> getDownloadedEpisode(int mode) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
List<Map> list;
//Ordered by date
if (mode == 0)
if (mode == 0) {
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, E.is_new,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -880,8 +892,7 @@ class DBHelper {
WHERE E.enclosure_url != E.media_id
ORDER BY E.download_date DESC""",
);
//Ordered by date
else if (mode == 1)
} else if (mode == 1) {
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,E.is_new,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -889,8 +900,7 @@ class DBHelper {
WHERE E.enclosure_url != E.media_id
ORDER BY E.download_date ASC""",
);
//Ordered by size
else if (mode == 2)
} else if (mode == 2) {
list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,E.is_new,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -898,6 +908,7 @@ class DBHelper {
WHERE E.enclosure_url != E.media_id
ORDER BY E.enclosure_length DESC""",
);
}
for (var i in list) {
episodes.add(
EpisodeBrief(
@ -926,9 +937,9 @@ class DBHelper {
Future<List<EpisodeBrief>> getGroupNewRssItem(List<String> group) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
if (group.length > 0) {
List<String> s = group.map<String>((e) => "'$e'").toList();
var s = group.map<String>((e) => "'$e'").toList();
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.is_new, E.media_id,
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
@ -957,7 +968,7 @@ class DBHelper {
removeGroupNewMark(List<String> group) async {
var dbClient = await database;
if (group.length > 0) {
List<String> s = group.map<String>((e) => "'$e'").toList();
var s = group.map<String>((e) => "'$e'").toList();
await dbClient.transaction((txn) async {
await txn.rawUpdate(
"UPDATE Episodes SET is_new = 0 WHERE feed_id in (${s.join(',')})");
@ -976,7 +987,7 @@ class DBHelper {
Future<List<EpisodeBrief>> getLikedRssItem(int i, int sortBy) async {
var dbClient = await database;
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
if (sortBy == 0) {
List<Map> list = await dbClient.rawQuery(
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
@ -1021,7 +1032,7 @@ class DBHelper {
setLiked(String url) async {
var dbClient = await database;
int milliseconds = DateTime.now().millisecondsSinceEpoch;
var milliseconds = DateTime.now().millisecondsSinceEpoch;
await dbClient.transaction((txn) async {
await txn.rawUpdate(
"UPDATE Episodes SET liked = 1, liked_date = ? WHERE enclosure_url= ?",
@ -1053,7 +1064,7 @@ class DBHelper {
Future<int> saveDownloaded(String url, String id) async {
var dbClient = await database;
int milliseconds = DateTime.now().millisecondsSinceEpoch;
var milliseconds = DateTime.now().millisecondsSinceEpoch;
int count;
await dbClient.transaction((txn) async {
count = await txn.rawUpdate(
@ -1065,7 +1076,7 @@ class DBHelper {
Future<int> saveMediaId(String url, String path, String id, int size) async {
var dbClient = await database;
int milliseconds = DateTime.now().millisecondsSinceEpoch;
var milliseconds = DateTime.now().millisecondsSinceEpoch;
int count;
await dbClient.transaction((txn) async {
count = await txn.rawUpdate(
@ -1083,7 +1094,7 @@ class DBHelper {
"UPDATE Episodes SET downloaded = 'ND', media_id = ? WHERE enclosure_url = ?",
[url, url]);
});
print('Deleted ' + url);
print('Deleted $url');
return count;
}
@ -1139,9 +1150,9 @@ class DBHelper {
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]);
if (list.isEmpty)
if (list.isEmpty) {
return null;
else {
} else {
episode = EpisodeBrief(
list.first['title'],
list.first['enclosure_url'],

View File

@ -1,19 +1,19 @@
import 'package:feature_discovery/feature_discovery.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:feature_discovery/feature_discovery.dart';
import 'package:provider/provider.dart';
import 'generated/l10n.dart';
import 'state/podcast_group.dart';
import 'state/audio_state.dart';
import 'state/setting_state.dart';
import 'state/download_state.dart';
import 'state/refresh_podcast.dart';
import 'home/home.dart';
import 'intro_slider/app_intro.dart';
import 'state/audio_state.dart';
import 'state/download_state.dart';
import 'state/podcast_group.dart';
import 'state/refresh_podcast.dart';
import 'state/setting_state.dart';
final SettingState themeSetting = SettingState();
Future main() async {
@ -38,7 +38,7 @@ Future main() async {
child: MyApp(),
),
);
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(
var systemUiOverlayStyle = SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);

View File

@ -1,30 +1,30 @@
import 'dart:io';
import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:html/parser.dart';
import 'package:tsacdop/state/download_state.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:provider/provider.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:html/parser.dart';
import 'package:line_icons/line_icons.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:provider/provider.dart';
import '../type/podcastlocal.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../util/episodegrid.dart';
import '../home/audioplayer.dart';
import '../type/fireside_data.dart';
import '../util/extension_helper.dart';
import '../util/custom_widget.dart';
import '../util/general_dialog.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../state/download_state.dart';
import '../type/episodebrief.dart';
import '../type/fireside_data.dart';
import '../type/play_histroy.dart';
import '../type/podcastlocal.dart';
import '../util/custom_widget.dart';
import '../util/episodegrid.dart';
import '../util/extension_helper.dart';
import '../util/general_dialog.dart';
class PodcastDetail extends StatefulWidget {
PodcastDetail({Key key, @required this.podcastLocal, this.hide = false})
@ -105,27 +105,28 @@ class _PodcastDetailState extends State<PodcastDetail> {
gravity: ToastGravity.TOP,
);
bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
var autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
if (autoDownload) {
var downloader = Provider.of<DownloadState>(context, listen: false);
var result = await Connectivity().checkConnectivity();
KeyValueStorage autoDownloadStorage =
KeyValueStorage(autoDownloadNetworkKey);
int autoDownloadNetwork = await autoDownloadStorage.getInt();
var autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
var autoDownloadNetwork = await autoDownloadStorage.getInt();
if (autoDownloadNetwork == 1) {
List<EpisodeBrief> episodes =
await dbHelper.getNewEpisodes(podcastLocal.id);
var episodes = await dbHelper.getNewEpisodes(podcastLocal.id);
// For safety
if (episodes.length < 100)
for (var episode in episodes)
if (episodes.length < 100) {
for (var episode in episodes) {
downloader.startTask(episode, showNotification: false);
}
}
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes =
await dbHelper.getNewEpisodes(podcastLocal.id);
var episodes = await dbHelper.getNewEpisodes(podcastLocal.id);
//For safety
if (episodes.length < 100)
for (var episode in episodes)
if (episodes.length < 100) {
for (var episode in episodes) {
downloader.startTask(episode, showNotification: false);
}
}
}
}
} else {
@ -140,16 +141,19 @@ class _PodcastDetailState extends State<PodcastDetail> {
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal,
{int count, bool reverse, Filter filter, String query}) async {
var dbHelper = DBHelper();
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
_episodeCount = await dbHelper.getPodcastCounts(podcastLocal.id);
KeyValueStorage storage = KeyValueStorage(podcastLayoutKey);
int index = await storage.getInt(defaultValue: 1);
var storage = KeyValueStorage(podcastLayoutKey);
var index = await storage.getInt(defaultValue: 1);
if (_layout == null) _layout = Layout.values[index];
episodes = await dbHelper.getRssItem(podcastLocal.id, count, reverse,
filter: filter, query: query, hideListened: _hideListened);
episodes = await dbHelper.getRssItem(podcastLocal.id, count,
reverse: reverse,
filter: filter,
query: query,
hideListened: _hideListened);
if (podcastLocal.provider.contains('fireside')) {
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
var data = FiresideData(podcastLocal.id, podcastLocal.link);
await data.getData();
_backgroundImage = data.background;
_hosts = data.hosts;
@ -158,14 +162,12 @@ class _PodcastDetailState extends State<PodcastDetail> {
}
_markListened(String podcastId) async {
DBHelper dbHelper = DBHelper();
List<EpisodeBrief> episodes =
await dbHelper.getRssItem(podcastId, -1, true);
var dbHelper = DBHelper();
var episodes = await dbHelper.getRssItem(podcastId, -1, reverse: true);
for (var episode in episodes) {
bool marked = await dbHelper.checkMarked(episode);
var marked = await dbHelper.checkMarked(episode);
if (!marked) {
final PlayHistory history =
PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
if (mounted) setState(() {});
}
@ -316,7 +318,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
return _customPopupMenu(
tooltip: s.menu,
clip: false,
onSelected: (int value) {
onSelected: (value) {
switch (value) {
case 0:
widget.podcastLocal.link.launchUrl;
@ -443,9 +445,9 @@ class _PodcastDetailState extends State<PodcastDetail> {
)
],
onSelected: (value) {
if (value == 0)
if (value == 0) {
setState(() => _reverse = false);
else if (value == 1) setState(() => _reverse = true);
} else if (value == 1) setState(() => _reverse = true);
},
),
SizedBox(width: 10),
@ -538,25 +540,28 @@ class _PodcastDetailState extends State<PodcastDetail> {
onSelected: (value) {
switch (value) {
case 0:
if (_filter != Filter.all)
if (_filter != Filter.all) {
setState(() {
_filter = Filter.all;
_query = '';
});
}
break;
case 1:
if (_filter != Filter.liked)
if (_filter != Filter.liked) {
setState(() {
_query = '';
_filter = Filter.liked;
});
}
break;
case 2:
if (_filter != Filter.downloaded)
if (_filter != Filter.downloaded) {
setState(() {
_query = '';
_filter = Filter.downloaded;
});
}
break;
case 3:
showGeneralDialog(
@ -566,17 +571,16 @@ class _PodcastDetailState extends State<PodcastDetail> {
.modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext context,
Animation animaiton,
Animation secondaryAnimation) =>
SearchEpisdoe(
onSearch: (query) {
setState(() {
_query = query;
_filter = Filter.search;
});
},
));
pageBuilder:
(context, animaiton, secondaryAnimation) =>
SearchEpisdoe(
onSearch: (query) {
setState(() {
_query = query;
_filter = Filter.search;
});
},
));
break;
default:
}
@ -605,18 +609,19 @@ class _PodcastDetailState extends State<PodcastDetail> {
child: IconButton(
padding: EdgeInsets.zero,
onPressed: () {
if (_layout == Layout.three)
if (_layout == Layout.three) {
setState(() {
_layout = Layout.one;
});
else if (_layout == Layout.two)
} else if (_layout == Layout.two) {
setState(() {
_layout = Layout.three;
});
else
} else {
setState(() {
_layout = Layout.two;
});
}
},
icon: _layout == Layout.three
? IconPainter(
@ -638,7 +643,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
@override
Widget build(BuildContext context) {
Color _color = widget.podcastLocal.primaryColor.colorizedark();
var _color = widget.podcastLocal.primaryColor.colorizedark();
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
@ -683,20 +688,23 @@ class _PodcastDetailState extends State<PodcastDetail> {
_controller
.position.maxScrollExtent &&
snapshot.data.length == _top) {
if (mounted)
if (mounted) {
setState(() => _loadMore = true);
}
await Future.delayed(
Duration(seconds: 3));
if (mounted)
if (mounted) {
setState(() {
_top = _top + 36;
_loadMore = false;
});
}
}
if (_controller.offset > 0 &&
mounted &&
!_scroll)
!_scroll) {
setState(() => _scroll = true);
}
}),
physics:
const AlwaysScrollableScrollPhysics(),
@ -806,7 +814,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
(context, index) {
return _loadMore
? Container(
height: 2,
@ -856,7 +864,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
bool _expand;
void getDescription(String id) async {
var dbHelper = DBHelper();
String description = await dbHelper.getFeedDescription(id);
var description = await dbHelper.getFeedDescription(id);
if (description == null || description.isEmpty) {
_description = '';
} else {

View File

@ -8,9 +8,9 @@ import 'package:fluttertoast/fluttertoast.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/podcast_group.dart';
import '../type/podcastlocal.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../util/duraiton_picker.dart';
import '../util/extension_helper.dart';
import '../util/general_dialog.dart';
@ -33,20 +33,18 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
: Container(
color: Theme.of(context).primaryColor,
child: ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final PodcastLocal podcast =
widget.group.podcasts.removeAt(oldIndex);
final podcast = widget.group.podcasts.removeAt(oldIndex);
widget.group.podcasts.insert(newIndex, podcast);
});
widget.group.setOrderedPodcasts = widget.group.podcasts;
groupList.addToOrderChanged(widget.group);
},
children: widget.group.podcasts
.map<Widget>((PodcastLocal podcastLocal) {
children: widget.group.podcasts.map<Widget>((podcastLocal) {
return Container(
decoration:
BoxDecoration(color: Theme.of(context).primaryColor),
@ -84,7 +82,7 @@ class _PodcastCardState extends State<PodcastCard>
Future<int> getSkipSecond(String id) async {
var dbHelper = DBHelper();
int seconds = await dbHelper.getSkipSeconds(id);
var seconds = await dbHelper.getSkipSeconds(id);
_skipSeconds = seconds;
return seconds;
}
@ -95,23 +93,22 @@ class _PodcastCardState extends State<PodcastCard>
}
_setAutoDownload(String id, bool boo) async {
bool permission = await _checkPermmison();
var permission = await _checkPermmison();
if (permission) {
DBHelper dbHelper = DBHelper();
await dbHelper.saveAutoDownload(id, boo);
var dbHelper = DBHelper();
await dbHelper.saveAutoDownload(id, boo: boo);
}
}
Future<bool> _getAutoDownload(String id) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.getAutoDownload(id);
}
Future<bool> _checkPermmison() async {
PermissionStatus permission = await Permission.storage.status;
var permission = await Permission.storage.status;
if (permission != PermissionStatus.granted) {
Map<Permission, PermissionStatus> permissions =
await [Permission.storage].request();
var permissions = await [Permission.storage].request();
if (permissions[Permission.storage] == PermissionStatus.granted) {
return true;
} else {
@ -169,11 +166,11 @@ class _PodcastCardState extends State<PodcastCard>
@override
Widget build(BuildContext context) {
Color _c = (Theme.of(context).brightness == Brightness.light)
var _c = (Theme.of(context).brightness == Brightness.light)
? widget.podcastLocal.primaryColor.colorizedark()
: widget.podcastLocal.primaryColor.colorizeLight();
final s = context.s;
double _width = MediaQuery.of(context).size.width;
var _width = MediaQuery.of(context).size.width;
var groupList = context.watch<GroupList>();
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal.id);
@ -190,10 +187,11 @@ class _PodcastCardState extends State<PodcastCard>
onTap: () => setState(
() {
_loadMenu = !_loadMenu;
if (_value == 0)
if (_value == 0) {
_controller.forward();
else
} else {
_controller.reverse();
}
},
),
child: Container(
@ -278,8 +276,8 @@ class _PodcastCardState extends State<PodcastCard>
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: groupList.groups
.map<Widget>((PodcastGroup group) {
children:
groupList.groups.map<Widget>((group) {
return Container(
padding: EdgeInsets.only(left: 5.0),
child: FilterChip(
@ -287,7 +285,7 @@ class _PodcastCardState extends State<PodcastCard>
label: Text(group.name),
selected:
_selectedGroups.contains(group),
onSelected: (bool value) {
onSelected: (value) {
setState(() {
if (!value) {
_selectedGroups.remove(group);
@ -328,11 +326,12 @@ class _PodcastCardState extends State<PodcastCard>
msg: s.toastSettingSaved,
gravity: ToastGravity.BOTTOM,
);
} else
} else {
Fluttertoast.showToast(
msg: s.toastOneGroup,
gravity: ToastGravity.BOTTOM,
);
}
},
icon: Icon(Icons.done),
),
@ -400,11 +399,8 @@ class _PodcastCardState extends State<PodcastCard>
Icons.fast_forward,
size: _value == 0 ? 1 : 20 * (_value),
),
tooltip: 'Skip' +
(snapshot.data == 0
? ''
: _stringForSeconds(
snapshot.data.toDouble())),
tooltip:
'Skip${snapshot.data == 0 ? '' : _stringForSeconds(snapshot.data.toDouble())}',
onTap: () {
generalDialog(
context,
@ -550,7 +546,7 @@ class _RenameGroupState extends State<RenameGroup> {
if (list.contains(_newName)) {
setState(() => _error = 1);
} else {
PodcastGroup newGroup = PodcastGroup(_newName,
var newGroup = PodcastGroup(_newName,
color: widget.group.color,
id: widget.group.id,
podcastList: widget.group.podcastList);

View File

@ -1,20 +1,20 @@
import 'dart:math' as math;
import 'package:feature_discovery/feature_discovery.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:line_icons/line_icons.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:feature_discovery/feature_discovery.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import '../state/podcast_group.dart';
import 'podcast_group.dart';
import 'podcastlist.dart';
import '../util/pageroute.dart';
import '../util/extension_helper.dart';
import '../util/general_dialog.dart';
import '../util/pageroute.dart';
import 'custom_tabview.dart';
import 'podcast_group.dart';
import 'podcastlist.dart';
const String addGroupFeature = 'addGroupFeature';
const String configureGroup = 'configureFeature';
@ -50,10 +50,11 @@ class _PodcastManageState extends State<PodcastManage>
duration: const Duration(milliseconds: 500), vsync: this);
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_fraction = _animation.value;
});
}
});
_menuAnimation = Tween(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(parent: _menuController, curve: Curves.easeIn))
@ -69,7 +70,7 @@ class _PodcastManageState extends State<PodcastManage>
}
});
FeatureDiscovery.isDisplayed(context, addGroupFeature).then((value) {
if (!value)
if (!value) {
WidgetsBinding.instance.addPostFrameCallback((_) {
FeatureDiscovery.discoverFeatures(context, const <String>{
addGroupFeature,
@ -77,6 +78,7 @@ class _PodcastManageState extends State<PodcastManage>
configurePodcast
});
});
}
});
}
@ -247,8 +249,7 @@ class _PodcastManageState extends State<PodcastManage>
.modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext context, Animation animaiton,
Animation secondaryAnimation) =>
pageBuilder: (context, animaiton, secondaryAnimation) =>
AddGroup()),
icon: Icon(Icons.add)),
),
@ -262,8 +263,8 @@ class _PodcastManageState extends State<PodcastManage>
return true;
},
child: Consumer<GroupList>(builder: (_, groupList, __) {
bool _isLoading = groupList.isLoading;
List<PodcastGroup> _groups = groupList.groups;
var _isLoading = groupList.isLoading;
var _groups = groupList.groups;
return _isLoading
? Center()
: Stack(
@ -390,11 +391,9 @@ class _PodcastManageState extends State<PodcastManage>
transitionDuration:
const Duration(
milliseconds: 300),
pageBuilder: (BuildContext
context,
Animation animaiton,
Animation
secondaryAnimation) =>
pageBuilder: (context,
animaiton,
secondaryAnimation) =>
RenameGroup(
group: _groups[_index],
));

View File

@ -1,19 +1,19 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:provider/provider.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/podcast_group.dart';
import '../type/podcastlocal.dart';
import '../local_storage/sqflite_localpodcast.dart';
import 'podcast_detail.dart';
import '../util/pageroute.dart';
import '../util/extension_helper.dart';
import '../util/pageroute.dart';
import 'podcast_detail.dart';
class AboutPodcast extends StatefulWidget {
final PodcastLocal podcastLocal;
@ -29,7 +29,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
void getDescription(String id) async {
var dbHelper = DBHelper();
String description = await dbHelper.getFeedDescription(id);
var description = await dbHelper.getFeedDescription(id);
_description = description;
setState(() {
_load = true;
@ -100,7 +100,7 @@ class _PodcastListState extends State<PodcastList> {
@override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
var _width = MediaQuery.of(context).size.width;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -132,7 +132,7 @@ class _PodcastListState extends State<PodcastList> {
crossAxisCount: 3,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
(context, index) {
return InkWell(
onTap: () {
Navigator.push(
@ -153,9 +153,8 @@ class _PodcastListState extends State<PodcastList> {
barrierColor: Colors.black54,
transitionDuration:
const Duration(milliseconds: 200),
pageBuilder: (BuildContext context,
Animation animaiton,
Animation secondaryAnimation) =>
pageBuilder: (context, animaiton,
secondaryAnimation) =>
AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness:

View File

@ -2,18 +2,17 @@ import 'dart:convert';
import 'package:dio/dio.dart';
import '../type/searchpodcast.dart';
import '../type/searchepisodes.dart';
import '../.env.dart';
import '../type/searchepisodes.dart';
import '../type/searchpodcast.dart';
class SearchEngine {
Future<SearchPodcast<dynamic>> searchPodcasts(
{String searchText, int nextOffset}) async {
String apiKey = environment['apiKey'];
String url = "https://listen-api.listennotes.com/api/v2/search?q=" +
Uri.encodeComponent(searchText) +
"&sort_by_date=0&type=podcast&offset=$nextOffset";
Response response = await Dio().get(url,
var apiKey = environment['apiKey'];
var url = "https://listen-api.listennotes.com/api/v2/search?q="
"${Uri.encodeComponent(searchText)}${"&sort_by_date=0&type=podcast&offset=$nextOffset"}";
var response = await Dio().get(url,
options: Options(headers: {
'X-ListenAPI-Key': "$apiKey",
'Accept': "application/json"
@ -25,10 +24,10 @@ class SearchEngine {
Future<SearchEpisodes<dynamic>> fetchEpisode(
{String id, int nextEpisodeDate}) async {
String apiKey = environment['apiKey'];
String url =
var apiKey = environment['apiKey'];
var url =
"https://listen-api.listennotes.com/api/v2/podcasts/$id?next_episode_pub_date=$nextEpisodeDate";
Response response = await Dio().get(url,
var response = await Dio().get(url,
options: Options(headers: {
'X-ListenAPI-Key': "$apiKey",
'Accept': "application/json"

View File

@ -35,7 +35,7 @@ class PodcastsBackup {
builder.element('outline', nest: () {
builder.attribute('text', '${group.name}');
builder.attribute('title', '${group.name}');
for (var e in group.podcasts)
for (var e in group.podcasts) {
builder.element(
'outline',
nest: () {
@ -46,6 +46,7 @@ class PodcastsBackup {
},
isSelfClosing: true,
);
}
});
}
});
@ -54,15 +55,15 @@ class PodcastsBackup {
}
static parseOMPL(File file) {
Map<String, List<OmplOutline>> data = Map();
String opml = file.readAsStringSync();
var data = <String, List<OmplOutline>>{};
var opml = file.readAsStringSync();
var content = xml.XmlDocument.parse(opml);
String title =
var title =
content.findAllElements('head').first.findElements('title').first.text;
print(title);
var groups = content.findAllElements('body').first.findElements('outline');
if (title != 'Tsacdop Feed Groups') {
List<OmplOutline> total = content
var total = content
.findAllElements('outline')
.map((ele) => OmplOutline.parse(ele))
.toList();
@ -72,7 +73,7 @@ class PodcastsBackup {
}
for (var element in groups) {
String title = element.getAttribute('title');
var title = element.getAttribute('title');
var total = element
.findElements('outline')
.map((ele) => OmplOutline.parse(ele))

View File

@ -1,23 +1,22 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:line_icons/line_icons.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/type/settings_backup.dart';
import 'package:wc_flutter_share/wc_flutter_share.dart';
import '../service/ompl_build.dart';
import '../state/podcast_group.dart';
import '../state/setting_state.dart';
import '../type/settings_backup.dart';
import '../util/extension_helper.dart';
import '../service/ompl_build.dart';
class DataBackup extends StatefulWidget {
@override
@ -29,8 +28,8 @@ class _DataBackupState extends State<DataBackup> {
var groups = context.read<GroupList>().groups;
var ompl = PodcastsBackup(groups).omplBuilder();
var tempdir = await getTemporaryDirectory();
DateTime now = DateTime.now();
String datePlus = now.year.toString() +
var now = DateTime.now();
var datePlus = now.year.toString() +
now.month.toString() +
now.day.toString() +
now.second.toString();
@ -45,7 +44,7 @@ class _DataBackupState extends State<DataBackup> {
}
Future<void> _shareFile(File file) async {
final Uint8List bytes = await file.readAsBytes();
final bytes = await file.readAsBytes();
await WcFlutterShare.share(
sharePopupTitle: 'share Clip',
fileName: file.path.split('/').last,
@ -55,11 +54,11 @@ class _DataBackupState extends State<DataBackup> {
Future<File> _exportSetting(BuildContext context) async {
var settings = context.read<SettingState>();
SettingsBackup settingsBack = await settings.backup();
var settingsBack = await settings.backup();
var json = settingsBack.toJson();
var tempdir = await getTemporaryDirectory();
DateTime now = DateTime.now();
String datePlus = now.year.toString() +
var now = DateTime.now();
var datePlus = now.year.toString() +
now.month.toString() +
now.day.toString() +
now.second.toString();
@ -71,10 +70,10 @@ class _DataBackupState extends State<DataBackup> {
Future _importSetting(String path, BuildContext context) async {
final s = context.s;
var settings = context.read<SettingState>();
File file = File(path);
var file = File(path);
try {
String json = file.readAsStringSync();
SettingsBackup backup = SettingsBackup.fromJson(jsonDecode(json));
var json = file.readAsStringSync();
var backup = SettingsBackup.fromJson(jsonDecode(json));
await settings.restore(backup);
Fluttertoast.showToast(
msg: s.toastImportSettingsSuccess,
@ -92,11 +91,11 @@ class _DataBackupState extends State<DataBackup> {
void _getFilePath(BuildContext context) async {
final s = context.s;
try {
String filePath = await FilePicker.getFilePath(type: FileType.any);
var filePath = await FilePicker.getFilePath(type: FileType.any);
if (filePath == '') {
return;
}
print('File Path' + filePath);
print('File Path$filePath');
Fluttertoast.showToast(
msg: s.toastReadFile,
gravity: ToastGravity.BOTTOM,
@ -166,7 +165,7 @@ class _DataBackupState extends State<DataBackup> {
],
),
onPressed: () async {
File file = await _exportOmpl(context);
var file = await _exportOmpl(context);
await _saveFile(file);
}),
SizedBox(width: 10),
@ -188,7 +187,7 @@ class _DataBackupState extends State<DataBackup> {
],
),
onPressed: () async {
File file = await _exportOmpl(context);
var file = await _exportOmpl(context);
await _shareFile(file);
})
],
@ -230,7 +229,7 @@ class _DataBackupState extends State<DataBackup> {
],
),
onPressed: () async {
File file = await _exportSetting(context);
var file = await _exportSetting(context);
await _saveFile(file);
}),
SizedBox(width: 10),
@ -253,7 +252,7 @@ class _DataBackupState extends State<DataBackup> {
],
),
onPressed: () async {
File file = await _exportSetting(context);
var file = await _exportSetting(context);
await _shareFile(file);
}),
SizedBox(width: 10),

View File

@ -2,16 +2,16 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:line_icons/line_icons.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/download_state.dart';
import '../type/episodebrief.dart';
import '../util/extension_helper.dart';
import '../state/download_state.dart';
import '../local_storage/sqflite_localpodcast.dart';
class DownloadsManage extends StatefulWidget {
@override
@ -29,14 +29,14 @@ class _DownloadsManageState extends State<DownloadsManage> {
List<EpisodeBrief> _selectedList;
Future<List<EpisodeBrief>> _getDownloadedEpisode(int mode) async {
List<EpisodeBrief> episodes = [];
var episodes = <EpisodeBrief>[];
var dbHelper = DBHelper();
episodes = await dbHelper.getDownloadedEpisode(mode);
return episodes;
}
Future<int> _isListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
@ -60,16 +60,17 @@ class _DownloadsManageState extends State<DownloadsManage> {
_delSelectedEpisodes() async {
setState(() => _clearing = true);
// await Future.forEach(_selectedList, (EpisodeBrief episode) async
for (EpisodeBrief episode in _selectedList) {
for (var episode in _selectedList) {
var downloader = Provider.of<DownloadState>(context, listen: false);
await downloader.delTask(episode);
if (mounted) setState(() {});
}
await Future.delayed(Duration(seconds: 1));
if (mounted)
if (mounted) {
setState(() {
_clearing = false;
});
}
await Future.delayed(Duration(seconds: 1));
if (mounted) setState(() => _selectedList = []);
_getStorageSize();
@ -78,7 +79,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
String _downloadDateToString(BuildContext context,
{int downloadDate, int pubDate}) {
final s = context.s;
DateTime date = DateTime.fromMillisecondsSinceEpoch(downloadDate);
var date = DateTime.fromMillisecondsSinceEpoch(downloadDate);
var diffrence = DateTime.now().toUtc().difference(date);
if (diffrence.inHours < 24) {
return s.hoursAgo(diffrence.inHours);
@ -91,11 +92,13 @@ class _DownloadsManageState extends State<DownloadsManage> {
}
int sumSelected() {
int sum = 0;
var sum = 0;
if (_selectedList.length == 0) {
return sum;
} else {
for (var episode in _selectedList) sum += episode.enclosureLength;
for (var episode in _selectedList) {
sum += episode.enclosureLength;
}
return sum;
}
}
@ -238,12 +241,13 @@ class _DownloadsManageState extends State<DownloadsManage> {
),
],
onSelected: (value) {
if (value == 0)
if (value == 0) {
setState(() => _mode = 0);
else if (value == 1)
} else if (value == 1) {
setState(() => _mode = 1);
else if (value == 2)
} else if (value == 2) {
setState(() => _mode = 2);
}
},
),
),
@ -340,17 +344,14 @@ class _DownloadsManageState extends State<DownloadsManage> {
if (_episodes[index]
.enclosureLength !=
0)
Text(((_episodes[index]
.enclosureLength) ~/
1000000)
.toString() +
' Mb'),
Text(
'${(_episodes[index].enclosureLength) ~/ 1000000} Mb'),
],
),
trailing: Checkbox(
value: _selectedList.contains(
_episodes[index]),
onChanged: (bool boo) {
onChanged: (boo) {
if (boo) {
setState(() =>
_selectedList.add(
@ -385,7 +386,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
left: context.width / 2 - 50,
bottom: _selectedList.length == 0 ? -100 : 30,
child: InkWell(
onTap: () => _delSelectedEpisodes(),
onTap: _delSelectedEpisodes,
child: Stack(
alignment: _clearing
? Alignment.centerLeft
@ -407,7 +408,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
LineIcons.trash_alt_solid,
color: Colors.white,
),
Text((sumSelected() ~/ 1000000).toString() + 'Mb',
Text('${sumSelected() ~/ 1000000}Mb',
style: TextStyle(color: Colors.white)),
],
),

View File

@ -1,20 +1,19 @@
import 'package:dio/dio.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:dio/dio.dart';
import 'package:intl/intl.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import 'package:line_icons/line_icons.dart';
import 'package:tsacdop/state/podcast_group.dart';
import 'package:provider/provider.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../webfeed/webfeed.dart';
import '../type/searchpodcast.dart';
import '../util/extension_helper.dart';
import '../type/play_histroy.dart';
import '../state/podcast_group.dart';
import '../type/play_histroy.dart';
import '../type/searchpodcast.dart';
import '../type/sub_history.dart';
import '../util/extension_helper.dart';
import '../webfeed/webfeed.dart';
class PlayedHistory extends StatefulWidget {
@override
@ -24,42 +23,40 @@ class PlayedHistory extends StatefulWidget {
class _PlayedHistoryState extends State<PlayedHistory>
with SingleTickerProviderStateMixin {
Future<List<PlayHistory>> getPlayHistory(int top) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
List<PlayHistory> playHistory;
playHistory = await dbHelper.getPlayHistory(top);
for (var record in playHistory) await record.getEpisode();
for (var record in playHistory) {
await record.getEpisode();
}
return playHistory;
}
_loadMoreData() async {
// await Future.delayed(Duration(seconds: 3));
if (mounted)
if (mounted) {
setState(() {
_top = _top + 100;
});
}
}
int _top = 100;
Future<List<SubHistory>> getSubHistory() async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.getSubHistory();
}
static String _stringForSeconds(double seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
TabController _controller;
List<int> list = const [0, 1, 2, 3, 4, 5, 6];
Future<List<FlSpot>> getData() async {
var dbHelper = DBHelper();
List<FlSpot> stats = [];
var stats = <FlSpot>[];
for (var day in list) {
double mins = await dbHelper.listenMins(7 - day);
var mins = await dbHelper.listenMins(7 - day);
stats.add(FlSpot(day.toDouble(), mins));
}
return stats;
@ -72,11 +69,11 @@ class _PlayedHistoryState extends State<PlayedHistory>
);
var subscribeWorker = context.watch<GroupList>();
try {
BaseOptions options = new BaseOptions(
var options = BaseOptions(
connectTimeout: 10000,
receiveTimeout: 10000,
);
Response response = await Dio(options).get(url);
var response = await Dio(options).get(url);
var p = RssFeed.parse(response.data);
var podcast = OnlinePodcast(
rss: url,
@ -84,7 +81,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
publisher: p.author,
description: p.description,
image: p.itunes.image.href);
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title,
var item = SubscribeItem(podcast.rss, podcast.title,
imgUrl: podcast.image, group: 'Home');
subscribeWorker.setSubscribeItem(item);
} on DioError catch (e) {
@ -123,7 +120,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
backgroundColor: Theme.of(context).primaryColor,
body: SafeArea(
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxScrolled) {
headerSliverBuilder: (context, innerBoxScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: Theme.of(context).primaryColor,
@ -132,8 +129,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
floating: false,
pinned: true,
flexibleSpace: LayoutBuilder(
builder:
(BuildContext context, BoxConstraints constraints) {
builder: (context, constraints) {
top = constraints.biggest.height;
return FlexibleSpaceBar(
title: top < 70 + MediaQuery.of(context).padding.top
@ -181,10 +177,10 @@ class _PlayedHistoryState extends State<PlayedHistory>
FutureBuilder<List<PlayHistory>>(
future: getPlayHistory(_top),
builder: (context, snapshot) {
double _width = MediaQuery.of(context).size.width;
var _width = MediaQuery.of(context).size.width;
return snapshot.hasData
? NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top) _loadMoreData();
@ -194,10 +190,9 @@ class _PlayedHistoryState extends State<PlayedHistory>
//shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
double seekValue =
snapshot.data[index].seekValue;
double seconds = snapshot.data[index].seconds;
itemBuilder: (context, index) {
var seekValue = snapshot.data[index].seekValue;
var seconds = snapshot.data[index].seconds;
return Container(
padding:
const EdgeInsets.symmetric(vertical: 5),
@ -263,8 +258,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
child: Text(
seconds == 0 && seekValue == 1
? s.mark
: _stringForSeconds(
seconds),
: seconds.toInt().toTime,
style: TextStyle(
color: Colors.white),
),
@ -293,8 +287,8 @@ class _PlayedHistoryState extends State<PlayedHistory>
// shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
bool _status = snapshot.data[index].status;
itemBuilder: (context, index) {
var _status = snapshot.data[index].status;
return Container(
color: context.scaffoldBackgroundColor,
child: Column(
@ -381,7 +375,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
return Container(
color: _color,
child: _tabBar,
);
@ -481,7 +475,7 @@ class HistoryChart extends StatelessWidget {
),
lineBarsData: [
LineChartBarData(
spots: this.stats,
spots: stats,
isCurved: true,
colors: [context.accentColor],
preventCurveOverShooting: true,

View File

@ -3,8 +3,8 @@ import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart';
import '../util/extension_helper.dart';
import '../generated/l10n.dart';
import '../util/extension_helper.dart';
class LanguagesSetting extends StatefulWidget {
const LanguagesSetting({Key key}) : super(key: key);
@ -52,7 +52,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
trailing: Radio<Locale>(
value: Locale(Intl.systemLocale),
groupValue: Locale(Intl.getCurrentLocale()),
onChanged: (Locale locale) async {
onChanged: (locale) async {
await S.load(locale);
setState(() {});
}),
@ -68,7 +68,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
trailing: Radio<Locale>(
value: Locale('en'),
groupValue: Locale(Intl.getCurrentLocale()),
onChanged: (Locale locale) async {
onChanged: (locale) async {
await S.load(locale);
setState(() {});
}),
@ -84,7 +84,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
trailing: Radio<Locale>(
value: Locale('zh_Hans'),
groupValue: Locale(Intl.getCurrentLocale()),
onChanged: (Locale locale) async {
onChanged: (locale) async {
await S.load(locale);
setState(() {});
}),
@ -100,7 +100,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
trailing: Radio<Locale>(
value: Locale('fr'),
groupValue: Locale(Intl.getCurrentLocale()),
onChanged: (Locale locale) async {
onChanged: (locale) async {
await S.load(locale);
setState(() {});
}),

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../util/extension_helper.dart';
import '../util/episodegrid.dart';
import '../util/custom_widget.dart';
import '../local_storage/key_value_storage.dart';
import '../util/custom_widget.dart';
import '../util/episodegrid.dart';
import '../util/extension_helper.dart';
import 'popup_menu.dart';
class LayoutSetting extends StatefulWidget {
@ -16,8 +16,8 @@ class LayoutSetting extends StatefulWidget {
class _LayoutSettingState extends State<LayoutSetting> {
Future<Layout> _getLayout(String key) async {
KeyValueStorage keyValueStorage = KeyValueStorage(key);
int layout = await keyValueStorage.getInt();
var keyValueStorage = KeyValueStorage(key);
var layout = await keyValueStorage.getInt();
return Layout.values[layout];
}
@ -31,7 +31,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
padding: const EdgeInsets.only(top: 10.0, bottom: 10.0),
child: InkWell(
onTap: () async {
KeyValueStorage storage = KeyValueStorage(key);
var storage = KeyValueStorage(key);
await storage.saveInt(option.index);
setState(() {});
},

View File

@ -2,17 +2,17 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_time_picker_spinner/flutter_time_picker_spinner.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter_time_picker_spinner/flutter_time_picker_spinner.dart';
import '../state/setting_state.dart';
import '../home/audioplayer.dart';
import '../util/general_dialog.dart';
import '../util/extension_helper.dart';
import '../state/setting_state.dart';
import '../util/custom_dropdown.dart';
import '../util/extension_helper.dart';
import '../util/general_dialog.dart';
const List secondsToSelect = [10, 15, 20, 25, 30, 45, 60];
@ -95,7 +95,7 @@ class PlaySetting extends StatelessWidget {
children: [
InkWell(
onTap: () {
int startTime = data.item1;
var startTime = data.item1;
generalDialog(
context,
content: TimePickerSpinner(
@ -111,7 +111,7 @@ class PlaySetting extends StatelessWidget {
normalTextStyle: GoogleFonts.teko(
textStyle:
TextStyle(fontSize: 40, color: Colors.black38)),
onTimeChange: (DateTime time) {
onTimeChange: (time) {
startTime = time.hour * 60 + time.minute;
},
),
@ -177,7 +177,7 @@ class PlaySetting extends StatelessWidget {
textStyle:
TextStyle(fontSize: 40, color: Colors.black38)),
is24HourMode: false,
onTimeChange: (DateTime time) {
onTimeChange: (time) {
endTime = time.hour * 60 + time.minute;
},
),
@ -329,7 +329,7 @@ class PlaySetting extends StatelessWidget {
displayItemCount: 5,
isDense: true,
value: data,
onChanged: (int value) =>
onChanged: (value) =>
settings.setFastForwardSeconds = value,
items: secondsToSelect
.map<DropdownMenuItem<int>>((e) {
@ -352,7 +352,7 @@ class PlaySetting extends StatelessWidget {
displayItemCount: 5,
isDense: true,
value: data,
onChanged: (int value) =>
onChanged: (value) =>
settings.setRewindSeconds = value,
items: secondsToSelect
.map<DropdownMenuItem<int>>((e) {
@ -392,7 +392,7 @@ class PlaySetting extends StatelessWidget {
displayItemCount: 5,
isDense: true,
value: data,
onChanged: (int value) =>
onChanged: (value) =>
settings.setDefaultSleepTimer = value,
items:
minsToSelect.map<DropdownMenuItem<int>>((e) {

View File

@ -1,11 +1,11 @@
import 'package:flare_flutter/flare_actor.dart';
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/extension_helper.dart';
import '../util/custom_widget.dart';
import '../local_storage/key_value_storage.dart';
import '../util/custom_widget.dart';
import '../util/extension_helper.dart';
class PopupMenuSetting extends StatefulWidget {
const PopupMenuSetting({Key key}) : super(key: key);
@ -16,27 +16,25 @@ class PopupMenuSetting extends StatefulWidget {
class _PopupMenuSettingState extends State<PopupMenuSetting> {
Future<List<int>> _getEpisodeMenu() async {
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
List<int> list = await popupMenuStorage.getMenu();
var popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
var list = await popupMenuStorage.getMenu();
return list;
}
Future<bool> _getTapToOpenPopupMenu() async {
KeyValueStorage tapToOpenPopupMenuStorage =
KeyValueStorage(tapToOpenPopupMenuKey);
bool boo = await tapToOpenPopupMenuStorage.getBool(defaultValue: false);
var tapToOpenPopupMenuStorage = KeyValueStorage(tapToOpenPopupMenuKey);
var boo = await tapToOpenPopupMenuStorage.getBool(defaultValue: false);
return boo;
}
_saveEpisodeMene(List<int> list) async {
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
var popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
await popupMenuStorage.saveMenu(list);
if (mounted) setState(() {});
}
_saveTapToOpenPopupMenu(bool boo) async {
KeyValueStorage tapToOpenPopupMenuStorage =
KeyValueStorage(tapToOpenPopupMenuKey);
var tapToOpenPopupMenuStorage = KeyValueStorage(tapToOpenPopupMenuKey);
await tapToOpenPopupMenuStorage.saveBool(boo);
if (mounted) setState(() {});
}
@ -57,12 +55,12 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
? null
: () {
if (e >= 10) {
int index = menu.indexOf(e);
var index = menu.indexOf(e);
menu.remove(e);
menu.insert(index, e - 10);
_saveEpisodeMene(menu);
} else if (e < 10) {
int index = menu.indexOf(e);
var index = menu.indexOf(e);
menu.remove(e);
menu.insert(index, e + 10);
_saveEpisodeMene(menu);
@ -72,14 +70,14 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
value: e < 10,
onChanged: e == 0
? null
: (bool boo) {
: (boo) {
if (boo && e >= 10) {
int index = menu.indexOf(e);
var index = menu.indexOf(e);
menu.remove(e);
menu.insert(index, e - 10);
_saveEpisodeMene(menu);
} else if (e < 10) {
int index = menu.indexOf(e);
var index = menu.indexOf(e);
menu.remove(e);
menu.insert(index, e + 10);
_saveEpisodeMene(menu);
@ -143,7 +141,7 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
scale: 0.9,
child: Switch(
value: snapshot.data,
onChanged: (bool boo) => _saveTapToOpenPopupMenu(boo)),
onChanged: _saveTapToOpenPopupMenu),
),
),
),
@ -151,13 +149,13 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
future: _getEpisodeMenu(),
initialData: [0, 1, 12, 13, 14],
builder: (context, snapshot) {
List<int> menu = snapshot.data;
var menu = snapshot.data;
return Expanded(
child: ListView(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
children: menu.map<Widget>((int e) {
int i = e % 10;
children: menu.map<Widget>((e) {
var i = e % 10;
switch (i) {
case 0:
return _popupMenuItem(menu, e,

View File

@ -1,25 +1,25 @@
import 'dart:math' as math;
import 'package:feature_discovery/feature_discovery.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:line_icons/line_icons.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:feature_discovery/feature_discovery.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../util/extension_helper.dart';
import '../intro_slider/app_intro.dart';
import '../home/home.dart';
import '../intro_slider/app_intro.dart';
import '../podcasts/podcast_manage.dart';
import 'theme.dart';
import 'layouts.dart';
import 'storage.dart';
import 'history.dart';
import 'syncing.dart';
import 'libries.dart';
import 'languages.dart';
import 'play_setting.dart';
import '../util/extension_helper.dart';
import 'data_backup.dart';
import 'history.dart';
import 'languages.dart';
import 'layouts.dart';
import 'libries.dart';
import 'play_setting.dart';
import 'storage.dart';
import 'syncing.dart';
import 'theme.dart';
class Settings extends StatefulWidget {
@override

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import '../local_storage/key_value_storage.dart';
import '../settings/downloads_manage.dart';
import '../state/setting_state.dart';
import '../local_storage/key_value_storage.dart';
import '../util/extension_helper.dart';
import '../util/custom_dropdown.dart';
import '../util/extension_helper.dart';
class StorageSetting extends StatefulWidget {
@override
@ -20,13 +20,13 @@ class _StorageSettingState extends State<StorageSetting>
AnimationController _controller;
Animation<double> _animation;
_getCacheMax() async {
int cache =
var cache =
await cacheStorage.getInt(defaultValue: (200 * 1024 * 1024).toInt());
if (cache == 0) {
await cacheStorage.saveInt((200 * 1024 * 1024).toInt());
cache = 200 * 1024 * 1024;
}
int value = cache ~/ (1024 * 1024);
var value = cache ~/ (1024 * 1024);
if (value > 100) {
_controller = AnimationController(
vsync: this, duration: Duration(milliseconds: value * 2));
@ -40,14 +40,14 @@ class _StorageSettingState extends State<StorageSetting>
}
Future<bool> _getAutoDownloadNetwork() async {
KeyValueStorage storage = KeyValueStorage(autoDownloadNetworkKey);
bool value = await storage.getBool(defaultValue: false);
var storage = KeyValueStorage(autoDownloadNetworkKey);
var value = await storage.getBool(defaultValue: false);
return value;
}
Future<int> _getAutoDeleteDays() async {
KeyValueStorage storage = KeyValueStorage(autoDeleteKey);
int days = await storage.getInt();
var storage = KeyValueStorage(autoDeleteKey);
var days = await storage.getInt();
if (days == 0) {
storage.saveInt(30);
return 30;
@ -56,13 +56,13 @@ class _StorageSettingState extends State<StorageSetting>
}
_setAutoDeleteDays(int days) async {
KeyValueStorage storage = KeyValueStorage(autoDeleteKey);
var storage = KeyValueStorage(autoDeleteKey);
await storage.saveInt(days);
setState(() {});
}
_setAudtDownloadNetwork(bool boo) async {
KeyValueStorage storage = KeyValueStorage(autoDownloadNetworkKey);
var storage = KeyValueStorage(autoDownloadNetworkKey);
await storage.saveBool(boo);
}
@ -272,7 +272,7 @@ class _StorageSettingState extends State<StorageSetting>
min: 100,
max: 1000,
divisions: 9,
onChanged: (double val) {
onChanged: (val) {
setState(() {
_value = val;
});

View File

@ -4,8 +4,8 @@ import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import '../state/setting_state.dart';
import '../util/extension_helper.dart';
import '../util/custom_dropdown.dart';
import '../util/extension_helper.dart';
class SyncingSetting extends StatelessWidget {
@override
@ -79,10 +79,11 @@ class SyncingSetting extends StatelessWidget {
value: data.item1,
onChanged: (boo) async {
settings.autoUpdate = boo;
if (boo)
if (boo) {
settings.setWorkManager(data.item2);
else
} else {
settings.cancelWork();
}
}),
),
),

View File

@ -54,8 +54,7 @@ class ThemeSetting extends StatelessWidget {
.modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext context, Animation animaiton,
Animation secondaryAnimation) =>
pageBuilder: (context, animaiton, secondaryAnimation) =>
AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

@ -1,16 +1,16 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:just_audio/just_audio.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../type/playlist.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
MediaControl playControl = MediaControl(
androidIcon: 'drawable/ic_stat_play_circle_filled',
@ -68,7 +68,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
EpisodeBrief _episode;
/// Current playlist.
Playlist _queue = Playlist();
final Playlist _queue = Playlist();
/// Notifier for playlist change.
bool _queueUpdate = false;
@ -173,12 +173,12 @@ class AudioPlayerNotifier extends ChangeNotifier {
}
Future _getAutoPlay() async {
int i = await autoPlayStorage.getInt();
var i = await autoPlayStorage.getInt();
_autoPlay = i == 0;
}
Future _getAutoSleepTimer() async {
int i = await autoSleepTimerStorage.getInt();
var i = await autoSleepTimerStorage.getInt();
_autoSleepTimer = i == 1;
}
@ -193,7 +193,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
_queueUpdate = false;
await _getAutoSleepTimer();
await AudioService.connect();
bool running = AudioService.running;
var running = AudioService.running;
if (running) {}
}
@ -202,26 +202,25 @@ class AudioPlayerNotifier extends ChangeNotifier {
await _getAutoPlay();
_lastPostion = await positionStorage.getInt();
if (_lastPostion > 0 && _queue.playlist.length > 0) {
final EpisodeBrief episode = _queue.playlist.first;
final int duration = episode.duration * 1000;
final double seekValue = duration != 0 ? _lastPostion / duration : 1;
final PlayHistory history = PlayHistory(
episode.title, episode.enclosureUrl, _lastPostion / 1000, seekValue);
final episode = _queue.playlist.first;
final duration = episode.duration * 1000;
final seekValue = duration != 0 ? _lastPostion / duration : 1;
final history = PlayHistory(
episode.title, episode.enclosureUrl, _lastPostion ~/ 1000, seekValue);
await dbHelper.saveHistory(history);
}
KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey);
var lastWorkStorage = KeyValueStorage(lastWorkKey);
await lastWorkStorage.saveInt(0);
}
Future<void> episodeLoad(EpisodeBrief episode,
{int startPosition = 0}) async {
print(episode.enclosureUrl);
final EpisodeBrief episodeNew =
await dbHelper.getRssItemWithUrl(episode.enclosureUrl);
final episodeNew = await dbHelper.getRssItemWithUrl(episode.enclosureUrl);
//TODO load episode from last position when player running
if (playerRunning) {
PlayHistory history = PlayHistory(_episode.title, _episode.enclosureUrl,
backgroundAudioPosition / 1000, seekSliderValue);
final history = PlayHistory(_episode.title, _episode.enclosureUrl,
backgroundAudioPosition ~/ 1000, seekSliderValue);
await dbHelper.saveHistory(history);
AudioService.addQueueItemAt(episodeNew.toMediaItem(), 0);
_queue.playlist
@ -275,25 +274,26 @@ class AudioPlayerNotifier extends ChangeNotifier {
//Check autoplay setting, if true only add one episode, else add playlist.
await _getAutoPlay();
if (_autoPlay) {
for (var episode in _queue.playlist)
for (var episode in _queue.playlist) {
await AudioService.addQueueItem(episode.toMediaItem());
}
} else {
await AudioService.addQueueItem(_queue.playlist.first.toMediaItem());
}
//Check auto sleep timer setting
await _getAutoSleepTimer();
if (_autoSleepTimer) {
int startTime =
var startTime =
await autoSleepTimerStartStorage.getInt(defaultValue: 1380);
int endTime = await autoSleepTimerEndStorage.getInt(defaultValue: 360);
int currentTime = DateTime.now().hour * 60 + DateTime.now().minute;
var endTime = await autoSleepTimerEndStorage.getInt(defaultValue: 360);
var currentTime = DateTime.now().hour * 60 + DateTime.now().minute;
if ((startTime > endTime &&
(currentTime > startTime || currentTime < endTime)) ||
((startTime < endTime) &&
(currentTime > startTime && currentTime < endTime))) {
int mode = await autoSleepTimerModeStorage.getInt();
var mode = await autoSleepTimerModeStorage.getInt();
_sleepTimerMode = SleepTimerMode.values[mode];
int defaultTimer =
var defaultTimer =
await defaultSleepTimerStorage.getInt(defaultValue: 30);
sleepTimer(defaultTimer);
}
@ -304,7 +304,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
AudioService.currentMediaItemStream
.where((event) => event != null)
.listen((item) async {
EpisodeBrief episode = await dbHelper.getRssItemWithMediaId(item.id);
var episode = await dbHelper.getRssItemWithMediaId(item.id);
_backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
if (episode != null) {
@ -334,11 +334,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
_lastPostion = 0;
notifyListeners();
await positionStorage.saveInt(_lastPostion);
final PlayHistory history = PlayHistory(
_episode.title,
_episode.enclosureUrl,
backgroundAudioPosition / 1000,
seekSliderValue);
final history = PlayHistory(_episode.title, _episode.enclosureUrl,
backgroundAudioPosition ~/ 1000, seekSliderValue);
dbHelper.saveHistory(history);
}
});
@ -370,9 +367,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
});
//double s = _currentSpeed ?? 1.0;
int getPosition = 0;
var getPosition = 0;
Timer.periodic(Duration(milliseconds: 500), (timer) {
double s = _currentSpeed ?? 1.0;
var s = _currentSpeed ?? 1.0;
if (_noSlide) {
if (_playing && !buffering) {
getPosition = _currentPosition +
@ -380,16 +377,18 @@ class AudioPlayerNotifier extends ChangeNotifier {
.toInt();
_backgroundAudioPosition =
math.min(getPosition, _backgroundAudioDuration);
} else
} else {
_backgroundAudioPosition = _currentPosition ?? 0;
}
if (_backgroundAudioDuration != null &&
_backgroundAudioDuration != 0 &&
_backgroundAudioPosition != null) {
_seekSliderValue =
_backgroundAudioPosition / _backgroundAudioDuration ?? 0;
} else
} else {
_seekSliderValue = 0;
}
if (_backgroundAudioPosition > 0 &&
_backgroundAudioPosition < _backgroundAudioDuration) {
@ -442,25 +441,29 @@ class AudioPlayerNotifier extends ChangeNotifier {
}
addNewEpisode(List<String> group) async {
List<EpisodeBrief> newEpisodes = [];
if (group.first == 'All')
var newEpisodes = <EpisodeBrief>[];
if (group.first == 'All') {
newEpisodes = await dbHelper.getRecentNewRssItem();
else
} else {
newEpisodes = await dbHelper.getGroupNewRssItem(group);
if (newEpisodes.length > 0 && newEpisodes.length < 100)
for (var episode in newEpisodes) await addToPlaylist(episode);
if (group.first == 'All')
}
if (newEpisodes.length > 0 && newEpisodes.length < 100) {
for (var episode in newEpisodes) {
await addToPlaylist(episode);
}
}
if (group.first == 'All') {
await dbHelper.removeAllNewMark();
else
} else {
await dbHelper.removeGroupNewMark(group);
}
}
updateMediaItem(EpisodeBrief episode) async {
int index = _queue.playlist
var index = _queue.playlist
.indexWhere((item) => item.enclosureUrl == episode.enclosureUrl);
if (index > 0) {
EpisodeBrief episodeNew =
await dbHelper.getRssItemWithUrl(episode.enclosureUrl);
var episodeNew = await dbHelper.getRssItemWithUrl(episode.enclosureUrl);
await delFromPlaylist(episode);
await addToPlaylistAt(episodeNew, index);
}
@ -471,7 +474,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
if (playerRunning) {
await AudioService.removeQueueItem(episodeNew.toMediaItem());
}
int index = await _queue.delFromPlaylist(episodeNew);
var index = await _queue.delFromPlaylist(episodeNew);
_queueUpdate = !_queueUpdate;
notifyListeners();
return index;
@ -499,7 +502,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
}
forwardAudio(int s) {
int pos = _backgroundAudioPosition + s * 1000;
var pos = _backgroundAudioPosition + s * 1000;
AudioService.seekTo(Duration(milliseconds: pos));
}
@ -513,8 +516,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
seekTo(int position) async {
if (_audioState != AudioProcessingState.connecting &&
_audioState != AudioProcessingState.none)
_audioState != AudioProcessingState.none) {
await AudioService.seekTo(Duration(milliseconds: position));
}
}
sliderSeek(double val) async {
@ -597,8 +601,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
class AudioPlayerTask extends BackgroundAudioTask {
KeyValueStorage cacheStorage = KeyValueStorage(cacheMaxKey);
List<MediaItem> _queue = [];
AudioPlayer _audioPlayer = AudioPlayer();
final List<MediaItem> _queue = [];
final AudioPlayer _audioPlayer = AudioPlayer();
AudioProcessingState _skipState;
bool _playing;
bool _interrupted = false;
@ -664,10 +668,11 @@ class AudioPlayerTask extends BackgroundAudioTask {
}
void playPause() {
if (AudioServiceBackground.state.playing)
if (AudioServiceBackground.state.playing) {
onPause();
else
} else {
onPlay();
}
}
@override
@ -688,10 +693,11 @@ class AudioPlayerTask extends BackgroundAudioTask {
await AudioServiceBackground.setMediaItem(mediaItem);
await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax);
print(mediaItem.title);
Duration duration = await _audioPlayer.durationFuture;
if (duration != null)
var duration = await _audioPlayer.durationFuture;
if (duration != null) {
await AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration));
}
_skipState = null;
// Resume playback if we were playing
// if (_playing) {
@ -716,15 +722,17 @@ class AudioPlayerTask extends BackgroundAudioTask {
}
await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax);
var duration = await _audioPlayer.durationFuture;
if (duration != null)
if (duration != null) {
await AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration));
}
playFromStart();
} else {
_playing = true;
if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting ||
_audioPlayer.playbackEvent.state != AudioPlaybackState.none)
_audioPlayer.playbackEvent.state != AudioPlaybackState.none) {
_audioPlayer.play();
}
}
}
}
@ -732,12 +740,13 @@ class AudioPlayerTask extends BackgroundAudioTask {
playFromStart() async {
_playing = true;
if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting ||
_audioPlayer.playbackEvent.state != AudioPlaybackState.none)
_audioPlayer.playbackEvent.state != AudioPlaybackState.none) {
try {
_audioPlayer.play();
} catch (e) {
_setState(processingState: AudioProcessingState.error);
}
}
if (mediaItem.extras['skip'] > 0) {
_audioPlayer.seek(Duration(seconds: mediaItem.extras['skip']));
}
@ -757,17 +766,18 @@ class AudioPlayerTask extends BackgroundAudioTask {
@override
void onSeekTo(Duration position) {
if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting ||
_audioPlayer.playbackEvent.state != AudioPlaybackState.none)
_audioPlayer.playbackEvent.state != AudioPlaybackState.none) {
_audioPlayer.seek(position);
}
}
@override
void onClick(MediaButton button) {
if (button == MediaButton.media)
if (button == MediaButton.media) {
playPause();
else if (button == MediaButton.next)
} else if (button == MediaButton.next) {
_seekRelative(fastForwardInterval);
else if (button == MediaButton.previous) _seekRelative(-rewindInterval);
} else if (button == MediaButton.previous) _seekRelative(-rewindInterval);
}
Future<void> _seekRelative(Duration offset) async {
@ -812,7 +822,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
await AudioServiceBackground.setQueue(_queue);
await AudioServiceBackground.setMediaItem(mediaItem);
await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax);
Duration duration = await _audioPlayer.durationFuture ?? Duration.zero;
var duration = await _audioPlayer.durationFuture ?? Duration.zero;
AudioServiceBackground.setMediaItem(
mediaItem.copyWith(duration: duration));
playFromStart();

View File

@ -28,30 +28,28 @@ class EpisodeTask {
void downloadCallback(String id, DownloadTaskStatus status, int progress) {
print('Homepage callback task in $id status ($status) $progress');
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
final send = IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
void autoDownloadCallback(String id, DownloadTaskStatus status, int progress) {
print('Autodownload callback task in $id status ($status) $progress');
final SendPort send =
IsolateNameServer.lookupPortByName('auto_downloader_send_port');
final send = IsolateNameServer.lookupPortByName('auto_downloader_send_port');
send.send([id, status, progress]);
}
//For background auto downlaod
class AutoDownloader {
DBHelper dbHelper = DBHelper();
List<EpisodeTask> _episodeTasks = [];
Completer _completer = Completer();
final List<EpisodeTask> _episodeTasks = [];
final Completer _completer = Completer();
AutoDownloader() {
FlutterDownloader.registerCallback(autoDownloadCallback);
}
bindBackgroundIsolate() {
ReceivePort _port = ReceivePort();
bool isSuccess = IsolateNameServer.registerPortWithName(
var _port = ReceivePort();
var isSuccess = IsolateNameServer.registerPortWithName(
_port.sendPort, 'auto_downloader_send_port');
if (!isSuccess) {
IsolateNameServer.removePortNameMapping('auto_downloader_send_port');
@ -89,10 +87,9 @@ class AutoDownloader {
Future _saveMediaId(EpisodeTask episodeTask) async {
final completeTask = await FlutterDownloader.loadTasksWithRawQuery(
query: "SELECT * FROM task WHERE task_id = '${episodeTask.taskId}'");
String filePath = 'file://' +
path.join(completeTask.first.savedDir,
Uri.encodeComponent(completeTask.first.filename));
FileStat fileStat = await File(
var filePath =
'file://${path.join(completeTask.first.savedDir, Uri.encodeComponent(completeTask.first.filename))}';
var fileStat = await File(
path.join(completeTask.first.savedDir, completeTask.first.filename))
.stat();
await dbHelper.saveMediaId(episodeTask.episode.enclosureUrl, filePath,
@ -106,22 +103,20 @@ class AutoDownloader {
{bool showNotification = false}) async {
for (var episode in episodes) {
final dir = await getExternalStorageDirectory();
String localPath = path.join(dir.path, episode.feedTitle);
var localPath = path.join(dir.path, episode.feedTitle);
final saveDir = Directory(localPath);
bool hasExisted = await saveDir.exists();
var hasExisted = await saveDir.exists();
if (!hasExisted) {
saveDir.create();
}
DateTime now = DateTime.now();
String datePlus = now.year.toString() +
var now = DateTime.now();
var datePlus = now.year.toString() +
now.month.toString() +
now.day.toString() +
now.second.toString();
String fileName = episode.title +
datePlus +
'.' +
episode.enclosureUrl.split('/').last.split('.').last;
String taskId = await FlutterDownloader.enqueue(
var fileName =
'${episode.title}$datePlus.${episode.enclosureUrl.split('/').last.split('.').last}';
var taskId = await FlutterDownloader.enqueue(
fileName: fileName,
url: episode.enclosureUrl,
savedDir: localPath,
@ -157,24 +152,26 @@ class DownloadState extends ChangeNotifier {
_loadTasks() async {
_episodeTasks = [];
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
var tasks = await FlutterDownloader.loadTasks();
if (tasks.length != 0)
if (tasks.length != 0) {
for (var task in tasks) {
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(task.url);
if (episode == null)
var episode = await dbHelper.getRssItemWithUrl(task.url);
if (episode == null) {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
else
} else {
_episodeTasks.add(EpisodeTask(episode, task.taskId,
progress: task.progress, status: task.status));
}
}
}
notifyListeners();
}
void _bindBackgroundIsolate() {
ReceivePort _port = ReceivePort();
bool isSuccess = IsolateNameServer.registerPortWithName(
var _port = ReceivePort();
var isSuccess = IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
@ -195,8 +192,9 @@ class DownloadState extends ChangeNotifier {
_saveMediaId(episodeTask).then((value) {
notifyListeners();
});
} else
} else {
notifyListeners();
}
}
}
});
@ -206,16 +204,15 @@ class DownloadState extends ChangeNotifier {
episodeTask.status = DownloadTaskStatus.complete;
final completeTask = await FlutterDownloader.loadTasksWithRawQuery(
query: "SELECT * FROM task WHERE task_id = '${episodeTask.taskId}'");
String filePath = 'file://' +
path.join(completeTask.first.savedDir,
Uri.encodeComponent(completeTask.first.filename));
var filePath =
'file://${path.join(completeTask.first.savedDir, Uri.encodeComponent(completeTask.first.filename))}';
print(filePath);
FileStat fileStat = await File(
var fileStat = await File(
path.join(completeTask.first.savedDir, completeTask.first.filename))
.stat();
dbHelper.saveMediaId(episodeTask.episode.enclosureUrl, filePath,
episodeTask.taskId, fileStat.size);
EpisodeBrief episode =
var episode =
await dbHelper.getRssItemWithUrl(episodeTask.episode.enclosureUrl);
_removeTask(episodeTask.episode);
_episodeTasks.add(EpisodeTask(episode, episodeTask.taskId,
@ -245,25 +242,23 @@ class DownloadState extends ChangeNotifier {
Future startTask(EpisodeBrief episode, {bool showNotification = true}) async {
var dbHelper = DBHelper();
bool isDownloaded = await dbHelper.isDownloaded(episode.enclosureUrl);
var isDownloaded = await dbHelper.isDownloaded(episode.enclosureUrl);
if (!isDownloaded) {
final dir = await getExternalStorageDirectory();
String localPath = path.join(dir.path, episode.feedTitle);
var localPath = path.join(dir.path, episode.feedTitle);
final saveDir = Directory(localPath);
bool hasExisted = await saveDir.exists();
var hasExisted = await saveDir.exists();
if (!hasExisted) {
saveDir.create();
}
DateTime now = DateTime.now();
String datePlus = now.year.toString() +
var now = DateTime.now();
var datePlus = now.year.toString() +
now.month.toString() +
now.day.toString() +
now.second.toString();
String fileName = episode.title +
datePlus +
'.' +
episode.enclosureUrl.split('/').last.split('.').last;
String taskId = await FlutterDownloader.enqueue(
var fileName =
'${episode.title}$datePlus.${episode.enclosureUrl.split('/').last.split('.').last}';
var taskId = await FlutterDownloader.enqueue(
fileName: fileName,
url: episode.enclosureUrl,
savedDir: localPath,
@ -277,14 +272,14 @@ class DownloadState extends ChangeNotifier {
}
Future pauseTask(EpisodeBrief episode) async {
EpisodeTask task = episodeToTask(episode);
var task = episodeToTask(episode);
await FlutterDownloader.pause(taskId: task.taskId);
}
Future resumeTask(EpisodeBrief episode) async {
EpisodeTask task = episodeToTask(episode);
String newTaskId = await FlutterDownloader.resume(taskId: task.taskId);
int index = _episodeTasks.indexOf(task);
var task = episodeToTask(episode);
var newTaskId = await FlutterDownloader.resume(taskId: task.taskId);
var index = _episodeTasks.indexOf(task);
_removeTask(episode);
FlutterDownloader.remove(taskId: task.taskId);
var dbHelper = DBHelper();
@ -293,10 +288,10 @@ class DownloadState extends ChangeNotifier {
}
Future retryTask(EpisodeBrief episode) async {
EpisodeTask task = episodeToTask(episode);
String newTaskId = await FlutterDownloader.retry(taskId: task.taskId);
var task = episodeToTask(episode);
var newTaskId = await FlutterDownloader.retry(taskId: task.taskId);
await FlutterDownloader.remove(taskId: task.taskId);
int index = _episodeTasks.indexOf(task);
var index = _episodeTasks.indexOf(task);
_removeTask(episode);
var dbHelper = DBHelper();
_episodeTasks.insert(index, EpisodeTask(episode, newTaskId));
@ -304,20 +299,21 @@ class DownloadState extends ChangeNotifier {
}
Future removeTask(EpisodeBrief episode) async {
EpisodeTask task = episodeToTask(episode);
var task = episodeToTask(episode);
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: false);
}
Future delTask(EpisodeBrief episode) async {
EpisodeTask task = episodeToTask(episode);
var task = episodeToTask(episode);
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await dbHelper.delDownloaded(episode.enclosureUrl);
for (var episodeTask in _episodeTasks) {
if (episodeTask.taskId == task.taskId)
if (episodeTask.taskId == task.taskId) {
episodeTask.status = DownloadTaskStatus.undefined;
}
notifyListeners();
}
_removeTask(episode);
@ -330,24 +326,27 @@ class DownloadState extends ChangeNotifier {
_autoDelete() async {
print('Start auto delete outdated episodes');
KeyValueStorage autoDeleteStorage = KeyValueStorage(autoDeleteKey);
int autoDelete = await autoDeleteStorage.getInt();
if (autoDelete == 0)
var autoDeleteStorage = KeyValueStorage(autoDeleteKey);
var autoDelete = await autoDeleteStorage.getInt();
if (autoDelete == 0) {
await autoDeleteStorage.saveInt(30);
else if (autoDelete > 0) {
int deadline = DateTime.now()
} else if (autoDelete > 0) {
var deadline = DateTime.now()
.subtract(Duration(days: autoDelete))
.millisecondsSinceEpoch;
List<EpisodeBrief> episodes = await dbHelper.getOutdatedEpisode(deadline);
var episodes = await dbHelper.getOutdatedEpisode(deadline);
if (episodes.isNotEmpty) {
for (var episode in episodes) await delTask(episode);
for (var episode in episodes) {
await delTask(episode);
}
}
final tasks = await FlutterDownloader.loadTasksWithRawQuery(
query:
'SELECT * FROM task WHERE time_created < $deadline AND status = 3');
for (var task in tasks)
for (var task in tasks) {
FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
}
}
}
}

View File

@ -3,22 +3,20 @@ import 'dart:io';
import 'dart:isolate';
import 'dart:math' as math;
import 'package:color_thief_flutter/color_thief_flutter.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_isolate/flutter_isolate.dart';
import 'package:tsacdop/local_storage/key_value_storage.dart';
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
import 'package:uuid/uuid.dart';
import 'package:path_provider/path_provider.dart';
import 'package:image/image.dart' as img;
import 'package:color_thief_flutter/color_thief_flutter.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';
import '../webfeed/webfeed.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../type/fireside_data.dart';
import '../type/podcastlocal.dart';
import '../webfeed/webfeed.dart';
class GroupEntity {
final String name;
@ -33,7 +31,7 @@ class GroupEntity {
}
static GroupEntity fromJson(Map<String, Object> json) {
List<String> list = List.from(json['podcastList']);
var list = List<String>.from(json['podcastList']);
return GroupEntity(json['name'] as String, json['id'] as String,
json['color'] as String, list);
}
@ -65,7 +63,7 @@ class PodcastGroup {
Color getColor() {
if (color != '#000000') {
int colorInt = int.parse('FF' + color.toUpperCase(), radix: 16);
var colorInt = int.parse('FF${color.toUpperCase()}', radix: 16);
return Color(colorInt).withOpacity(1.0);
} else {
return Colors.blue[400];
@ -80,9 +78,7 @@ class PodcastGroup {
List<PodcastLocal> _orderedPodcasts;
List<PodcastLocal> get ordereddPodcasts => _orderedPodcasts;
set setOrderedPodcasts(List<PodcastLocal> list) {
_orderedPodcasts = list;
}
set setOrderedPodcasts(List<PodcastLocal> list) => _orderedPodcasts = list;
GroupEntity toEntity() {
return GroupEntity(name, id, color, podcastList);
@ -127,7 +123,7 @@ class SubscribeItem {
class GroupList extends ChangeNotifier {
/// List of all gourps.
List<PodcastGroup> _groups = [];
final List<PodcastGroup> _groups = [];
List<PodcastGroup> get groups => _groups;
DBHelper dbHelper = DBHelper();
@ -142,7 +138,7 @@ class GroupList extends ChangeNotifier {
bool get isLoading => _isLoading;
/// Svae ordered gourps info before saved.
List<PodcastGroup> _orderChanged = [];
final List<PodcastGroup> _orderChanged = [];
List<PodcastGroup> get orderChanged => _orderChanged;
/// Subscribe worker isolate
@ -175,13 +171,14 @@ class GroupList extends ChangeNotifier {
await _createIsolate();
_created = true;
listen();
} else
} else {
subSendPort.send([
_subscribeItem.url,
_subscribeItem.title,
_subscribeItem.imgUrl,
_subscribeItem.group
]);
}
}
Future<void> _createIsolate() async {
@ -207,8 +204,9 @@ class GroupList extends ChangeNotifier {
message[0],
subscribeState: SubscribeState.values[message[2]],
));
if (message.length == 5)
if (message.length == 5) {
_subscribeNewPodcast(id: message[3], groupName: message[4]);
}
} else if (message is String && message == "done") {
subIsolate.kill();
subIsolate = null;
@ -231,7 +229,9 @@ class GroupList extends ChangeNotifier {
clearOrderChanged() async {
if (_orderChanged.length > 0) {
for (var group in _orderChanged) await group.getPodcasts();
for (var group in _orderChanged) {
await group.getPodcasts();
}
_orderChanged.clear();
// notifyListeners();
}
@ -254,8 +254,10 @@ class GroupList extends ChangeNotifier {
_isLoading = true;
notifyListeners();
storage.getGroups().then((loadgroups) async {
_groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e)));
for (var group in _groups) await group.getPodcasts();
_groups.addAll(loadgroups.map(PodcastGroup.fromEntity));
for (var group in _groups) {
await group.getPodcasts();
}
_isLoading = false;
notifyListeners();
});
@ -263,7 +265,9 @@ class GroupList extends ChangeNotifier {
/// Update podcasts of each group
Future updateGroups() async {
for (var group in _groups) await group.getPodcasts();
for (var group in _groups) {
await group.getPodcasts();
}
notifyListeners();
}
@ -279,10 +283,11 @@ class GroupList extends ChangeNotifier {
/// Remove group.
Future delGroup(PodcastGroup podcastGroup) async {
_isLoading = true;
for (var podcast in podcastGroup.podcastList)
for (var podcast in podcastGroup.podcastList) {
if (!_groups.first.podcastList.contains(podcast)) {
_groups[0].podcastList.insert(0, podcast);
}
}
await _saveGroup();
_groups.remove(podcastGroup);
await _groups[0].getPodcasts();
@ -313,24 +318,25 @@ class GroupList extends ChangeNotifier {
}
Future updatePodcast(String id) async {
int counts = await dbHelper.getPodcastCounts(id);
for (var group in _groups)
var counts = await dbHelper.getPodcastCounts(id);
for (var group in _groups) {
if (group.podcastList.contains(id)) {
group.podcasts.firstWhere((podcast) => podcast.id == id)
..episodeCount = counts;
notifyListeners();
}
}
}
/// Subscribe podcast from OMPL.
Future<bool> _subscribeNewPodcast(
{String id, String groupName = 'Home'}) async {
//List<String> groupNames = _groups.map((e) => e.name).toList();
for (PodcastGroup group in _groups) {
for (var group in _groups) {
if (group.name == groupName) {
if (group.podcastList.contains(id))
if (group.podcastList.contains(id)) {
return true;
else {
} else {
_isLoading = true;
notifyListeners();
group.podcastList.insert(0, id);
@ -354,11 +360,12 @@ class GroupList extends ChangeNotifier {
}
List<PodcastGroup> getPodcastGroup(String id) {
List<PodcastGroup> result = [];
for (var group in _groups)
var result = <PodcastGroup>[];
for (var group in _groups) {
if (group.podcastList.contains(id)) {
result.add(group);
}
}
return result;
}
@ -374,9 +381,13 @@ class GroupList extends ChangeNotifier {
group.podcastList.remove(id);
}
}
for (var s in list) s.podcastList.insert(0, id);
for (var s in list) {
s.podcastList.insert(0, id);
}
await _saveGroup();
for (var group in _groups) await group.getPodcasts();
for (var group in _groups) {
await group.getPodcasts();
}
_isLoading = false;
notifyListeners();
}
@ -385,10 +396,14 @@ class GroupList extends ChangeNotifier {
removePodcast(String id) async {
_isLoading = true;
notifyListeners();
for (var group in _groups) group.podcastList.remove(id);
for (var group in _groups) {
group.podcastList.remove(id);
}
await _saveGroup();
await dbHelper.delPodcastLocal(id);
for (var group in _groups) await group.getPodcasts();
for (var group in _groups) {
await group.getPodcasts();
}
_isLoading = false;
notifyListeners();
}
@ -402,37 +417,37 @@ class GroupList extends ChangeNotifier {
}
Future<void> subIsolateEntryPoint(SendPort sendPort) async {
List<SubscribeItem> items = [];
bool _running = false;
final List<String> listColor = [
var items = <SubscribeItem>[];
var _running = false;
final listColor = <String>[
'388E3C',
'1976D2',
'D32F2F',
'00796B',
];
ReceivePort subReceivePort = ReceivePort();
var subReceivePort = ReceivePort();
sendPort.send(subReceivePort.sendPort);
Future<String> _getColor(File file) async {
final imageProvider = FileImage(file);
var colorImage = await getImageFromProvider(imageProvider);
var color = await getColorFromImage(colorImage);
String primaryColor = color.toString();
var primaryColor = color.toString();
return primaryColor;
}
Future<void> _subscribe(SubscribeItem item) async {
var dbHelper = DBHelper();
String rss = item.url;
var rss = item.url;
sendPort.send([item.title, item.url, 1]);
BaseOptions options = BaseOptions(
var options = BaseOptions(
connectTimeout: 20000,
receiveTimeout: 20000,
);
print(rss);
try {
Response response = await Dio(options).get(rss);
var response = await Dio(options).get(rss);
RssFeed p;
try {
p = RssFeed.parse(response.data);
@ -444,47 +459,46 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
items.removeWhere((element) => element.url == item.url);
if (items.isNotEmpty) {
await _subscribe(items.first);
} else
} else {
sendPort.send("done");
}
}
var dir = await getApplicationDocumentsDirectory();
String realUrl =
var realUrl =
response.redirects.isEmpty ? rss : response.realUri.toString();
String checkUrl = await dbHelper.checkPodcast(realUrl);
var checkUrl = await dbHelper.checkPodcast(realUrl);
/// If url not existe in database.
if (checkUrl == '') {
img.Image thumbnail;
String imageUrl;
try {
Response<List<int>> imageResponse =
await Dio().get<List<int>>(p.itunes.image.href,
options: Options(
responseType: ResponseType.bytes,
receiveTimeout: 90000,
));
var imageResponse = await Dio().get<List<int>>(p.itunes.image.href,
options: Options(
responseType: ResponseType.bytes,
receiveTimeout: 90000,
));
imageUrl = p.itunes.image.href;
img.Image image = img.decodeImage(imageResponse.data);
var image = img.decodeImage(imageResponse.data);
thumbnail = img.copyResize(image, width: 300);
} catch (e) {
try {
Response<List<int>> imageResponse =
await Dio().get<List<int>>(item.imgUrl,
options: Options(
responseType: ResponseType.bytes,
receiveTimeout: 90000,
));
var imageResponse = await Dio().get<List<int>>(item.imgUrl,
options: Options(
responseType: ResponseType.bytes,
receiveTimeout: 90000,
));
imageUrl = item.imgUrl;
img.Image image = img.decodeImage(imageResponse.data);
var image = img.decodeImage(imageResponse.data);
thumbnail = img.copyResize(image, width: 300);
} catch (e) {
print(e);
try {
int index = math.Random().nextInt(3);
Response<List<int>> imageResponse = await Dio().get<List<int>>(
var index = math.Random().nextInt(3);
var imageResponse = await Dio().get<List<int>>(
"https://ui-avatars.com/api/?size=300&background="
"${listColor[index]}&color=fff&name=${item.title}&length=2&bold=true",
options: Options(responseType: ResponseType.bytes));
@ -499,28 +513,29 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
items.removeWhere((element) => element.url == item.url);
if (items.length > 0) {
await _subscribe(items.first);
} else
} else {
sendPort.send("done");
}
}
}
}
String uuid = Uuid().v4();
var uuid = Uuid().v4();
File("${dir.path}/$uuid.png")
..writeAsBytesSync(img.encodePng(thumbnail));
String imagePath = "${dir.path}/$uuid.png";
String primaryColor = await _getColor(File("${dir.path}/$uuid.png"));
String author = p.itunes.author ?? p.author ?? '';
String provider = p.generator ?? '';
String link = p.link ?? '';
PodcastLocal podcastLocal = PodcastLocal(p.title, imageUrl, realUrl,
var imagePath = "${dir.path}/$uuid.png";
var primaryColor = await _getColor(File("${dir.path}/$uuid.png"));
var author = p.itunes.author ?? p.author ?? '';
var provider = p.generator ?? '';
var link = p.link ?? '';
var podcastLocal = PodcastLocal(p.title, imageUrl, realUrl,
primaryColor, author, uuid, imagePath, provider, link,
description: p.description);
await dbHelper.savePodcastLocal(podcastLocal);
sendPort.send([item.title, item.url, 2, uuid, item.group]);
if (provider.contains('fireside')) {
FiresideData data = FiresideData(uuid, link);
var data = FiresideData(uuid, link);
try {
await data.fatchData();
} catch (e) {
@ -537,8 +552,9 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
items.removeAt(0);
if (items.length > 0) {
await _subscribe(items.first);
} else
} else {
sendPort.send("done");
}
} else {
sendPort.send([item.title, realUrl, 5, checkUrl, item.group]);
await Future.delayed(Duration(seconds: 2));
@ -546,8 +562,9 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
items.removeAt(0);
if (items.length > 0) {
await _subscribe(items.first);
} else
} else {
sendPort.send("done");
}
}
} catch (e) {
print(e);
@ -557,8 +574,9 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
items.removeWhere((element) => element.url == item.url);
if (items.length > 0) {
await _subscribe(items.first);
} else
} else {
sendPort.send("done");
}
}
}

View File

@ -5,7 +5,6 @@ import 'package:flutter_isolate/flutter_isolate.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../type/podcastlocal.dart';
enum RefreshState { none, fetch, error, artwork }
@ -68,14 +67,14 @@ class RefreshWorker extends ChangeNotifier {
}
Future<void> refreshIsolateEntryPoint(SendPort sendPort) async {
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
var refreshstorage = KeyValueStorage(refreshdateKey);
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
var dbHelper = DBHelper();
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
var podcastList = await dbHelper.getPodcastLocalAll();
for (var podcastLocal in podcastList) {
sendPort.send([podcastLocal.title, 1]);
int updateCount = await dbHelper.updatePodcastRss(podcastLocal);
print('Refresh ' + podcastLocal.title + updateCount.toString());
var updateCount = await dbHelper.updatePodcastRss(podcastLocal);
print('Refresh ${podcastLocal.title}$updateCount');
}
sendPort.send("done");
}

View File

@ -1,47 +1,45 @@
import 'dart:io';
import 'dart:ui';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:workmanager/workmanager.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../type/podcastlocal.dart';
import '../type/episodebrief.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../type/settings_backup.dart';
import 'download_state.dart';
void callbackDispatcher() {
if (Platform.isAndroid)
if (Platform.isAndroid) {
Workmanager.executeTask((task, inputData) async {
var dbHelper = DBHelper();
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
var podcastList = await dbHelper.getPodcastLocalAll();
//lastWork is a indicator for if the app was opened since last backgroundwork
//if the app wes opend,then the old marked new episode would be marked not new.
KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey);
int lastWork = await lastWorkStorage.getInt();
for (PodcastLocal podcastLocal in podcastList) {
var lastWorkStorage = KeyValueStorage(lastWorkKey);
var lastWork = await lastWorkStorage.getInt();
for (var podcastLocal in podcastList) {
await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
print('Refresh ' + podcastLocal.title);
print('Refresh ${podcastLocal.title}');
}
await FlutterDownloader.initialize();
AutoDownloader downloader = AutoDownloader();
var downloader = AutoDownloader();
KeyValueStorage autoDownloadStorage =
KeyValueStorage(autoDownloadNetworkKey);
int autoDownloadNetwork = await autoDownloadStorage.getInt();
var autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
var autoDownloadNetwork = await autoDownloadStorage.getInt();
var result = await Connectivity().checkConnectivity();
if (autoDownloadNetwork == 1) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
var episodes = await dbHelper.getNewEpisodes('all');
// For safety
if (episodes.length < 100 && episodes.length > 0) {
downloader.bindBackgroundIsolate();
await downloader.startTask(episodes);
}
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
var episodes = await dbHelper.getNewEpisodes('all');
//For safety
if (episodes.length < 100 && episodes.length > 0) {
downloader.bindBackgroundIsolate();
@ -49,10 +47,11 @@ void callbackDispatcher() {
}
}
await lastWorkStorage.saveInt(1);
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
var refreshstorage = KeyValueStorage(refreshdateKey);
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
return Future.value(true);
});
}
}
ThemeData lightTheme = ThemeData(
@ -259,12 +258,9 @@ class SettingState extends ChangeNotifier {
_getSleepTimerData();
_getPlayerSeconds();
_getUpdateInterval().then((value) async {
if (_initUpdateTag == 0)
if (_initUpdateTag == 0) {
setWorkManager(24);
/// Restart worker if anythin changed in worker callback.
/// varsion 2 add auto download new episodes
else if (_autoUpdate && _initialShowIntor == 1) {
} else if (_autoUpdate && _initialShowIntor == 1) {
await cancelWork();
setWorkManager(_initUpdateTag);
await saveShowIntro(2);
@ -273,14 +269,14 @@ class SettingState extends ChangeNotifier {
}
Future _getTheme() async {
int mode = await themeStorage.getInt();
var mode = await themeStorage.getInt();
_theme = ThemeMode.values[mode];
}
Future _getAccentSetColor() async {
String colorString = await accentStorage.getString();
var colorString = await accentStorage.getString();
if (colorString.isNotEmpty) {
int color = int.parse('FF' + colorString.toUpperCase(), radix: 16);
var color = int.parse('FF${colorString.toUpperCase()}', radix: 16);
_accentSetColor = Color(color).withOpacity(1.0);
} else {
_accentSetColor = Colors.teal[500];
@ -393,37 +389,37 @@ class SettingState extends ChangeNotifier {
}
Future<SettingsBackup> backup() async {
int theme = await themeStorage.getInt();
String accentColor = await accentStorage.getString();
bool realDark = await realDarkStorage.getBool(defaultValue: false);
bool autoPlay =
var theme = await themeStorage.getInt();
var accentColor = await accentStorage.getString();
var realDark = await realDarkStorage.getBool(defaultValue: false);
var autoPlay =
await autoPlayStorage.getBool(defaultValue: true, reverse: true);
bool autoUpdate =
var autoUpdate =
await autoupdateStorage.getBool(defaultValue: true, reverse: true);
int updateInterval = await intervalStorage.getInt();
bool downloadUsingData = await downloadUsingDataStorage.getBool(
var updateInterval = await intervalStorage.getInt();
var downloadUsingData = await downloadUsingDataStorage.getBool(
defaultValue: true, reverse: true);
int cacheMax = await cacheStorage.getInt(defaultValue: 500 * 1024 * 1024);
int podcastLayout = await podcastLayoutStorage.getInt();
int recentLayout = await recentLayoutStorage.getInt();
int favLayout = await favLayoutStorage.getInt();
int downloadLayout = await downloadLayoutStorage.getInt();
bool autoDownloadNetwork =
var cacheMax = await cacheStorage.getInt(defaultValue: 500 * 1024 * 1024);
var podcastLayout = await podcastLayoutStorage.getInt();
var recentLayout = await recentLayoutStorage.getInt();
var favLayout = await favLayoutStorage.getInt();
var downloadLayout = await downloadLayoutStorage.getInt();
var autoDownloadNetwork =
await autoDownloadStorage.getBool(defaultValue: false);
List<String> episodePopupMenu =
var episodePopupMenu =
await KeyValueStorage(episodePopupMenuKey).getStringList();
int autoDelete = await autoDeleteStorage.getInt();
bool autoSleepTimer =
var autoDelete = await autoDeleteStorage.getInt();
var autoSleepTimer =
await autoSleepTimerStorage.getBool(defaultValue: false);
int autoSleepTimerStart = await autoSleepTimerStartStorage.getInt();
int autoSleepTimerEnd = await autoSleepTimerEndStorage.getInt();
int autoSleepTimerMode = await autoSleepTimerModeStorage.getInt();
int defaultSleepTime = await defaultSleepTimerStorage.getInt();
bool tapToOpenPopupMenu = await KeyValueStorage(tapToOpenPopupMenuKey)
var autoSleepTimerStart = await autoSleepTimerStartStorage.getInt();
var autoSleepTimerEnd = await autoSleepTimerEndStorage.getInt();
var autoSleepTimerMode = await autoSleepTimerModeStorage.getInt();
var defaultSleepTime = await defaultSleepTimerStorage.getInt();
var tapToOpenPopupMenu = await KeyValueStorage(tapToOpenPopupMenuKey)
.getBool(defaultValue: false);
int fastForwardSeconds =
var fastForwardSeconds =
await fastForwardSecondsStorage.getInt(defaultValue: 30);
int rewindSeconds = await rewindSecondsStorage.getInt(defaultValue: 10);
var rewindSeconds = await rewindSecondsStorage.getInt(defaultValue: 10);
return SettingsBackup(
theme: theme,

View File

@ -17,29 +17,29 @@ class FiresideData {
DBHelper dbHelper = DBHelper();
String parseLink(String link) {
if (link == "http://www.shengfm.cn/")
if (link == "http://www.shengfm.cn/") {
return "https://guiguzaozhidao.fireside.fm/";
else
} else {
return link;
}
}
Future fatchData() async {
Response response = await Dio().get(parseLink(link));
var response = await Dio().get(parseLink(link));
if (response.statusCode == 200) {
var doc = parse(response.data);
RegExp reg = RegExp(r'https(.+)jpg');
String backgroundImage = reg.stringMatch(doc.body
var reg = RegExp(r'https(.+)jpg');
var backgroundImage = reg.stringMatch(doc.body
.getElementsByClassName('hero-background')
.first
.attributes
.toString());
var ul = doc.body.getElementsByClassName('episode-hosts').first.children;
List<PodcastHost> hosts = [];
var hosts = <PodcastHost>[];
for (var element in ul) {
PodcastHost host;
String name = element.text.trim();
String image =
element.children.first.children.first.attributes.toString();
var name = element.text.trim();
var image = element.children.first.children.first.attributes.toString();
print(reg.stringMatch(image));
host = PodcastHost(
@ -49,7 +49,7 @@ class FiresideData {
hosts.add(host);
}
List<String> data = [
var data = <String>[
id,
backgroundImage,
json.encode({'hosts': hosts.map((host) => host.toJson()).toList()})
@ -59,7 +59,7 @@ class FiresideData {
}
Future getData() async {
List<String> data = await dbHelper.getFiresideData(id);
var data = await dbHelper.getFiresideData(id);
_background = data[0];
if (data[1] != '') {
_hosts = json
@ -67,8 +67,9 @@ class FiresideData {
.cast<Map<String, Object>>()
.map<PodcastHost>(PodcastHost.fromJson)
.toList();
} else
} else {
_hosts = null;
}
}
}

View File

@ -3,13 +3,25 @@ import 'episodebrief.dart';
class PlayHistory {
DBHelper dbHelper = DBHelper();
/// Episdoe title.
String title;
/// Episode url
String url;
double seconds;
/// Play record seconds.
int seconds;
/// Play record count,
double seekValue;
/// Listened date.
DateTime playdate;
PlayHistory(this.title, this.url, this.seconds, this.seekValue,
{this.playdate});
EpisodeBrief _episode;
EpisodeBrief get episode => _episode;

View File

@ -1,5 +1,5 @@
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import 'episodebrief.dart';
class Playlist {
@ -12,21 +12,21 @@ class Playlist {
KeyValueStorage storage = KeyValueStorage('playlist');
getPlaylist() async {
List<String> urls = await storage.getStringList();
var urls = await storage.getStringList();
if (urls.length == 0) {
_playlist = [];
} else {
_playlist = [];
for (String url in urls) {
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(url);
for (var url in urls) {
var episode = await dbHelper.getRssItemWithUrl(url);
if (episode != null) _playlist.add(episode);
}
}
}
savePlaylist() async {
List<String> urls = [];
var urls = <String>[];
urls.addAll(_playlist.map((e) => e.enclosureUrl));
await storage.saveStringList(urls.toSet().toList());
}
@ -48,10 +48,10 @@ class Playlist {
}
Future<int> delFromPlaylist(EpisodeBrief episodeBrief) async {
int index = _playlist.indexOf(episodeBrief);
var index = _playlist.indexOf(episodeBrief);
_playlist.removeWhere(
(episode) => episode.enclosureUrl == episodeBrief.enclosureUrl);
print('delete' + episodeBrief.title);
print('delete${episodeBrief.title}');
await savePlaylist();
return index;
}

View File

@ -69,7 +69,9 @@ class OnlinePodcast {
int get hashCode => hashValues(id, title);
int get interval {
if (count < 1) return null;
if (count < 1) {
return null;
}
return (latestPubDate - earliestPubDate) ~/ count;
}
}

View File

@ -75,7 +75,7 @@ class SettingsBackup {
}
static SettingsBackup fromJson(Map<String, Object> json) {
List<String> list = List.from(json['episodePopupMenu']);
var list = List<String>.from(json['episodePopupMenu']);
return SettingsBackup(
theme: json['theme'] as int,
accentColor: json['accentColor'] as String,

View File

@ -41,7 +41,8 @@ class _AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
..addListener(() {
if (mounted) setState(() {});
});
_animation = Tween<double>(begin: 0, end: initSize).animate(_controller);
_animation =
Tween<double>(begin: 0, end: initSize).animate(_slowController);
_controller.forward();
super.initState();
}
@ -60,7 +61,7 @@ class _AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
child: (_animation.value > widget.minHeight + 30)
? Positioned.fill(
child: GestureDetector(
onTap: () => _backToMini(),
onTap: _backToMini,
child: Container(
color: Theme.of(context)
.scaffoldBackgroundColor
@ -73,8 +74,8 @@ class _AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
Container(
alignment: Alignment.bottomCenter,
child: GestureDetector(
onVerticalDragStart: (event) => _start(event),
onVerticalDragUpdate: (event) => _update(event),
onVerticalDragStart: _start,
onVerticalDragUpdate: _update,
onVerticalDragEnd: (event) => _end(),
child: Container(
height: (_animation.value >= widget.maxHeight)

View File

@ -51,20 +51,20 @@ class _DropdownMenuPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final double selectedItemOffset = getSelectedItemOffset();
final Tween<double> top = Tween<double>(
final selectedItemOffset = getSelectedItemOffset();
final top = Tween<double>(
begin: selectedItemOffset.clamp(0.0, size.height - _kMenuItemHeight)
as double,
end: 0.0,
);
final Tween<double> bottom = Tween<double>(
final bottom = Tween<double>(
begin: (top.begin + _kMenuItemHeight).clamp(_kMenuItemHeight, size.height)
as double,
end: size.height,
);
final Rect rect = Rect.fromLTRB(
final rect = Rect.fromLTRB(
0.0, top.evaluate(resize), size.width, bottom.evaluate(resize));
_painter.paint(canvas, rect.topLeft, ImageConfiguration(size: rect.size));
@ -134,7 +134,7 @@ class _DropdownMenuItemButtonState<T>
}
if (focused && inTraditionalMode) {
final _MenuLimits menuLimits = widget.route.getMenuLimits(
final menuLimits = widget.route.getMenuLimits(
widget.buttonRect, widget.constraints.maxHeight, widget.itemIndex);
widget.route.scrollController.animateTo(
menuLimits.scrollOffset,
@ -145,8 +145,7 @@ class _DropdownMenuItemButtonState<T>
}
void _handleOnTap() {
final DropdownMenuItem<T> dropdownMenuItem =
widget.route.items[widget.itemIndex].item;
final dropdownMenuItem = widget.route.items[widget.itemIndex].item;
if (dropdownMenuItem.onTap != null) {
dropdownMenuItem.onTap();
@ -161,14 +160,14 @@ class _DropdownMenuItemButtonState<T>
@override
Widget build(BuildContext context) {
CurvedAnimation opacity;
final double unit = 0.5 / (widget.route.items.length + 1.5);
final unit = 0.5 / (widget.route.items.length + 1.5);
if (widget.itemIndex == widget.route.selectedIndex) {
opacity = CurvedAnimation(
parent: widget.route.animation, curve: const Threshold(0.0));
} else {
final double start =
final start =
(0.5 + (widget.itemIndex + 1) * unit).clamp(0.0, 1.0) as double;
final double end = (start + 1.5 * unit).clamp(0.0, 1.0) as double;
final end = (start + 1.5 * unit).clamp(0.0, 1.0) as double;
opacity = CurvedAnimation(
parent: widget.route.animation, curve: Interval(start, end));
}
@ -244,10 +243,9 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
// When the menu is dismissed we just fade the entire thing out
// in the first 0.25s.
assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final _DropdownRoute<T> route = widget.route;
final List<Widget> children = <Widget>[
final localizations = MaterialLocalizations.of(context);
final route = widget.route;
final children = <Widget>[
for (int itemIndex = 0; itemIndex < route.items.length; ++itemIndex)
_DropdownMenuItemButton<T>(
route: widget.route,
@ -315,13 +313,13 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {
// the view height. This ensures a tappable area outside of the simple menu
// with which to dismiss the menu.
// -- https://material.io/design/components/menus.html#usage
final double maxHeight = displayItemCount == null
final maxHeight = displayItemCount == null
? math.max(0.0, constraints.maxHeight - 2 * _kMenuItemHeight)
: math.min(_kMenuItemHeight * displayItemCount,
constraints.maxHeight - 2 * _kMenuItemHeight);
// The width of a menu should be at most the view width. This ensures that
// the menu does not extend past the left and right edges of the screen.
final double width = math.min(constraints.maxWidth, buttonRect.width);
final width = math.min(constraints.maxWidth, buttonRect.width);
return BoxConstraints(
minWidth: width,
maxWidth: width,
@ -332,11 +330,11 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {
@override
Offset getPositionForChild(Size size, Size childSize) {
final _MenuLimits menuLimits =
final menuLimits =
route.getMenuLimits(buttonRect, size.height, route.selectedIndex);
assert(() {
final Rect container = Offset.zero & size;
final container = Offset.zero & size;
if (container.intersect(buttonRect) == buttonRect) {
// If the button was entirely on-screen, then verify
// that the menu is also on-screen.
@ -440,8 +438,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return LayoutBuilder(builder: (context, constraints) {
return _DropdownRoutePage<T>(
route: this,
constraints: constraints,
@ -463,12 +460,12 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
}
double getItemOffset(int index) {
double offset = kMaterialListPadding.top;
var offset = kMaterialListPadding.top;
if (items.isNotEmpty && index > 0) {
assert(items.length == itemHeights?.length);
offset += itemHeights
.sublist(0, index)
.reduce((double total, double height) => total + height);
.reduce((total, height) => total + height);
}
return offset;
}
@ -479,30 +476,31 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
// that's possible given availableHeight.
_MenuLimits getMenuLimits(
Rect buttonRect, double availableHeight, int index) {
final double maxMenuHeight = availableHeight - 2.0 * _kMenuItemHeight;
final double buttonTop = buttonRect.top;
final double buttonBottom = math.min(buttonRect.bottom, availableHeight);
final double selectedItemOffset = getItemOffset(index);
final maxMenuHeight = availableHeight - 2.0 * _kMenuItemHeight;
final buttonTop = buttonRect.top;
final buttonBottom = math.min(buttonRect.bottom, availableHeight);
final selectedItemOffset = getItemOffset(index);
// If the button is placed on the bottom or top of the screen, its top or
// bottom may be less than [_kMenuItemHeight] from the edge of the screen.
// In this case, we want to change the menu limits to align with the top
// or bottom edge of the button.
final double topLimit = math.min(_kMenuItemHeight, buttonTop);
final double bottomLimit =
final topLimit = math.min(_kMenuItemHeight, buttonTop);
final bottomLimit =
math.max(availableHeight - _kMenuItemHeight, buttonBottom);
double menuTop = (buttonTop - selectedItemOffset) -
var menuTop = (buttonTop - selectedItemOffset) -
(itemHeights[selectedIndex] - buttonRect.height) / 2.0;
double preferredMenuHeight = kMaterialListPadding.vertical;
if (items.isNotEmpty)
var preferredMenuHeight = kMaterialListPadding.vertical;
if (items.isNotEmpty) {
preferredMenuHeight +=
itemHeights.reduce((double total, double height) => total + height);
itemHeights.reduce((total, height) => total + height);
}
// If there are too many elements in the menu, we need to shrink it down
// so it is at most the maxMenuHeight.
final double menuHeight = math.min(maxMenuHeight, preferredMenuHeight);
double menuBottom = menuTop + menuHeight;
final menuHeight = math.min(maxMenuHeight, preferredMenuHeight);
var menuBottom = menuTop + menuHeight;
// If the computed top or bottom of the menu are outside of the range
// specified, we need to bring them into range. If the item height is larger
@ -522,7 +520,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
// shown - subsequently we leave the scroll offset where the user left
// it. This scroll offset is only accurate for fixed height menu items
// (the default).
final double scrollOffset = preferredMenuHeight <= maxMenuHeight
final scrollOffset = preferredMenuHeight <= maxMenuHeight
? 0
: math.max(0.0, selectedItemOffset - (buttonTop - menuTop));
@ -569,13 +567,13 @@ class _DropdownRoutePage<T> extends StatelessWidget {
// Otherwise the initialScrollOffset is just a rough approximation based on
// treating the items as if their heights were all equal to kMinInteractveDimension.
if (route.scrollController == null) {
final _MenuLimits menuLimits =
final menuLimits =
route.getMenuLimits(buttonRect, constraints.maxHeight, selectedIndex);
route.scrollController =
ScrollController(initialScrollOffset: menuLimits.scrollOffset);
}
final TextDirection textDirection = Directionality.of(context);
final textDirection = Directionality.of(context);
Widget menu = _DropdownMenu<T>(
route: route,
padding: padding.resolve(textDirection),
@ -593,7 +591,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
removeLeft: true,
removeRight: true,
child: Builder(
builder: (BuildContext context) {
builder: (context) {
return CustomSingleChildLayout(
delegate: _DropdownMenuRouteLayout<T>(
buttonRect: buttonRect,
@ -850,7 +848,7 @@ class MyDropdownButton<T> extends StatefulWidget {
items == null ||
items.isEmpty ||
value == null ||
items.where((DropdownMenuItem<T> item) {
items.where((item) {
return item.value == value;
}).length ==
1,
@ -1128,7 +1126,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
// ActivateAction.key: _createAction,
// };
focusNode.addListener(_handleFocusChanged);
final FocusManager focusManager = WidgetsBinding.instance.focusManager;
final focusManager = WidgetsBinding.instance.focusManager;
_focusHighlightMode = focusManager.highlightMode;
focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
}
@ -1187,12 +1185,9 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
}
assert(widget.value == null ||
widget.items
.where((DropdownMenuItem<T> item) => item.value == widget.value)
.length ==
1);
widget.items.where((item) => item.value == widget.value).length == 1);
_selectedIndex = null;
for (int itemIndex = 0; itemIndex < widget.items.length; itemIndex++) {
for (var itemIndex = 0; itemIndex < widget.items.length; itemIndex++) {
if (widget.items[itemIndex].value == widget.value) {
_selectedIndex = itemIndex;
return;
@ -1204,20 +1199,18 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
widget.style ?? Theme.of(context).textTheme.subtitle1;
void _handleTap() {
final RenderBox itemBox = context.findRenderObject() as RenderBox;
final Rect itemRect = itemBox.localToGlobal(Offset.zero) & itemBox.size;
final TextDirection textDirection = Directionality.of(context);
final EdgeInsetsGeometry menuMargin =
ButtonTheme.of(context).alignedDropdown
? _kAlignedMenuMargin
: _kUnalignedMenuMargin;
final itemBox = context.findRenderObject() as RenderBox;
final itemRect = itemBox.localToGlobal(Offset.zero) & itemBox.size;
final textDirection = Directionality.of(context);
final menuMargin = ButtonTheme.of(context).alignedDropdown
? _kAlignedMenuMargin
: _kUnalignedMenuMargin;
final List<_MenuItem<T>> menuItems =
List<_MenuItem<T>>(widget.items.length);
for (int index = 0; index < widget.items.length; index += 1) {
final menuItems = List<_MenuItem<T>>(widget.items.length);
for (var index = 0; index < widget.items.length; index += 1) {
menuItems[index] = _MenuItem<T>(
item: widget.items[index],
onLayout: (Size size) {
onLayout: (size) {
// If [_dropdownRoute] is null and onLayout is called, this means
// that performLayout was called on a _DropdownRoute that has not
// left the widget tree but is already on its way out.
@ -1248,8 +1241,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
displayItemCount: widget.displayItemCount,
);
Navigator.push(context, _dropdownRoute)
.then<void>((_DropdownRouteResult<T> newValue) {
Navigator.push(context, _dropdownRoute).then<void>((newValue) {
_removeDropdownRoute();
if (!mounted || newValue == null) return;
if (widget.onChanged != null) widget.onChanged(newValue.result);
@ -1274,7 +1266,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
// Similarly, we don't reduce the height of the button so much that its icon
// would be clipped.
double get _denseButtonHeight {
final double fontSize =
final fontSize =
_textStyle.fontSize ?? Theme.of(context).textTheme.subtitle1.fontSize;
return math.max(fontSize, math.max(widget.iconSize, _kDenseButtonHeight));
}
@ -1311,11 +1303,11 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
widget.onChanged != null;
Orientation _getOrientation(BuildContext context) {
Orientation result = MediaQuery.of(context, nullOk: true)?.orientation;
var result = MediaQuery.of(context, nullOk: true)?.orientation;
if (result == null) {
// If there's no MediaQuery, then use the window aspect to determine
// orientation.
final Size size = window.physicalSize;
final size = window.physicalSize;
result = size.width > size.height
? Orientation.landscape
: Orientation.portrait;
@ -1337,7 +1329,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
final Orientation newOrientation = _getOrientation(context);
final newOrientation = _getOrientation(context);
_lastOrientation ??= newOrientation;
if (newOrientation != _lastOrientation) {
_removeDropdownRoute();
@ -1359,10 +1351,11 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
int hintIndex;
if (widget.hint != null || (!_enabled && widget.disabledHint != null)) {
Widget displayedHint =
var displayedHint =
_enabled ? widget.hint : widget.disabledHint ?? widget.hint;
if (widget.selectedItemBuilder == null)
if (widget.selectedItemBuilder == null) {
displayedHint = _DropdownMenuItemContainer(child: displayedHint);
}
hintIndex = items.length;
items.add(DefaultTextStyle(
@ -1374,13 +1367,13 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
));
}
final EdgeInsetsGeometry padding = ButtonTheme.of(context).alignedDropdown
final padding = ButtonTheme.of(context).alignedDropdown
? _kAlignedButtonPadding
: _kUnalignedButtonPadding;
// If value is null (then _selectedIndex is null) or if disabled then we
// display the hint or nothing at all.
final int index = _enabled ? (_selectedIndex ?? hintIndex) : hintIndex;
final index = _enabled ? (_selectedIndex ?? hintIndex) : hintIndex;
Widget innerItemsWidget;
if (items.isEmpty) {
innerItemsWidget = Container();
@ -1390,7 +1383,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
alignment: AlignmentDirectional.centerStart,
children: widget.isDense
? items
: items.map((Widget item) {
: items.map((item) {
return widget.itemHeight != null
? SizedBox(height: widget.itemHeight, child: item)
: Column(
@ -1400,7 +1393,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
);
}
const Icon defaultIcon = Icon(Icons.arrow_drop_down);
const defaultIcon = Icon(Icons.arrow_drop_down);
Widget result = DefaultTextStyle(
style: _textStyle,
@ -1434,8 +1427,7 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
);
if (!DropdownButtonHideUnderline.at(context)) {
final double bottom =
(widget.isDense || widget.itemHeight == null) ? 0.0 : 8.0;
final bottom = (widget.isDense || widget.itemHeight == null) ? 0.0 : 8.0;
result = Stack(
children: <Widget>[
result,
@ -1508,7 +1500,7 @@ class DropdownButtonFormField<T> extends FormField<T> {
items == null ||
items.isEmpty ||
value == null ||
items.where((DropdownMenuItem<T> item) {
items.where((item) {
return item.value == value;
}).length ==
1,
@ -1529,11 +1521,9 @@ class DropdownButtonFormField<T> extends FormField<T> {
initialValue: value,
validator: validator,
autovalidate: autovalidate,
builder: (FormFieldState<T> field) {
final _DropdownButtonFormFieldState<T> state =
field as _DropdownButtonFormFieldState<T>;
final InputDecoration effectiveDecoration =
decoration.applyDefaults(
builder: (field) {
final state = field as _DropdownButtonFormFieldState<T>;
final effectiveDecoration = decoration.applyDefaults(
Theme.of(field.context).inputDecorationTheme,
);
return InputDecorator(

View File

@ -8,11 +8,10 @@ class MyRectangularTrackShape extends RectangularSliderTrackShape {
bool isEnabled = false,
bool isDiscrete = false,
}) {
final double trackHeight = sliderTheme.trackHeight;
final double trackLeft = offset.dx;
final double trackTop =
offset.dy + (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width;
final trackHeight = sliderTheme.trackHeight;
final trackLeft = offset.dx;
final trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final trackWidth = parentBox.size.width;
return Rect.fromLTWH(trackLeft - 5, trackTop, trackWidth, trackHeight);
}
}
@ -49,8 +48,8 @@ class MyRoundSliderThumpShape extends SliderComponentShape {
double textScaleFactor,
Size sizeWithOverflow,
}) {
final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>(
final canvas = context.canvas;
final radiusTween = Tween<double>(
begin: _disabledThumbRadius,
end: enabledThumbRadius,
);

View File

@ -1,8 +1,10 @@
import 'dart:io';
import 'dart:ui' as ui;
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'episodegrid.dart';
import 'extension_helper.dart';
//Layout change indicator
@ -12,7 +14,7 @@ class LayoutPainter extends CustomPainter {
LayoutPainter(this.scale, this.color);
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
var _paint = Paint()
..color = color
..strokeWidth = 1.0
..style = PaintingStyle.stroke
@ -76,7 +78,7 @@ class StarSky extends CustomPainter {
Offset(10, 26)
].map((e) => Offset(e.dx * 10 + 250, e.dy * 10)).toList();
Paint paint = Paint()
var paint = Paint()
..color = Colors.white
..strokeWidth = 2.0
..strokeCap = StrokeCap.round;
@ -93,17 +95,17 @@ class StarSky extends CustomPainter {
//Listened indicator
class ListenedPainter extends CustomPainter {
Color _color;
final Color _color;
double stroke;
ListenedPainter(this._color, {this.stroke = 1.0});
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
var _paint = Paint()
..color = _color
..strokeWidth = stroke
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Path _path = Path();
var _path = Path();
_path.moveTo(size.width / 6, size.height * 3 / 8);
_path.lineTo(size.width / 6, size.height * 5 / 8);
_path.moveTo(size.width / 3, size.height / 4);
@ -126,17 +128,17 @@ class ListenedPainter extends CustomPainter {
//Listened Completely indicator
class ListenedAllPainter extends CustomPainter {
Color _color;
double stroke;
ListenedAllPainter(this._color, {this.stroke = 1.0});
final Color color;
final double stroke;
ListenedAllPainter(this.color, {this.stroke = 1.0});
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
..color = _color
var _paint = Paint()
..color = color
..strokeWidth = stroke
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Path _path = Path();
var _path = Path();
_path.moveTo(size.width / 6, size.height * 3 / 8);
_path.lineTo(size.width / 6, size.height * 5 / 8);
_path.moveTo(size.width / 3, size.height / 4);
@ -160,17 +162,17 @@ class ListenedAllPainter extends CustomPainter {
//Mark Listened indicator
class MarkListenedPainter extends CustomPainter {
Color _color;
final Color color;
double stroke;
MarkListenedPainter(this._color, {this.stroke = 1.0});
MarkListenedPainter(this.color, {this.stroke = 1.0});
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
..color = _color
var _paint = Paint()
..color = color
..strokeWidth = stroke
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Path _path = Path();
var _path = Path();
_path.moveTo(size.width / 6, size.height * 3 / 8);
_path.lineTo(size.width / 6, size.height * 5 / 8);
_path.moveTo(size.width / 3, size.height / 4);
@ -203,17 +205,17 @@ class HideListenedPainter extends CustomPainter {
{this.color, this.stroke = 1.0, this.backgroundColor, this.fraction});
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
var _paint = Paint()
..color = color
..strokeWidth = stroke
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Paint _linePaint = Paint()
var _linePaint = Paint()
..color = backgroundColor
..strokeWidth = stroke * 2
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Path _path = Path();
var _path = Path();
_path.moveTo(size.width / 6, size.height * 3 / 8);
_path.lineTo(size.width / 6, size.height * 5 / 8);
@ -227,12 +229,13 @@ class HideListenedPainter extends CustomPainter {
_path.lineTo(size.width * 2 / 3, size.height * 3 / 4);
canvas.drawPath(_path, _paint);
if (fraction > 0)
if (fraction > 0) {
canvas.drawLine(
Offset(size.width, size.height) / 5,
Offset(size.width, size.height) / 5 +
Offset(size.width, size.height) * 3 / 5 * fraction,
_linePaint);
}
}
@override
@ -260,10 +263,11 @@ class _HideListenedState extends State<HideListened>
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_fraction = animation.value;
});
}
});
if (widget.hideListened) _controller.forward();
}
@ -271,10 +275,11 @@ class _HideListenedState extends State<HideListened>
@override
void didUpdateWidget(HideListened oldWidget) {
if (oldWidget.hideListened != widget.hideListened) {
if (widget.hideListened)
if (widget.hideListened) {
_controller.forward();
else
} else {
_controller.reverse();
}
}
super.didUpdateWidget(oldWidget);
}
@ -297,17 +302,17 @@ class _HideListenedState extends State<HideListened>
//Add new episode to palylist
class AddToPlaylistPainter extends CustomPainter {
Color _color;
Color _textColor;
AddToPlaylistPainter(this._color, this._textColor);
final Color color;
final Color textColor;
AddToPlaylistPainter(this.color, this.textColor);
@override
void paint(Canvas canvas, Size size) {
Paint _paint = Paint()
..color = _color
var _paint = Paint()
..color = color
..strokeWidth = 1
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
Path _path = Path();
var _path = Path();
_path.moveTo(0, 0);
_path.lineTo(size.width * 4 / 7, 0);
_path.moveTo(0, size.height / 3);
@ -328,7 +333,7 @@ class AddToPlaylistPainter extends CustomPainter {
text: TextSpan(
text: 'N',
style: TextStyle(
fontStyle: FontStyle.italic, color: _textColor, fontSize: 10),
fontStyle: FontStyle.italic, color: textColor, fontSize: 10),
))
..layout();
textPainter.paint(canvas, Offset(size.width * 4 / 7, size.height / 3));
@ -343,9 +348,9 @@ class AddToPlaylistPainter extends CustomPainter {
//Wave play indicator
class WavePainter extends CustomPainter {
double _fraction;
final double _fraction;
double _value;
Color _color;
final Color _color;
WavePainter(this._fraction, this._color);
@override
void paint(Canvas canvas, Size size) {
@ -354,8 +359,8 @@ class WavePainter extends CustomPainter {
} else {
_value = 1 - _fraction;
}
Path _path = Path();
Paint _paint = Paint()
var _path = Path();
var _paint = Paint()
..color = _color
..strokeWidth = 2.0
..strokeCap = StrokeCap.round
@ -410,10 +415,11 @@ class _WaveLoaderState extends State<WaveLoader>
vsync: this, duration: Duration(milliseconds: 1000));
animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_fraction = animation.value;
});
}
});
_controller.forward();
_controller.addStatusListener((status) {
@ -442,8 +448,8 @@ class _WaveLoaderState extends State<WaveLoader>
class LovePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
Path _path = Path();
Paint _paint = Paint()
var _path = Path();
var _paint = Paint()
..color = Colors.red
..strokeWidth = 2.0
..strokeCap = StrokeCap.round;
@ -473,9 +479,9 @@ class LovePainter extends CustomPainter {
//Line buffer indicator
//Not used
class LinePainter extends CustomPainter {
double _fraction;
final double _fraction;
Paint _paint;
Color _maincolor;
final Color _maincolor;
LinePainter(this._fraction, this._maincolor) {
_paint = Paint()
..color = _maincolor
@ -512,10 +518,11 @@ class _LineLoaderState extends State<LineLoader>
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
animation = Tween(begin: 0.0, end: 1.0).animate(controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_fraction = animation.value;
});
}
});
controller.forward();
controller.addStatusListener((status) {
@ -563,10 +570,11 @@ class _ImageRotateState extends State<ImageRotate>
);
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_value = _animation.value;
});
}
});
_controller.forward();
_controller.addStatusListener((status) {
@ -721,10 +729,11 @@ class _HeartSetState extends State<HeartSet>
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_value = _animation.value;
});
}
});
_controller.forward();
@ -778,10 +787,11 @@ class _HeartOpenState extends State<HeartOpen>
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
..addListener(() {
if (mounted)
if (mounted) {
setState(() {
_value = _animation.value;
});
}
});
_controller.forward();
@ -799,8 +809,8 @@ class _HeartOpenState extends State<HeartOpen>
}
Widget _position(int i) {
double scale = _list[i];
double position = _list[i + 1];
var scale = _list[i];
var position = _list[i + 1];
return Positioned(
left: widget.width * position,
bottom: widget.height * _value * scale,
@ -812,9 +822,9 @@ class _HeartOpenState extends State<HeartOpen>
);
}
List<double> _list =
final List<double> _list =
List<double>.generate(20, (index) => math.Random().nextDouble());
List<int> _index = List<int>.generate(19, (index) => index);
final List<int> _index = List<int>.generate(19, (index) => index);
@override
Widget build(BuildContext context) {
return Stack(
@ -826,7 +836,7 @@ class _HeartOpenState extends State<HeartOpen>
child: Icon(Icons.favorite,
color: Colors.blue.withOpacity(0.7), size: 20 * _value),
),
..._index.map<Widget>((e) => _position(e)).toList(),
..._index.map<Widget>(_position).toList(),
],
);
}
@ -887,10 +897,6 @@ class DownloadPainter extends CustomPainter {
..color = color
..strokeWidth = 2.0
..strokeCap = StrokeCap.round;
var _linePaint = Paint()
..color = color.withAlpha(70)
..strokeWidth = 2.0
..strokeCap = StrokeCap.round;
var _circlePaint = Paint()
..color = color.withAlpha(70)
..style = PaintingStyle.stroke
@ -899,8 +905,8 @@ class DownloadPainter extends CustomPainter {
..color = progressColor
..style = PaintingStyle.stroke
..strokeWidth = 2;
double width = size.width;
double height = size.height;
var width = size.width;
var height = size.height;
var center = Offset(size.width / 2, size.height / 2);
if (pauseProgress == 0) {
canvas.drawLine(
@ -911,10 +917,10 @@ class DownloadPainter extends CustomPainter {
Offset(width / 2, height * 4 / 5), _paint);
}
if (fraction == 0)
if (fraction == 0) {
canvas.drawLine(
Offset(width / 5, height), Offset(width * 4 / 5, height), _paint);
else {
} else {
canvas.drawArc(Rect.fromCircle(center: center, radius: width / 2),
math.pi / 2, math.pi * fraction, false, _circlePaint);
canvas.drawArc(Rect.fromCircle(center: center, radius: width / 2),
@ -947,3 +953,50 @@ class DownloadPainter extends CustomPainter {
oldDelegate.pauseProgress != pauseProgress;
}
}
/// Layout icon button.
class LayoutButton extends StatelessWidget {
const LayoutButton({this.layout, this.onPressed, Key key}) : super(key: key);
final Layout layout;
final ValueChanged<Layout> onPressed;
@override
Widget build(BuildContext context) {
return IconButton(
padding: EdgeInsets.zero,
onPressed: () {
if (layout == Layout.three) {
onPressed(Layout.one);
} else if (layout == Layout.two) {
onPressed(Layout.three);
} else {
onPressed(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),
),
),
);
}
}

View File

@ -10,7 +10,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
const Duration _kDialAnimateDuration = const Duration(milliseconds: 200);
const Duration _kDialAnimateDuration = Duration(milliseconds: 200);
const double _kDurationPickerWidthPortrait = 328.0;
const double _kDurationPickerWidthLandscape = 512.0;
@ -51,50 +51,49 @@ class _DialPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
const double _epsilon = .001;
const double _sweep = _kTwoPi - _epsilon;
const double _startAngle = -math.pi / 2.0;
const _epsilon = .001;
const _sweep = _kTwoPi - _epsilon;
const _startAngle = -math.pi / 2.0;
final double radius = size.shortestSide / 2.0;
final Offset center = new Offset(size.width / 2.0, size.height / 2.0);
final Offset centerPoint = center;
final radius = size.shortestSide / 2.0;
final center = Offset(size.width / 2.0, size.height / 2.0);
final centerPoint = center;
double pctTheta = (0.25 - (theta % _kTwoPi) / _kTwoPi) % 1.0;
var pctTheta = (0.25 - (theta % _kTwoPi) / _kTwoPi) % 1.0;
// Draw the background outer ring
canvas.drawCircle(
centerPoint, radius, new Paint()..color = backgroundColor);
canvas.drawCircle(centerPoint, radius, Paint()..color = backgroundColor);
// Draw a translucent circle for every hour
for (int i = 0; i < multiplier; i = i + 1) {
for (var i = 0; i < multiplier; i = i + 1) {
canvas.drawCircle(centerPoint, radius,
new Paint()..color = accentColor.withOpacity((i == 0) ? 0.3 : 0.1));
Paint()..color = accentColor.withOpacity((i == 0) ? 0.3 : 0.1));
}
// Draw the inner background circle
canvas.drawCircle(centerPoint, radius * 0.88,
new Paint()..color = Theme.of(context).canvasColor);
Paint()..color = Theme.of(context).canvasColor);
// Get the offset point for an angle value of theta, and a distance of _radius
Offset getOffsetForTheta(double theta, double _radius) {
return center +
new Offset(_radius * math.cos(theta), -_radius * math.sin(theta));
Offset(_radius * math.cos(theta), -_radius * math.sin(theta));
}
// Draw the handle that is used to drag and to indicate the position around the circle
final Paint handlePaint = new Paint()..color = accentColor;
final Offset handlePoint = getOffsetForTheta(theta, radius - 10.0);
final handlePaint = Paint()..color = accentColor;
final handlePoint = getOffsetForTheta(theta, radius - 10.0);
canvas.drawCircle(handlePoint, 20.0, handlePaint);
// Draw the Text in the center of the circle which displays hours and mins
String minutes = (multiplier == 0) ? '' : "${multiplier}min ";
var minutes = (multiplier == 0) ? '' : "${multiplier}min ";
// int minutes = (pctTheta * 60).round();
// minutes = minutes == 60 ? 0 : minutes;
String seconds = "$secondHand";
var seconds = "$secondHand";
TextPainter textDurationValuePainter = new TextPainter(
var textDurationValuePainter = TextPainter(
textAlign: TextAlign.center,
text: new TextSpan(
text: TextSpan(
text: '$minutes$seconds',
style: Theme.of(context)
.textTheme
@ -102,28 +101,28 @@ class _DialPainter extends CustomPainter {
.copyWith(fontSize: size.shortestSide * 0.15)),
textDirection: TextDirection.ltr)
..layout();
Offset middleForValueText = new Offset(
var middleForValueText = Offset(
centerPoint.dx - (textDurationValuePainter.width / 2),
centerPoint.dy - textDurationValuePainter.height / 2);
textDurationValuePainter.paint(canvas, middleForValueText);
TextPainter textMinPainter = new TextPainter(
var textMinPainter = TextPainter(
textAlign: TextAlign.center,
text: new TextSpan(
text: TextSpan(
text: 'sec', //th: ${theta}',
style: Theme.of(context).textTheme.bodyText1),
textDirection: TextDirection.ltr)
..layout();
textMinPainter.paint(
canvas,
new Offset(
Offset(
centerPoint.dx - (textMinPainter.width / 2),
centerPoint.dy +
(textDurationValuePainter.height / 2) -
textMinPainter.height / 2));
// Draw an arc around the circle for the amount of the circle that has elapsed.
var elapsedPainter = new Paint()
var elapsedPainter = Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..color = accentColor.withOpacity(0.3)
@ -131,7 +130,7 @@ class _DialPainter extends CustomPainter {
..strokeWidth = radius * 0.12;
canvas.drawArc(
new Rect.fromCircle(
Rect.fromCircle(
center: centerPoint,
radius: radius - radius * 0.12 / 2,
),
@ -144,12 +143,11 @@ class _DialPainter extends CustomPainter {
// Paint the labels (the minute strings)
void paintLabels(List<TextPainter> labels) {
if (labels == null) return;
final double labelThetaIncrement = -_kTwoPi / labels.length;
double labelTheta = _kPiByTwo;
final labelThetaIncrement = -_kTwoPi / labels.length;
var labelTheta = _kPiByTwo;
for (TextPainter label in labels) {
final Offset labelOffset =
new Offset(-label.width / 2.0, -label.height / 2.0);
for (var label in labels) {
final labelOffset = Offset(-label.width / 2.0, -label.height / 2.0);
label.paint(
canvas, getOffsetForTheta(labelTheta, radius - 40.0) + labelOffset);
@ -183,21 +181,20 @@ class _Dial extends StatefulWidget {
/// The resolution of mins of the dial, i.e. if snapToMins = 5.0, only durations of 5min intervals will be selectable.
final double snapToMins;
@override
_DialState createState() => new _DialState();
_DialState createState() => _DialState();
}
class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
_thetaController = new AnimationController(
_thetaController = AnimationController(
duration: _kDialAnimateDuration,
vsync: this,
);
_thetaTween =
new Tween<double>(begin: _getThetaForDuration(widget.duration));
_theta = _thetaTween.animate(new CurvedAnimation(
parent: _thetaController, curve: Curves.fastOutSlowIn))
_thetaTween = Tween<double>(begin: _getThetaForDuration(widget.duration));
_theta = _thetaTween.animate(
CurvedAnimation(parent: _thetaController, curve: Curves.fastOutSlowIn))
..addListener(() => setState(() {}));
_thetaController.addStatusListener((status) {
// if (status == AnimationStatus.completed && _hours != _snappedHours) {
@ -238,7 +235,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
Animation<double> _theta;
AnimationController _thetaController;
double _pct = 0.0;
final double _pct = 0.0;
int _seconds = 0;
bool _dragging = false;
int _minutes = 0;
@ -249,8 +246,8 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
}
void _animateTo(double targetTheta) {
final double currentTheta = _theta.value;
double beginTheta =
final currentTheta = _theta.value;
var beginTheta =
_nearest(targetTheta, currentTheta, currentTheta + _kTwoPi);
beginTheta = _nearest(targetTheta, beginTheta, currentTheta - _kTwoPi);
_thetaTween
@ -284,9 +281,8 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
void _updateThetaForPan() {
setState(() {
final Offset offset = _position - _center;
final double angle =
(math.atan2(offset.dx, offset.dy) - _kPiByTwo) % _kTwoPi;
final offset = _position - _center;
final angle = (math.atan2(offset.dx, offset.dy) - _kPiByTwo) % _kTwoPi;
// Stop accidental abrupt pans from making the dial seem like it starts from 1h.
// (happens when wanting to pan from 0 clockwise, but when doing so quickly, one actually pans from before 0 (e.g. setting the duration to 59mins, and then crossing 0, which would then mean 1h 1min).
@ -315,10 +311,10 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
}
void _handlePanUpdate(DragUpdateDetails details) {
double oldTheta = _theta.value;
var oldTheta = _theta.value;
_position += details.delta;
_updateThetaForPan();
double newTheta = _theta.value;
var newTheta = _theta.value;
// _updateRotations(oldTheta, newTheta);
_updateTurningAngle(oldTheta, newTheta);
_notifyOnChangedIfNeeded();
@ -344,7 +340,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
double _angleToSeconds(double angle) {
// Coordinate transformation from mathematical COS to dial COS
double dialAngle = _kPiByTwo - angle;
var dialAngle = _kPiByTwo - angle;
// Turn dial angle into minutes, may go beyond 60 minutes (multiple turns)
return dialAngle / _kTwoPi * 60.0;
@ -393,27 +389,27 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
}
List<TextPainter> _buildSeconds(TextTheme textTheme) {
final TextStyle style = textTheme.subtitle1;
final style = textTheme.subtitle1;
const List<Duration> _secondsMarkerValues = const <Duration>[
const Duration(seconds: 0),
const Duration(seconds: 5),
const Duration(seconds: 10),
const Duration(seconds: 15),
const Duration(seconds: 20),
const Duration(seconds: 25),
const Duration(seconds: 30),
const Duration(seconds: 35),
const Duration(seconds: 40),
const Duration(seconds: 45),
const Duration(seconds: 50),
const Duration(seconds: 55),
const _secondsMarkerValues = <Duration>[
Duration(seconds: 0),
Duration(seconds: 5),
Duration(seconds: 10),
Duration(seconds: 15),
Duration(seconds: 20),
Duration(seconds: 25),
Duration(seconds: 30),
Duration(seconds: 35),
Duration(seconds: 40),
Duration(seconds: 45),
Duration(seconds: 50),
Duration(seconds: 55),
];
final List<TextPainter> labels = <TextPainter>[];
for (Duration duration in _secondsMarkerValues) {
var painter = new TextPainter(
text: new TextSpan(style: style, text: duration.inSeconds.toString()),
final labels = <TextPainter>[];
for (var duration in _secondsMarkerValues) {
var painter = TextPainter(
text: TextSpan(style: style, text: duration.inSeconds.toString()),
textDirection: TextDirection.ltr,
)..layout();
labels.add(painter);
@ -433,20 +429,20 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
break;
}
final ThemeData theme = Theme.of(context);
final theme = Theme.of(context);
int selectedDialValue;
_minutes = _minuteHand(_turningAngle);
_seconds = _secondHand(_turningAngle);
return new GestureDetector(
return GestureDetector(
excludeFromSemantics: true,
onPanStart: _handlePanStart,
onPanUpdate: _handlePanUpdate,
onPanEnd: _handlePanEnd,
onTapUp: _handleTapUp,
child: new CustomPaint(
painter: new _DialPainter(
child: CustomPaint(
painter: _DialPainter(
pct: _pct,
multiplier: _minutes,
secondHand: _seconds,
@ -482,7 +478,7 @@ class _DurationPickerDialog extends StatefulWidget {
final double snapToMins;
@override
_DurationPickerDialogState createState() => new _DurationPickerDialogState();
_DurationPickerDialogState createState() => _DurationPickerDialogState();
}
class _DurationPickerDialogState extends State<_DurationPickerDialog> {
@ -520,34 +516,34 @@ class _DurationPickerDialogState extends State<_DurationPickerDialog> {
@override
Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context));
final ThemeData theme = Theme.of(context);
final theme = Theme.of(context);
final Widget picker = new Padding(
final Widget picker = Padding(
padding: const EdgeInsets.all(16.0),
child: new AspectRatio(
child: AspectRatio(
aspectRatio: 1.0,
child: new _Dial(
child: _Dial(
duration: _selectedDuration,
onChanged: _handleTimeChanged,
snapToMins: widget.snapToMins,
)));
final Widget actions = ButtonBar(children: <Widget>[
new FlatButton(
child: new Text(localizations.cancelButtonLabel),
FlatButton(
child: Text(localizations.cancelButtonLabel),
onPressed: _handleCancel),
new FlatButton(
child: new Text(localizations.okButtonLabel), onPressed: _handleOk),
FlatButton(
child: Text(localizations.okButtonLabel), onPressed: _handleOk),
]);
final Dialog dialog = new Dialog(child: new OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
final Widget pickerAndActions = new Container(
final dialog =
Dialog(child: OrientationBuilder(builder: (context, orientation) {
final Widget pickerAndActions = Container(
color: theme.dialogBackgroundColor,
child: new Column(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Expanded(
Expanded(
child:
picker), // picker grows and shrinks with the available space
actions,
@ -558,26 +554,26 @@ class _DurationPickerDialogState extends State<_DurationPickerDialog> {
assert(orientation != null);
switch (orientation) {
case Orientation.portrait:
return new SizedBox(
return SizedBox(
width: _kDurationPickerWidthPortrait,
height: _kDurationPickerHeightPortrait,
child: new Column(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Expanded(
Expanded(
child: pickerAndActions,
),
]));
case Orientation.landscape:
return new SizedBox(
return SizedBox(
width: _kDurationPickerWidthLandscape,
height: _kDurationPickerHeightLandscape,
child: new Row(
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Flexible(
Flexible(
child: pickerAndActions,
),
]));
@ -585,7 +581,7 @@ class _DurationPickerDialogState extends State<_DurationPickerDialog> {
return null;
}));
return new Theme(
return Theme(
data: theme.copyWith(
dialogBackgroundColor: Colors.transparent,
),
@ -621,8 +617,8 @@ Future<Duration> showDurationPicker(
return await showDialog<Duration>(
context: context,
builder: (BuildContext context) => new _DurationPickerDialog(
initialTime: initialTime, snapToMins: snapToMins),
builder: (context) =>
_DurationPickerDialog(initialTime: initialTime, snapToMins: snapToMins),
);
}

View File

@ -1,26 +1,26 @@
import 'dart:io';
import 'dart:ui';
import 'package:auto_animated/auto_animated.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:focused_menu/focused_menu.dart';
import 'package:focused_menu/modals.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:line_icons/line_icons.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:auto_animated/auto_animated.dart';
import 'open_container.dart';
import '../episodes/episode_detail.dart';
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../state/download_state.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../episodes/episode_detail.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart';
import 'extension_helper.dart';
import 'custom_widget.dart';
import 'extension_helper.dart';
import 'open_container.dart';
enum Layout { three, two, one }
@ -50,49 +50,47 @@ class EpisodeGrid extends StatelessWidget {
}) : super(key: key);
Future<int> _isListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
Future<Tuple5<int, bool, bool, bool, List<int>>> _initData(
EpisodeBrief episode) async {
List<int> menuList = await _getEpisodeMenu();
bool tapToOpen = await _getTapToOpenPopupMenu();
int listened = await _isListened(episode);
bool liked = await _isLiked(episode);
bool downloaded = await _isDownloaded(episode);
var menuList = await _getEpisodeMenu();
var tapToOpen = await _getTapToOpenPopupMenu();
var listened = await _isListened(episode);
var liked = await _isLiked(episode);
var downloaded = await _isDownloaded(episode);
return Tuple5(listened, liked, downloaded, tapToOpen, menuList);
}
Future<bool> _isLiked(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
Future<List<int>> _getEpisodeMenu() async {
KeyValueStorage popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
List<int> list = await popupMenuStorage.getMenu();
var popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
var list = await popupMenuStorage.getMenu();
return list;
}
Future<bool> _isDownloaded(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
var dbHelper = DBHelper();
return await dbHelper.isDownloaded(episode.enclosureUrl);
}
Future<bool> _getTapToOpenPopupMenu() async {
KeyValueStorage tapToOpenPopupMenuStorage =
KeyValueStorage(tapToOpenPopupMenuKey);
bool boo = await tapToOpenPopupMenuStorage.getBool(defaultValue: false);
var tapToOpenPopupMenuStorage = KeyValueStorage(tapToOpenPopupMenuKey);
var boo = await tapToOpenPopupMenuStorage.getBool(defaultValue: false);
return boo;
}
_markListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
bool marked = await dbHelper.checkMarked(episode);
var dbHelper = DBHelper();
var marked = await dbHelper.checkMarked(episode);
if (!marked) {
final PlayHistory history =
PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
}
}
@ -107,11 +105,6 @@ class EpisodeGrid extends StatelessWidget {
await dbHelper.setUniked(url);
}
String _stringForSeconds(double seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
/// Episode title widget.
Widget _title(EpisodeBrief episode) => Container(
alignment:
@ -201,7 +194,7 @@ class EpisodeGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
double _width = context.width;
var _width = context.width;
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
var downloader = Provider.of<DownloadState>(context, listen: false);
final options = LiveOptions(
@ -227,7 +220,7 @@ class EpisodeGrid extends StatelessWidget {
crossAxisSpacing: 6.0,
),
itemBuilder: (context, index, animation) {
Color _c = (Theme.of(context).brightness == Brightness.light)
var _c = (Theme.of(context).brightness == Brightness.light)
? episodes[index].primaryColor.colorizedark()
: episodes[index].primaryColor.colorizeLight();
scrollController.addListener(() {});
@ -247,12 +240,12 @@ class EpisodeGrid extends StatelessWidget {
Tuple5<int, bool, bool, bool, List<int>>>(
future: _initData(episodes[index]),
initialData: Tuple5(0, false, false, false, []),
builder: (BuildContext context, AsyncSnapshot snapshot) {
int isListened = snapshot.data.item1;
bool isLiked = snapshot.data.item2;
bool isDownloaded = snapshot.data.item3;
bool tapToOpen = snapshot.data.item4;
List<int> menuList = snapshot.data.item5;
builder: (context, snapshot) {
var isListened = snapshot.data.item1;
var isLiked = snapshot.data.item2;
var isDownloaded = snapshot.data.item3;
var tapToOpen = snapshot.data.item4;
var menuList = snapshot.data.item5;
return Container(
decoration: BoxDecoration(
borderRadius:
@ -314,8 +307,9 @@ class EpisodeGrid extends StatelessWidget {
color: Theme.of(context).accentColor,
),
onPressed: () {
if (data.item1 != episodes[index])
if (data.item1 != episodes[index]) {
audio.episodeLoad(episodes[index]);
}
}),
menuList.contains(1)
? FocusedMenuItem(
@ -431,8 +425,9 @@ class EpisodeGrid extends StatelessWidget {
LineIcons.download_solid,
color: Colors.green),
onPressed: () {
if (!isDownloaded)
if (!isDownloaded) {
downloader.startTask(episodes[index]);
}
})
: null
],
@ -509,10 +504,9 @@ class EpisodeGrid extends StatelessWidget {
? Container(
alignment: Alignment.center,
child: Text(
_stringForSeconds(
episodes[index]
.duration
.toDouble()),
episodes[index]
.duration
.toTime,
style: TextStyle(
fontSize: _width / 35),
),
@ -545,11 +539,7 @@ class EpisodeGrid extends StatelessWidget {
? Container(
alignment: Alignment.center,
child: Text(
((episodes[index]
.enclosureLength) ~/
1000000)
.toString() +
'MB',
'${(episodes[index].enclosureLength) ~/ 1000000}MB',
style: TextStyle(
fontSize: _width / 35),
),
@ -623,7 +613,7 @@ class OpenContainerWrapper extends StatelessWidget {
closedShape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0))),
transitionType: ContainerTransitionType.fadeThrough,
openBuilder: (BuildContext context, VoidCallback _, bool boo) {
openBuilder: (context, _, boo) {
return EpisodeDetail(
episodeItem: episode,
hide: boo,

View File

@ -25,7 +25,7 @@ extension IntExtension on int {
String toDate(BuildContext context) {
if (this == null) return '';
final s = context.s;
DateTime date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
var date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
var difference = DateTime.now().toUtc().difference(date);
if (difference.inHours < 24) {
return s.hoursAgo(difference.inHours);
@ -38,20 +38,21 @@ extension IntExtension on int {
}
String get toTime =>
'${(this ~/ 60)}:${(this.truncate() % 60).toString().padLeft(2, '0')}';
'${(this ~/ 60)}:${(truncate() % 60).toString().padLeft(2, '0')}';
String toInterval(BuildContext context) {
if (this == null || this.isNegative) return '';
if (this == null || isNegative) return '';
final s = context.s;
var interval = Duration(milliseconds: this);
if (interval.inHours <= 48)
if (interval.inHours <= 48) {
return s.publishedDaily;
else if (interval.inDays > 2 && interval.inDays <= 14)
} else if (interval.inDays > 2 && interval.inDays <= 14) {
return s.publishedWeekly;
else if (interval.inDays > 14 && interval.inDays < 60)
} else if (interval.inDays > 14 && interval.inDays < 60) {
return s.publishedMonthly;
else
} else {
return s.publishedYearly;
}
}
}

View File

@ -11,8 +11,7 @@ generalDialog(BuildContext context,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext context, Animation animaiton,
Animation secondaryAnimation) =>
pageBuilder: (context, animaiton, secondaryAnimation) =>
AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

@ -48,7 +48,7 @@ class _RenderMenuItem extends RenderShiftedBox {
child.layout(constraints, parentUsesSize: true);
size = constraints.constrain(child.size);
}
final BoxParentData childParentData = child.parentData as BoxParentData;
final childParentData = child.parentData as BoxParentData;
childParentData.offset = Offset.zero;
onLayout(size);
}
@ -66,16 +66,16 @@ class _PopupMenu<T> extends StatelessWidget {
@override
Widget build(BuildContext context) {
final double unit = 1.0 /
final unit = 1.0 /
(route.items.length +
1.5); // 1.0 for the width and 0.5 for the last item's fade.
final List<Widget> children = <Widget>[];
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
final children = <Widget>[];
final popupMenuTheme = PopupMenuTheme.of(context);
for (int i = 0; i < route.items.length; i += 1) {
final double start = (i + 1) * unit;
final double end = (start + 1.5 * unit).clamp(0.0, 1.0) as double;
final CurvedAnimation opacity = CurvedAnimation(
for (var i = 0; i < route.items.length; i += 1) {
final start = (i + 1) * unit;
final end = (start + 1.5 * unit).clamp(0.0, 1.0) as double;
final opacity = CurvedAnimation(
parent: route.animation,
curve: Interval(start, end),
);
@ -89,7 +89,7 @@ class _PopupMenu<T> extends StatelessWidget {
}
children.add(
_MenuItem(
onLayout: (Size size) {
onLayout: (size) {
route.itemSizes[i] = size;
},
child: FadeTransition(
@ -100,11 +100,9 @@ class _PopupMenu<T> extends StatelessWidget {
);
}
final CurveTween opacity =
CurveTween(curve: const Interval(0.0, 1.0 / 3.0));
final CurveTween width = CurveTween(curve: Interval(0.0, unit));
final CurveTween height =
CurveTween(curve: Interval(0.0, unit * route.items.length));
final opacity = CurveTween(curve: const Interval(0.0, 1.0 / 3.0));
final width = CurveTween(curve: Interval(0.0, unit));
final height = CurveTween(curve: Interval(0.0, unit * route.items.length));
final Widget child = ConstrainedBox(
constraints: const BoxConstraints(
@ -119,9 +117,7 @@ class _PopupMenu<T> extends StatelessWidget {
explicitChildNodes: true,
label: semanticLabel,
child: SingleChildScrollView(
padding: const EdgeInsets.only(
bottom: _kMenuVerticalPadding
),
padding: const EdgeInsets.only(bottom: _kMenuVerticalPadding),
child: ListBody(children: children),
),
),
@ -130,12 +126,12 @@ class _PopupMenu<T> extends StatelessWidget {
return AnimatedBuilder(
animation: route.animation,
builder: (BuildContext context, Widget child) {
builder: (context, child) {
return Opacity(
opacity: opacity.evaluate(route.animation),
child: Material(
shape: route.shape ?? popupMenuTheme.shape,
color: route.color ?? popupMenuTheme.color,
color: route.color ?? popupMenuTheme.color,
type: MaterialType.card,
elevation: route.elevation ?? popupMenuTheme.elevation ?? 8.0,
child: Align(
@ -175,12 +171,12 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
@override
Offset getPositionForChild(Size size, Size childSize) {
double y = position.top;
var y = position.top;
if (selectedItemIndex != null && itemSizes != null) {
double selectedItemOffset =
_kMenuVerticalPadding;
for (int index = 0; index < selectedItemIndex; index += 1)
var selectedItemOffset = _kMenuVerticalPadding;
for (var index = 0; index < selectedItemIndex; index += 1) {
selectedItemOffset += itemSizes[index].height;
}
selectedItemOffset += itemSizes[selectedItemIndex].height / 2;
y = position.top +
(size.height - position.top - position.bottom) / 2.0 -
@ -206,14 +202,16 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
}
}
if (x < _kMenuScreenPadding)
if (x < _kMenuScreenPadding) {
x = _kMenuScreenPadding;
else if (x + childSize.width > size.width - _kMenuScreenPadding)
} else if (x + childSize.width > size.width - _kMenuScreenPadding) {
x = size.width - childSize.width - _kMenuScreenPadding;
if (y < _kMenuScreenPadding)
}
if (y < _kMenuScreenPadding) {
y = _kMenuScreenPadding;
else if (y + childSize.height > size.height - _kMenuScreenPadding)
} else if (y + childSize.height > size.height - _kMenuScreenPadding) {
y = size.height - childSize.height - _kMenuScreenPadding;
}
return Offset(x, y);
}
@ -283,7 +281,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
Animation<double> secondaryAnimation) {
int selectedItemIndex;
if (initialValue != null) {
for (int index = 0;
for (var index = 0;
selectedItemIndex == null && index < items.length;
index += 1) {
if (items[index].represents(initialValue)) selectedItemIndex = index;
@ -304,7 +302,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
removeLeft: true,
removeRight: true,
child: Builder(
builder: (BuildContext context) {
builder: (context) {
return CustomSingleChildLayout(
delegate: _PopupMenuRouteLayout(
position,
@ -339,7 +337,7 @@ Future<T> _showMenu<T>({
assert(captureInheritedThemes != null);
assert(debugCheckHasMaterialLocalizations(context));
String label = semanticLabel;
var label = semanticLabel;
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
@ -348,11 +346,13 @@ Future<T> _showMenu<T>({
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
label = semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel;
case TargetPlatform.windows:
label =
semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel;
}
return Navigator.of(context, rootNavigator: useRootNavigator).push(_PopupMenuRoute<T>(
return Navigator.of(context, rootNavigator: useRootNavigator)
.push(_PopupMenuRoute<T>(
position: position,
items: items,
initialValue: initialValue,
@ -368,7 +368,6 @@ Future<T> _showMenu<T>({
));
}
class MyPopupMenuButton<T> extends StatefulWidget {
/// Creates a button that shows a popup menu.
///
@ -428,11 +427,10 @@ class MyPopupMenuButton<T> extends StatefulWidget {
class MyPopupMenuButtonState<T> extends State<MyPopupMenuButton<T>> {
void showButtonMenu() {
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
final RenderBox button = context.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RelativeRect position = RelativeRect.fromRect(
final popupMenuTheme = PopupMenuTheme.of(context);
final button = context.findRenderObject() as RenderBox;
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(widget.offset, ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero),
@ -440,7 +438,7 @@ class MyPopupMenuButtonState<T> extends State<MyPopupMenuButton<T>> {
),
Offset.zero & overlay.size,
);
final List<PopupMenuEntry<T>> items = widget.itemBuilder(context);
final items = widget.itemBuilder(context);
// Only show the menu if there is something to show
if (items.isNotEmpty) {
_showMenu<T>(
@ -452,7 +450,7 @@ class MyPopupMenuButtonState<T> extends State<MyPopupMenuButton<T>> {
shape: widget.shape ?? popupMenuTheme.shape,
color: widget.color ?? popupMenuTheme.color,
captureInheritedThemes: widget.captureInheritedThemes,
).then<void>((T newValue) {
).then<void>((newValue) {
if (!mounted) return null;
if (newValue == null) {
if (widget.onCanceled != null) widget.onCanceled();
@ -482,7 +480,7 @@ class MyPopupMenuButtonState<T> extends State<MyPopupMenuButton<T>> {
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
if (widget.child != null)
if (widget.child != null) {
return Tooltip(
message:
widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip,
@ -492,6 +490,7 @@ class MyPopupMenuButtonState<T> extends State<MyPopupMenuButton<T>> {
child: widget.child,
),
);
}
return IconButton(
icon: widget.icon ?? _getIcon(Theme.of(context).platform),
@ -545,9 +544,9 @@ class MyPopupMenuItemState<int, W extends MyPopupMenuItem<int>>
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
TextStyle style = widget.textStyle ??
final theme = Theme.of(context);
final popupMenuTheme = PopupMenuTheme.of(context);
var style = widget.textStyle ??
popupMenuTheme.textStyle ??
theme.textTheme.subtitle1;

View File

@ -265,7 +265,7 @@ class _OpenContainerState extends State<OpenContainer> {
// shape: widget.closedShape,
child: Builder(
key: _closedBuilderKey,
builder: (BuildContext context) {
builder: (context) {
return widget.closedBuilder(context, openContainer, false);
},
),
@ -546,7 +546,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
TickerFuture didPush() {
_takeMeasurements(navigatorContext: hideableKey.currentContext);
animation.addStatusListener((AnimationStatus status) {
animation.addStatusListener((status) {
_lastAnimationStatus = _currentAnimationStatus;
_currentAnimationStatus = status;
switch (status) {
@ -584,7 +584,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
}) {
final RenderBox navigator =
Navigator.of(navigatorContext).context.findRenderObject();
final Size navSize = _getSize(navigator);
final navSize = _getSize(navigator);
_rectTween.end = Offset.zero & navSize;
void takeMeasurementsInSourceRoute([Duration _]) {
if (!navigator.attached || hideableKey.currentContext == null) {
@ -622,8 +622,8 @@ class _OpenContainerRoute extends ModalRoute<void> {
}
bool get _transitionWasInterrupted {
bool wasInProgress = false;
bool isInProgress = false;
var wasInProgress = false;
var isInProgress = false;
switch (_currentAnimationStatus) {
case AnimationStatus.completed:
@ -662,7 +662,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
alignment: Alignment.topLeft,
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
builder: (context, child) {
if (animation.isCompleted) {
return SizedBox.expand(
child: Material(
@ -671,7 +671,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
shape: openShape,
child: Builder(
key: _openBuilderKey,
builder: (BuildContext context) {
builder: (context) {
return openBuilder(context, closeContainer, false);
},
),
@ -719,7 +719,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
assert(closedOpacityTween != null);
assert(openOpacityTween != null);
final Rect rect = _rectTween.evaluate(curvedAnimation);
final rect = _rectTween.evaluate(curvedAnimation);
_positionTween.begin =
Offset(_rectTween.begin.left + 10, _rectTween.begin.top + 10);
_positionTween.end = Offset(
@ -728,7 +728,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
? MediaQuery.of(context).size.height - 100
: MediaQuery.of(context).size.height - 40);
double _width = MediaQuery.of(context).size.width;
var _width = MediaQuery.of(context).size.width;
_avatarScaleTween.begin = _width / 16;
_avatarScaleTween.end = 30;
return SizedBox.expand(
@ -766,7 +766,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
.evaluate(animation),
child: Builder(
key: closedBuilderKey,
builder: (BuildContext context) {
builder: (context) {
// Use dummy "open container" callback
// since we are in the process of opening.
return closedBuilder(
@ -789,7 +789,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
openOpacityTween.evaluate(animation),
child: Builder(
key: _openBuilderKey,
builder: (BuildContext context) {
builder: (context) {
return openBuilder(
context, closeContainer, true);
},
@ -845,8 +845,8 @@ class _FlippableTweenSequence<T> extends TweenSequence<T> {
_FlippableTweenSequence<T> get flipped {
if (_flipped == null) {
final List<TweenSequenceItem<T>> newItems = <TweenSequenceItem<T>>[];
for (int i = 0; i < _items.length; i++) {
final newItems = <TweenSequenceItem<T>>[];
for (var i = 0; i < _items.length; i++) {
newItems.add(TweenSequenceItem<T>(
tween: _items[i].tween,
weight: _items[_items.length - 1 - i].weight,

View File

@ -6,16 +6,16 @@ class SlideLeftRoute extends PageRouteBuilder {
SlideLeftRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
context,
animation,
secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
context,
animation,
secondaryAnimation,
child,
) =>
SlideTransition(
position: Tween<Offset>(
@ -33,17 +33,17 @@ class SlideLeftHideRoute extends PageRouteBuilder {
SlideLeftHideRoute({this.page, this.transitionPage})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
context,
animation,
secondaryAnimation,
) =>
page,
transitionDuration: Duration(milliseconds: 300),
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
context,
animation,
secondaryAnimation,
child,
) {
return SlideTransition(
position: Tween<Offset>(
@ -60,16 +60,16 @@ class SlideUptRoute extends PageRouteBuilder {
SlideUptRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
context,
animation,
secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
context,
animation,
secondaryAnimation,
child,
) =>
SlideTransition(
position: Tween<Offset>(
@ -87,16 +87,16 @@ class ScaleRoute extends PageRouteBuilder {
ScaleRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
context,
animation,
secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
context,
animation,
secondaryAnimation,
child,
) =>
ScaleTransition(
scale: Tween<double>(

View File

@ -44,7 +44,7 @@ class AtomFeed {
try {
feedElement = document.findElements("feed").first;
} on StateError {
throw new ArgumentError("feed not found");
throw ArgumentError("feed not found");
}
return AtomFeed(

View File

@ -14,6 +14,6 @@ class AtomGenerator {
var uri = element.getAttribute("uri");
var version = element.getAttribute("version");
var value = element.text;
return new AtomGenerator(uri, version, value);
return AtomGenerator(uri, version, value);
}
}

View File

@ -1,10 +1,11 @@
import 'package:tsacdop/webfeed/domain/media/category.dart';
import 'package:tsacdop/webfeed/domain/media/content.dart';
import 'package:tsacdop/webfeed/domain/media/credit.dart';
import 'package:tsacdop/webfeed/domain/media/rating.dart';
import 'package:tsacdop/webfeed/util/helpers.dart';
import 'package:xml/xml.dart';
import '../../util/helpers.dart';
import 'category.dart';
import 'content.dart';
import 'credit.dart';
import 'rating.dart';
class Group {
final List<Content> contents;
final List<Credit> credits;
@ -22,17 +23,17 @@ class Group {
if (element == null) {
return null;
}
return new Group(
return Group(
contents: element.findElements("media:content").map((e) {
return new Content.parse(e);
return Content.parse(e);
}).toList(),
credits: element.findElements("media:credit").map((e) {
return new Credit.parse(e);
return Credit.parse(e);
}).toList(),
category: new Category.parse(
category: Category.parse(
findElementOrNull(element, "media:category"),
),
rating: new Rating.parse(
rating: Rating.parse(
findElementOrNull(element, "media:rating"),
),
);

View File

@ -19,6 +19,7 @@ dependencies:
connectivity: ^0.4.9
dio: ^3.0.9
extended_nested_scroll_view: ^1.0.1
effective_dart: ^1.2.4
feature_discovery: ^0.10.0
file_picker: ^1.12.0
flutter_html: ^0.11.1