Notification scheduling working
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
Reference in New Issue
Block a user