120 lines
4.1 KiB
Dart
120 lines
4.1 KiB
Dart
// Copyright 2020 Ben Hills and the project contributors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
import 'dart:isolate';
|
|
import 'dart:ui';
|
|
|
|
import 'package:pinepods_mobile/core/environment.dart';
|
|
import 'package:pinepods_mobile/entities/downloadable.dart';
|
|
import 'package:pinepods_mobile/services/download/download_manager.dart';
|
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
|
import 'package:logging/logging.dart';
|
|
|
|
/// A [DownloadManager] for handling downloading of podcasts on a mobile device.
|
|
@pragma('vm:entry-point')
|
|
class MobileDownloaderManager implements DownloadManager {
|
|
static const portName = 'downloader_send_port';
|
|
final log = Logger('MobileDownloaderManager');
|
|
final ReceivePort _port = ReceivePort();
|
|
final downloadController = StreamController<DownloadProgress>();
|
|
var _lastUpdateTime = 0;
|
|
|
|
@override
|
|
Stream<DownloadProgress> get downloadProgress => downloadController.stream;
|
|
|
|
MobileDownloaderManager() {
|
|
_init();
|
|
}
|
|
|
|
Future _init() async {
|
|
log.fine('Initialising download manager');
|
|
|
|
await FlutterDownloader.initialize();
|
|
IsolateNameServer.removePortNameMapping(portName);
|
|
|
|
IsolateNameServer.registerPortWithName(_port.sendPort, portName);
|
|
|
|
var tasks = await FlutterDownloader.loadTasks();
|
|
|
|
// Update the status of any tasks that may have been updated whilst
|
|
// Pinepods was close or in the background.
|
|
if (tasks != null && tasks.isNotEmpty) {
|
|
for (var t in tasks) {
|
|
_updateDownloadState(id: t.taskId, progress: t.progress, status: t.status);
|
|
|
|
/// If we are not queued or running we can safely clean up this event
|
|
if (t.status != DownloadTaskStatus.enqueued && t.status != DownloadTaskStatus.running) {
|
|
FlutterDownloader.remove(taskId: t.taskId, shouldDeleteContent: false);
|
|
}
|
|
}
|
|
}
|
|
|
|
_port.listen((dynamic data) {
|
|
final id = (data as List<dynamic>)[0] as String;
|
|
final status = DownloadTaskStatus.fromInt(data[1] as int);
|
|
final progress = data[2] as int;
|
|
|
|
_updateDownloadState(id: id, progress: progress, status: status);
|
|
});
|
|
|
|
FlutterDownloader.registerCallback(downloadCallback);
|
|
}
|
|
|
|
@override
|
|
Future<String?> enqueueTask(String url, String downloadPath, String fileName) async {
|
|
return await FlutterDownloader.enqueue(
|
|
url: url,
|
|
savedDir: downloadPath,
|
|
fileName: fileName,
|
|
showNotification: true,
|
|
openFileFromNotification: false,
|
|
headers: {
|
|
'User-Agent': Environment.userAgent(),
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
IsolateNameServer.removePortNameMapping(portName);
|
|
downloadController.close();
|
|
}
|
|
|
|
void _updateDownloadState({required String id, required int progress, required DownloadTaskStatus status}) {
|
|
var state = DownloadState.none;
|
|
var updateTime = DateTime.now().millisecondsSinceEpoch;
|
|
|
|
if (status == DownloadTaskStatus.enqueued) {
|
|
state = DownloadState.queued;
|
|
} else if (status == DownloadTaskStatus.canceled) {
|
|
state = DownloadState.cancelled;
|
|
} else if (status == DownloadTaskStatus.complete) {
|
|
state = DownloadState.downloaded;
|
|
} else if (status == DownloadTaskStatus.running) {
|
|
state = DownloadState.downloading;
|
|
} else if (status == DownloadTaskStatus.failed) {
|
|
state = DownloadState.failed;
|
|
} else if (status == DownloadTaskStatus.paused) {
|
|
state = DownloadState.paused;
|
|
}
|
|
|
|
/// If we are running, we want to limit notifications to 1 per second. Otherwise,
|
|
/// small downloads can cause a flood of events. Any other status we always want
|
|
/// to push through.
|
|
if (status != DownloadTaskStatus.running ||
|
|
progress == 0 ||
|
|
progress == 100 ||
|
|
updateTime > _lastUpdateTime + 1000) {
|
|
downloadController.add(DownloadProgress(id, progress, state));
|
|
_lastUpdateTime = updateTime;
|
|
}
|
|
}
|
|
|
|
@pragma('vm:entry-point')
|
|
static void downloadCallback(String id, int status, int progress) {
|
|
IsolateNameServer.lookupPortByName('downloader_send_port')?.send([id, status, progress]);
|
|
}
|
|
}
|