diff --git a/lib/episodes/episodedetail.dart b/lib/episodes/episodedetail.dart index b76a3c0..9c1f30b 100644 --- a/lib/episodes/episodedetail.dart +++ b/lib/episodes/episodedetail.dart @@ -15,6 +15,7 @@ import 'package:google_fonts/google_fonts.dart'; import '../state/audiostate.dart'; import '../type/episodebrief.dart'; import '../local_storage/sqflite_localpodcast.dart'; +import '../local_storage/key_value_storage.dart'; import '../util/context_extension.dart'; import '../util/custompaint.dart'; import 'episodedownload.dart'; @@ -52,14 +53,16 @@ class _EpisodeDetailState extends State { ScrollController _controller; _scrollListener() { if (_controller.offset > _controller.position.maxScrollExtent * 0.8) { - setState(() { - _showMenu = true; - }); + if (!_showMenu) + setState(() { + _showMenu = true; + }); } else if (_controller.offset < _controller.position.maxScrollExtent * 0.8) { - setState(() { - _showMenu = false; - }); + if (_showMenu) + setState(() { + _showMenu = false; + }); } } @@ -267,7 +270,7 @@ class _EpisodeDetailState extends State { ? (_description.contains('<')) ? Html( padding: EdgeInsets.only( - left: 20.0, right: 20, bottom: 20), + left: 20.0, right: 20, bottom: 50), defaultTextStyle: // GoogleFonts.libreBaskerville( GoogleFonts.martel( @@ -290,7 +293,7 @@ class _EpisodeDetailState extends State { padding: EdgeInsets.only( left: 20.0, right: 20.0, - bottom: 20.0), + bottom: 50.0), alignment: Alignment.topLeft, child: SelectableLinkify( onOpen: (link) { @@ -398,7 +401,7 @@ class _MenuBarState extends State { setUnliked(String url) async { var dbHelper = DBHelper(); - await dbHelper.setUniked(url); + await dbHelper.setUniked(url); setState(() {}); } @@ -423,24 +426,25 @@ class _MenuBarState extends State { ), ); + OverlayEntry _createOverlayEntry() { + RenderBox renderBox = context.findRenderObject(); + var offset = renderBox.localToGlobal(Offset.zero); + return OverlayEntry( + builder: (constext) => Positioned( + left: offset.dx + 50, + top: offset.dy - 60, + child: Container( + width: 70, + height: 100, + //color: Colors.grey[200], + child: HeartOpen(width: 50, height: 80)), + ), + ); + } + @override Widget build(BuildContext context) { var audio = Provider.of(context, listen: false); - OverlayEntry _createOverlayEntry() { - RenderBox renderBox = context.findRenderObject(); - var offset = renderBox.localToGlobal(Offset.zero); - return OverlayEntry( - builder: (constext) => Positioned( - left: offset.dx + 50, - top: offset.dy - 60, - child: Container( - width: 70, - height: 100, - //color: Colors.grey[200], - child: HeartOpen(width: 50, height: 80)), - ), - ); - } return Container( height: 50.0, diff --git a/lib/local_storage/sqflite_localpodcast.dart b/lib/local_storage/sqflite_localpodcast.dart index 0df34f3..2f934ef 100644 --- a/lib/local_storage/sqflite_localpodcast.dart +++ b/lib/local_storage/sqflite_localpodcast.dart @@ -899,6 +899,7 @@ class DBHelper { var dbClient = await database; List episodes = []; List list; + //Ordered by date if (mode == 0) list = await dbClient.rawQuery( """SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, @@ -908,6 +909,7 @@ class DBHelper { WHERE E.enclosure_url != E.media_id ORDER BY E.download_date DESC""", ); + //Ordered by date else if (mode == 1) list = await dbClient.rawQuery( """SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, @@ -917,6 +919,7 @@ class DBHelper { WHERE E.enclosure_url != E.media_id ORDER BY E.download_date ASC""", ); + //Ordered by size else if (mode == 2) list = await dbClient.rawQuery( """SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, @@ -926,24 +929,12 @@ class DBHelper { WHERE E.enclosure_url != E.media_id ORDER BY E.enclosure_length DESC""", ); - for (int x = 0; x < list.length; x++) { - int size; - if (list[x]['enclosure_length'] == null || - list[x]['enclosure_length'] == 0) { - String uri = list[x]['media_id']; - FileStat fileStat = await File(uri.substring(6)).stat(); - size = fileStat.size; - await dbClient.rawUpdate( - "UPDATE Episodes SET enclosure_length = ?, WHERE media_id = ?", - [size, uri]); - } else - size = list[x]['enclosure_length']; episodes.add( EpisodeBrief( list[x]['title'], list[x]['enclosure_url'], - size, + list[x]['enclosure_length'], list[x]['milliseconds'], list[x]['feed_title'], list[x]['primaryColor'], @@ -1115,12 +1106,12 @@ class DBHelper { return count; } - Future saveMediaId(String url, String path, String id) async { + Future saveMediaId(String url, String path, String id, int size) async { var dbClient = await database; int milliseconds = DateTime.now().millisecondsSinceEpoch; int count = await dbClient.rawUpdate( - "UPDATE Episodes SET media_id = ?, download_date = ?, downloaded = ? WHERE enclosure_url = ?", - [path, milliseconds, id, url]); + "UPDATE Episodes SET enclosure_length = ?, media_id = ?, download_date = ?, downloaded = ? WHERE enclosure_url = ?", + [size, path, milliseconds, id, url]); return count; } diff --git a/lib/podcasts/podcastgroup.dart b/lib/podcasts/podcastgroup.dart index 22e9934..5fe03dd 100644 --- a/lib/podcasts/podcastgroup.dart +++ b/lib/podcasts/podcastgroup.dart @@ -366,8 +366,8 @@ class _PodcastCardState extends State color: snapshot.data ? Colors.white : null), - height: _value == 0 ? 1 : 18 * _value, - width: _value == 0 ? 1 : 18 * _value, + height: _value == 0 ? 1 : 20 * _value, + width: _value == 0 ? 1 : 20 * _value, decoration: BoxDecoration( border: snapshot.data ? Border.all( diff --git a/lib/settings/downloads_manage.dart b/lib/settings/downloads_manage.dart index bc0ec22..62cb195 100644 --- a/lib/settings/downloads_manage.dart +++ b/lib/settings/downloads_manage.dart @@ -43,13 +43,14 @@ class _DownloadsManageState extends State { _size = 0; _fileNum = 0; var dir = await getExternalStorageDirectory(); + print(dir.path); dir.list().forEach((d) { var fileDir = Directory(d.path); fileDir.list().forEach((file) async { await File(file.path).stat().then((value) { _size += value.size; _fileNum += 1; - setState(() {}); + if (mounted) setState(() {}); }); }); }); @@ -59,19 +60,16 @@ class _DownloadsManageState extends State { setState(() => _clearing = true); await Future.forEach(_selectedList, (EpisodeBrief episode) async { var downloader = Provider.of(context, listen: false); - await downloader.removeTask(episode); - // await FlutterDownloader.remove( - // taskId: episode.downloaded, shouldDeleteContent: true); - // var dbHelper = DBHelper(); - // await dbHelper.delDownloaded(episode.enclosureUrl); - setState(() {}); + await downloader.delTask(episode); + if (mounted) setState(() {}); }); await Future.delayed(Duration(seconds: 1)); - setState(() { - _clearing = false; - }); + if (mounted) + setState(() { + _clearing = false; + }); await Future.delayed(Duration(seconds: 1)); - setState(() => _selectedList = []); + if (mounted) setState(() => _selectedList = []); _getStorageSize(); } @@ -245,13 +243,16 @@ class _DownloadsManageState extends State { EdgeInsets.symmetric(horizontal: 5), ), Text('Listened Only'), - Checkbox( - value: _onlyListened, - onChanged: (value) { - setState(() { - _onlyListened = value; - }); - }), + Transform.scale( + scale: 0.8, + child: Checkbox( + value: _onlyListened, + onChanged: (value) { + setState(() { + _onlyListened = value; + }); + }), + ), ], ), ), @@ -277,8 +278,7 @@ class _DownloadsManageState extends State { future: _isListened(_episodes[index]), initialData: 0, builder: (context, snapshot) { - return (_onlyListened && - snapshot.data > 0) + return (_onlyListened && snapshot.data == 0) ? Center() : Column( children: [ diff --git a/lib/settings/libries.dart b/lib/settings/libries.dart index 9be4dc1..85fe6ac 100644 --- a/lib/settings/libries.dart +++ b/lib/settings/libries.dart @@ -39,7 +39,7 @@ class Libries extends StatelessWidget { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Google', style: Theme.of(context) @@ -61,7 +61,7 @@ class Libries extends StatelessWidget { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Fonts', style: Theme.of(context) @@ -83,7 +83,7 @@ class Libries extends StatelessWidget { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Plugins', style: Theme.of(context) diff --git a/lib/settings/play_setting.dart b/lib/settings/play_setting.dart index f923543..0162fea 100644 --- a/lib/settings/play_setting.dart +++ b/lib/settings/play_setting.dart @@ -56,9 +56,12 @@ class PlaySetting extends StatelessWidget { subtitle: Text('Autoplay next episode in playlist'), trailing: Selector( selector: (_, audio) => audio.autoPlay, - builder: (_, data, __) => Switch( - value: data, - onChanged: (boo) => audio.autoPlaySwitch = boo), + builder: (_, data, __) => Transform.scale( + scale: 0.9, + child: Switch( + value: data, + onChanged: (boo) => audio.autoPlaySwitch = boo), + ), ), ), Divider(height: 2), diff --git a/lib/settings/storage.dart b/lib/settings/storage.dart index 8c00d14..7e06d07 100644 --- a/lib/settings/storage.dart +++ b/lib/settings/storage.dart @@ -128,10 +128,13 @@ class _StorageSettingState extends State title: Text('Ask before using cellular data'), subtitle: Text( 'Ask to confirm when using cellular data to download episodes.'), - trailing: Switch( - value: data, - onChanged: (value) => - settings.downloadUsingData = value, + trailing: Transform.scale( + scale: 0.9, + child: Switch( + value: data, + onChanged: (value) => + settings.downloadUsingData = value, + ), ), ); }, @@ -152,12 +155,15 @@ class _StorageSettingState extends State Text('Auto download using cellular data'), subtitle: Text( 'You can set podcast auto download in group manage page.'), - trailing: Switch( - value: snapshot.data, - onChanged: (value) async { - await _setAudtDownloadNetwork(value); - setState(() {}); - }, + trailing: Transform.scale( + scale: 0.9, + child: Switch( + value: snapshot.data, + onChanged: (value) async { + await _setAudtDownloadNetwork(value); + setState(() {}); + }, + ), ), ); }), diff --git a/lib/settings/syncing.dart b/lib/settings/syncing.dart index d96ac74..d13a12b 100644 --- a/lib/settings/syncing.dart +++ b/lib/settings/syncing.dart @@ -71,15 +71,18 @@ class SyncingSetting extends StatelessWidget { title: Text('Enable syncing'), subtitle: Text( 'Refresh all podcasts in the background to get leatest episodes'), - trailing: Switch( - value: data.item1, - onChanged: (boo) async { - settings.autoUpdate = boo; - if (boo) - settings.setWorkManager(data.item2); - else - settings.cancelWork(); - }), + trailing: Transform.scale( + scale: 0.9, + child: Switch( + value: data.item1, + onChanged: (boo) async { + settings.autoUpdate = boo; + if (boo) + settings.setWorkManager(data.item2); + else + settings.cancelWork(); + }), + ), ), Divider(height: 2), ListTile( diff --git a/lib/settings/theme.dart b/lib/settings/theme.dart index 24f8506..8e72ba7 100644 --- a/lib/settings/theme.dart +++ b/lib/settings/theme.dart @@ -126,11 +126,14 @@ class ThemeSetting extends StatelessWidget { Text('Turn on if you think the night is not dark enough'), trailing: Selector( selector: (_, setting) => setting.realDark, - builder: (_, data, __) => Switch( - value: data, - onChanged: (boo) async { - settings.setRealDark = boo; - }), + builder: (_, data, __) => Transform.scale( + scale: 0.9, + child: Switch( + value: data, + onChanged: (boo) async { + settings.setRealDark = boo; + }), + ), ), ), Divider(height: 2), diff --git a/lib/state/audiostate.dart b/lib/state/audiostate.dart index 6f0573c..5f780e7 100644 --- a/lib/state/audiostate.dart +++ b/lib/state/audiostate.dart @@ -92,7 +92,7 @@ class Playlist { savePlaylist() async { List urls = []; urls.addAll(_playlist.map((e) => e.enclosureUrl)); - await storage.saveStringList(urls); + await storage.saveStringList(urls.toSet().toList()); } addToPlayList(EpisodeBrief episodeBrief) async { @@ -104,15 +104,16 @@ class Playlist { } addToPlayListAt(EpisodeBrief episodeBrief, int index) async { - _playlist.insert(index, episodeBrief); - await savePlaylist(); - dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl); + if (!_playlist.contains(episodeBrief)) { + _playlist.insert(index, episodeBrief); + await savePlaylist(); + dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl); + } } Future delFromPlaylist(EpisodeBrief episodeBrief) async { int index = _playlist.indexOf(episodeBrief); - _playlist - .removeWhere((item) => item.enclosureUrl == episodeBrief.enclosureUrl); + _playlist.removeWhere((episode) => episode == episodeBrief); await savePlaylist(); return index; } diff --git a/lib/state/download_state.dart b/lib/state/download_state.dart index b1f524c..2b24306 100644 --- a/lib/state/download_state.dart +++ b/lib/state/download_state.dart @@ -91,8 +91,11 @@ class AutoDownloader { String filePath = 'file://' + path.join(completeTask.first.savedDir, Uri.encodeComponent(completeTask.first.filename)); - await dbHelper.saveMediaId( - episodeTask.episode.enclosureUrl, filePath, episodeTask.taskId); + FileStat fileStat = await File( + path.join(completeTask.first.savedDir, completeTask.first.filename)) + .stat(); + await dbHelper.saveMediaId(episodeTask.episode.enclosureUrl, filePath, + episodeTask.taskId, fileStat.size); _episodeTasks.removeWhere((element) => element.episode.enclosureUrl == episodeTask.episode.enclosureUrl); if (_episodeTasks.length == 0) _unbindBackgroundIsolate(); @@ -200,8 +203,12 @@ class DownloadState extends ChangeNotifier { String filePath = 'file://' + path.join(completeTask.first.savedDir, Uri.encodeComponent(completeTask.first.filename)); - dbHelper.saveMediaId( - episodeTask.episode.enclosureUrl, filePath, episodeTask.taskId); + print(filePath); + FileStat fileStat = await File( + path.join(completeTask.first.savedDir, completeTask.first.filename)) + .stat(); + dbHelper.saveMediaId(episodeTask.episode.enclosureUrl, filePath, + episodeTask.taskId, fileStat.size); EpisodeBrief episode = await dbHelper.getRssItemWithUrl(episodeTask.episode.enclosureUrl); _removeTask(episodeTask.episode); @@ -231,33 +238,36 @@ class DownloadState extends ChangeNotifier { } Future startTask(EpisodeBrief episode, {bool showNotification = true}) async { - final dir = await getExternalStorageDirectory(); - String localPath = path.join(dir.path, episode.feedTitle); - final saveDir = Directory(localPath); - bool hasExisted = await saveDir.exists(); - if (!hasExisted) { - saveDir.create(); - } - DateTime now = DateTime.now(); - String 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( - fileName: fileName, - url: episode.enclosureUrl, - savedDir: localPath, - showNotification: showNotification, - openFileFromNotification: false, - ); - _episodeTasks.add(EpisodeTask(episode, taskId)); var dbHelper = DBHelper(); - await dbHelper.saveDownloaded(episode.enclosureUrl, taskId); - notifyListeners(); + bool isDownloaded = await dbHelper.isDownloaded(episode.enclosureUrl); + if (!isDownloaded) { + final dir = await getExternalStorageDirectory(); + String localPath = path.join(dir.path, episode.feedTitle); + final saveDir = Directory(localPath); + bool hasExisted = await saveDir.exists(); + if (!hasExisted) { + saveDir.create(); + } + DateTime now = DateTime.now(); + String 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( + fileName: fileName, + url: episode.enclosureUrl, + savedDir: localPath, + showNotification: showNotification, + openFileFromNotification: false, + ); + _episodeTasks.add(EpisodeTask(episode, taskId)); + await dbHelper.saveDownloaded(episode.enclosureUrl, taskId); + notifyListeners(); + } } Future pauseTask(EpisodeBrief episode) async { diff --git a/lib/util/open_container.dart b/lib/util/open_container.dart index 14c6519..1d2b11e 100644 --- a/lib/util/open_container.dart +++ b/lib/util/open_container.dart @@ -538,6 +538,7 @@ class _OpenContainerRoute extends ModalRoute { // the bounds of the enclosing [Navigator]. final RectTween _rectTween = RectTween(); final Tween _positionTween = Tween(); + final Tween _avatarScaleTween = Tween(); AnimationStatus _lastAnimationStatus; AnimationStatus _currentAnimationStatus; @@ -726,7 +727,10 @@ class _OpenContainerRoute extends ModalRoute { playerRunning ? MediaQuery.of(context).size.height - 100 : MediaQuery.of(context).size.height - 40); + double _width = MediaQuery.of(context).size.width; + _avatarScaleTween.begin = _width / 16; + _avatarScaleTween.end = 30; return SizedBox.expand( child: Stack( children: [ @@ -804,8 +808,8 @@ class _OpenContainerRoute extends ModalRoute { top: _positionTween.evaluate(secondCurvedAnimation).dy, left: _positionTween.evaluate(secondCurvedAnimation).dx, child: SizedBox( - height: _width / 16, - width: _width / 16, + height: _avatarScaleTween.evaluate(secondCurvedAnimation), + width: _avatarScaleTween.evaluate(secondCurvedAnimation), child: flightWidget, ), ), diff --git a/pubspec.yaml b/pubspec.yaml index ecdd874..6ac4646 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: permission_handler: ^5.0.0+hotfix.3 fluttertoast: ^4.0.1 intl: ^0.16.1 - url_launcher: ^5.4.7 + url_launcher: ^5.4.10 image: ^2.1.12 shared_preferences: ^0.5.7 uuid: ^2.0.4 @@ -44,7 +44,8 @@ dependencies: wc_flutter_share: ^0.2.1 video_player: ^0.10.11 auto_animated: ^2.1.0 - feature_discovery: ^0.10.0 + feature_discovery: ^0.10.0 + flutter_isolate: ^1.0.0+14 just_audio: git: url: https://github.com/stonega/just_audio.git @@ -57,7 +58,6 @@ dev_dependencies: sdk: flutter dependency_overrides: - flutter_isolate: "1.0.0+14" xml: "4.2.0" flutter: