|
|
|
|
@ -18,123 +18,43 @@ import 'package:test_sa/modules/tm_module/tasks/task_request_detail_view.dart';
|
|
|
|
|
import 'package:test_sa/modules/tm_module/gas_refill/gas_refill_details.dart';
|
|
|
|
|
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
|
|
|
|
|
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/services/pending_call_storage.dart';
|
|
|
|
|
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
|
|
|
|
|
import 'package:flutter_callkit_incoming/entities/entities.dart';
|
|
|
|
|
import 'package:test_sa/modules/cx_module/chat/call/services/call_notification_service.dart';
|
|
|
|
|
|
|
|
|
|
@pragma('vm:entry-point')
|
|
|
|
|
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
|
|
|
try {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [BACKGROUND] Push notification received', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [BACKGROUND] Message ID: ${message.messageId}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [BACKGROUND] Data payload: ${message.data}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [BACKGROUND] Notification title: ${message.notification?.title}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [BACKGROUND] Notification body: ${message.notification?.body}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
final notificationType = message.data['notificationType'] as String? ??
|
|
|
|
|
message.data['type'] as String?;
|
|
|
|
|
message.data['type'] as String?; // Backend uses 'type'
|
|
|
|
|
final transactionType = message.data['transactionType'] as String?;
|
|
|
|
|
|
|
|
|
|
log('📱 [BACKGROUND] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [BACKGROUND] Transaction Type: $transactionType', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// Handle incoming call notifications
|
|
|
|
|
if (notificationType == 'incoming_call' || transactionType == 'call') {
|
|
|
|
|
// Extract call data
|
|
|
|
|
final callId = message.data['callId'] as String? ??
|
|
|
|
|
message.data['sessionId'] as String? ??
|
|
|
|
|
DateTime.now().millisecondsSinceEpoch.toString();
|
|
|
|
|
|
|
|
|
|
final callerId = message.data['callerId'] as String? ??
|
|
|
|
|
message.data['callerEmployeeNumber'] as String? ??
|
|
|
|
|
message.data['sourceUserId'] as String? ??
|
|
|
|
|
'unknown';
|
|
|
|
|
|
|
|
|
|
final callerName = message.data['callerName'] as String? ??
|
|
|
|
|
message.data['callerUserName'] as String? ??
|
|
|
|
|
message.data['userName'] as String? ??
|
|
|
|
|
'Unknown Caller';
|
|
|
|
|
|
|
|
|
|
final callerEmployeeNumber = message.data['callerEmployeeNumber'] as String? ?? callerId;
|
|
|
|
|
|
|
|
|
|
final isVideoCall = () {
|
|
|
|
|
final value = message.data['isVideoCall'];
|
|
|
|
|
if (value is bool) return value;
|
|
|
|
|
if (value is String) return value.toLowerCase() == 'true';
|
|
|
|
|
if (message.data['callType'] == 'video') return true;
|
|
|
|
|
return false;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
final moduleId = message.data['moduleId'] as String? ??
|
|
|
|
|
message.data['applicationId']?.toString();
|
|
|
|
|
final referenceId = message.data['referenceId'] as String?;
|
|
|
|
|
final conversationId = message.data['conversationId'] as String?;
|
|
|
|
|
|
|
|
|
|
// CRITICAL FIX: DO NOT initialize any communication services in background isolate
|
|
|
|
|
// Only persist call data and show CallKit
|
|
|
|
|
///TODO need to find other solution to this after testing
|
|
|
|
|
await PendingCallStorage.savePendingCall(
|
|
|
|
|
callId: callId,
|
|
|
|
|
callerId: callerId,
|
|
|
|
|
callerName: callerName,
|
|
|
|
|
callerEmployeeNumber: callerEmployeeNumber,
|
|
|
|
|
isVideoCall: isVideoCall,
|
|
|
|
|
moduleId: moduleId,
|
|
|
|
|
referenceId: referenceId,
|
|
|
|
|
conversationId: conversationId,
|
|
|
|
|
);
|
|
|
|
|
// Show CallKit incoming call UI
|
|
|
|
|
log('📱 [BACKGROUND] Displaying CallKit UI...', name: 'FirebaseNotificationManger');
|
|
|
|
|
final callKitParams = CallKitParams(
|
|
|
|
|
id: callId,
|
|
|
|
|
nameCaller: callerName,
|
|
|
|
|
appName: 'ATOMS',
|
|
|
|
|
avatar: '',
|
|
|
|
|
handle: callerEmployeeNumber,
|
|
|
|
|
type: isVideoCall ? 1 : 0,
|
|
|
|
|
duration: 30000,
|
|
|
|
|
missedCallNotification: const NotificationParams(
|
|
|
|
|
showNotification: true,
|
|
|
|
|
isShowCallback: true,
|
|
|
|
|
subtitle: 'Missed call',
|
|
|
|
|
callbackText: 'Call back',
|
|
|
|
|
),
|
|
|
|
|
extra: {
|
|
|
|
|
'callId': callId,
|
|
|
|
|
'callerId': callerId,
|
|
|
|
|
'callerEmployeeNumber': callerEmployeeNumber,
|
|
|
|
|
'callerName': callerName,
|
|
|
|
|
'moduleId': moduleId ?? '',
|
|
|
|
|
'referenceId': referenceId ?? '',
|
|
|
|
|
'conversationId': conversationId ?? '',
|
|
|
|
|
'isVideoCall': isVideoCall,
|
|
|
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
|
|
|
},
|
|
|
|
|
android: const AndroidParams(
|
|
|
|
|
isCustomNotification: true,
|
|
|
|
|
isShowLogo: false,
|
|
|
|
|
ringtonePath: 'system_ringtone_default',
|
|
|
|
|
backgroundColor: '#0955fa',
|
|
|
|
|
backgroundUrl: '',
|
|
|
|
|
actionColor: '#4CAF50',
|
|
|
|
|
textColor: '#ffffff',
|
|
|
|
|
incomingCallNotificationChannelName: 'Incoming Calls',
|
|
|
|
|
missedCallNotificationChannelName: 'Missed Calls',
|
|
|
|
|
),
|
|
|
|
|
ios: const IOSParams(
|
|
|
|
|
iconName: 'CallKitLogo',
|
|
|
|
|
handleType: 'generic',
|
|
|
|
|
supportsVideo: true,
|
|
|
|
|
maximumCallGroups: 1,
|
|
|
|
|
maximumCallsPerCallGroup: 1,
|
|
|
|
|
audioSessionMode: 'default',
|
|
|
|
|
audioSessionActive: true,
|
|
|
|
|
audioSessionPreferredSampleRate: 44100.0,
|
|
|
|
|
audioSessionPreferredIOBufferDuration: 0.005,
|
|
|
|
|
supportsDTMF: true,
|
|
|
|
|
supportsHolding: false,
|
|
|
|
|
supportsGrouping: false,
|
|
|
|
|
supportsUngrouping: false,
|
|
|
|
|
ringtonePath: 'system_ringtone_default',
|
|
|
|
|
),
|
|
|
|
|
log('📞 [BACKGROUND] Incoming call notification detected', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📞 [BACKGROUND] Call data:', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callId: ${message.data['callId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerId: ${message.data['callerId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - calleeEmployeeNumber: ${message.data['calleeEmployeeNumber']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerName: ${message.data['callerName']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerUserName: ${message.data['callerUserName']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerEmployeeNumber: ${message.data['callerEmployeeNumber']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - isVideoCall: ${message.data['isVideoCall']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callType: ${message.data['callType']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - applicationId: ${message.data['applicationId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
await CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: message.data,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await FlutterCallkitIncoming.showCallkitIncoming(callKitParams);
|
|
|
|
|
log('✅ [BACKGROUND] CallKit UI displayed', name: 'FirebaseNotificationManger');
|
|
|
|
|
} else {
|
|
|
|
|
log('📱 [BACKGROUND] Non-call notification type', name: 'FirebaseNotificationManger');
|
|
|
|
|
}
|
|
|
|
|
@ -157,7 +77,7 @@ class FirebaseNotificationManger {
|
|
|
|
|
try {
|
|
|
|
|
if (!(await isGoogleServicesAvailable())) {
|
|
|
|
|
h_push.Push.enableLogger();
|
|
|
|
|
await h_push.Push.setAutoInitEnabled(true);
|
|
|
|
|
final result = await h_push.Push.setAutoInitEnabled(true);
|
|
|
|
|
h_push.Push.onMessageReceivedStream.listen(_onMessageReceived, onError: _onMessageReceiveError);
|
|
|
|
|
|
|
|
|
|
h_push.Push.getTokenStream.listen((hToken) {
|
|
|
|
|
@ -189,6 +109,10 @@ class FirebaseNotificationManger {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void _onMessageReceived(h_push.RemoteMessage remoteMessage) {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI] Message received', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI] Full message data: ${remoteMessage.toMap()}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void _onMessageReceiveError(Object error) {
|
|
|
|
|
@ -208,7 +132,7 @@ class FirebaseNotificationManger {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Future<void> handleMessage(context, Map<String, dynamic> messageData) async {
|
|
|
|
|
static void handleMessage(context, Map<String, dynamic> messageData) {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Processing notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Full message data: $messageData', name: 'FirebaseNotificationManger');
|
|
|
|
|
@ -219,106 +143,37 @@ class FirebaseNotificationManger {
|
|
|
|
|
final transactionType = messageData['transactionType'] as String?;
|
|
|
|
|
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Transaction Type: $transactionType', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (notificationType == 'incoming_call' || transactionType == 'call') {
|
|
|
|
|
// CRITICAL FIX: Show CallKit UI in foreground just like background
|
|
|
|
|
final callId = messageData['callId'] as String? ??
|
|
|
|
|
messageData['sessionId'] as String? ??
|
|
|
|
|
DateTime.now().millisecondsSinceEpoch.toString();
|
|
|
|
|
|
|
|
|
|
final callerId = messageData['callerId'] as String? ??
|
|
|
|
|
messageData['callerEmployeeNumber'] as String? ??
|
|
|
|
|
messageData['sourceUserId'] as String? ??
|
|
|
|
|
'unknown';
|
|
|
|
|
|
|
|
|
|
final callerName = messageData['callerName'] as String? ??
|
|
|
|
|
messageData['callerUserName'] as String? ??
|
|
|
|
|
messageData['userName'] as String? ??
|
|
|
|
|
'Unknown Caller';
|
|
|
|
|
|
|
|
|
|
final callerEmployeeNumber = messageData['callerEmployeeNumber'] as String? ?? callerId;
|
|
|
|
|
|
|
|
|
|
final isVideoCall = () {
|
|
|
|
|
final value = messageData['isVideoCall'];
|
|
|
|
|
if (value is bool) return value;
|
|
|
|
|
if (value is String) return value.toLowerCase() == 'true';
|
|
|
|
|
if (messageData['callType'] == 'video') return true;
|
|
|
|
|
return false;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
final moduleId = messageData['moduleId'] as String? ??
|
|
|
|
|
messageData['applicationId']?.toString();
|
|
|
|
|
final referenceId = messageData['referenceId'] as String?;
|
|
|
|
|
final conversationId = messageData['conversationId'] as String?;
|
|
|
|
|
|
|
|
|
|
log('📱 [FOREGROUND] Showing CallKit UI...', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// Show CallKit UI using CallKitService
|
|
|
|
|
try {
|
|
|
|
|
final callKitParams = CallKitParams(
|
|
|
|
|
id: callId,
|
|
|
|
|
nameCaller: callerName,
|
|
|
|
|
appName: 'ATOMS',
|
|
|
|
|
avatar: '',
|
|
|
|
|
handle: callerEmployeeNumber,
|
|
|
|
|
type: isVideoCall ? 1 : 0,
|
|
|
|
|
duration: 30000,
|
|
|
|
|
missedCallNotification: const NotificationParams(
|
|
|
|
|
showNotification: true,
|
|
|
|
|
isShowCallback: true,
|
|
|
|
|
subtitle: 'Missed call',
|
|
|
|
|
callbackText: 'Call back',
|
|
|
|
|
),
|
|
|
|
|
extra: {
|
|
|
|
|
'callId': callId,
|
|
|
|
|
'callerId': callerId,
|
|
|
|
|
'callerEmployeeNumber': callerEmployeeNumber,
|
|
|
|
|
'callerName': callerName,
|
|
|
|
|
'moduleId': moduleId ?? '',
|
|
|
|
|
'referenceId': referenceId ?? '',
|
|
|
|
|
'conversationId': conversationId ?? '',
|
|
|
|
|
'isVideoCall': isVideoCall,
|
|
|
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
|
|
|
},
|
|
|
|
|
android: const AndroidParams(
|
|
|
|
|
isCustomNotification: true,
|
|
|
|
|
isShowLogo: false,
|
|
|
|
|
ringtonePath: 'system_ringtone_default',
|
|
|
|
|
backgroundColor: '#0955fa',
|
|
|
|
|
backgroundUrl: '',
|
|
|
|
|
actionColor: '#4CAF50',
|
|
|
|
|
textColor: '#ffffff',
|
|
|
|
|
incomingCallNotificationChannelName: 'Incoming Calls',
|
|
|
|
|
missedCallNotificationChannelName: 'Missed Calls',
|
|
|
|
|
),
|
|
|
|
|
ios: const IOSParams(
|
|
|
|
|
iconName: 'CallKitLogo',
|
|
|
|
|
handleType: 'generic',
|
|
|
|
|
supportsVideo: true,
|
|
|
|
|
maximumCallGroups: 1,
|
|
|
|
|
maximumCallsPerCallGroup: 1,
|
|
|
|
|
audioSessionMode: 'default',
|
|
|
|
|
audioSessionActive: true,
|
|
|
|
|
audioSessionPreferredSampleRate: 44100.0,
|
|
|
|
|
audioSessionPreferredIOBufferDuration: 0.005,
|
|
|
|
|
supportsDTMF: true,
|
|
|
|
|
supportsHolding: false,
|
|
|
|
|
supportsGrouping: false,
|
|
|
|
|
supportsUngrouping: false,
|
|
|
|
|
ringtonePath: 'system_ringtone_default',
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await FlutterCallkitIncoming.showCallkitIncoming(callKitParams);
|
|
|
|
|
log('✅ [FOREGROUND] CallKit UI displayed successfully', name: 'FirebaseNotificationManger');
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
log('❌ [FOREGROUND] Failed to show CallKit UI: $e\nStack: $stackTrace', name: 'FirebaseNotificationManger');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log('<EFBFBD><EFBFBD><EFBFBD>══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📞 [FOREGROUND] Incoming call notification detected', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📞 [FOREGROUND] Call data:', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callId: ${messageData['callId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerId: ${messageData['callerId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - calleeEmployeeNumber: ${messageData['calleeEmployeeNumber']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerName: ${messageData['callerName']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerUserName: ${messageData['callerUserName']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerEmployeeNumber: ${messageData['callerEmployeeNumber']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - isVideoCall: ${messageData['isVideoCall']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callType: ${messageData['callType']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - moduleId: ${messageData['moduleId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - referenceId: ${messageData['referenceId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - conversationId: ${messageData['conversationId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - applicationId: ${messageData['applicationId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// Let CallNotificationService handle it
|
|
|
|
|
CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: messageData,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Non-call notification - processing normally', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Request Type: ${messageData["requestType"]}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Request Number: ${messageData["requestNumber"]}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HANDLE_MESSAGE] Module ID: ${messageData["moduleId"]}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (messageData["requestType"] != null && messageData["requestNumber"] != null) {
|
|
|
|
|
Widget? serviceClass;
|
|
|
|
|
@ -432,22 +287,32 @@ class FirebaseNotificationManger {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static initialized(BuildContext context) async {
|
|
|
|
|
log('🔧 [INIT] Initializing FirebaseNotificationManger', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
//TOD0 add platform check here also
|
|
|
|
|
if (!(await isGoogleServicesAvailable()) && Platform.isAndroid) {
|
|
|
|
|
log('📱 [INIT] Using Huawei Push Services', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// NEW: Handle Huawei initial notification
|
|
|
|
|
var initialNotification = await h_push.Push.getInitialNotification();
|
|
|
|
|
if (initialNotification != null) {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI_INITIAL] Initial notification found', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI_INITIAL] Full data: $initialNotification', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> remoteData = Map<String, dynamic>.from(initialNotification["extras"] as Map);
|
|
|
|
|
log('📱 [HUAWEI_INITIAL] Extras data: $remoteData', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// Check if it's a call notification
|
|
|
|
|
final notificationType = remoteData['notificationType'] as String?;
|
|
|
|
|
log('📱 [HUAWEI_INITIAL] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (notificationType == 'incoming_call') {
|
|
|
|
|
log('📞 [HUAWEI_INITIAL] Call notification - will be handled by SignalR', name: 'FirebaseNotificationManger');
|
|
|
|
|
// In foreground, SignalR will handle via OnReceiveCall event
|
|
|
|
|
log('📞 [HUAWEI_INITIAL] Processing call notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
await CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: remoteData,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
log('📱 [HUAWEI_INITIAL] Processing regular notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
handleMessage(context, remoteData);
|
|
|
|
|
@ -459,6 +324,11 @@ class FirebaseNotificationManger {
|
|
|
|
|
|
|
|
|
|
h_push.Push.onNotificationOpenedApp.listen((message) {
|
|
|
|
|
try {
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI_TAP] Notification tapped', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI_TAP] Message type: ${message.runtimeType}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [HUAWEI_TAP] Full message: $message', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (message is Map<String, dynamic>) {
|
|
|
|
|
Map<String, dynamic> remoteData = message;
|
|
|
|
|
remoteData = remoteData["extras"];
|
|
|
|
|
@ -469,8 +339,11 @@ class FirebaseNotificationManger {
|
|
|
|
|
log('📱 [HUAWEI_TAP] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (notificationType == 'incoming_call') {
|
|
|
|
|
log('📞 [HUAWEI_TAP] Call notification - will be handled by SignalR', name: 'FirebaseNotificationManger');
|
|
|
|
|
// In foreground, SignalR will handle via OnReceiveCall event
|
|
|
|
|
log('📞 [HUAWEI_TAP] Processing call notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: remoteData,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
log('📱 [HUAWEI_TAP] Processing regular notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
handleMessage(context, remoteData);
|
|
|
|
|
@ -478,7 +351,7 @@ class FirebaseNotificationManger {
|
|
|
|
|
}
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
} catch (ex) {
|
|
|
|
|
log('❌ [HUAWEI_TAP] Parsing error: $ex', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('❌ [HUAWEI_TAP] Parsing error: $ex', name: 'FirebaseNotificationManger', error: ex);
|
|
|
|
|
}
|
|
|
|
|
}, onError: (e) => log('❌ [HUAWEI_TAP] Error: ${e.toString()}', name: 'FirebaseNotificationManger'));
|
|
|
|
|
return;
|
|
|
|
|
@ -500,7 +373,7 @@ class FirebaseNotificationManger {
|
|
|
|
|
);
|
|
|
|
|
log('✅ [INIT] Notification permissions granted: ${settings.authorizationStatus}', name: 'FirebaseNotificationManger');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log('❌ [INIT] Permission request failed: $error', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('❌ [INIT] Permission request failed: $error', name: 'FirebaseNotificationManger', error: error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -514,18 +387,23 @@ class FirebaseNotificationManger {
|
|
|
|
|
|
|
|
|
|
FirebaseMessaging.instance.getInitialMessage().then((initialMessage) {
|
|
|
|
|
if (initialMessage != null) {
|
|
|
|
|
// Check if it's a call notification
|
|
|
|
|
final notificationType = initialMessage.data['notificationType'] as String? ??
|
|
|
|
|
initialMessage.data['type'] as String?;
|
|
|
|
|
final transactionType = initialMessage.data['transactionType'] as String?;
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [FCM_INITIAL] Initial message found', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [FCM_INITIAL] Message ID: ${initialMessage.messageId}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [FCM_INITIAL] Data: ${initialMessage.data}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [FCM_INITIAL] Title: ${initialMessage.notification?.title}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [FCM_INITIAL] Body: ${initialMessage.notification?.body}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// NEW: Check if it's a call notification
|
|
|
|
|
final notificationType = initialMessage.data['notificationType'] as String?;
|
|
|
|
|
log('📱 [FCM_INITIAL] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📱 [FCM_INITIAL] Transaction Type: $transactionType', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (notificationType == 'incoming_call' || transactionType == 'call') {
|
|
|
|
|
log('📞 [FCM_INITIAL] Call notification detected', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' ℹ️ Background handler already saved this call to SharedPreferences', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' ℹ️ AppLifecycleObserver will restore the call when app initializes', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' ℹ️ Skipping duplicate processing', name: 'FirebaseNotificationManger');
|
|
|
|
|
if (notificationType == 'incoming_call') {
|
|
|
|
|
log('📞 [FCM_INITIAL] Processing call notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: initialMessage.data,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
log('📱 [FCM_INITIAL] Processing regular notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
handleMessage(context, initialMessage.data);
|
|
|
|
|
@ -562,101 +440,11 @@ class FirebaseNotificationManger {
|
|
|
|
|
log(' - callType: ${message.data['callType']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - applicationId: ${message.data['applicationId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
// CRITICAL FIX: Show CallKit UI in foreground
|
|
|
|
|
final callId = message.data['callId'] as String? ??
|
|
|
|
|
message.data['sessionId'] as String? ??
|
|
|
|
|
DateTime.now().millisecondsSinceEpoch.toString();
|
|
|
|
|
|
|
|
|
|
final callerId = message.data['callerId'] as String? ??
|
|
|
|
|
message.data['callerEmployeeNumber'] as String? ??
|
|
|
|
|
message.data['sourceUserId'] as String? ??
|
|
|
|
|
'unknown';
|
|
|
|
|
|
|
|
|
|
final callerName = message.data['callerName'] as String? ??
|
|
|
|
|
message.data['callerUserName'] as String? ??
|
|
|
|
|
message.data['userName'] as String? ??
|
|
|
|
|
'Unknown Caller';
|
|
|
|
|
|
|
|
|
|
final callerEmployeeNumber = message.data['callerEmployeeNumber'] as String? ?? callerId;
|
|
|
|
|
|
|
|
|
|
final isVideoCall = () {
|
|
|
|
|
final value = message.data['isVideoCall'];
|
|
|
|
|
if (value is bool) return value;
|
|
|
|
|
if (value is String) return value.toLowerCase() == 'true';
|
|
|
|
|
if (message.data['callType'] == 'video') return true;
|
|
|
|
|
return false;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
final moduleId = message.data['moduleId'] as String? ??
|
|
|
|
|
message.data['applicationId']?.toString();
|
|
|
|
|
final referenceId = message.data['referenceId'] as String?;
|
|
|
|
|
final conversationId = message.data['conversationId'] as String?;
|
|
|
|
|
|
|
|
|
|
log('📱 [FOREGROUND] Showing CallKit UI...', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final callKitParams = CallKitParams(
|
|
|
|
|
id: callId,
|
|
|
|
|
nameCaller: callerName,
|
|
|
|
|
appName: 'ATOMS',
|
|
|
|
|
avatar: '',
|
|
|
|
|
handle: callerEmployeeNumber,
|
|
|
|
|
type: isVideoCall ? 1 : 0,
|
|
|
|
|
duration: 30000,
|
|
|
|
|
missedCallNotification: const NotificationParams(
|
|
|
|
|
showNotification: true,
|
|
|
|
|
isShowCallback: true,
|
|
|
|
|
subtitle: 'Missed call',
|
|
|
|
|
callbackText: 'Call back',
|
|
|
|
|
),
|
|
|
|
|
extra: {
|
|
|
|
|
'callId': callId,
|
|
|
|
|
'callerId': callerId,
|
|
|
|
|
'callerEmployeeNumber': callerEmployeeNumber,
|
|
|
|
|
'callerName': callerName,
|
|
|
|
|
'moduleId': moduleId ?? '',
|
|
|
|
|
'referenceId': referenceId ?? '',
|
|
|
|
|
'conversationId': conversationId ?? '',
|
|
|
|
|
'isVideoCall': isVideoCall,
|
|
|
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
|
|
|
},
|
|
|
|
|
android: const AndroidParams(
|
|
|
|
|
isCustomNotification: true,
|
|
|
|
|
isShowLogo: false,
|
|
|
|
|
ringtonePath: 'system_ringtone_default',
|
|
|
|
|
backgroundColor: '#0955fa',
|
|
|
|
|
backgroundUrl: '',
|
|
|
|
|
actionColor: '#4CAF50',
|
|
|
|
|
textColor: '#ffffff',
|
|
|
|
|
incomingCallNotificationChannelName: 'Incoming Calls',
|
|
|
|
|
missedCallNotificationChannelName: 'Missed Calls',
|
|
|
|
|
),
|
|
|
|
|
ios: const IOSParams(
|
|
|
|
|
iconName: 'CallKitLogo',
|
|
|
|
|
handleType: 'generic',
|
|
|
|
|
supportsVideo: true,
|
|
|
|
|
maximumCallGroups: 1,
|
|
|
|
|
maximumCallsPerCallGroup: 1,
|
|
|
|
|
audioSessionMode: 'default',
|
|
|
|
|
audioSessionActive: true,
|
|
|
|
|
audioSessionPreferredSampleRate: 44100.0,
|
|
|
|
|
audioSessionPreferredIOBufferDuration: 0.005,
|
|
|
|
|
supportsDTMF: true,
|
|
|
|
|
supportsHolding: false,
|
|
|
|
|
supportsGrouping: false,
|
|
|
|
|
supportsUngrouping: false,
|
|
|
|
|
ringtonePath: 'system_ringtone_default',
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
FlutterCallkitIncoming.showCallkitIncoming(callKitParams);
|
|
|
|
|
log('✅ [FOREGROUND] CallKit UI displayed successfully', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('ℹ️ [FOREGROUND] SignalR OnIncomingCallAsync will handle call state in CallManager', name: 'FirebaseNotificationManger');
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
log('❌ [FOREGROUND] Failed to show CallKit UI: $e\nStack: $stackTrace', name: 'FirebaseNotificationManger');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log('══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: message.data,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -668,8 +456,8 @@ class FirebaseNotificationManger {
|
|
|
|
|
NotificationManger.showNotification(
|
|
|
|
|
title: message.notification?.title ?? "",
|
|
|
|
|
subtext: message.notification?.body ?? "",
|
|
|
|
|
hashcode: int.tryParse("1234") ?? 1,
|
|
|
|
|
payload: json.encode(message.data),
|
|
|
|
|
hashcode: int.tryParse("1234" ?? "") ?? 1,
|
|
|
|
|
payload: json.encode(message.data),
|
|
|
|
|
context: context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -689,8 +477,16 @@ class FirebaseNotificationManger {
|
|
|
|
|
log('📱 [FCM_TAP] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
if (notificationType == 'incoming_call') {
|
|
|
|
|
log('📞 [NOTIFICATION TAP] Call notification tapped - will be handled by SignalR', name: 'FirebaseNotificationManger');
|
|
|
|
|
// In foreground, SignalR will handle via OnReceiveCall event
|
|
|
|
|
log('📞 [NOTIFICATION TAP] Incoming call notification tapped', name: 'FirebaseNotificationManger');
|
|
|
|
|
log('📞 [FCM_TAP] Call data:', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callId: ${message.data['callId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerId: ${message.data['callerId']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
log(' - callerName: ${message.data['callerName']}', name: 'FirebaseNotificationManger');
|
|
|
|
|
|
|
|
|
|
CallNotificationService().handleIncomingCallNotification(
|
|
|
|
|
notificationData: message.data,
|
|
|
|
|
source: 'push',
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
log('📱 [FCM_TAP] Processing regular notification', name: 'FirebaseNotificationManger');
|
|
|
|
|
handleMessage(context, message.data);
|
|
|
|
|
|