tasq/lib/services/notification_service.dart

127 lines
4.2 KiB
Dart

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
/// A thin wrapper around `flutter_local_notifications` that centralizes the
/// plugin instance and provides easy initialization/show helpers.
class NotificationService {
NotificationService._();
static final FlutterLocalNotificationsPlugin _plugin =
FlutterLocalNotificationsPlugin();
static const String _channelId = 'tasq_default_channel';
static const String _channelName = 'General';
static const String _highChannelId = 'tasq_custom_sound_channel_3';
static const String _highChannelName = 'High Priority';
/// Call during app startup, after any necessary permissions have been
/// granted. The callback receives a [NotificationResponse] when the user
/// taps the alert.
static Future<void> initialize({
void Function(NotificationResponse)? onDidReceiveNotificationResponse,
}) async {
const androidSettings = AndroidInitializationSettings(
'@mipmap/ic_launcher',
);
const iosSettings = DarwinInitializationSettings();
// Ensure the Android notification channel exists with sound and vibration
final androidImpl = _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>();
if (androidImpl != null) {
const channel = AndroidNotificationChannel(
_channelId,
_channelName,
description: 'General notifications for TasQ',
importance: Importance.defaultImportance,
playSound: true,
enableVibration: true,
);
const highChannel = AndroidNotificationChannel(
_highChannelId,
_highChannelName,
description: 'High priority notifications (sound + vibration)',
importance: Importance.max,
playSound: true,
enableVibration: true,
// 👇 Tell Android to use your specific file in the raw folder
sound: RawResourceAndroidNotificationSound('tasq_notification'),
);
try {
await androidImpl.createNotificationChannel(channel);
} catch (_) {}
try {
await androidImpl.createNotificationChannel(highChannel);
} catch (_) {}
}
await _plugin.initialize(
settings: const InitializationSettings(
android: androidSettings,
iOS: iosSettings,
),
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,
);
}
/// Show a simple notification immediately.
static Future<void> show({
required int id,
required String title,
required String body,
String? payload,
}) async {
const androidDetails = AndroidNotificationDetails(
_highChannelId,
_highChannelName,
importance: Importance.max,
priority: Priority.high,
playSound: true,
enableVibration: true,
sound: RawResourceAndroidNotificationSound('tasq_notification'),
);
const iosDetails = DarwinNotificationDetails(
sound: 'tasq_notification.wav',
);
await _plugin.show(
id: id,
title: title,
body: body,
notificationDetails: const NotificationDetails(
android: androidDetails,
iOS: iosDetails,
),
payload: payload,
);
}
/// Returns true when the high-priority channel appears to be muted or
/// has sound disabled on the device.
static Future<bool> isHighPriorityChannelMuted() async {
final androidImpl = _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>();
if (androidImpl == null) return false;
try {
final impl = androidImpl as dynamic;
final channel = await impl.getNotificationChannel(_highChannelId);
if (channel == null) return false;
final importance = channel.importance as dynamic;
final playSound = channel.playSound as bool?;
if (importance == null) return false;
// importance may be an enum-like value; compare using index if present.
try {
final impIndex = (importance.index is int)
? importance.index as int
: int.parse(importance.toString());
if (impIndex < Importance.high.index) return true;
} catch (_) {}
if (playSound == false) return true;
return false;
} catch (_) {
return false;
}
}
}