LCOV - code coverage report
Current view: | top level - lib/services - notification_service.dart |  |  | Coverage | Total | Hit
Test: | lcov.info |  | Lines: | 63.3 % | 49 | 31
Test Date: | 2025-08-12 03:21:51 |  | Functions: | - | 0 | 0

                Line data    Source code
           1              : import 'package:flutter/material.dart';
           2              : import 'package:flutter/foundation.dart';
           3              : import 'package:flutter_local_notifications/flutter_local_notifications.dart';
           4              : import 'package:timezone/timezone.dart' as tz;
           5              : import 'package:timezone/data/latest.dart' as tz_data;
           6              :
           7              : import '../models/event_model.dart';
           8              :
           9              : class NotificationService {
          10            3 :   static NotificationService _instance = NotificationService._internal();
          11            2 :   factory NotificationService() => _instance;
          12              :
          13              :   // 생성자에서 플러그인을 주입받을 수 있도록 변경
          14            1 :   NotificationService._internal([FlutterLocalNotificationsPlugin? plugin])
          15            1 :       : _notificationsPlugin = plugin ?? FlutterLocalNotificationsPlugin();
          16              :
          17              :   // 테스트용 인스턴스 설정 메서드
          18            1 :   static void setTestInstance(NotificationService instance) {
          19              :     _instance = instance;
          20              :   }
          21              :
          22              :   // 테스트용 인스턴스 생성 포함한 새 메서드
          23            1 :   @visibleForTesting
          24              :   static NotificationService createTestInstance(FlutterLocalNotificationsPlugin mockPlugin) {
          25            1 :     return NotificationService._internal(mockPlugin);
          26              :   }
          27              :
          28              :   final FlutterLocalNotificationsPlugin _notificationsPlugin;
          29              :   bool _isInitialized = false;
          30              :
          31              :   // 알림 서비스 초기화
          32            1 :   Future<void> initialize() async {
          33            1 :     if (_isInitialized) return;
          34              :
          35              :     // 타임존 초기화
          36            1 :     tz_data.initializeTimeZones();
          37              :
          38              :     // 안드로이드 설정
          39              :     const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
          40              :
          41              :     // iOS 설정
          42              :     const DarwinInitializationSettings iOSSettings = DarwinInitializationSettings(
          43              :       requestAlertPermission: true,
          44              :       requestBadgePermission: true,
          45              :       requestSoundPermission: true,
          46              :     );
          47              :
          48              :     // 초기화 설정
          49              :     const InitializationSettings initSettings = InitializationSettings(
          50              :       android: androidSettings,
          51              :       iOS: iOSSettings,
          52              :     );
          53              :
          54              :     // 알림 플러그인 초기화
          55            2 :     await _notificationsPlugin.initialize(
          56              :       initSettings,
          57            0 :       onDidReceiveNotificationResponse: (NotificationResponse response) {
          58              :         // 알림 클릭 시 처리
          59            0 :         debugPrint('알림 클릭: ${response.payload}');
          60              :       },
          61              :     );
          62              :
          63            1 :     _isInitialized = true;
          64            2 :     debugPrint('알림 서비스 초기화 완료');
          65              :   }
          66              :
          67              :   // 권한 요청
          68            1 :   Future<bool> requestPermission() async {
          69            2 :     if (!_isInitialized) await initialize();
          70              :
          71              :     // iOS에서 권한 요청
          72            1 :     final bool? result = await _notificationsPlugin
          73            1 :         .resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
          74            1 :         ?.requestPermissions(
          75              :           alert: true,
          76              :           badge: true,
          77              :           sound: true,
          78              :         );
          79              :
          80              :     return result ?? false;
          81              :   }
          82              :
          83              :   // 즉시 알림 표시
          84            0 :   Future<void> showNotification({
          85              :     required int id,
          86              :     required String title,
          87              :     required String body,
          88              :     String? payload,
          89              :   }) async {
          90            0 :     if (!_isInitialized) await initialize();
          91              :
          92              :     const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
          93              :       'event_channel',
          94              :       '이벤트 알림',
          95              :       channelDescription: '이벤트 관련 알림을 표시합니다',
          96              :       importance: Importance.high,
          97              :       priority: Priority.high,
          98              :       showWhen: true,
          99              :     );
         100              :
         101              :     const DarwinNotificationDetails iOSDetails = DarwinNotificationDetails(
         102              :       presentAlert: true,
         103              :       presentBadge: true,
         104              :       presentSound: true,
         105              :     );
         106              :
         107              :     const NotificationDetails notificationDetails = NotificationDetails(
         108              :       android: androidDetails,
         109              :       iOS: iOSDetails,
         110              :     );
         111              :
         112            0 :     await _notificationsPlugin.show(
         113              :       id,
         114              :       title,
         115              :       body,
         116              :       notificationDetails,
         117              :       payload: payload,
         118              :     );
         119              :   }
         120              :
         121              :   // 예약 알림 설정
         122            1 :   Future<void> scheduleNotification({
         123              :     required int id,
         124              :     required String title,
         125              :     required String body,
         126              :     required DateTime scheduledDate,
         127              :     String? payload,
         128              :   }) async {
         129            2 :     if (!_isInitialized) await initialize();
         130              :
         131            1 :     final tz.TZDateTime scheduledTZDate = tz.TZDateTime.from(
         132              :       scheduledDate,
         133            1 :       tz.local,
         134              :     );
         135              :
         136              :     const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
         137              :       'event_reminder_channel',
         138              :       '이벤트 리마인더',
         139              :       channelDescription: '예정된 이벤트에 대한 알림을 표시합니다',
         140              :       importance: Importance.high,
         141              :       priority: Priority.high,
         142              :     );
         143              :
         144              :     const DarwinNotificationDetails iOSDetails = DarwinNotificationDetails(
         145              :       presentAlert: true,
         146              :       presentBadge: true,
         147              :       presentSound: true,
         148              :     );
         149              :
         150              :     const NotificationDetails notificationDetails = NotificationDetails(
         151              :       android: androidDetails,
         152              :       iOS: iOSDetails,
         153              :     );
         154              :
         155            2 :     await _notificationsPlugin.zonedSchedule(
         156              :       id,
         157              :       title,
         158              :       body,
         159              :       scheduledTZDate,
         160              :       notificationDetails,
         161              :       androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
         162              :       matchDateTimeComponents: DateTimeComponents.time,
         163              :       payload: payload,
         164              :     );
         165              :   }
         166              :
         167              :   // 이벤트 시작 알림 설정
         168            1 :   Future<void> scheduleEventStartReminder(Event event) async {
         169            2 :     if (event.id.isEmpty) return;
         170              :
         171              :     // 이벤트 시작 1시간 전 알림
         172            2 :     final DateTime reminderTime = event.startDate.subtract(const Duration(hours: 1));
         173              :
         174              :     // 현재 시간이 알림 시간보다 이후라면 알림을 설정하지 않음
         175            2 :     if (reminderTime.isBefore(DateTime.now())) return;
         176              :
         177            1 :     await scheduleNotification(
         178            2 :       id: event.id.hashCode,
         179              :       title: '이벤트 시작 알림',
         180            2 :       body: '${event.title} 이벤트가 1시간 후에 시작됩니다.',
         181              :       scheduledDate: reminderTime,
         182            1 :       payload: event.id,
         183              :     );
         184              :   }
         185              :
         186              :   // 이벤트 등록 마감 알림 설정
         187            0 :   Future<void> scheduleRegistrationDeadlineReminder(Event event) async {
         188            0 :     if (event.id.isEmpty || event.registrationDeadline == null) return;
         189              :
         190              :     // 등록 마감 1일 전 알림
         191            0 :     final DateTime reminderTime = event.registrationDeadline!.subtract(const Duration(days: 1));
         192              :
         193              :     // 현재 시간이 알림 시간보다 이후라면 알림을 설정하지 않음
         194            0 :     if (reminderTime.isBefore(DateTime.now())) return;
         195              :
         196            0 :     await scheduleNotification(
         197            0 :       id: event.id.hashCode + 1, // 이벤트 시작 알림과 ID가 겹치지 않도록 함
         198              :       title: '이벤트 등록 마감 알림',
         199            0 :       body: '${event.title} 이벤트의 등록이 내일 마감됩니다.',
         200              :       scheduledDate: reminderTime,
         201            0 :       payload: event.id,
         202              :     );
         203              :   }
         204              :
         205              :   // 특정 이벤트의 모든 알림 취소
         206            0 :   Future<void> cancelEventNotifications(String eventId) async {
         207            0 :     await _notificationsPlugin.cancel(eventId.hashCode);
         208            0 :     await _notificationsPlugin.cancel(eventId.hashCode + 1);
         209              :   }
         210              :
         211              :   // 모든 알림 취소
         212            0 :   Future<void> cancelAllNotifications() async {
         213            0 :     await _notificationsPlugin.cancelAll();
         214              :   }
         215              : }

Generated by: LCOV version 2.3.1-1