added background task support

This commit is contained in:
Benimautner 2023-01-07 16:55:40 +01:00
parent 40fb339a59
commit 59f0e7ec7e
6 changed files with 138 additions and 63 deletions

View File

@ -20,6 +20,7 @@ import 'package:vikunja_app/models/user.dart';
import 'package:vikunja_app/service/services.dart'; import 'package:vikunja_app/service/services.dart';
import 'package:timezone/data/latest_all.dart' as tz; import 'package:timezone/data/latest_all.dart' as tz;
import 'package:flutter_local_notifications/flutter_local_notifications.dart'as notifs; import 'package:flutter_local_notifications/flutter_local_notifications.dart'as notifs;
import 'package:workmanager/workmanager.dart';
class VikunjaGlobal extends StatefulWidget { class VikunjaGlobal extends StatefulWidget {
@ -108,7 +109,18 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
late String currentTimeZone; late String currentTimeZone;
void updateWorkmanagerDuration() {
Workmanager().cancelAll().then((value) {
settingsManager.getWorkmanagerDuration().then((duration) =>
{
if(duration.inMinutes > 0) {
Workmanager().registerPeriodicTask(
"update-tasks", "update-tasks", frequency: duration,
initialDelay: Duration.zero, inputData: {})
}
});
});
}
@override @override
void initState() { void initState() {
@ -129,6 +141,8 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
versionChecker.postVersionCheckSnackbar(); versionChecker.postVersionCheckSnackbar();
} }
}); });
updateWorkmanagerDuration();
} }
void changeUser(User newUser, {String? token, String? base}) async { void changeUser(User newUser, {String? token, String? base}) async {
@ -194,6 +208,7 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
); );
} }
} }
print("notifications scheduled successfully");
} }

View File

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
import 'package:vikunja_app/global.dart'; import 'package:vikunja_app/global.dart';
import 'package:vikunja_app/pages/home.dart'; import 'package:vikunja_app/pages/home.dart';
import 'package:vikunja_app/pages/user/login.dart'; import 'package:vikunja_app/pages/user/login.dart';
@ -17,7 +18,21 @@ class IgnoreCertHttpOverrides extends HttpOverrides {
} }
} }
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) {
print("Native called background task: $task"); //simpleTask will be emitted here.
if(task == "update-tasks") {
//TODO
}
return get(Uri.parse("https://webhook.site/"), headers: {"task":"$task", "data":"$inputData"}).then((value) => Future.value(true));
});
}
void main() { void main() {
WidgetsFlutterBinding.ensureInitialized();
Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
runApp(VikunjaGlobal( runApp(VikunjaGlobal(
child: new VikunjaApp(home: HomePage(), key: UniqueKey(),), child: new VikunjaApp(home: HomePage(), key: UniqueKey(),),
login: new VikunjaApp(home: LoginPage(), key: UniqueKey(),))); login: new VikunjaApp(home: LoginPage(), key: UniqueKey(),)));

View File

@ -15,36 +15,45 @@ class SettingsPageState extends State<SettingsPage> {
bool? ignoreCertificates; bool? ignoreCertificates;
bool? getVersionNotifications; bool? getVersionNotifications;
String? versionTag, newestVersionTag; String? versionTag, newestVersionTag;
late TextEditingController durationTextController;
bool initialized = false;
void init() {
durationTextController = TextEditingController();
VikunjaGlobal.of(context)
.listService
.getAll()
.then((value) => setState(() => taskListList = value));
VikunjaGlobal.of(context).listService.getDefaultList().then((value) =>
setState(
() => defaultList = value == null ? null : int.tryParse(value)));
VikunjaGlobal.of(context).settingsManager.getIgnoreCertificates().then(
(value) =>
setState(() => ignoreCertificates = value == "1" ? true : false));
VikunjaGlobal.of(context).settingsManager.getVersionNotifications().then(
(value) => setState(
() => getVersionNotifications = value == "1" ? true : false));
VikunjaGlobal.of(context)
.versionChecker
.getCurrentVersionTag()
.then((value) => setState(() => versionTag = value));
VikunjaGlobal.of(context)
.settingsManager
.getWorkmanagerDuration()
.then((value) => setState(() => durationTextController.text = (value.inMinutes.toString())));
initialized = true;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (taskListList == null) if (!initialized) init();
VikunjaGlobal.of(context)
.listService
.getAll()
.then((value) => setState(() => taskListList = value));
if (defaultList == null)
VikunjaGlobal.of(context).listService.getDefaultList().then((value) =>
setState(
() => defaultList = value == null ? null : int.tryParse(value)));
if (ignoreCertificates == null)
VikunjaGlobal.of(context).settingsManager.getIgnoreCertificates().then(
(value) =>
setState(() => ignoreCertificates = value == "1" ? true : false));
if (getVersionNotifications == null)
VikunjaGlobal.of(context).settingsManager.getVersionNotifications().then(
(value) => setState(
() => getVersionNotifications = value == "1" ? true : false));
if (versionTag == null)
VikunjaGlobal.of(context)
.versionChecker
.getCurrentVersionTag()
.then((value) => setState(() => versionTag = value));
return new Scaffold( return new Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("Settings"), title: Text("Settings"),
@ -86,20 +95,44 @@ class SettingsPageState extends State<SettingsPage> {
VikunjaGlobal.of(context).client.reload_ignore_certs(value); VikunjaGlobal.of(context).client.reload_ignore_certs(value);
}) })
: ListTile(title: Text("...")), : ListTile(title: Text("...")),
Padding(padding: EdgeInsets.only(left: 15, right: 15),
child: Row(children: [
Flexible(
child: TextField(
controller: durationTextController,
decoration: InputDecoration(
labelText: 'Background Refresh Interval (minutes): ',
helperText: 'Minimum: 15, Set limit of 0 for no refresh',
),
)),
TextButton(
onPressed: () => VikunjaGlobal.of(context)
.settingsManager
.setWorkmanagerDuration(Duration(
minutes: int.parse(durationTextController.text))),
child: Text("Save")),
]))
,
getVersionNotifications != null getVersionNotifications != null
? CheckboxListTile( ? CheckboxListTile(
title: Text("Get Version Notifications"), title: Text("Get Version Notifications"),
value: getVersionNotifications, value: getVersionNotifications,
onChanged: (value) { onChanged: (value) {
setState(() => getVersionNotifications = value); setState(() => getVersionNotifications = value);
if(value != null) if (value != null)
VikunjaGlobal.of(context).settingsManager.setVersionNotifications(value); VikunjaGlobal.of(context)
.settingsManager
.setVersionNotifications(value);
}) })
: ListTile(title: Text("...")), : ListTile(title: Text("...")),
TextButton(onPressed: () { TextButton(
sendTestNotification(VikunjaGlobal.of(context).notificationsPlugin onPressed: () {
, VikunjaGlobal.of(context).platformChannelSpecificsReminders); sendTestNotification(
}, child: Text("Send test notification")), VikunjaGlobal.of(context).notificationsPlugin,
VikunjaGlobal.of(context)
.platformChannelSpecificsReminders);
},
child: Text("Send test notification")),
TextButton( TextButton(
onPressed: () => VikunjaGlobal.of(context) onPressed: () => VikunjaGlobal.of(context)
.versionChecker .versionChecker

View File

@ -231,7 +231,8 @@ class SettingsManager {
Map<String, String> defaults = { Map<String, String> defaults = {
"ignore-certificates": "0", "ignore-certificates": "0",
"get-version-notifications": "1" "get-version-notifications": "1",
"workmanager-duration": "0"
}; };
SettingsManager(this._storage) { SettingsManager(this._storage) {
@ -245,16 +246,26 @@ class SettingsManager {
Future<String?> getIgnoreCertificates() { Future<String?> getIgnoreCertificates() {
return _storage.read(key: "ignore-certificates"); return _storage.read(key: "ignore-certificates");
} }
Future<String?> getVersionNotifications() {
return _storage.read(key: "get-version-notifications");
}
void setIgnoreCertificates(bool value) { void setIgnoreCertificates(bool value) {
_storage.write(key: "ignore-certificates", value: value ? "1" : "0"); _storage.write(key: "ignore-certificates", value: value ? "1" : "0");
} }
Future<String?> getVersionNotifications() {
return _storage.read(key: "get-version-notifications");
}
void setVersionNotifications(bool value) { void setVersionNotifications(bool value) {
_storage.write(key: "get-version-notifications", value: value ? "1" : "0"); _storage.write(key: "get-version-notifications", value: value ? "1" : "0");
} }
Future<Duration> getWorkmanagerDuration() {
return _storage.read(key: "workmanager-duration").then((value) => Duration(minutes: int.parse(value ?? "0")));
}
void setWorkmanagerDuration(Duration duration) {
_storage.write(key: "workmanager-duration", value: duration.inMinutes.toString());
}
} }

View File

@ -42,7 +42,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.2" version: "2.9.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -70,14 +70,7 @@ packages:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
checked_yaml: checked_yaml:
dependency: transitive dependency: transitive
description: description:
@ -98,7 +91,7 @@ packages:
name: clock name: clock
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -168,7 +161,7 @@ packages:
name: fake_async name: fake_async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@ -400,21 +393,21 @@ packages:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.11" version: "0.12.12"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4" version: "0.1.5"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.8.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -463,7 +456,7 @@ packages:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.1" version: "1.8.2"
path_drawing: path_drawing:
dependency: transitive dependency: transitive
description: description:
@ -608,7 +601,7 @@ packages:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.2" version: "1.9.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -629,35 +622,35 @@ packages:
name: string_scanner name: string_scanner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
test: test:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: test name: test
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.21.1" version: "1.21.4"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.9" version: "0.4.12"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.13" version: "0.4.16"
timezone: timezone:
dependency: transitive dependency: transitive
description: description:
@ -798,6 +791,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
workmanager:
dependency: "direct main"
description:
name: workmanager
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -820,5 +820,5 @@ packages:
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
sdks: sdks:
dart: ">=2.17.0 <3.0.0" dart: ">=2.18.0 <3.0.0"
flutter: ">=3.0.0" flutter: ">=3.0.0"

View File

@ -4,7 +4,7 @@ description: Vikunja as Flutter cross platform app
version: 0.0.24-alpha version: 0.0.24-alpha
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.18.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
@ -29,6 +29,7 @@ dependencies:
dotted_border: ^2.0.0+2 dotted_border: ^2.0.0+2
package_info_plus: ^3.0.2 package_info_plus: ^3.0.2
url_launcher: ^6.1.7 url_launcher: ^6.1.7
workmanager: ^0.5.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: