From 0b20c24984615464836c12fa6bc2859574274242 Mon Sep 17 00:00:00 2001 From: stonegate Date: Sat, 13 Jun 2020 01:56:13 +0800 Subject: [PATCH] Auto download --- lib/home/importompl.dart | 6 +- lib/settings/history.dart | 52 +++++++--------- lib/settings/layouts.dart | 7 ++- lib/settings/play_setting.dart | 2 +- lib/settings/settting.dart | 4 +- lib/settings/storage.dart | 4 +- lib/settings/syncing.dart | 5 +- lib/settings/theme.dart | 2 +- lib/state/download_state.dart | 110 +++++++++++++++++++++++++++++++-- lib/state/podcast_group.dart | 2 +- lib/state/refresh_podcast.dart | 2 - lib/state/settingstate.dart | 44 ++++++------- 12 files changed, 169 insertions(+), 71 deletions(-) diff --git a/lib/home/importompl.dart b/lib/home/importompl.dart index 7e9270c..7f4dc56 100644 --- a/lib/home/importompl.dart +++ b/lib/home/importompl.dart @@ -42,14 +42,14 @@ class Import extends StatelessWidget { // For safety if (episodes.length < 100) episodes.forEach((episode) { - downloader.startTask(episode, showNotification: false); + downloader.startTask(episode, showNotification: true); }); } else if (result == ConnectivityResult.wifi) { List episodes = await dbHelper.getNewEpisodes('all'); //For safety if (episodes.length < 100) episodes.forEach((episode) { - downloader.startTask(episode, showNotification: false); + downloader.startTask(episode, showNotification: true); }); } } @@ -66,9 +66,9 @@ class Import extends StatelessWidget { case SubscribeState.start: return importColumn("Subscribe ${item.title}", context); case SubscribeState.subscribe: - groupList.subscribeNewPodcast(item.id); return importColumn("Fetch data ${item.title}", context); case SubscribeState.fetch: + groupList.subscribeNewPodcast(item.id); // groupList.updatePodcast(item.id); return importColumn("Subscribe success ${item.title}", context); case SubscribeState.exist: diff --git a/lib/settings/history.dart b/lib/settings/history.dart index 3bb16c2..7152084 100644 --- a/lib/settings/history.dart +++ b/lib/settings/history.dart @@ -328,36 +328,28 @@ class _PlayedHistoryState extends State Text(snapshot.data[index].title), ], ), - subtitle: Row( - children: [ - _status - ? Text(DateTime.now() - .difference(snapshot - .data[index].subDate) - .inDays - .toString() + - ' day ago') - : Text(snapshot.data[index].delDate - .difference(snapshot - .data[index].subDate) - .inDays - .toString() + - ' day on your device'), - Spacer(), - !_status - ? Text( - 'Removed at ' + - DateFormat.yMd() - .add_jm() - .format(snapshot - .data[index] - .delDate), - style: TextStyle( - color: Colors.red), - ) - : Center(), - ], - ), + subtitle: _status + ? Text(DateTime.now() + .difference(snapshot + .data[index].subDate) + .inDays + .toString() + + ' day ago') + : Text( + 'Removed at ' + + DateFormat.yMd() + .add_jm() + .format(snapshot + .data[index].delDate), + style: TextStyle(color: Colors.red), + ), + // Text(snapshot.data[index].delDate + // .difference(snapshot + // .data[index].subDate) + // .inDays + // .toString() + + // ' day on your device'), + trailing: !_status ? Material( color: Colors.transparent, diff --git a/lib/settings/layouts.dart b/lib/settings/layouts.dart index 4686e9c..7fbb477 100644 --- a/lib/settings/layouts.dart +++ b/lib/settings/layouts.dart @@ -133,7 +133,7 @@ class _LayoutSettingState extends State { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Episode popup menu', style: Theme.of(context) @@ -156,9 +156,12 @@ class _LayoutSettingState extends State { subtitle: Text('Change the menu when long tap episode'), ), Divider(height: 2), + Padding( + padding: EdgeInsets.all(10.0), + ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Default grid view', style: Theme.of(context) diff --git a/lib/settings/play_setting.dart b/lib/settings/play_setting.dart index 8f491ba..f923543 100644 --- a/lib/settings/play_setting.dart +++ b/lib/settings/play_setting.dart @@ -36,7 +36,7 @@ class PlaySetting extends StatelessWidget { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Playlist', style: Theme.of(context) diff --git a/lib/settings/settting.dart b/lib/settings/settting.dart index 1a111e2..73f8fc9 100644 --- a/lib/settings/settting.dart +++ b/lib/settings/settting.dart @@ -132,7 +132,7 @@ class _SettingsState extends State ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Prefrence', style: Theme.of(context) @@ -241,7 +241,7 @@ class _SettingsState extends State children: [ Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Info', style: Theme.of(context) diff --git a/lib/settings/storage.dart b/lib/settings/storage.dart index b63d6eb..8c00d14 100644 --- a/lib/settings/storage.dart +++ b/lib/settings/storage.dart @@ -105,7 +105,7 @@ class _StorageSettingState extends State ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Network', style: Theme.of(context) @@ -174,7 +174,7 @@ class _StorageSettingState extends State ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Storage', style: Theme.of(context) diff --git a/lib/settings/syncing.dart b/lib/settings/syncing.dart index aaf3485..d96ac74 100644 --- a/lib/settings/syncing.dart +++ b/lib/settings/syncing.dart @@ -40,7 +40,7 @@ class SyncingSetting extends StatelessWidget { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Syncing', style: Theme.of(context) @@ -48,6 +48,9 @@ class SyncingSetting extends StatelessWidget { .bodyText1 .copyWith(color: Theme.of(context).accentColor)), ), + Padding( + padding: EdgeInsets.all(5.0), + ), ListView( physics: const BouncingScrollPhysics(), shrinkWrap: true, diff --git a/lib/settings/theme.dart b/lib/settings/theme.dart index 74aa387..24f8506 100644 --- a/lib/settings/theme.dart +++ b/lib/settings/theme.dart @@ -32,7 +32,7 @@ class ThemeSetting extends StatelessWidget { ), Container( height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 80), + padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text('Interface', style: Theme.of(context) diff --git a/lib/state/download_state.dart b/lib/state/download_state.dart index bf0ebcd..b1f524c 100644 --- a/lib/state/download_state.dart +++ b/lib/state/download_state.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'dart:isolate'; import 'dart:ui'; @@ -7,9 +8,9 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; -import 'package:tsacdop/local_storage/key_value_storage.dart'; -import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; +import '../local_storage/key_value_storage.dart'; +import '../local_storage/sqflite_localpodcast.dart'; import '../type/episodebrief.dart'; class EpisodeTask { @@ -32,6 +33,107 @@ void downloadCallback(String id, DownloadTaskStatus status, int progress) { 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'); + send.send([id, status, progress]); +} + +//For background auto downlaod +class AutoDownloader { + DBHelper dbHelper = DBHelper(); + List _episodeTasks = []; + Completer _completer = Completer(); + AutoDownloader() { + FlutterDownloader.registerCallback(autoDownloadCallback); + } + + bindBackgroundIsolate() { + print('start listen'); + ReceivePort _port = ReceivePort(); + bool isSuccess = IsolateNameServer.registerPortWithName( + _port.sendPort, 'auto_downloader_send_port'); + if (!isSuccess) { + _unbindBackgroundIsolate(); + //bindBackgroundIsolate(); + return; + } + _port.listen((dynamic data) { + String id = data[0]; + DownloadTaskStatus status = data[1]; + int progress = data[2]; + _episodeTasks.forEach((episodeTask) { + if (episodeTask.taskId == id) { + episodeTask.status = status; + episodeTask.progress = progress; + if (status == DownloadTaskStatus.complete) { + _saveMediaId(episodeTask); + } else if (status == DownloadTaskStatus.failed) { + _episodeTasks.removeWhere((element) => + element.episode.enclosureUrl == + episodeTask.episode.enclosureUrl); + if (_episodeTasks.length == 0) _unbindBackgroundIsolate(); + } + } + }); + }); + } + + void _unbindBackgroundIsolate() { + IsolateNameServer.removePortNameMapping('auto_downloader_send_port'); + _completer?.complete(); + } + + 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)); + await dbHelper.saveMediaId( + episodeTask.episode.enclosureUrl, filePath, episodeTask.taskId); + _episodeTasks.removeWhere((element) => + element.episode.enclosureUrl == episodeTask.episode.enclosureUrl); + if (_episodeTasks.length == 0) _unbindBackgroundIsolate(); + } + + Future startTask(List episodes, + {bool showNotification = true}) async { + episodes.forEach((episode) 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); + }); + await _completer.future; + return; + } +} + +//For download episode inside app class DownloadState extends ChangeNotifier { DBHelper dbHelper = DBHelper(); List _episodeTasks = []; @@ -39,13 +141,13 @@ class DownloadState extends ChangeNotifier { DownloadState() { _autoDelete(); + _bindBackgroundIsolate(); + FlutterDownloader.registerCallback(downloadCallback); } @override void addListener(VoidCallback listener) async { _loadTasks(); - _bindBackgroundIsolate(); - FlutterDownloader.registerCallback(downloadCallback); super.addListener(listener); } diff --git a/lib/state/podcast_group.dart b/lib/state/podcast_group.dart index 8f9b78b..b484b98 100644 --- a/lib/state/podcast_group.dart +++ b/lib/state/podcast_group.dart @@ -1,5 +1,5 @@ -import 'dart:collection'; import 'dart:core'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:tsacdop/local_storage/key_value_storage.dart'; diff --git a/lib/state/refresh_podcast.dart b/lib/state/refresh_podcast.dart index 2425cd7..5c14fb4 100644 --- a/lib/state/refresh_podcast.dart +++ b/lib/state/refresh_podcast.dart @@ -76,7 +76,5 @@ Future refreshIsolateEntryPoint(SendPort sendPort) async { int updateCount = await dbHelper.updatePodcastRss(podcastLocal); print('Refresh ' + podcastLocal.title + updateCount.toString()); }); - // KeyValueStorage refreshcountstorage = KeyValueStorage('refreshcount'); - // await refreshcountstorage.saveInt(i); sendPort.send("done"); } diff --git a/lib/state/settingstate.dart b/lib/state/settingstate.dart index 8cc2be7..e3ad564 100644 --- a/lib/state/settingstate.dart +++ b/lib/state/settingstate.dart @@ -1,8 +1,11 @@ import 'dart:io'; +import 'dart:isolate'; +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 '../local_storage/sqflite_localpodcast.dart'; import '../local_storage/key_value_storage.dart'; @@ -20,30 +23,27 @@ void callbackDispatcher() { KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey); int lastWork = await lastWorkStorage.getInt(); await Future.forEach(podcastList, (podcastLocal) async { - int updateCount = - await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork); - bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id); - if (autoDownload && updateCount > 0) { - var result = await Connectivity().checkConnectivity(); - KeyValueStorage autoDownloadStorage = - KeyValueStorage(autoDownloadNetworkKey); - int autoDownloadNetwork = await autoDownloadStorage.getInt(); - if (autoDownloadNetwork == 1) { - List episodes = - await dbHelper.getNewEpisodes(podcastLocal.id); - episodes.forEach((episode) { - DownloadState().startTask(episode, showNotification: true); - }); - } else if (result == ConnectivityResult.wifi) { - List episodes = - await dbHelper.getNewEpisodes(podcastLocal.id); - episodes.forEach((episode) { - DownloadState().startTask(episode, showNotification: true); - }); - } - } + await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork); print('Refresh ' + podcastLocal.title); }); + await FlutterDownloader.initialize(); + AutoDownloader downloader = AutoDownloader(); + downloader.bindBackgroundIsolate(); + KeyValueStorage autoDownloadStorage = + KeyValueStorage(autoDownloadNetworkKey); + int autoDownloadNetwork = await autoDownloadStorage.getInt(); + var result = await Connectivity().checkConnectivity(); + if (autoDownloadNetwork == 1) { + List episodes = await dbHelper.getNewEpisodes('all'); + // For safety + if (episodes.length < 100 && episodes.length > 0) + await downloader.startTask(episodes); + } else if (result == ConnectivityResult.wifi) { + List episodes = await dbHelper.getNewEpisodes('all'); + //For safety + if (episodes.length < 100 && episodes.length > 0) + await downloader.startTask(episodes); + } await lastWorkStorage.saveInt(1); KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey); await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);