From 83c78cbaf7eb60f00b4f0acf3a2d40661547b82d Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 20 Jul 2026 16:30:08 +0300 Subject: [PATCH] updates --- ios/AppWidgetExtension.entitlements | 10 - ...ppWidgetLiveActivityExtension.entitlements | 10 - lib/core/utils/push_notification_handler.dart | 280 +++++++++--------- lib/core/utils/utils.dart | 51 ++++ ...patient_queue_live_notification_model.dart | 3 + .../my_appointments_view_model.dart | 14 + 6 files changed, 208 insertions(+), 160 deletions(-) delete mode 100644 ios/AppWidgetExtension.entitlements delete mode 100644 ios/AppWidgetLiveActivityExtension.entitlements create mode 100644 lib/features/my_appointments/models/patient_queue_live_notification_model.dart diff --git a/ios/AppWidgetExtension.entitlements b/ios/AppWidgetExtension.entitlements deleted file mode 100644 index a1ca053c..00000000 --- a/ios/AppWidgetExtension.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.application-groups - - group.alhabib.patientApp - - - diff --git a/ios/AppWidgetLiveActivityExtension.entitlements b/ios/AppWidgetLiveActivityExtension.entitlements deleted file mode 100644 index a1ca053c..00000000 --- a/ios/AppWidgetLiveActivityExtension.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.application-groups - - group.alhabib.patientApp - - - diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart index 212cfc8d..e687f30f 100644 --- a/lib/core/utils/push_notification_handler.dart +++ b/lib/core/utils/push_notification_handler.dart @@ -476,166 +476,166 @@ class PushNotificationHandler { } else if (status.isDenied) { // Notification permissions denied - await requestPermissions(); + await requestPermissions(); - if (Platform.isIOS) { - voIPKit.getVoIPToken().then((value) { - log("🎈 APNS VOIP KIT TOKEN: $value"); - Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? ""); - // AppSharedPreferences().setString(APNS_TOKEN, value!); - }); - - voIPKit.onDidUpdatePushToken = (String token) { - log('🎈 example: onDidUpdatePushToken: $token'); - }; - - voIPKit.onDidReceiveIncomingPush = ( - Map payload, - ) async { - log('🎈 example: onDidReceiveIncomingPush $payload'); - // _timeOut(); - }; - - voIPKit.onDidRejectIncomingCall = ( - String uuid, - String callerId, - ) async { - try { - log('🎈 example: onDidRejectIncomingCall $uuid - $callerId'); - timeOutTimer.cancel(); - // Cleanup on reject with API call - if (_currentCallId != null) { - await _endCallAndCleanup(_currentCallId!, isDeclined: true); - } - } catch (err) { - log(err.toString()); - } - }; - - voIPKit.onDidAcceptIncomingCall = ( - String uuid, - String callerId, - ) async { - log('🎈 example: onDidAcceptIncomingCall $uuid - $callerId'); - await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); - await voIPKit.callConnected(); - - // Cleanup on accept - cancel timer and reset flags - _callTimeoutTimer?.cancel(); - _callTimeoutTimer = null; - _isCallInProgress = false; - _currentCallId = null; - await Future.delayed(Duration(seconds: 2)).then((val) async { - await voIPKit.endCall(); + if (Platform.isIOS) { + voIPKit.getVoIPToken().then((value) { + log("🎈 APNS VOIP KIT TOKEN: $value"); + Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? ""); + // AppSharedPreferences().setString(APNS_TOKEN, value!); }); - // Navigator.pushNamed( - // locator().navigatorKey.currentContext!, - // "zoom_call_page", - // arguments: CallArguments("hoover-dam", "123", "Patient", "40", "1", false), - // ); - - // Navigator.pushNamed(navigatorKey.currentContext!, VIDEO_CALL_SCREEN, - // arguments: VideoArgus( - // reservationId: int.parse(callerId), token: null, isVideo: true)); + voIPKit.onDidUpdatePushToken = (String token) { + log('🎈 example: onDidUpdatePushToken: $token'); + }; + + voIPKit.onDidReceiveIncomingPush = ( + Map payload, + ) async { + log('🎈 example: onDidReceiveIncomingPush $payload'); + // _timeOut(); + }; + + voIPKit.onDidRejectIncomingCall = ( + String uuid, + String callerId, + ) async { + try { + log('🎈 example: onDidRejectIncomingCall $uuid - $callerId'); + timeOutTimer.cancel(); + // Cleanup on reject with API call + if (_currentCallId != null) { + await _endCallAndCleanup(_currentCallId!, isDeclined: true); + } + } catch (err) { + log(err.toString()); + } + }; + + voIPKit.onDidAcceptIncomingCall = ( + String uuid, + String callerId, + ) async { + log('🎈 example: onDidAcceptIncomingCall $uuid - $callerId'); + await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); + await voIPKit.callConnected(); + + // Cleanup on accept - cancel timer and reset flags + _callTimeoutTimer?.cancel(); + _callTimeoutTimer = null; + _isCallInProgress = false; + _currentCallId = null; + await Future.delayed(Duration(seconds: 2)).then((val) async { + await voIPKit.endCall(); + }); + + // Navigator.pushNamed( + // locator().navigatorKey.currentContext!, + // "zoom_call_page", + // arguments: CallArguments("hoover-dam", "123", "Patient", "40", "1", false), + // ); + + // Navigator.pushNamed(navigatorKey.currentContext!, VIDEO_CALL_SCREEN, + // arguments: VideoArgus( + // reservationId: int.parse(callerId), token: null, isVideo: true)); + + // timeOutTimer.cancel(); + }; + } - // timeOutTimer.cancel(); - }; - } + if (Platform.isAndroid) { + try { + final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) { + log(err); + }); + if (fcmToken != null) onToken(fcmToken); + // } + } catch (ex) { + log("Notification Exception: $ex"); + } + FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); + } - if (Platform.isAndroid) { - try { - final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) { - log(err); + if (Platform.isIOS) { + final permission = await FirebaseMessaging.instance.requestPermission(); + await FirebaseMessaging.instance.getAPNSToken().then((value) async { + log("APNS token: $value"); + await Utils.saveStringFromPrefs(CacheConst.apnsToken, value.toString()); }); - if (fcmToken != null) onToken(fcmToken); - // } - } catch (ex) { - log("Notification Exception: $ex"); + await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( + alert: true, // Required to display a heads up notification + badge: true, + sound: true, + ); + // if (permission.authorizationStatus == AuthorizationStatus.denied) return; + } else { + final permission = await FirebaseMessaging.instance.requestPermission(); } - FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); - } - - if (Platform.isIOS) { - final permission = await FirebaseMessaging.instance.requestPermission(); - await FirebaseMessaging.instance.getAPNSToken().then((value) async { - log("APNS token: $value"); - await Utils.saveStringFromPrefs(CacheConst.apnsToken, value.toString()); - }); - await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( - alert: true, // Required to display a heads up notification - badge: true, - sound: true, - ); - // if (permission.authorizationStatus == AuthorizationStatus.denied) return; - } else { - final permission = await FirebaseMessaging.instance.requestPermission(); - } - try { - FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async { - - if (message != null) { - if (Platform.isIOS) { - await Future.delayed(Duration(milliseconds: 3000)).then((value) { - if (message != null) newMessage(message); - }); - } else if (message != null) newMessage(message); + try { + FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async { + if (message != null) { + if (Platform.isIOS) { + await Future.delayed(Duration(milliseconds: 3000)).then((value) { + if (message != null) newMessage(message); + }); + } else if (message != null) newMessage(message); + } + }); + } catch (ex) {} + + FirebaseMessaging.onMessage.listen((RemoteMessage message) async { + log("Firebase onMessage!!!"); + // showCallkitIncoming(); + if (Platform.isIOS) { + await Future.delayed(Duration(milliseconds: 3000)).then((value) { + newMessage(message); + }); + } else { + newMessage(message); } }); - } catch (ex) {} - FirebaseMessaging.onMessage.listen((RemoteMessage message) async { - log("Firebase onMessage!!!"); - // showCallkitIncoming(); - if (Platform.isIOS) { - await Future.delayed(Duration(milliseconds: 3000)).then((value) { + FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { + log("Firebase onMessageOpenedApp!!!"); + if (Platform.isIOS) { + await Future.delayed(Duration(milliseconds: 3000)).then((value) { + newMessage(message); + }); + } else { newMessage(message); - }); - } else { - newMessage(message); - } - }); + } + }); - FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { - log("Firebase onMessageOpenedApp!!!"); if (Platform.isIOS) { - await Future.delayed(Duration(milliseconds: 3000)).then((value) { - newMessage(message); + FirebaseMessaging.instance.getAPNSToken().then((String? token) { + log("Push Notification getAPNSToken: ${token!}"); + }).catchError((err) { + log("Push Notification getAPNSToken ERR: ${err.toString()}"); }); - } else { - newMessage(message); } - }); - if (Platform.isIOS) { - FirebaseMessaging.instance.getAPNSToken().then((String? token) { - log("Push Notification getAPNSToken: ${token!}"); + FirebaseMessaging.instance.getToken().then((String? token) { + log("Push Notification getToken: ${token!}"); + onToken(token!); }).catchError((err) { - log("Push Notification getAPNSToken ERR: ${err.toString()}"); + log(err.toString()); }); - } - FirebaseMessaging.instance.getToken().then((String? token) { - log("Push Notification getToken: ${token!}"); - onToken(token!); - }).catchError((err) { - log(err.toString()); - }); - - FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) { - log("Push Notification onTokenRefresh: $fcm_token"); - onToken(fcm_token); - }); - - if (Platform.isAndroid) { - final deviceInfo = DeviceInfoPlugin(); - final androidInfo = await deviceInfo.androidInfo; - - // int sdkInt = androidInfo.version.sdkInt ?? 0; - // if (sdkInt >= 33) { - // await FlutterCallkitIncoming.requestFullIntentPermission(); - // } + FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) { + log("Push Notification onTokenRefresh: $fcm_token"); + onToken(fcm_token); + }); + + if (Platform.isAndroid) { + final deviceInfo = DeviceInfoPlugin(); + final androidInfo = await deviceInfo.androidInfo; + + // int sdkInt = androidInfo.version.sdkInt ?? 0; + // if (sdkInt >= 33) { + // await FlutterCallkitIncoming.requestFullIntentPermission(); + // } + } } } diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 320bd1cf..e53b1fc4 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -15,8 +15,11 @@ import 'package:get_it/get_it.dart'; import 'package:google_api_availability/google_api_availability.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/cache_consts.dart'; +import 'package:hmg_patient_app_new/core/common_models/patient_queue.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/live_activty_helper_channel.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; @@ -30,6 +33,7 @@ import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart'; import 'package:hmg_patient_app_new/widgets/loading_dialog.dart'; import 'package:hmg_patient_app_new/widgets/accessibility/preserve_image_colors.dart'; import 'package:hmg_patient_app_new/widgets/accessibility/preserve_svg_colors.dart'; +import 'package:live_activities/live_activities.dart'; import 'package:lottie/lottie.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -1172,4 +1176,51 @@ class Utils { return normalized; } + + static addLiveNotification(dynamic data, {bool isBackgroundMessage = false}) async { + var liveActivity = LiveActivities(); + + liveActivity.init(appGroupId: "group.alhabib.patientApp"); + if (Platform.isIOS) { + LiveActivityHelperChannel.removeData(); + } + var queueNoForWhichActivityIdCreated = await Utils.getStringFromPrefs(CacheConst.patientQueueNoForLiveActivity); + var activityId = await Utils.getStringFromPrefs(data.data['queueNo'] ?? ""); + + var buttonText = Utils.getCardButtonText(int.parse(data.data['callType'] ?? "1"), data.data['roomNo']); + PatientQueue _patientQueue = PatientQueue( + queueNo: data.data['queueNo'] ?? "IME-1", + currentlyServingStatus: (data.data['servingCallType'] ?? 0) == "1" ? LocaleKeys.callForVitalSign.tr() : LocaleKeys.callForDoctor.tr(), + currentlyServingToken: data.data['servingQueueNo'] ?? "IME-3", + nextTicketStatus: buttonText, + callType: int.parse(data.data['callType'] ?? "1"), + currentlyServingCallType: int.parse(data.data['servingCallType'] ?? "1"), + yourTicketHeader: LocaleKeys.yourQueueNumber.tr(), + currentlyServingTitle: LocaleKeys.currentServing.tr()); + if ((queueNoForWhichActivityIdCreated ?? "") != (_patientQueue.queueNo ?? "")) { + liveActivity.endActivity(activityId); + activityId = ""; + } + + if (activityId == "") { + String? id = await liveActivity.createActivity( + DateTime.now().millisecondsSinceEpoch.toString(), + _patientQueue.toMap(), + ); + await Utils.saveStringFromPrefs(_patientQueue.queueNo ?? "", id ?? ""); + await Utils.saveStringFromPrefs(CacheConst.patientQueueNoForLiveActivity ?? "", _patientQueue.queueNo ?? ""); + return; + } + if (Platform.isIOS) { + await liveActivity.createOrUpdateActivity( + activityId, + _patientQueue.toMap(), + ); + } else { + await liveActivity.updateActivity( + activityId, + _patientQueue.toMap(), + ); + } + } } diff --git a/lib/features/my_appointments/models/patient_queue_live_notification_model.dart b/lib/features/my_appointments/models/patient_queue_live_notification_model.dart new file mode 100644 index 00000000..d151e6db --- /dev/null +++ b/lib/features/my_appointments/models/patient_queue_live_notification_model.dart @@ -0,0 +1,3 @@ +class QueueLiveNotificationModel { + Map data = {}; +} diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index b480a354..1fe3dccb 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/patient_queue_live_notification_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/reminder_type.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_rated_response_model.dart'; @@ -1026,6 +1027,19 @@ class MyAppointmentsViewModel extends ChangeNotifier { // if (patientQueueDetailsList.length > 1) { patientQueueDetailsList.removeWhere((element) => element.patientID == patientArrivedAppointmentsHistoryList.first.patientID); // } + + QueueLiveNotificationModel queueLiveNotificationModel = QueueLiveNotificationModel(); + + queueLiveNotificationModel.data = { + "queueNo": currentPatientQueueDetails.queueNo, + "callType": currentPatientQueueDetails.callType.toString(), + "servingQueueNo": patientQueueDetailsList.isEmpty ? null : patientQueueDetailsList.first.queueNo, + "servingCallType": patientQueueDetailsList.isEmpty ? null : patientQueueDetailsList.first.callType.toString(), + "roomNo": patientQueueDetailsList.isEmpty ? "" : patientQueueDetailsList.first.roomNo, + "servingNow": "true" + }; + + Utils.addLiveNotification(queueLiveNotificationModel, isBackgroundMessage: false); } else { patientQueueDetailsList.clear(); isPatientHasQueueAppointment = false;