Auto download

This commit is contained in:
stonegate 2020-06-13 01:56:13 +08:00
parent e69a2dbc00
commit 0b20c24984
12 changed files with 169 additions and 71 deletions

View File

@ -42,14 +42,14 @@ class Import extends StatelessWidget {
// For safety // For safety
if (episodes.length < 100) if (episodes.length < 100)
episodes.forEach((episode) { episodes.forEach((episode) {
downloader.startTask(episode, showNotification: false); downloader.startTask(episode, showNotification: true);
}); });
} else if (result == ConnectivityResult.wifi) { } else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all'); List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
//For safety //For safety
if (episodes.length < 100) if (episodes.length < 100)
episodes.forEach((episode) { episodes.forEach((episode) {
downloader.startTask(episode, showNotification: false); downloader.startTask(episode, showNotification: true);
}); });
} }
} }
@ -66,9 +66,9 @@ class Import extends StatelessWidget {
case SubscribeState.start: case SubscribeState.start:
return importColumn("Subscribe ${item.title}", context); return importColumn("Subscribe ${item.title}", context);
case SubscribeState.subscribe: case SubscribeState.subscribe:
groupList.subscribeNewPodcast(item.id);
return importColumn("Fetch data ${item.title}", context); return importColumn("Fetch data ${item.title}", context);
case SubscribeState.fetch: case SubscribeState.fetch:
groupList.subscribeNewPodcast(item.id);
// groupList.updatePodcast(item.id); // groupList.updatePodcast(item.id);
return importColumn("Subscribe success ${item.title}", context); return importColumn("Subscribe success ${item.title}", context);
case SubscribeState.exist: case SubscribeState.exist:

View File

@ -328,36 +328,28 @@ class _PlayedHistoryState extends State<PlayedHistory>
Text(snapshot.data[index].title), Text(snapshot.data[index].title),
], ],
), ),
subtitle: Row( subtitle: _status
children: <Widget>[ ? Text(DateTime.now()
_status .difference(snapshot
? Text(DateTime.now() .data[index].subDate)
.difference(snapshot .inDays
.data[index].subDate) .toString() +
.inDays ' day ago')
.toString() + : Text(
' day ago') 'Removed at ' +
: Text(snapshot.data[index].delDate DateFormat.yMd()
.difference(snapshot .add_jm()
.data[index].subDate) .format(snapshot
.inDays .data[index].delDate),
.toString() + style: TextStyle(color: Colors.red),
' day on your device'), ),
Spacer(), // Text(snapshot.data[index].delDate
!_status // .difference(snapshot
? Text( // .data[index].subDate)
'Removed at ' + // .inDays
DateFormat.yMd() // .toString() +
.add_jm() // ' day on your device'),
.format(snapshot
.data[index]
.delDate),
style: TextStyle(
color: Colors.red),
)
: Center(),
],
),
trailing: !_status trailing: !_status
? Material( ? Material(
color: Colors.transparent, color: Colors.transparent,

View File

@ -133,7 +133,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Episode popup menu', child: Text('Episode popup menu',
style: Theme.of(context) style: Theme.of(context)
@ -156,9 +156,12 @@ class _LayoutSettingState extends State<LayoutSetting> {
subtitle: Text('Change the menu when long tap episode'), subtitle: Text('Change the menu when long tap episode'),
), ),
Divider(height: 2), Divider(height: 2),
Padding(
padding: EdgeInsets.all(10.0),
),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Default grid view', child: Text('Default grid view',
style: Theme.of(context) style: Theme.of(context)

View File

@ -36,7 +36,7 @@ class PlaySetting extends StatelessWidget {
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Playlist', child: Text('Playlist',
style: Theme.of(context) style: Theme.of(context)

View File

@ -132,7 +132,7 @@ class _SettingsState extends State<Settings>
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Prefrence', child: Text('Prefrence',
style: Theme.of(context) style: Theme.of(context)
@ -241,7 +241,7 @@ class _SettingsState extends State<Settings>
children: <Widget>[ children: <Widget>[
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Info', child: Text('Info',
style: Theme.of(context) style: Theme.of(context)

View File

@ -105,7 +105,7 @@ class _StorageSettingState extends State<StorageSetting>
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Network', child: Text('Network',
style: Theme.of(context) style: Theme.of(context)
@ -174,7 +174,7 @@ class _StorageSettingState extends State<StorageSetting>
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Storage', child: Text('Storage',
style: Theme.of(context) style: Theme.of(context)

View File

@ -40,7 +40,7 @@ class SyncingSetting extends StatelessWidget {
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Syncing', child: Text('Syncing',
style: Theme.of(context) style: Theme.of(context)
@ -48,6 +48,9 @@ class SyncingSetting extends StatelessWidget {
.bodyText1 .bodyText1
.copyWith(color: Theme.of(context).accentColor)), .copyWith(color: Theme.of(context).accentColor)),
), ),
Padding(
padding: EdgeInsets.all(5.0),
),
ListView( ListView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,

View File

@ -32,7 +32,7 @@ class ThemeSetting extends StatelessWidget {
), ),
Container( Container(
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('Interface', child: Text('Interface',
style: Theme.of(context) style: Theme.of(context)

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:isolate'; import 'dart:isolate';
import 'dart:ui'; import 'dart:ui';
@ -7,9 +8,9 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path; 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'; import '../type/episodebrief.dart';
class EpisodeTask { class EpisodeTask {
@ -32,6 +33,107 @@ void downloadCallback(String id, DownloadTaskStatus status, int progress) {
send.send([id, status, 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<EpisodeTask> _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<EpisodeBrief> 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 { class DownloadState extends ChangeNotifier {
DBHelper dbHelper = DBHelper(); DBHelper dbHelper = DBHelper();
List<EpisodeTask> _episodeTasks = []; List<EpisodeTask> _episodeTasks = [];
@ -39,13 +141,13 @@ class DownloadState extends ChangeNotifier {
DownloadState() { DownloadState() {
_autoDelete(); _autoDelete();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
} }
@override @override
void addListener(VoidCallback listener) async { void addListener(VoidCallback listener) async {
_loadTasks(); _loadTasks();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
super.addListener(listener); super.addListener(listener);
} }

View File

@ -1,5 +1,5 @@
import 'dart:collection';
import 'dart:core'; import 'dart:core';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tsacdop/local_storage/key_value_storage.dart'; import 'package:tsacdop/local_storage/key_value_storage.dart';

View File

@ -76,7 +76,5 @@ Future<void> refreshIsolateEntryPoint(SendPort sendPort) async {
int updateCount = await dbHelper.updatePodcastRss(podcastLocal); int updateCount = await dbHelper.updatePodcastRss(podcastLocal);
print('Refresh ' + podcastLocal.title + updateCount.toString()); print('Refresh ' + podcastLocal.title + updateCount.toString());
}); });
// KeyValueStorage refreshcountstorage = KeyValueStorage('refreshcount');
// await refreshcountstorage.saveInt(i);
sendPort.send("done"); sendPort.send("done");
} }

View File

@ -1,8 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:connectivity/connectivity.dart'; import 'package:connectivity/connectivity.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart'; import 'package:workmanager/workmanager.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import '../local_storage/sqflite_localpodcast.dart'; import '../local_storage/sqflite_localpodcast.dart';
import '../local_storage/key_value_storage.dart'; import '../local_storage/key_value_storage.dart';
@ -20,30 +23,27 @@ void callbackDispatcher() {
KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey); KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey);
int lastWork = await lastWorkStorage.getInt(); int lastWork = await lastWorkStorage.getInt();
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async { await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
int updateCount = await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
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<EpisodeBrief> episodes =
await dbHelper.getNewEpisodes(podcastLocal.id);
episodes.forEach((episode) {
DownloadState().startTask(episode, showNotification: true);
});
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes =
await dbHelper.getNewEpisodes(podcastLocal.id);
episodes.forEach((episode) {
DownloadState().startTask(episode, showNotification: true);
});
}
}
print('Refresh ' + podcastLocal.title); 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<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
// For safety
if (episodes.length < 100 && episodes.length > 0)
await downloader.startTask(episodes);
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
//For safety
if (episodes.length < 100 && episodes.length > 0)
await downloader.startTask(episodes);
}
await lastWorkStorage.saveInt(1); await lastWorkStorage.saveInt(1);
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey); KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch); await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);