Skip to content

Commit

Permalink
Option for using persistent notifications (#107)
Browse files Browse the repository at this point in the history
* Add an option to receive persistent notifications

* Add notification methods to NotificationService

* Reschedule notification when the daily entry status change

* Reschedule notification when the user open the app if there is a daily entry
  • Loading branch information
AdrasteonDev authored Nov 20, 2023
1 parent 28ba039 commit eb8afa4
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 125 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## v1.5.3 - 09/2023
## v1.5.3 - 10/2023
- Added option to receive persistent notifications
- Added option to change calendar days color for colorblind users
- Added option to disable date filter in experimental file picker
- Added reverse filter order button in experimental file picker
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Bagas Wastu (@bagaswastu)
- Harry Schiller (@waitingwittykitty)
- David Coker (@daoxve)
- Adrasteon (@AdrasteonDev)

## Testing & Feedback
- Augusto Vesco
Expand Down
5 changes: 5 additions & 0 deletions lib/bindings/initial_binding.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import 'package:get/get.dart';

import '../controllers/lang_controller.dart';
import '../utils/notification_service.dart';

class InitialBinding extends Bindings {
@override
void dependencies() {
Get.put<LanguageController>(LanguageController());
Get.put<NotificationService>(
NotificationService(),
permanent: true,
);
}
}
9 changes: 9 additions & 0 deletions lib/controllers/daily_entry_controller.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:get/get.dart';

import '../utils/date_format_utils.dart';
import '../utils/notification_service.dart';
import '../utils/shared_preferences_util.dart';

class DailyEntryController extends GetxController {
Expand All @@ -11,11 +12,15 @@ class DailyEntryController extends GetxController {
}

final dailyEntry = SharedPrefsUtil.getBool('dailyEntry')?.obs ?? false.obs;
final NotificationService notificationService = Get.find();

void updateDaily({bool value = true}) {
SharedPrefsUtil.putBool('dailyEntry', value);
dailyEntry.value = value;
dailyEntry.refresh();

// Remove the existing notification and schedule it again
notificationService.rescheduleNotification(DateTime.now());
}

void _checkTodayEntry() {
Expand All @@ -28,5 +33,9 @@ class DailyEntryController extends GetxController {
dailyEntry.value = false;
dailyEntry.refresh();
}

// Remove the existing notification and schedule it again if there is a daily entry
if(dailyEntry.value)
notificationService.rescheduleNotification(DateTime.now());
}
}
1 change: 1 addition & 0 deletions lib/lang/en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const Map<String, String> en = {
'notifications': 'Notifications',
'enableNotifications': 'Enable Notifications',
'scheduleTime': 'Schedule Time',
'usePersistentNotifications': 'Persistent notifications',
'test': 'Test',
'notificationTitle': 'Heyy!',
'notificationBody': 'Do not forget to record 1 second of your day 👀',
Expand Down
1 change: 1 addition & 0 deletions lib/lang/fr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const Map<String, String> fr = {
'notifications': 'Notifications',
'enableNotifications': 'Activer les notifications',
'scheduleTime': 'Horaire',
'usePersistentNotifications': 'Notifications persistantes',
'test': 'Test',
'notificationTitle': 'Hé !',
'notificationBody': "N'oubliez pas d'enregistrer une seconde de votre journée. 👀",
Expand Down
192 changes: 74 additions & 118 deletions lib/pages/home/notification/widgets/switch_notifications.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

import '../../../../controllers/daily_entry_controller.dart';
import '../../../../routes/app_pages.dart';
import '../../../../utils/constants.dart';
import '../../../../utils/notification_service.dart';
import '../../../../utils/shared_preferences_util.dart';
import '../../../../utils/theme.dart';
import '../../../../utils/utils.dart';

class SwitchNotificationsComponent extends StatefulWidget {
@override
Expand All @@ -21,106 +15,26 @@ class SwitchNotificationsComponent extends StatefulWidget {

class _SwitchNotificationsComponentState
extends State<SwitchNotificationsComponent> {
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

final int notificationId = 1;
late bool isSwitchToggled;
late bool isNotificationSwitchToggled;
TimeOfDay scheduledTimeOfDay = const TimeOfDay(hour: 20, minute: 00);
late bool isPersistentSwitchToggled;
final NotificationService notificationService = Get.find();

@override
void initState() {
super.initState();
isSwitchToggled = NotificationService().isNotificationActivated();
isNotificationSwitchToggled = notificationService.isNotificationActivated();
isPersistentSwitchToggled = notificationService.isPersistentNotificationActivated();

// Sets the default values for scheduled time
getScheduledTime();

/// Initializing notification settings
tz.initializeTimeZones();

const AndroidInitializationSettings androidInitializationSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');

const DarwinInitializationSettings iosInitializationSettings =
DarwinInitializationSettings();

const InitializationSettings initializationSettings =
InitializationSettings(
android: androidInitializationSettings,
iOS: iosInitializationSettings,
);

flutterLocalNotificationsPlugin.initialize(initializationSettings);
scheduledTimeOfDay = notificationService.getScheduledTime();
}

@override
void dispose() {
super.dispose();
}

final platformNotificationDetails = const NotificationDetails(
android: AndroidNotificationDetails(
'channel id',
'channel name',
channelDescription: 'channel description',
),
);

// Checks for the scheduled time and sets it to a value in shared prefs
void getScheduledTime() {
final int hour = SharedPrefsUtil.getInt('scheduledTimeHour') ?? 20;
final int minute = SharedPrefsUtil.getInt('scheduledTimeMinute') ?? 00;
scheduledTimeOfDay = TimeOfDay(hour: hour, minute: minute);
}

Future<void> scheduleNotification() async {
final now = DateTime.now();

// sets the scheduled time in DateTime format
final String setTime = DateTime(
now.year,
now.month,
now.day,
scheduledTimeOfDay.hour,
scheduledTimeOfDay.minute,
).toString();

Utils.logInfo('[NOTIFICATIONS] - Scheduled with setTime=$setTime');

/// Schedule notification
await flutterLocalNotificationsPlugin.zonedSchedule(
notificationId,
'notificationTitle'.tr,
'notificationBody'.tr,
tz.TZDateTime.parse(tz.local, setTime),
platformNotificationDetails,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
// Allow notification to be shown daily
matchDateTimeComponents: DateTimeComponents.time,
);
}

Future<void> showTestNotification() async {
await flutterLocalNotificationsPlugin.show(
notificationId,
'test'.tr,
'test'.tr,
platformNotificationDetails,
);

// Feedback to the user that the notification was called
await Fluttertoast.showToast(
msg: 'done'.tr,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: AppColors.dark,
textColor: Colors.white,
fontSize: 16.0,
);
}

@override
Widget build(BuildContext context) {
return Column(
Expand All @@ -139,31 +53,23 @@ class _SwitchNotificationsComponentState
),
),
Switch(
value: isSwitchToggled,
value: isNotificationSwitchToggled,
onChanged: (value) async {
if (value) {
Utils.logInfo(
'[NOTIFICATIONS] - Notifications were enabled',
);
await notificationService.turnOnNotifications();

/// Schedule notification if switch in ON
await Utils.requestPermission(Permission.notification);
await scheduleNotification();
} else {
Utils.logInfo(
'[NOTIFICATIONS] - Notifications were disabled',
await notificationService.scheduleNotification(
scheduledTimeOfDay.hour,
scheduledTimeOfDay.minute,
DateTime.now()
);

/// Cancel notification if switch is OFF
flutterLocalNotificationsPlugin.cancelAll();
} else {
await notificationService.turnOffNotifications();
}

/// Save notification on SharedPrefs
NotificationService().switchNotification();

/// Update switch value
setState(() {
isSwitchToggled = !isSwitchToggled;
isNotificationSwitchToggled = !isNotificationSwitchToggled;
});
},
activeTrackColor: AppColors.mainColor.withOpacity(0.4),
Expand Down Expand Up @@ -227,23 +133,25 @@ class _SwitchNotificationsComponentState
if (newTimeOfDay == null) return;

// Enable notification if it's disabled
if (!isSwitchToggled) {
print('here');
await Utils.requestPermission(Permission.notification);
NotificationService().switchNotification();
if (!isNotificationSwitchToggled) {
await notificationService.turnOnNotifications();
setState(() {
isSwitchToggled = true;
isNotificationSwitchToggled = true;
});
}

SharedPrefsUtil.putInt('scheduledTimeHour', newTimeOfDay.hour);
SharedPrefsUtil.putInt('scheduledTimeMinute', newTimeOfDay.minute);
notificationService.setScheduledTime(newTimeOfDay.hour,
newTimeOfDay.minute);

setState(() {
scheduledTimeOfDay = newTimeOfDay;
});

await scheduleNotification();
await notificationService.scheduleNotification(
scheduledTimeOfDay.hour,
scheduledTimeOfDay.minute,
DateTime.now()
);
},
child: Container(
padding:
Expand All @@ -268,6 +176,54 @@ class _SwitchNotificationsComponentState
),
),
const Divider(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'usePersistentNotifications'.tr,
style: TextStyle(
fontSize: MediaQuery.of(context).size.width * 0.045,
),
),
Switch(
value: isPersistentSwitchToggled,
onChanged: (value) async {
if (value) {
notificationService.activatePersistentNotifications();
} else {
notificationService.deactivatePersistentNotifications();
}

/// Schedule notification if switch in ON
if(isNotificationSwitchToggled && !isNotificationSwitchToggled){
await notificationService.turnOnNotifications();
setState(() {
isNotificationSwitchToggled = true;
});
}

if(isNotificationSwitchToggled){
await notificationService.scheduleNotification(
scheduledTimeOfDay.hour,
scheduledTimeOfDay.minute,
DateTime.now()
);
}

/// Update switch value
setState(() {
isPersistentSwitchToggled = !isPersistentSwitchToggled;
});
},
activeTrackColor: AppColors.mainColor.withOpacity(0.4),
activeColor: AppColors.mainColor,
),
],
),
),
const Divider(),
],
);
}
Expand Down
Loading

0 comments on commit eb8afa4

Please sign in to comment.