Notification scheduling working

This commit is contained in:
laurenspriem
2025-05-21 10:57:49 +05:30
parent 8deb52301a
commit 4cc8ff2fb1
2 changed files with 177 additions and 0 deletions

View File

@@ -1,9 +1,12 @@
import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import "package:flutter_timezone/flutter_timezone.dart";
import "package:logging/logging.dart";
import "package:photos/services/sync/remote_sync_service.dart";
import "package:shared_preferences/shared_preferences.dart";
import 'package:timezone/data/latest_10y.dart' as tzdb;
import "package:timezone/timezone.dart" as tz;
class NotificationService {
static final NotificationService instance =
@@ -24,11 +27,14 @@ class NotificationService {
_preferences = preferences;
}
bool timezoneInitialized = false;
Future<void> initialize(
void Function(
NotificationResponse notificationResponse,
) onNotificationTapped,
) async {
await initTimezones();
const androidSettings = AndroidInitializationSettings('notification_icon');
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: false,
@@ -59,6 +65,14 @@ class NotificationService {
}
}
Future<void> initTimezones() async {
if (timezoneInitialized) return;
tzdb.initializeTimeZones();
final String currentTimeZone = await FlutterTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(currentTimeZone));
timezoneInitialized = true;
}
Future<void> requestPermissions() async {
bool? result;
if (Platform.isIOS) {
@@ -127,4 +141,82 @@ class NotificationService {
payload: payload,
);
}
Future<void> scheduleNotification(
String title,
String message, {
required int id,
String channelID = "io.ente.photos",
String channelName = "ente",
String payload = "ente://home",
required DateTime dateTime,
}) async {
_logger.info(
"Scheduling notification with: $title, $message, $channelID, $channelName, $payload",
);
await initTimezones();
if (!hasGrantedPermissions()) {
_logger.warning("Notification permissions not granted");
await requestPermissions();
if (!hasGrantedPermissions()) {
_logger.severe("Failed to get notification permissions");
return;
}
} else {
_logger.info("Notification permissions already granted");
}
final androidSpecs = AndroidNotificationDetails(
channelID,
channelName,
channelDescription: 'ente alerts',
importance: Importance.max,
priority: Priority.high,
category: AndroidNotificationCategory.reminder,
showWhen: false,
);
final iosSpecs = DarwinNotificationDetails(threadIdentifier: channelID);
final platformChannelSpecs =
NotificationDetails(android: androidSpecs, iOS: iosSpecs);
final scheduledDate = tz.TZDateTime.local(
dateTime.year,
dateTime.month,
dateTime.day,
dateTime.hour,
dateTime.minute,
dateTime.second,
);
// final tz.TZDateTime scheduledDate = tz.TZDateTime.now(tz.local).add(delay);
await _notificationsPlugin.zonedSchedule(
id,
title,
message,
scheduledDate,
platformChannelSpecs,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.wallClockTime,
androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
payload: payload,
);
_logger.info(
"Scheduled notification with: $title, $message, $channelID, $channelName, $payload",
);
}
Future<void> clearAllScheduledNotifications() async {
_logger.info("Clearing all scheduled notifications");
final pending = await _notificationsPlugin.pendingNotificationRequests();
if (pending.isEmpty) {
_logger.info("No pending notifications to clear");
return;
}
for (final request in pending) {
_logger.info("Clearing notification with id: ${request.id}");
await _notificationsPlugin.cancel(request.id);
}
}
Future<int> pendingNotifications() async {
final pending = await _notificationsPlugin.pendingNotificationRequests();
return pending.length;
}
}

View File

@@ -12,6 +12,7 @@ import "package:photos/services/machine_learning/ml_indexing_isolate.dart";
import 'package:photos/services/machine_learning/ml_service.dart';
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
import "package:photos/services/memory_home_widget_service.dart";
import "package:photos/services/notification_service.dart";
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/components/captioned_text_widget.dart';
import 'package:photos/ui/components/expandable_menu_item_widget.dart';
@@ -66,6 +67,90 @@ class _MLDebugSectionWidgetState extends State<MLDebugSectionWidget> {
logger.info("Building ML Debug section options");
return Column(
children: [
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Now notification",
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
try {
await NotificationService.instance.showNotification(
"On this day",
"Look back on X memories 🌄",
channelID: "memoryID",
channelName: "onThisDay",
payload: "memoryID",
);
} catch (e, s) {
logger.warning('test notification failed ', e, s);
await showGenericErrorDialog(context: context, error: e);
}
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Soon notification",
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
try {
await NotificationService.instance.scheduleNotification(
"On this day",
"Look back on X memories 🌄",
id: 1,
channelID: "memoryID",
channelName: "On this day",
payload: "memoryID",
dateTime: DateTime.now().add(const Duration(seconds: 5)),
);
showShortToast(context, 'Done');
} catch (e, s) {
logger.warning('soon exact notification failed ', e, s);
await showGenericErrorDialog(context: context, error: e);
}
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Show pending notifications",
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
try {
final amount =
await NotificationService.instance.pendingNotifications();
showShortToast(context, '$amount pending notifications');
} catch (e) {
await showGenericErrorDialog(context: context, error: e);
}
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Clear pending notifications",
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
try {
await NotificationService.instance.clearAllScheduledNotifications();
showShortToast(context, 'Done');
} catch (e) {
await showGenericErrorDialog(context: context, error: e);
}
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: FutureBuilder<IndexStatus>(