Files
iiEsaywebUIapp/lib/features/chat/services/voice_call_notification_service.dart
cogwheel0 e98f5cbf0f feat: integrate flutter_local_notifications for enhanced voice call notifications
- Added flutter_local_notifications dependency to manage notifications during voice calls.
- Implemented notification handling in VoiceCallService to update call status and manage user interactions.
- Enabled wake lock functionality to keep the screen on during calls and prevent audio interruptions.
- Updated AndroidManifest.xml to include necessary permissions for Bluetooth and foreground services.
- Enhanced notification actions to allow users to mute, unmute, or end calls directly from notifications.
2025-10-09 00:01:35 +05:30

226 lines
6.9 KiB
Dart

import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
/// Service to manage persistent notifications for voice calls
class VoiceCallNotificationService {
static final VoiceCallNotificationService _instance =
VoiceCallNotificationService._internal();
factory VoiceCallNotificationService() => _instance;
VoiceCallNotificationService._internal();
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
bool _initialized = false;
// Notification IDs and channels
static const String _channelId = 'voice_call_channel';
static const String _channelName = 'Voice Call';
static const String _channelDescription =
'Ongoing voice call notifications';
static const int _notificationId = 2001;
// Action IDs
static const String _actionMute = 'mute_call';
static const String _actionUnmute = 'unmute_call';
static const String _actionEndCall = 'end_call';
// Callback for handling notification actions
void Function(String action)? onActionPressed;
Future<void> initialize() async {
if (_initialized) return;
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
);
const settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await _notifications.initialize(
settings,
onDidReceiveNotificationResponse: _handleNotificationResponse,
onDidReceiveBackgroundNotificationResponse:
_handleBackgroundNotificationResponse,
);
// Create notification channel for Android
if (Platform.isAndroid) {
await _createAndroidNotificationChannel();
}
_initialized = true;
}
Future<void> _createAndroidNotificationChannel() async {
const androidChannel = AndroidNotificationChannel(
_channelId,
_channelName,
description: _channelDescription,
importance: Importance.high,
playSound: false,
enableVibration: false,
showBadge: false,
);
await _notifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(androidChannel);
}
void _handleNotificationResponse(NotificationResponse response) {
final action = response.actionId;
if (action != null && onActionPressed != null) {
onActionPressed!(action);
}
}
@pragma('vm:entry-point')
static void _handleBackgroundNotificationResponse(
NotificationResponse response,
) {
// Background action handling
// Note: This runs in an isolate, so we can't directly call instance methods
// Actions will be handled when app returns to foreground
}
/// Show ongoing voice call notification
Future<void> showCallNotification({
required String modelName,
required bool isMuted,
required bool isSpeaking,
}) async {
if (!_initialized) {
print('VoiceCallNotification: Initializing...');
await initialize();
}
print('VoiceCallNotification: Showing notification for $modelName (muted: $isMuted, speaking: $isSpeaking)');
final status = isSpeaking ? 'Speaking...' : 'Listening...';
final muteAction = isMuted ? 'Unmute' : 'Mute';
final muteActionId = isMuted ? _actionUnmute : _actionMute;
if (Platform.isAndroid) {
final androidDetails = AndroidNotificationDetails(
_channelId,
_channelName,
channelDescription: _channelDescription,
importance: Importance.high,
priority: Priority.high,
ongoing: true,
autoCancel: false,
playSound: false,
enableVibration: false,
showWhen: true,
usesChronometer: true,
chronometerCountDown: false,
category: AndroidNotificationCategory.call,
visibility: NotificationVisibility.public,
icon: '@mipmap/ic_launcher',
colorized: false,
actions: <AndroidNotificationAction>[
AndroidNotificationAction(
muteActionId,
muteAction,
icon: DrawableResourceAndroidBitmap(
isMuted ? '@drawable/ic_mic_on' : '@drawable/ic_mic_off',
),
showsUserInterface: false,
cancelNotification: false,
),
AndroidNotificationAction(
_actionEndCall,
'End Call',
icon: DrawableResourceAndroidBitmap('@drawable/ic_call_end'),
showsUserInterface: true,
cancelNotification: true,
),
],
);
await _notifications.show(
_notificationId,
'Voice Call with $modelName',
status,
NotificationDetails(android: androidDetails),
);
} else if (Platform.isIOS) {
// iOS doesn't support action buttons for ongoing notifications
// Use a simpler persistent notification
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: false,
presentSound: false,
interruptionLevel: InterruptionLevel.timeSensitive,
);
await _notifications.show(
_notificationId,
'Voice Call with $modelName',
status,
const NotificationDetails(iOS: iosDetails),
);
}
}
/// Update notification status
Future<void> updateCallStatus({
required String modelName,
required bool isMuted,
required bool isSpeaking,
}) async {
await showCallNotification(
modelName: modelName,
isMuted: isMuted,
isSpeaking: isSpeaking,
);
}
/// Cancel the voice call notification
Future<void> cancelNotification() async {
await _notifications.cancel(_notificationId);
}
/// Check if notifications are enabled
Future<bool> areNotificationsEnabled() async {
if (Platform.isAndroid) {
final androidImpl = _notifications.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
return await androidImpl?.areNotificationsEnabled() ?? false;
} else if (Platform.isIOS) {
// iOS doesn't have a direct check, assume enabled if initialized
return _initialized;
}
return false;
}
/// Request notification permissions
Future<bool> requestPermissions() async {
if (Platform.isAndroid) {
final androidImpl = _notifications.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
final granted = await androidImpl?.requestNotificationsPermission();
return granted ?? false;
} else if (Platform.isIOS) {
final iosImpl = _notifications
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>();
final granted = await iosImpl?.requestPermissions(
alert: true,
badge: false,
sound: false,
);
return granted ?? false;
}
return false;
}
}