Skip to content

Commit 1e23661

Browse files
authored
Merge pull request #278 from Kaps61929/fix/notification_problem_solved
notification issue resolved
2 parents fb72fe2 + d2ea3be commit 1e23661

File tree

9 files changed

+138
-82
lines changed

9 files changed

+138
-82
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<uses-permission android:name="android.permission.INTERNET" />
44
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
55
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
6+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
7+
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
8+
<uses-permission android:name="android.permission.VIBRATE" />
69
<application
710
android:label="TaskWarrior"
811
android:name="${applicationName}"
@@ -52,5 +55,16 @@
5255
<meta-data
5356
android:name="flutterEmbedding"
5457
android:value="2" />
58+
59+
60+
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
61+
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
62+
<intent-filter>
63+
<action android:name="android.intent.action.BOOT_COMPLETED"/>
64+
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
65+
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
66+
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
67+
</intent-filter>
68+
</receiver>
5569
</application>
5670
</manifest>

lib/model/data.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'dart:convert';
55
import 'dart:io';
66

77
import 'package:taskwarrior/model/json/task.dart';
8+
import 'package:taskwarrior/services/notification_services.dart';
89
import 'package:taskwarrior/widgets/taskc/payload.dart';
910
import 'package:taskwarrior/widgets/taskw.dart';
1011

@@ -148,6 +149,21 @@ class Data {
148149
}
149150

150151
void mergeTask(Task task) {
152+
NotificationService notificationService = NotificationService();
153+
notificationService.initiliazeNotification();
154+
155+
if (task.status == 'pending' && task.due != null) {
156+
int notificationid = notificationService.calculateNotificationId(
157+
task.due!, task.description, task.id);
158+
notificationService.cancelNotification(notificationid);
159+
notificationService.sendNotification(
160+
task.due!, task.description, task.id);
161+
} else if (task.due != null) {
162+
int notificationid = notificationService.calculateNotificationId(
163+
task.due!, task.description, task.id);
164+
165+
notificationService.cancelNotification(notificationid);
166+
}
151167
_mergeTasks([task]);
152168
File('${home.path}/.task/backlog.data').writeAsStringSync(
153169
'${json.encode(task.rebuild((b) => b..id = null).toJson())}\n',

lib/model/storage/storage_widget.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ class _StorageWidgetState extends State<StorageWidget> {
241241

242242
void mergeTask(Task task) {
243243
storage.data.mergeTask(task);
244+
244245
_refreshTasks();
245246
setState(() {});
246247
}
Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// ignore_for_file: depend_on_referenced_packages
22

3+
import 'dart:convert';
4+
5+
import 'package:crypto/crypto.dart';
36
import 'package:flutter/foundation.dart';
47
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
58
import 'package:timezone/data/latest.dart' as tz;
@@ -23,22 +26,33 @@ class NotificationService {
2326
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
2427
}
2528

26-
void sendNotification(DateTime dtb, String task) async {
29+
// Function to create a unique notification ID
30+
int calculateNotificationId(
31+
DateTime scheduledTime, String taskname, int? taskid) {
32+
String combinedString = '${scheduledTime.toIso8601String()}$taskname';
33+
34+
// Calculate SHA-256 hash
35+
var sha2561 = sha256.convert(utf8.encode(combinedString));
36+
37+
// Convert the first 8 characters of the hash to an integer
38+
int notificationId =
39+
int.parse(sha2561.toString().substring(0, 8), radix: 16) % 2147483647;
40+
if (taskid != null) {
41+
notificationId = (notificationId + taskid) % 2147483647;
42+
}
43+
44+
return notificationId;
45+
}
46+
47+
void sendNotification(DateTime dtb, String taskname, int? taskid) async {
2748
DateTime dateTime = DateTime.now();
2849
tz.initializeTimeZones();
2950
if (kDebugMode) {
3051
print("date and time are:-$dateTime");
3152
print("date and time are:-$dtb");
3253
}
3354
final tz.TZDateTime scheduledAt =
34-
tz.TZDateTime.from(dtb.add(const Duration(minutes: 1)), tz.local);
35-
final tz.TZDateTime scheduledAt1 = tz.TZDateTime.local(dateTime.year,
36-
dateTime.month, dateTime.day, dateTime.hour, dateTime.minute);
37-
// print("date and time are:-" + dateTime.toString());
38-
if (kDebugMode) {
39-
print("dtb is :-$scheduledAt");
40-
print("date and time are scheduled2:-$scheduledAt1");
41-
}
55+
tz.TZDateTime.from(dtb.add(const Duration(minutes: 0)), tz.local);
4256

4357
AndroidNotificationDetails androidNotificationDetails =
4458
const AndroidNotificationDetails('channelId', 'TaskReminder',
@@ -49,18 +63,37 @@ class NotificationService {
4963
NotificationDetails notificationDetails =
5064
NotificationDetails(android: androidNotificationDetails);
5165

52-
_flutterLocalNotificationsPlugin.zonedSchedule(
53-
scheduledAt.day * 100 + scheduledAt.hour * 10 + scheduledAt.minute,
54-
'Task Warrior Reminder',
55-
'Hey! Your task of $task is still pending',
56-
scheduledAt,
57-
notificationDetails,
58-
uiLocalNotificationDateInterpretation:
59-
UILocalNotificationDateInterpretation.absoluteTime,
60-
// ignore: deprecated_member_use
61-
androidAllowWhileIdle: true);
66+
// Generate a unique notification ID based on the scheduled time and task name
67+
int notificationId = calculateNotificationId(dtb, taskname, taskid);
68+
69+
await _flutterLocalNotificationsPlugin
70+
.zonedSchedule(
71+
notificationId,
72+
'Task Warrior Reminder',
73+
'Hey! Your task of $taskname is still pending',
74+
scheduledAt,
75+
notificationDetails,
76+
uiLocalNotificationDateInterpretation:
77+
UILocalNotificationDateInterpretation.absoluteTime,
78+
// ignore: deprecated_member_use
79+
androidAllowWhileIdle: true)
80+
.then((value) {
81+
if (kDebugMode) {
82+
print('Notification scheduled successfully');
83+
}
84+
}).catchError((error) {
85+
if (kDebugMode) {
86+
print('Error scheduling notification: $error');
87+
}
88+
});
89+
6290
if (kDebugMode) {
6391
print(scheduledAt.day * 100 + scheduledAt.hour * 10 + scheduledAt.minute);
6492
}
6593
}
94+
95+
// Delete previously scheduled notification with a specific ID
96+
void cancelNotification(int notificationId) async {
97+
await _flutterLocalNotificationsPlugin.cancel(notificationId);
98+
}
6699
}

lib/services/task_details.dart

Lines changed: 53 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class _DetailRouteState extends State<DetailRoute> {
4646

4747
void saveChanges() async {
4848
var now = DateTime.now().toUtc();
49+
4950
modify.save(
5051
modified: () => now,
5152
);
@@ -70,7 +71,7 @@ class _DetailRouteState extends State<DetailRoute> {
7071

7172
await Navigator.of(context).pushAndRemoveUntil(
7273
MaterialPageRoute(builder: (context) => const HomePage()),
73-
(Route<dynamic> route) => false);
74+
(Route<dynamic> route) => false);
7475
}
7576
// ignore: use_build_context_synchronously
7677
return showDialog(
@@ -84,9 +85,7 @@ class _DetailRouteState extends State<DetailRoute> {
8485
saveChanges();
8586
Navigator.of(context).pushNamedAndRemoveUntil(
8687
HomePage.routeName,
87-
88-
(route) => false,
89-
88+
(route) => false,
9089
);
9190
setState(() {});
9291
},
@@ -96,7 +95,7 @@ class _DetailRouteState extends State<DetailRoute> {
9695
onPressed: () {
9796
Navigator.of(context).pushNamedAndRemoveUntil(
9897
HomePage.routeName,
99-
(route) => false,
98+
(route) => false,
10099
);
101100
},
102101
child: const Text('No'),
@@ -158,46 +157,46 @@ class _DetailRouteState extends State<DetailRoute> {
158157
floatingActionButton: (modify.changes.isEmpty)
159158
? null
160159
: FloatingActionButton(
161-
heroTag: "btn1",
162-
onPressed: () {
163-
showDialog(
164-
context: context,
165-
builder: (context) {
166-
return AlertDialog(
167-
scrollable: true,
168-
title: const Text('Review changes:'),
169-
content: SingleChildScrollView(
170-
scrollDirection: Axis.horizontal,
171-
child: Text(
172-
modify.changes.entries
173-
.map((entry) => '${entry.key}:\n'
174-
' old: ${entry.value['old']}\n'
175-
' new: ${entry.value['new']}')
176-
.toList()
177-
.join('\n'),
178-
style: GoogleFonts.poppins(),
179-
),
180-
),
181-
actions: [
182-
TextButton(
183-
onPressed: () {
184-
Navigator.of(context).pop();
185-
},
186-
child: const Text('Cancel'),
187-
),
188-
ElevatedButton(
189-
onPressed: () {
190-
saveChanges();
191-
},
192-
child: const Text('Submit'),
193-
),
194-
],
195-
);
196-
},
197-
);
198-
},
199-
child: const Icon(Icons.save),
200-
),
160+
heroTag: "btn1",
161+
onPressed: () {
162+
showDialog(
163+
context: context,
164+
builder: (context) {
165+
return AlertDialog(
166+
scrollable: true,
167+
title: const Text('Review changes:'),
168+
content: SingleChildScrollView(
169+
scrollDirection: Axis.horizontal,
170+
child: Text(
171+
modify.changes.entries
172+
.map((entry) => '${entry.key}:\n'
173+
' old: ${entry.value['old']}\n'
174+
' new: ${entry.value['new']}')
175+
.toList()
176+
.join('\n'),
177+
style: GoogleFonts.poppins(),
178+
),
179+
),
180+
actions: [
181+
TextButton(
182+
onPressed: () {
183+
Navigator.of(context).pop();
184+
},
185+
child: const Text('Cancel'),
186+
),
187+
ElevatedButton(
188+
onPressed: () {
189+
saveChanges();
190+
},
191+
child: const Text('Submit'),
192+
),
193+
],
194+
);
195+
},
196+
);
197+
},
198+
child: const Icon(Icons.save),
199+
),
201200
),
202201
);
203202
}
@@ -276,12 +275,12 @@ class AttributeWidget extends StatelessWidget {
276275
value: localValue,
277276
callback: callback,
278277
);
279-
// case 'annotations':
280-
// return AnnotationsWidget(
281-
// name: name,
282-
// value: localValue,
283-
// callback: callback,
284-
// );
278+
// case 'annotations':
279+
// return AnnotationsWidget(
280+
// name: name,
281+
// value: localValue,
282+
// callback: callback,
283+
// );
285284
default:
286285
return Card(
287286
color: AppSettings.isDarkMode
@@ -301,15 +300,15 @@ class AttributeWidget extends StatelessWidget {
301300
fontWeight: FontWeight.bold,
302301
fontSize: 15,
303302
color:
304-
AppSettings.isDarkMode ? Colors.white : Colors.black,
303+
AppSettings.isDarkMode ? Colors.white : Colors.black,
305304
),
306305
),
307306
Text(
308307
localValue?.toString() ?? "not selected",
309308
style: GoogleFonts.poppins(
310309
fontSize: 15,
311310
color:
312-
AppSettings.isDarkMode ? Colors.white : Colors.black,
311+
AppSettings.isDarkMode ? Colors.white : Colors.black,
313312
),
314313
)
315314
],
@@ -361,7 +360,7 @@ class TagsWidget extends StatelessWidget {
361360
),
362361
TextSpan(
363362
text:
364-
'${(value as ListBuilder?)?.build() ?? 'not selected'}',
363+
'${(value as ListBuilder?)?.build() ?? 'not selected'}',
365364
style: GoogleFonts.poppins(
366365
fontSize: 15,
367366
color: AppSettings.isDarkMode

lib/widgets/add_Task.dart

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import 'package:shared_preferences/shared_preferences.dart';
1010
import 'package:taskwarrior/config/app_settings.dart';
1111
import 'package:taskwarrior/controller/WidgetController.dart';
1212
import 'package:taskwarrior/model/storage/storage_widget.dart';
13-
import 'package:taskwarrior/services/notification_services.dart';
1413
import 'package:taskwarrior/widgets/taskfunctions/taskparser.dart';
1514
import 'package:taskwarrior/widgets/taskw.dart';
1615

@@ -277,12 +276,6 @@ class _AddTaskBottomSheetState extends State<AddTaskBottomSheet> {
277276
);
278277
if (dateTime.isAfter(DateTime.now())) {
279278
due = dateTime.toUtc();
280-
NotificationService notificationService =
281-
NotificationService();
282-
notificationService.initiliazeNotification();
283-
284-
notificationService.sendNotification(
285-
dateTime, namecontroller.text);
286279

287280
dueString =
288281
DateFormat("dd-MM-yyyy HH:mm").format(dateTime);

lib/widgets/taskfunctions/patch.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// ignore_for_file: depend_on_referenced_packages
22

33
import 'package:built_collection/built_collection.dart';
4-
54
import 'package:taskwarrior/model/json.dart';
65

76
Task patch(Task task, Map<String, dynamic> updates) {

pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ packages:
202202
source: hosted
203203
version: "0.3.3+8"
204204
crypto:
205-
dependency: transitive
205+
dependency: "direct main"
206206
description:
207207
name: crypto
208208
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ dependencies:
4747
tutorial_coach_mark: ^1.2.11
4848
url_launcher: ^6.1.14
4949
uuid: ^4.2.2
50+
crypto: ^3.0.1
5051

5152
dev_dependencies:
5253
build_runner: ^2.1.11

0 commit comments

Comments
 (0)