Files
rmtPocketWatcher/flutter_app/lib/services/notification_service.dart
2025-12-14 21:53:46 -05:00

150 lines
4.6 KiB
Dart

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter/foundation.dart';
class NotificationService {
static final NotificationService _instance = NotificationService._internal();
factory NotificationService() => _instance;
NotificationService._internal();
final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin();
bool _initialized = false;
Future<void> initialize() async {
if (_initialized) return;
try {
// Android initialization
const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
// iOS initialization
const DarwinInitializationSettings iosSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
// Linux/Windows initialization
const LinuxInitializationSettings linuxSettings = LinuxInitializationSettings(
defaultActionName: 'Open rmtPocketWatcher',
);
const InitializationSettings settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
linux: linuxSettings,
);
await _notifications.initialize(
settings,
onDidReceiveNotificationResponse: _onNotificationTapped,
);
_initialized = true;
if (kDebugMode) {
print('NotificationService initialized successfully');
}
} catch (e) {
if (kDebugMode) {
print('Failed to initialize notifications: $e');
}
// Don't throw - allow app to continue without notifications
}
}
void _onNotificationTapped(NotificationResponse response) {
// Handle notification tap if needed
if (kDebugMode) {
print('Notification tapped: ${response.payload}');
}
}
Future<void> showPriceAlert({
required String title,
required String body,
required double auecAmount,
required double price,
required String seller,
}) async {
if (!_initialized) await initialize();
if (!_initialized) return; // Skip if initialization failed
try {
// Android notification details
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
'price_alerts',
'Price Alerts',
channelDescription: 'Notifications for AUEC price alerts from rmtPocketWatcher',
importance: Importance.high,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound('notifcation'),
icon: '@mipmap/ic_launcher',
largeIcon: DrawableResourceAndroidBitmap('@mipmap/ic_launcher'),
enableVibration: true,
enableLights: true,
showWhen: true,
);
// iOS notification details
const DarwinNotificationDetails iosDetails = DarwinNotificationDetails(
sound: 'notifcation.mp3', // iOS looks in main bundle, not assets
presentAlert: true,
presentBadge: true,
presentSound: true,
badgeNumber: 1,
subtitle: 'Lambda Banking Conglomerate',
threadIdentifier: 'price_alerts',
);
// Linux/Windows notification details
final LinuxNotificationDetails linuxDetails = LinuxNotificationDetails(
icon: AssetsLinuxIcon('assets/logo.png'),
sound: AssetsLinuxSound('assets/notifcation.mp3'),
category: LinuxNotificationCategory.imReceived,
urgency: LinuxNotificationUrgency.critical,
);
final NotificationDetails details = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
linux: linuxDetails,
);
final int notificationId = DateTime.now().millisecondsSinceEpoch.remainder(100000);
await _notifications.show(
notificationId,
title,
body,
details,
payload: 'price_alert:$seller:$auecAmount:$price',
);
if (kDebugMode) {
print('Price alert notification sent: $title - $body');
}
} catch (e) {
if (kDebugMode) {
print('Failed to show notification: $e');
}
}
}
Future<void> requestPermissions() async {
if (!_initialized) await initialize();
// Request permissions for iOS
await _notifications
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
// Request permissions for Android 13+
await _notifications
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
}
}