mirror of
https://github.com/go-vikunja/app
synced 2024-06-02 18:49:47 +00:00
implemented scheduled local notifications for reminders and tasks
This commit is contained in:
parent
db70990a3c
commit
a6fab24598
|
@ -31,6 +31,13 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
.post('/tasks/${task.id}', body: task.toJSON())
|
||||
.then((map) => Task.fromJson(map));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Task>> getAll() {
|
||||
return client
|
||||
.get('/tasks/all')
|
||||
.then((value) => value.map<Task>((taskJson) => Task.fromJson(taskJson)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Task>> getByOptions(TaskServiceOptions options) {
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
//import 'dart:math';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:vikunja_app/api/client.dart';
|
||||
import 'package:vikunja_app/api/list_implementation.dart';
|
||||
import 'package:vikunja_app/api/namespace_implementation.dart';
|
||||
import 'package:vikunja_app/api/task_implementation.dart';
|
||||
import 'package:vikunja_app/api/user_implementation.dart';
|
||||
import 'package:vikunja_app/managers/notifications.dart';
|
||||
import 'package:vikunja_app/managers/user.dart';
|
||||
import 'package:vikunja_app/models/user.dart';
|
||||
import 'package:vikunja_app/service/services.dart';
|
||||
import 'package:timezone/data/latest_all.dart' as tz;
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class VikunjaGlobal extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
@ -45,12 +52,21 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
|
||||
ListService get listService => new ListAPIService(client, _storage);
|
||||
|
||||
FlutterLocalNotificationsPlugin get notificationsPlugin => new FlutterLocalNotificationsPlugin();
|
||||
|
||||
TaskServiceOptions get taskServiceOptions => new TaskServiceOptions();
|
||||
|
||||
NotificationClass get notifications => new NotificationClass();
|
||||
|
||||
NotificationAppLaunchDetails notifLaunch;
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadCurrentUser();
|
||||
tz.initializeTimeZones();
|
||||
notificationInitializer();
|
||||
}
|
||||
|
||||
void changeUser(User newUser, {String token, String base}) async {
|
||||
|
@ -78,6 +94,27 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
});
|
||||
}
|
||||
|
||||
void notificationInitializer() async {
|
||||
notifLaunch = await notificationsPlugin.getNotificationAppLaunchDetails();
|
||||
await notifications.initNotifications(notificationsPlugin);
|
||||
requestIOSPermissions(notificationsPlugin);
|
||||
|
||||
}
|
||||
|
||||
void scheduleDueNotifications() {
|
||||
notificationsPlugin.cancelAll().then((value) {
|
||||
taskService.getAll().then((value) =>
|
||||
value.forEach((task) {
|
||||
if(task.reminders != null)
|
||||
task.reminders.forEach((reminder) {
|
||||
scheduleNotification("This is your reminder for '" + task.title + "'", task.description, notificationsPlugin, reminder);
|
||||
});
|
||||
scheduleNotification("The task '" + task.title + "' is due.", task.description, notificationsPlugin, task.due);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void logoutUser(BuildContext context) {
|
||||
_storage.deleteAll().then((_) {
|
||||
|
@ -138,6 +175,9 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
if (_loading) {
|
||||
return new Center(child: new CircularProgressIndicator());
|
||||
}
|
||||
if(client != null) {
|
||||
scheduleDueNotifications();
|
||||
}
|
||||
return new _VikunjaGlobalInherited(
|
||||
data: this,
|
||||
child: client == null ? widget.login : widget.child,
|
||||
|
|
81
lib/managers/notifications.dart
Normal file
81
lib/managers/notifications.dart
Normal file
|
@ -0,0 +1,81 @@
|
|||
// https://medium.com/@fuzzymemory/adding-scheduled-notifications-in-your-flutter-application-19be1f82ade8
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
|
||||
import 'package:timezone/data/latest_all.dart' as tz;
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart'as notifs;
|
||||
import 'package:rxdart/subjects.dart' as rxSub;
|
||||
|
||||
class NotificationClass{
|
||||
final int id;
|
||||
final String title;
|
||||
final String body;
|
||||
final String payload;
|
||||
NotificationClass({this.id, this.body, this.payload, this.title});
|
||||
|
||||
final rxSub.BehaviorSubject<NotificationClass> didReceiveLocalNotificationSubject =
|
||||
rxSub.BehaviorSubject<NotificationClass>();
|
||||
final rxSub.BehaviorSubject<String> selectNotificationSubject =
|
||||
rxSub.BehaviorSubject<String>();
|
||||
|
||||
Future<void> initNotifications(notifs.FlutterLocalNotificationsPlugin notifsPlugin) async {
|
||||
var initializationSettingsAndroid =
|
||||
notifs.AndroidInitializationSettings('vikunja_logo');
|
||||
var initializationSettingsIOS = notifs.IOSInitializationSettings(
|
||||
requestAlertPermission: false,
|
||||
requestBadgePermission: false,
|
||||
requestSoundPermission: false,
|
||||
onDidReceiveLocalNotification:
|
||||
(int id, String title, String body, String payload) async {
|
||||
didReceiveLocalNotificationSubject
|
||||
.add(NotificationClass(id: id, title: title, body: body, payload: payload));
|
||||
});
|
||||
var initializationSettings = notifs.InitializationSettings(
|
||||
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
|
||||
await notifsPlugin.initialize(initializationSettings,
|
||||
onSelectNotification: (String payload) async {
|
||||
if (payload != null) {
|
||||
print('notification payload: ' + payload);
|
||||
}
|
||||
selectNotificationSubject.add(payload);
|
||||
});
|
||||
print("Notifications initialised successfully");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> scheduleNotification(String title, String description,
|
||||
notifs.FlutterLocalNotificationsPlugin notifsPlugin,
|
||||
DateTime scheduledTime, {String id}) async {
|
||||
if(scheduledTime.difference(DateTime.now()) < Duration.zero)
|
||||
return;
|
||||
if(id == null)
|
||||
id = DateTime.now().toString();
|
||||
var androidSpecifics = notifs.AndroidNotificationDetails(
|
||||
"Vikunja",
|
||||
"Due Date Reminder",
|
||||
channelDescription: "description",
|
||||
icon: 'vikunja_logo',
|
||||
importance: notifs.Importance.high
|
||||
);
|
||||
var iOSSpecifics = notifs.IOSNotificationDetails();
|
||||
var platformChannelSpecifics = notifs.NotificationDetails(
|
||||
android: androidSpecifics, iOS: iOSSpecifics);
|
||||
final String currentTimeZone = await FlutterNativeTimezone.getLocalTimezone();
|
||||
tz.TZDateTime time = tz.TZDateTime.from(scheduledTime,tz.getLocation(currentTimeZone));
|
||||
//time.add(Duration(hours: -2));
|
||||
await notifsPlugin.zonedSchedule(Random().nextInt(100000000), title, description,
|
||||
time, platformChannelSpecifics, androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: notifs.UILocalNotificationDateInterpretation.wallClockTime); // This literally schedules the notification
|
||||
}
|
||||
|
||||
void requestIOSPermissions(
|
||||
notifs.FlutterLocalNotificationsPlugin notifsPlugin) {
|
||||
notifsPlugin.resolvePlatformSpecificImplementation<notifs.IOSFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermissions(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
}
|
|
@ -4,6 +4,8 @@ import 'package:vikunja_app/global.dart';
|
|||
import '../components/AddDialog.dart';
|
||||
import '../components/TaskTile.dart';
|
||||
import '../models/task.dart';
|
||||
import '../managers/notifications.dart';
|
||||
import '../main.dart';
|
||||
|
||||
class LandingPage extends StatefulWidget {
|
||||
@override
|
||||
|
@ -25,6 +27,7 @@ class LandingPageState extends State<LandingPage> {
|
|||
Widget build(BuildContext context) {
|
||||
if(_list == null)
|
||||
_loadList(context);
|
||||
VikunjaGlobal.of(context).scheduleDueNotifications();
|
||||
return new Scaffold(
|
||||
body: new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
|
|
@ -178,6 +178,12 @@ class MockedTaskService implements TaskService {
|
|||
// TODO: implement getByOptions
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Task>> getAll() {
|
||||
// TODO: implement getAll
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
class MockedUserService implements UserService {
|
||||
|
|
|
@ -92,6 +92,7 @@ abstract class TaskService {
|
|||
Future<Task> update(Task task);
|
||||
Future delete(int taskId);
|
||||
Future<Task> add(int listId, Task task);
|
||||
Future<List<Task>> getAll();
|
||||
Future<List<Task>> getByOptions(TaskServiceOptions options);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,15 @@ String durationToHumanReadable(Duration dur) {
|
|||
return dur.inDays.toString() + " days";
|
||||
if(dur.inDays.abs() == 1)
|
||||
return dur.inDays.toString() + " day";
|
||||
|
||||
if(dur.inHours.abs() > 1)
|
||||
return dur.inHours.toString() + " hours";
|
||||
if(dur.inHours.abs() == 1)
|
||||
return dur.inHours.toString() + "1 hour";
|
||||
return "under 1 hour";
|
||||
return dur.inHours.toString() + " hour";
|
||||
|
||||
if(dur.inMinutes.abs() > 1)
|
||||
return dur.inMinutes.toString() + " minutes";
|
||||
if(dur.inMinutes.abs() == 1)
|
||||
return dur.inMinutes.toString() + " minute";
|
||||
return "under 1 minute";
|
||||
}
|
Loading…
Reference in New Issue
Block a user