tsacdop-podcast-app-android/lib/state/download_state.dart

366 lines
12 KiB
Dart
Raw Normal View History

2020-06-12 19:56:13 +02:00
import 'dart:async';
2020-08-10 20:41:22 +02:00
import 'dart:developer' as developer;
2020-03-31 18:36:20 +02:00
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
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;
2020-06-12 19:56:13 +02:00
import '../local_storage/key_value_storage.dart';
import '../local_storage/sqflite_localpodcast.dart';
2020-05-06 18:50:32 +02:00
import '../type/episodebrief.dart';
2020-03-31 18:36:20 +02:00
class EpisodeTask {
final String taskId;
int progress;
DownloadTaskStatus status;
final EpisodeBrief episode;
EpisodeTask(
this.episode,
this.taskId, {
this.progress = 0,
this.status = DownloadTaskStatus.undefined,
});
}
void downloadCallback(String id, DownloadTaskStatus status, int progress) {
2020-08-10 20:41:22 +02:00
developer.log('Homepage callback task in $id status ($status) $progress');
2020-07-26 12:20:42 +02:00
final send = IsolateNameServer.lookupPortByName('downloader_send_port');
2020-03-31 18:36:20 +02:00
send.send([id, status, progress]);
}
2020-06-12 19:56:13 +02:00
void autoDownloadCallback(String id, DownloadTaskStatus status, int progress) {
2020-08-10 20:41:22 +02:00
developer
.log('Autodownload callback task in $id status ($status) $progress');
2020-07-26 12:20:42 +02:00
final send = IsolateNameServer.lookupPortByName('auto_downloader_send_port');
2020-06-12 19:56:13 +02:00
send.send([id, status, progress]);
}
//For background auto downlaod
class AutoDownloader {
DBHelper dbHelper = DBHelper();
2020-07-26 12:20:42 +02:00
final List<EpisodeTask> _episodeTasks = [];
final Completer _completer = Completer();
2020-06-12 19:56:13 +02:00
AutoDownloader() {
FlutterDownloader.registerCallback(autoDownloadCallback);
}
bindBackgroundIsolate() {
2020-07-26 12:20:42 +02:00
var _port = ReceivePort();
var isSuccess = IsolateNameServer.registerPortWithName(
2020-06-12 19:56:13 +02:00
_port.sendPort, 'auto_downloader_send_port');
if (!isSuccess) {
2020-06-16 06:40:51 +02:00
IsolateNameServer.removePortNameMapping('auto_downloader_send_port');
bindBackgroundIsolate();
2020-06-12 19:56:13 +02:00
return;
}
_port.listen((dynamic data) {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
2020-07-13 09:41:59 +02:00
for (var episodeTask in _episodeTasks) {
2020-06-12 19:56:13 +02:00
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();
}
}
2020-07-13 09:41:59 +02:00
}
2020-06-12 19:56:13 +02:00
});
}
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}'");
2020-07-26 12:20:42 +02:00
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,
episodeTask.taskId, fileStat.size);
2020-06-12 19:56:13 +02:00
_episodeTasks.removeWhere((element) =>
element.episode.enclosureUrl == episodeTask.episode.enclosureUrl);
if (_episodeTasks.length == 0) _unbindBackgroundIsolate();
}
Future startTask(List<EpisodeBrief> episodes,
2020-06-16 06:40:51 +02:00
{bool showNotification = false}) async {
2020-07-13 09:41:59 +02:00
for (var episode in episodes) {
2020-06-12 19:56:13 +02:00
final dir = await getExternalStorageDirectory();
2020-07-26 12:20:42 +02:00
var localPath = path.join(dir.path, episode.feedTitle);
2020-06-12 19:56:13 +02:00
final saveDir = Directory(localPath);
2020-07-26 12:20:42 +02:00
var hasExisted = await saveDir.exists();
2020-06-12 19:56:13 +02:00
if (!hasExisted) {
saveDir.create();
}
2020-07-26 12:20:42 +02:00
var now = DateTime.now();
var datePlus = now.year.toString() +
2020-06-12 19:56:13 +02:00
now.month.toString() +
now.day.toString() +
now.second.toString();
2020-07-26 12:20:42 +02:00
var fileName =
'${episode.title}$datePlus.${episode.enclosureUrl.split('/').last.split('.').last}';
var taskId = await FlutterDownloader.enqueue(
2020-06-12 19:56:13 +02:00
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);
2020-07-13 09:41:59 +02:00
}
2020-06-12 19:56:13 +02:00
await _completer.future;
return;
}
}
//For download episode inside app
2020-03-31 18:36:20 +02:00
class DownloadState extends ChangeNotifier {
DBHelper dbHelper = DBHelper();
List<EpisodeTask> _episodeTasks = [];
List<EpisodeTask> get episodeTasks => _episodeTasks;
DownloadState() {
_autoDelete();
2020-06-12 19:56:13 +02:00
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
}
2020-03-31 18:36:20 +02:00
@override
void addListener(VoidCallback listener) async {
_loadTasks();
super.addListener(listener);
}
2020-08-19 12:09:05 +02:00
Future<void> _loadTasks() async {
2020-03-31 18:36:20 +02:00
_episodeTasks = [];
2020-07-26 12:20:42 +02:00
var dbHelper = DBHelper();
2020-03-31 18:36:20 +02:00
var tasks = await FlutterDownloader.loadTasks();
2020-07-26 12:20:42 +02:00
if (tasks.length != 0) {
2020-07-13 09:41:59 +02:00
for (var task in tasks) {
2020-07-26 12:20:42 +02:00
var episode = await dbHelper.getRssItemWithUrl(task.url);
if (episode == null) {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
2020-07-26 12:20:42 +02:00
} else {
2020-08-19 12:09:05 +02:00
if (task.status == DownloadTaskStatus.complete) {
var exist =
await File(path.join(task.savedDir, task.filename)).exists();
if (!exist) {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await dbHelper.delDownloaded(episode.enclosureUrl);
} else {
_episodeTasks.add(EpisodeTask(episode, task.taskId,
progress: task.progress, status: task.status));
}
} else {
_episodeTasks.add(EpisodeTask(episode, task.taskId,
progress: task.progress, status: task.status));
}
2020-07-26 12:20:42 +02:00
}
2020-07-13 09:41:59 +02:00
}
2020-07-26 12:20:42 +02:00
}
2020-03-31 18:36:20 +02:00
notifyListeners();
}
void _bindBackgroundIsolate() {
2020-07-26 12:20:42 +02:00
var _port = ReceivePort();
var isSuccess = IsolateNameServer.registerPortWithName(
2020-03-31 18:36:20 +02:00
_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
_bindBackgroundIsolate();
return;
}
_port.listen((dynamic data) {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
2020-07-13 09:41:59 +02:00
for (var episodeTask in _episodeTasks) {
2020-03-31 18:36:20 +02:00
if (episodeTask.taskId == id) {
episodeTask.status = status;
episodeTask.progress = progress;
if (status == DownloadTaskStatus.complete) {
_saveMediaId(episodeTask).then((value) {
notifyListeners();
});
2020-07-26 12:20:42 +02:00
} else {
2020-03-31 18:36:20 +02:00
notifyListeners();
2020-07-26 12:20:42 +02:00
}
2020-03-31 18:36:20 +02:00
}
2020-07-13 09:41:59 +02:00
}
2020-03-31 18:36:20 +02:00
});
}
Future _saveMediaId(EpisodeTask episodeTask) async {
episodeTask.status = DownloadTaskStatus.complete;
final completeTask = await FlutterDownloader.loadTasksWithRawQuery(
2020-04-11 19:23:12 +02:00
query: "SELECT * FROM task WHERE task_id = '${episodeTask.taskId}'");
2020-07-26 12:20:42 +02:00
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();
dbHelper.saveMediaId(episodeTask.episode.enclosureUrl, filePath,
episodeTask.taskId, fileStat.size);
2020-07-26 12:20:42 +02:00
var episode =
2020-04-11 19:23:12 +02:00
await dbHelper.getRssItemWithUrl(episodeTask.episode.enclosureUrl);
_removeTask(episodeTask.episode);
_episodeTasks.add(EpisodeTask(episode, episodeTask.taskId,
progress: 100, status: DownloadTaskStatus.complete));
2020-03-31 18:36:20 +02:00
}
void _unbindBackgroundIsolate() {
IsolateNameServer.removePortNameMapping('downloader_send_port');
}
EpisodeTask episodeToTask(EpisodeBrief episode) {
return _episodeTasks
.firstWhere((task) => task.episode.enclosureUrl == episode.enclosureUrl,
orElse: () {
return EpisodeTask(
episode,
'',
);
});
}
@override
void dispose() {
_unbindBackgroundIsolate();
super.dispose();
}
Future startTask(EpisodeBrief episode, {bool showNotification = true}) async {
2020-06-07 20:42:27 +02:00
var dbHelper = DBHelper();
2020-07-26 12:20:42 +02:00
var isDownloaded = await dbHelper.isDownloaded(episode.enclosureUrl);
if (!isDownloaded) {
final dir = await getExternalStorageDirectory();
2020-07-26 12:20:42 +02:00
var localPath = path.join(dir.path, episode.feedTitle);
final saveDir = Directory(localPath);
2020-07-26 12:20:42 +02:00
var hasExisted = await saveDir.exists();
if (!hasExisted) {
saveDir.create();
}
2020-07-26 12:20:42 +02:00
var now = DateTime.now();
var datePlus = now.year.toString() +
now.month.toString() +
now.day.toString() +
now.second.toString();
2020-07-26 12:20:42 +02:00
var fileName =
'${episode.title}$datePlus.${episode.enclosureUrl.split('/').last.split('.').last}';
var 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();
}
2020-03-31 18:36:20 +02:00
}
Future pauseTask(EpisodeBrief episode) async {
2020-07-26 12:20:42 +02:00
var task = episodeToTask(episode);
2020-03-31 18:36:20 +02:00
await FlutterDownloader.pause(taskId: task.taskId);
}
Future resumeTask(EpisodeBrief episode) async {
2020-07-26 12:20:42 +02:00
var task = episodeToTask(episode);
var newTaskId = await FlutterDownloader.resume(taskId: task.taskId);
var index = _episodeTasks.indexOf(task);
2020-03-31 18:36:20 +02:00
_removeTask(episode);
FlutterDownloader.remove(taskId: task.taskId);
var dbHelper = DBHelper();
_episodeTasks.insert(index, EpisodeTask(episode, newTaskId));
2020-06-27 20:27:39 +02:00
await dbHelper.saveDownloaded(episode.enclosureUrl, newTaskId);
2020-03-31 18:36:20 +02:00
}
Future retryTask(EpisodeBrief episode) async {
2020-07-26 12:20:42 +02:00
var task = episodeToTask(episode);
var newTaskId = await FlutterDownloader.retry(taskId: task.taskId);
2020-03-31 18:36:20 +02:00
await FlutterDownloader.remove(taskId: task.taskId);
2020-07-26 12:20:42 +02:00
var index = _episodeTasks.indexOf(task);
2020-03-31 18:36:20 +02:00
_removeTask(episode);
var dbHelper = DBHelper();
_episodeTasks.insert(index, EpisodeTask(episode, newTaskId));
2020-06-27 20:27:39 +02:00
await dbHelper.saveDownloaded(episode.enclosureUrl, newTaskId);
2020-03-31 18:36:20 +02:00
}
Future removeTask(EpisodeBrief episode) async {
2020-07-26 12:20:42 +02:00
var task = episodeToTask(episode);
2020-03-31 18:36:20 +02:00
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: false);
}
Future delTask(EpisodeBrief episode) async {
2020-07-26 12:20:42 +02:00
var task = episodeToTask(episode);
2020-03-31 18:36:20 +02:00
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await dbHelper.delDownloaded(episode.enclosureUrl);
2020-07-13 09:41:59 +02:00
for (var episodeTask in _episodeTasks) {
2020-07-26 12:20:42 +02:00
if (episodeTask.taskId == task.taskId) {
2020-03-31 18:36:20 +02:00
episodeTask.status = DownloadTaskStatus.undefined;
2020-07-26 12:20:42 +02:00
}
2020-03-31 18:36:20 +02:00
notifyListeners();
2020-07-13 09:41:59 +02:00
}
2020-03-31 18:36:20 +02:00
_removeTask(episode);
}
_removeTask(EpisodeBrief episode) {
_episodeTasks.removeWhere((element) => element.episode == episode);
2020-06-27 20:27:39 +02:00
notifyListeners();
2020-03-31 18:36:20 +02:00
}
_autoDelete() async {
2020-08-10 20:41:22 +02:00
developer.log('Start auto delete outdated episodes');
2020-07-26 12:20:42 +02:00
var autoDeleteStorage = KeyValueStorage(autoDeleteKey);
var autoDelete = await autoDeleteStorage.getInt();
if (autoDelete == 0) {
await autoDeleteStorage.saveInt(30);
2020-07-26 12:20:42 +02:00
} else if (autoDelete > 0) {
var deadline = DateTime.now()
.subtract(Duration(days: autoDelete))
.millisecondsSinceEpoch;
2020-07-26 12:20:42 +02:00
var episodes = await dbHelper.getOutdatedEpisode(deadline);
2020-07-13 09:41:59 +02:00
if (episodes.isNotEmpty) {
2020-07-26 12:20:42 +02:00
for (var episode in episodes) {
await delTask(episode);
}
}
final tasks = await FlutterDownloader.loadTasksWithRawQuery(
query:
'SELECT * FROM task WHERE time_created < $deadline AND status = 3');
2020-07-26 12:20:42 +02:00
for (var task in tasks) {
2020-07-13 09:41:59 +02:00
FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
2020-07-26 12:20:42 +02:00
}
}
}
2020-03-31 18:36:20 +02:00
}