206 lines
6.1 KiB
Dart
206 lines
6.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:timezone/timezone.dart' as tz;
|
|
import 'package:timezone/data/latest.dart' as tz_data;
|
|
|
|
import '../models/event_model.dart';
|
|
|
|
class NotificationService {
|
|
static NotificationService _instance = NotificationService._internal();
|
|
factory NotificationService() => _instance;
|
|
NotificationService._internal();
|
|
|
|
// 테스트용 인스턴스 설정 메서드
|
|
static void setTestInstance(NotificationService instance) {
|
|
_instance = instance;
|
|
}
|
|
|
|
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
|
|
bool _isInitialized = false;
|
|
|
|
// 알림 서비스 초기화
|
|
Future<void> initialize() async {
|
|
if (_isInitialized) return;
|
|
|
|
// 타임존 초기화
|
|
tz_data.initializeTimeZones();
|
|
|
|
// 안드로이드 설정
|
|
const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
|
|
// iOS 설정
|
|
const DarwinInitializationSettings iOSSettings = DarwinInitializationSettings(
|
|
requestAlertPermission: true,
|
|
requestBadgePermission: true,
|
|
requestSoundPermission: true,
|
|
);
|
|
|
|
// 초기화 설정
|
|
const InitializationSettings initSettings = InitializationSettings(
|
|
android: androidSettings,
|
|
iOS: iOSSettings,
|
|
);
|
|
|
|
// 알림 플러그인 초기화
|
|
await _notificationsPlugin.initialize(
|
|
initSettings,
|
|
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
|
// 알림 클릭 시 처리
|
|
debugPrint('알림 클릭: ${response.payload}');
|
|
},
|
|
);
|
|
|
|
_isInitialized = true;
|
|
debugPrint('알림 서비스 초기화 완료');
|
|
}
|
|
|
|
// 권한 요청
|
|
Future<bool> requestPermission() async {
|
|
if (!_isInitialized) await initialize();
|
|
|
|
// iOS에서 권한 요청
|
|
final bool? result = await _notificationsPlugin
|
|
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
|
|
?.requestPermissions(
|
|
alert: true,
|
|
badge: true,
|
|
sound: true,
|
|
);
|
|
|
|
return result ?? false;
|
|
}
|
|
|
|
// 즉시 알림 표시
|
|
Future<void> showNotification({
|
|
required int id,
|
|
required String title,
|
|
required String body,
|
|
String? payload,
|
|
}) async {
|
|
if (!_isInitialized) await initialize();
|
|
|
|
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
|
|
'event_channel',
|
|
'이벤트 알림',
|
|
channelDescription: '이벤트 관련 알림을 표시합니다',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
showWhen: true,
|
|
);
|
|
|
|
const DarwinNotificationDetails iOSDetails = DarwinNotificationDetails(
|
|
presentAlert: true,
|
|
presentBadge: true,
|
|
presentSound: true,
|
|
);
|
|
|
|
const NotificationDetails notificationDetails = NotificationDetails(
|
|
android: androidDetails,
|
|
iOS: iOSDetails,
|
|
);
|
|
|
|
await _notificationsPlugin.show(
|
|
id,
|
|
title,
|
|
body,
|
|
notificationDetails,
|
|
payload: payload,
|
|
);
|
|
}
|
|
|
|
// 예약 알림 설정
|
|
Future<void> scheduleNotification({
|
|
required int id,
|
|
required String title,
|
|
required String body,
|
|
required DateTime scheduledDate,
|
|
String? payload,
|
|
}) async {
|
|
if (!_isInitialized) await initialize();
|
|
|
|
final tz.TZDateTime scheduledTZDate = tz.TZDateTime.from(
|
|
scheduledDate,
|
|
tz.local,
|
|
);
|
|
|
|
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
|
|
'event_reminder_channel',
|
|
'이벤트 리마인더',
|
|
channelDescription: '예정된 이벤트에 대한 알림을 표시합니다',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
);
|
|
|
|
const DarwinNotificationDetails iOSDetails = DarwinNotificationDetails(
|
|
presentAlert: true,
|
|
presentBadge: true,
|
|
presentSound: true,
|
|
);
|
|
|
|
const NotificationDetails notificationDetails = NotificationDetails(
|
|
android: androidDetails,
|
|
iOS: iOSDetails,
|
|
);
|
|
|
|
await _notificationsPlugin.zonedSchedule(
|
|
id,
|
|
title,
|
|
body,
|
|
scheduledTZDate,
|
|
notificationDetails,
|
|
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
|
|
matchDateTimeComponents: DateTimeComponents.time,
|
|
payload: payload,
|
|
);
|
|
}
|
|
|
|
// 이벤트 시작 알림 설정
|
|
Future<void> scheduleEventStartReminder(Event event) async {
|
|
if (event.id.isEmpty) return;
|
|
|
|
// 이벤트 시작 1시간 전 알림
|
|
final DateTime reminderTime = event.startDate.subtract(const Duration(hours: 1));
|
|
|
|
// 현재 시간이 알림 시간보다 이후라면 알림을 설정하지 않음
|
|
if (reminderTime.isBefore(DateTime.now())) return;
|
|
|
|
await scheduleNotification(
|
|
id: event.id.hashCode,
|
|
title: '이벤트 시작 알림',
|
|
body: '${event.title} 이벤트가 1시간 후에 시작됩니다.',
|
|
scheduledDate: reminderTime,
|
|
payload: event.id,
|
|
);
|
|
}
|
|
|
|
// 이벤트 등록 마감 알림 설정
|
|
Future<void> scheduleRegistrationDeadlineReminder(Event event) async {
|
|
if (event.id.isEmpty || event.registrationDeadline == null) return;
|
|
|
|
// 등록 마감 1일 전 알림
|
|
final DateTime reminderTime = event.registrationDeadline!.subtract(const Duration(days: 1));
|
|
|
|
// 현재 시간이 알림 시간보다 이후라면 알림을 설정하지 않음
|
|
if (reminderTime.isBefore(DateTime.now())) return;
|
|
|
|
await scheduleNotification(
|
|
id: event.id.hashCode + 1, // 이벤트 시작 알림과 ID가 겹치지 않도록 함
|
|
title: '이벤트 등록 마감 알림',
|
|
body: '${event.title} 이벤트의 등록이 내일 마감됩니다.',
|
|
scheduledDate: reminderTime,
|
|
payload: event.id,
|
|
);
|
|
}
|
|
|
|
// 특정 이벤트의 모든 알림 취소
|
|
Future<void> cancelEventNotifications(String eventId) async {
|
|
await _notificationsPlugin.cancel(eventId.hashCode);
|
|
await _notificationsPlugin.cancel(eventId.hashCode + 1);
|
|
}
|
|
|
|
// 모든 알림 취소
|
|
Future<void> cancelAllNotifications() async {
|
|
await _notificationsPlugin.cancelAll();
|
|
}
|
|
}
|