NIM_E31222534/Androidnya/lib/services/notification_service.dart

349 lines
12 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_init;
import 'package:posyandu/models/jadwal_model.dart';
import 'dart:io';
import 'package:intl/intl.dart';
class NotificationService {
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Singleton pattern
static final NotificationService _instance = NotificationService._internal();
factory NotificationService() {
return _instance;
}
NotificationService._internal();
Future<void> init() async {
// Inisialisasi timezone untuk notifikasi terjadwal
tz_init.initializeTimeZones();
// Konfigurasi untuk Android yang ditingkatkan agar tampil di latar belakang
final AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
// Konfigurasi untuk iOS
final DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
requestSoundPermission: true,
requestBadgePermission: true,
requestAlertPermission: true,
defaultPresentSound: true,
defaultPresentAlert: true,
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse details) {
print('onDidReceiveNotificationResponse: ${details.payload}');
// Disini bisa ditambahkan kode untuk navigasi ke halaman tertentu
},
);
// Minta izin notifikasi eksplisit (terutama untuk Android 13+)
if (Platform.isAndroid) {
try {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
// Setup notification channels untuk Android 8.0+
await _setupNotificationChannels();
} catch (e) {
print('Error saat meminta izin notifikasi: $e');
}
} else if (Platform.isIOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
print('NotificationService initialized');
}
// Setup notification channels dengan prioritas tinggi
Future<void> _setupNotificationChannels() async {
// Channel untuk notifikasi jadwal dengan prioritas tinggi
final AndroidNotificationChannelGroup channelGroup =
AndroidNotificationChannelGroup(
'posyandu_group',
'Notifikasi Posyandu',
description: 'Semua notifikasi Posyandu',
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannelGroup(channelGroup);
// Channel untuk jadwal terdekat (prioritas tertinggi)
final jadwalChannel = AndroidNotificationChannel(
'jadwal_reminder_urgent_channel',
'Pengingat Jadwal Penting',
description: 'Pengingat untuk jadwal penting yang akan datang',
importance: Importance.high,
enableVibration: true,
playSound: true,
enableLights: true,
ledColor: Colors.teal,
groupId: 'posyandu_group',
showBadge: true,
sound: RawResourceAndroidNotificationSound('notification'),
);
// Channel untuk notifikasi standar
final standardChannel = AndroidNotificationChannel(
'jadwal_posyandu_channel',
'Jadwal Posyandu',
description: 'Notifikasi untuk jadwal Posyandu',
importance: Importance.high,
enableVibration: true,
playSound: true,
groupId: 'posyandu_group',
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(jadwalChannel);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(standardChannel);
}
// Notifikasi langsung (instant) dengan prioritas tinggi
Future<void> showNotification({
required int id,
required String title,
required String body,
String? payload,
}) async {
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
'jadwal_posyandu_channel',
'Jadwal Posyandu',
channelDescription: 'Notifikasi untuk jadwal Posyandu',
importance: Importance.high,
priority: Priority.high,
fullScreenIntent: true, // Tampilkan sebagai full screen intent
visibility: NotificationVisibility.public, // Tampilkan di lock screen
category: AndroidNotificationCategory.reminder, // Kategori pengingat
color: Colors.teal,
ledColor: Colors.teal,
ledOnMs: 1000,
ledOffMs: 500,
enableLights: true,
playSound: true,
enableVibration: true,
ticker: 'Notifikasi Posyandu',
timeoutAfter: 300000, // 5 menit timeout
autoCancel: true,
ongoing: false,
);
const NotificationDetails platformDetails = NotificationDetails(
android: androidDetails,
iOS: DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
);
await flutterLocalNotificationsPlugin.show(
id,
title,
body,
platformDetails,
payload: payload,
);
print('Notification shown: ID = $id, Title = $title');
}
// Notifikasi terjadwal berdasarkan jadwal
Future<void> scheduleNotification({
required JadwalModel jadwal,
int reminderHoursBefore = 24,
}) async {
final int notificationId = jadwal.id?.hashCode ?? DateTime.now().millisecondsSinceEpoch.remainder(100000);
// Tentukan waktu notifikasi (1 hari sebelum jadwal)
final scheduleDate = jadwal.tanggal.subtract(Duration(hours: reminderHoursBefore));
// Jika jadwalnya sudah lewat, tidak perlu menjadwalkan notifikasi
if (scheduleDate.isBefore(DateTime.now())) {
print('Jadwal sudah lewat, tidak perlu notifikasi: ${jadwal.nama}');
return;
}
final formattedDate = DateFormat('dd MMM yyyy').format(jadwal.tanggal);
final formattedTime = jadwal.waktu ?? '-';
String jenisIcon = '';
// Tentukan icon berdasarkan jenis jadwal
switch (jadwal.jenis.toLowerCase()) {
case 'imunisasi':
jenisIcon = '💉';
break;
case 'vitamin':
jenisIcon = '💊';
break;
case 'pemeriksaan rutin':
jenisIcon = '🏥';
break;
default:
jenisIcon = '📅';
}
final bool isUrgent = reminderHoursBefore <= 2; // Jika 2 jam atau kurang sebelum jadwal, anggap urgent
final AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
isUrgent ? 'jadwal_reminder_urgent_channel' : 'jadwal_reminder_channel',
isUrgent ? 'Pengingat Jadwal Penting' : 'Pengingat Jadwal',
channelDescription: isUrgent ? 'Pengingat untuk jadwal penting yang akan datang' : 'Pengingat untuk jadwal yang akan datang',
importance: Importance.high,
priority: Priority.high,
fullScreenIntent: isUrgent, // Full screen intent untuk urgent notifications
visibility: NotificationVisibility.public,
category: AndroidNotificationCategory.reminder,
color: Colors.teal,
ledColor: Colors.teal,
ledOnMs: 1000,
ledOffMs: 500,
enableLights: true,
playSound: true,
enableVibration: true,
ticker: 'Notifikasi Posyandu',
when: jadwal.tanggal.millisecondsSinceEpoch, // Tampilkan waktu jadwal
);
final NotificationDetails platformDetails = NotificationDetails(
android: androidDetails,
iOS: DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
);
final tz.TZDateTime notificationTime = tz.TZDateTime.from(scheduleDate, tz.local);
await flutterLocalNotificationsPlugin.zonedSchedule(
notificationId,
'Pengingat Jadwal $jenisIcon',
'${jadwal.nama} pada $formattedDate jam $formattedTime',
notificationTime,
platformDetails,
payload: 'jadwal_id=${jadwal.id}',
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
matchDateTimeComponents: DateTimeComponents.time,
);
print('Jadwal notifikasi diatur untuk: ${jadwal.nama} pada $formattedDate ${jadwal.waktu}, ID: $notificationId');
}
// Notifikasi saat jadwal baru tersedia dengan prioritas tinggi
Future<void> showNewJadwalNotification(JadwalModel jadwal) async {
final int notificationId = jadwal.id?.hashCode ?? DateTime.now().millisecondsSinceEpoch.remainder(100000);
final formattedDate = DateFormat('dd MMM yyyy').format(jadwal.tanggal);
String jenisIcon = '';
switch (jadwal.jenis.toLowerCase()) {
case 'imunisasi':
jenisIcon = '💉';
break;
case 'vitamin':
jenisIcon = '💊';
break;
case 'pemeriksaan rutin':
jenisIcon = '🏥';
break;
default:
jenisIcon = '📅';
}
// Gunakan prioritas tinggi untuk notifikasi jadwal baru
final AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
'jadwal_posyandu_channel',
'Jadwal Posyandu',
channelDescription: 'Notifikasi untuk jadwal Posyandu',
importance: Importance.high,
priority: Priority.high,
fullScreenIntent: true, // Tampilkan sebagai full screen intent
visibility: NotificationVisibility.public,
category: AndroidNotificationCategory.message,
color: Colors.teal,
ledColor: Colors.teal,
enableLights: true,
playSound: true,
enableVibration: true,
ticker: 'Jadwal baru tersedia',
);
final NotificationDetails platformDetails = NotificationDetails(
android: androidDetails,
iOS: DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
);
await flutterLocalNotificationsPlugin.show(
notificationId,
'Jadwal Baru $jenisIcon',
'${jadwal.nama} telah dijadwalkan pada $formattedDate',
platformDetails,
payload: 'jadwal_id=${jadwal.id}',
);
print('New schedule notification shown: ID = $notificationId, Title = ${jadwal.nama}');
}
// Atur notifikasi untuk semua jadwal yang akan datang
Future<void> scheduleAllUpcomingNotifications(List<JadwalModel> jadwalList) async {
print('Menyiapkan notifikasi untuk ${jadwalList.length} jadwal yang akan datang');
// Bersihkan semua notifikasi yang sudah ada
await flutterLocalNotificationsPlugin.cancelAll();
// Jadwalkan notifikasi baru untuk setiap jadwal yang akan datang
for (final jadwal in jadwalList) {
// Jadwalkan notifikasi 1 hari sebelum jadwal
await scheduleNotification(jadwal: jadwal, reminderHoursBefore: 24);
// Jadwalkan juga notifikasi 2 jam sebelum jadwal untuk pengingat terakhir
await scheduleNotification(jadwal: jadwal, reminderHoursBefore: 2);
}
}
// Batalkan semua notifikasi
Future<void> cancelAllNotifications() async {
await flutterLocalNotificationsPlugin.cancelAll();
print('Semua notifikasi dibatalkan');
}
// Batalkan notifikasi berdasarkan ID
Future<void> cancelNotification(int id) async {
await flutterLocalNotificationsPlugin.cancel(id);
print('Notifikasi dengan ID $id dibatalkan');
}
}