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