huawei calling added

main_production_audio_video_call
Sikander Saleem 2 weeks ago
parent 42effa2db0
commit 97d60b3031

@ -46,8 +46,42 @@ Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
}
}
/// Android: receives callkit events while no UI listener is active (app killed / not on LandPage).
/// When the app is in the foreground, events go to `callingEvents` in land_page.dart instead.
@pragma('vm:entry-point')
Future<void> backgroundMessageCallback(h_push.RemoteMessage remoteMessage) async {
final payload = huaweiMessageData(remoteMessage);
if (payload == null || payload["type"] != "incoming_call") return;
// Start the callkit background engine so a Decline tapped while killed reaches the caller.
try {
await FlutterCallkitIncoming.onBackgroundMessage(onBackgroundMessage);
} catch (_) {}
await handleDataMessage(payload, true);
}
Map<String, dynamic>? huaweiMessageData(h_push.RemoteMessage remoteMessage) {
try {
Map<String, dynamic>? map;
final raw = remoteMessage.data;
if (raw != null && raw.isNotEmpty) {
final decoded = json.decode(raw);
if (decoded is Map) map = Map<String, dynamic>.from(decoded);
}
map ??= remoteMessage.dataOfMap == null ? null : Map<String, dynamic>.from(remoteMessage.dataOfMap!);
if (map == null) return null;
if (map["type"] == null && map["data"] != null) {
final inner = map["data"] is String ? json.decode(map["data"]) : map["data"];
if (inner is Map) map = Map<String, dynamic>.from(inner);
}
return {
for (final e in map.entries)
if (e.value != null) e.key: e.value is String ? e.value : (e.value is Map || e.value is List ? json.encode(e.value) : e.value.toString()),
};
} catch (e) {
print("HMS data parse error: $e");
return null;
}
}
@pragma('vm:entry-point')
Future<void> onBackgroundMessage(CallEvent event) async {
switch (event) {
@ -59,16 +93,6 @@ Future<void> onBackgroundMessage(CallEvent event) async {
}
}
/// iOS: the single app-wide subscription to [FlutterCallkitIncoming.onEvent], started in main().
///
/// When the app is killed, a VoIP push launches it in the background with no UI: the splash
/// animation never completes, LandPage is never built and its listener never exists, so a
/// Decline tapped on the CallKit screen was dropped and the caller kept ringing.
/// Starting this in main() means a listener exists before the user can respond.
///
/// It must be the ONLY onEvent subscription: every `onEvent` getter call creates a new
/// EventChannel stream, and listening again replaces the previous native handler.
/// LandPage therefore receives events through [uiHandler] instead of listening itself.
class CallKitEvents {
static StreamSubscription<CallEvent?>? _subscription;
@ -113,6 +137,7 @@ class FirebaseNotificationManger {
static String? voipToken;
static Timer? _missedCallTimer;
static String? _activeMissedCallId;
static StreamSubscription<h_push.RemoteMessage>? _hmsMessageSubscription;
static String? _acceptedCallInProgress;
@ -124,6 +149,7 @@ class FirebaseNotificationManger {
}
static void endAcceptedCall() => _acceptedCallInProgress = null;
/// iOS only. The VoIP token is registered natively in AppDelegate (PushKit) and stored
/// by flutter_callkit_incoming. It is sent to the backend as `voIPToken` on chat login.
static Future<void> refreshVoipToken() async {
@ -159,11 +185,11 @@ class FirebaseNotificationManger {
if (!(await isGoogleServicesAvailable())) {
h_push.Push.enableLogger();
final result = await h_push.Push.setAutoInitEnabled(true);
h_push.Push.onMessageReceivedStream.listen(_onMessageReceived, onError: _onMessageReceiveError);
listenHmsForegroundMessages();
h_push.Push.getTokenStream.listen((hToken) {
// onToken(token);
// print("Huawei Token: ${hToken}");
print("Huawei Token: ${hToken}");
token = hToken;
}, onError: (e) {
print("Huawei TokenError" + e.toString());
@ -191,8 +217,17 @@ class FirebaseNotificationManger {
//print("pushToken:$token");
}
static Stream<h_push.RemoteMessage>? _hmsMessageStream;
static Stream<h_push.RemoteMessage> get hmsMessageStream => _hmsMessageStream ??= h_push.Push.onMessageReceivedStream.asBroadcastStream();
static void listenHmsForegroundMessages() {
_hmsMessageSubscription ??= hmsMessageStream.listen(_onMessageReceived, onError: _onMessageReceiveError);
}
static void _onMessageReceived(h_push.RemoteMessage remoteMessage) {
print("onMessageReceivedStream:${remoteMessage.toMap()}");
final payload = huaweiMessageData(remoteMessage);
if (payload != null && payload["type"] == "incoming_call") {
handleDataMessage(payload, false);
}
}
static void _onMessageReceiveError(Object error) {
@ -332,6 +367,7 @@ class FirebaseNotificationManger {
}
if (!(await isGoogleServicesAvailable()) && Platform.isAndroid) {
listenHmsForegroundMessages();
var initialNotification = await h_push.Push.getInitialNotification();
if (initialNotification != null) {
Map<String, dynamic> remoteData = Map<String, dynamic>.from(initialNotification["extras"] as Map);

@ -148,6 +148,7 @@ import 'providers/service_request_providers/loan_availability_provider.dart';
import 'providers/service_request_providers/reject_reason_provider.dart';
import 'modules/cleaner_module/pages/cleaner_search_form_view.dart';
import 'modules/cleaner_module/provider/cleaner_provider.dart';
import 'package:huawei_push/huawei_push.dart' as h_push;
// class MyHttpOverrides extends HttpOverrides {
// @override
@ -160,6 +161,11 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
FlutterCallkitIncoming.onBackgroundMessage(onBackgroundMessage);
if (!(await FirebaseNotificationManger.isGoogleServicesAvailable())) {
try {
await h_push.Push.registerBackgroundMessageHandler(backgroundMessageCallback);
} catch (_) {}
}
}
// iOS: listen for CallKit events before anything else, so a Decline on a call that launched
// the app from killed state (no UI yet) still reaches the caller.

@ -11,6 +11,7 @@ import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:huawei_push/huawei_push.dart' as h_push;
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/notification/firebase_notification_manger.dart';
import 'package:test_sa/controllers/notification/notification_manger.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/string_extensions.dart';
@ -438,21 +439,26 @@ class CMRequestUtils {
Completer<bool> completer = Completer<bool>();
if (Platform.isAndroid && !(await isGoogleServicesAvailable())) {
// If not using Firebase (i.e., if Google Services is unavailable)
h_push.Push.onMessageReceivedStream.listen((h_push.RemoteMessage remoteMessage) {
StreamSubscription<h_push.RemoteMessage>? subscription;
void finish(bool value) {
if (completer.isCompleted) return;
completer.complete(value);
subscription?.cancel();
}
subscription = FirebaseNotificationManger.hmsMessageStream.listen((h_push.RemoteMessage remoteMessage) {
ConfirmArrivalNotificationModel notificationModel = ConfirmArrivalNotificationModel.fromJson(remoteMessage.toMap());
if (notificationModel.requestId != null && notificationModel.accept != null) {
if (notificationModel.accept == 'True') {
isVerified = true;
completer.complete(isVerified);
finish(isVerified);
} else if (notificationModel.accept == 'False') {
isVerified = false;
completer.complete(isVerified);
finish(isVerified);
}
}
}, onError: (Object error) {
log("onMessageReceivedStream:${error.toString()}");
completer.complete(false); // Handle any error
finish(false); // Handle any error
});
} else {
// Using Firebase

Loading…
Cancel
Save