import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'package:device_calendar/device_calendar.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; 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/notification_manger.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; import '../service_request_detail_provider.dart'; class ServiceRequestUtils { static double calculateAndAssignWorkingHours({ required DateTime? startTime, required DateTime? endTime, required TextEditingController workingHoursController, required Function(double) updateModel, // A callback to update the model }) { if (startTime != null && endTime != null) { Duration difference = endTime.difference(startTime); double hours = difference.inMinutes / 60.0; // Calculate hours as a decimal workingHoursController.text = hours.toStringAsFixed(1); // Format to 1 decimal places updateModel(hours); // Call the function to update the model return hours; } else { return -1; // Indicating invalid input } } static bool isLocalUrl(String url) { if (url.isEmpty != false) return false; return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\'); } static String calculateTimeDifference(DateTime startDate, DateTime endDate) { try { Duration diff = startDate.difference(endDate); int days = diff.inDays; int hours = diff.inHours.remainder(24); int minutes = diff.inMinutes.remainder(60); int seconds = diff.inSeconds.remainder(60); List parts = []; if (days > 0) parts.add('$days ${days == 1 ? 'day' : 'days'}'); if (hours > 0) parts.add('$hours ${hours == 1 ? 'hour' : 'hours'}'); if (minutes > 0) parts.add('$minutes ${minutes == 1 ? 'minute' : 'minutes'}'); if (seconds > 0) parts.add('$seconds ${seconds == 1 ? 'second' : 'seconds'}'); String timeDifference = parts.isEmpty ? '' : parts.join(', '); return 'Action duration: $timeDifference'; } catch (e) { return ''; } } static String formatTimerDuration(int seconds) { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int remainingSeconds = seconds % 60; String formattedDuration = ''; if (hours > 0) { formattedDuration += '$hours hour${hours > 1 ? 's' : ''} '; } if (minutes > 0) { formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} '; } if (remainingSeconds > 0) { formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} '; } if (formattedDuration.isEmpty) { formattedDuration = 'Less than a second'; } return formattedDuration.trim(); } static String formatTimerDurationCM(int seconds) { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int remainingSeconds = seconds % 60; String formattedDuration = ''; if (hours > 0) { formattedDuration += '$hours hour${hours > 1 ? 's' : ''} '; } if (minutes > 0) { formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} '; } if (remainingSeconds > 0) { formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} '; } if (formattedDuration.isEmpty) { formattedDuration = 'Less than a minute'; } return formattedDuration.trim(); } static String formatTotalWorkingHours(double timeInHours) { // Convert hours → seconds int totalSeconds = (timeInHours * 3600).round(); int hours = totalSeconds ~/ 3600; int minutes = (totalSeconds % 3600) ~/ 60; int remainingSeconds = totalSeconds % 60; String formattedDuration = ''; if (hours > 0) { formattedDuration += '$hours hour${hours > 1 ? 's' : ''} '; } if (minutes > 0) { formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} '; } if (remainingSeconds > 0) { formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} '; } if (formattedDuration.isEmpty) { formattedDuration = 'Less than a minute'; } return formattedDuration.trim(); } static void getQrCode({required BuildContext context}) async { showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); ServiceRequestDetailProvider requestDetailProvider = Provider.of(context, listen: false); String? base64String = await requestDetailProvider.getQrCode(workOrderId: requestDetailProvider.currentWorkOrder!.data!.requestId ?? 0); Navigator.pop(context); if (base64String != null) { // You have the base64 string, now you can display it Uint8List bytes = base64Decode(base64String); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( backgroundColor: Colors.white, title: context.translation.scanQr.heading6(context).center, content: Image.memory( bytes, // Displaying the QR code from base64 fit: BoxFit.contain, // Ensure the image fits well in the dialog errorBuilder: (context, error, stackTrace) { return const Icon(Icons.error, color: Colors.red); }, ), ); }, ); } else { log('Failed to get the QR code'); } } static void scheduleOrUpdateNotification({required BuildContext context, required DateTime scheduleDateTime, required String workOrderId}) async { DateTime scheduleTime = scheduleDateTime; await NotificationManger.cancelNotificationById(workOrderId.hashCode); NotificationManger.scheduleNotification(id: workOrderId.hashCode, title: 'Scheduled Notification', body: '$scheduleTime', scheduledNotificationDateTime: scheduleTime); } //steps: //add permission for andriod.. //add string in info.plist for ios... //tz.TZDateTime.from(movieScreening.dateTime, tz.local) some time you need to add this while making build.. //create event.. static void addOrUpdateEventToCalendar({ required BuildContext context, required DateTime start, required DateTime end, required String title, String? workOrderNumber, }) async { final deviceCalendarPlugin = DeviceCalendarPlugin(); final currentLocation = getLocation(await FlutterTimezone.getLocalTimezone()); TZDateTime startDate = TZDateTime.from(start, currentLocation); TZDateTime endDate = TZDateTime.from(end, currentLocation); setLocalLocation(currentLocation); final calendarsResult = await deviceCalendarPlugin.retrieveCalendars(); if (calendarsResult.data?.isEmpty ?? true) { 'No calendars available on the device.'.showToast; return; } // Select a write able calendar final writableCalendar = calendarsResult.data?.firstWhere( (calendar) => calendar.isReadOnly == false, orElse: () => Calendar(id: '', name: 'Default', isReadOnly: true), ); if (writableCalendar == null || writableCalendar.isReadOnly == true) { log('No writable calendar found.'); 'No writable calendar found.'.showToast; return; // Handle the case where no writable calendar exists } final eventsResult = await deviceCalendarPlugin.retrieveEvents( writableCalendar.id!, RetrieveEventsParams(startDate: start.subtract(Duration(days: 7)), endDate: end.add(Duration(days: 30))), ); if (!(eventsResult.isSuccess && eventsResult.data != null)) { "Failed to retrieve reminders.".showToast; return; } // Find an event with the matching workOrderNumber in the description final matchingEvent = eventsResult.data!.firstWhere( (event) => event.description?.contains('$workOrderNumber') ?? false, orElse: () => Event('0', eventId: null), ); if (matchingEvent.eventId != null) { // Update the existing event matchingEvent.title = title; matchingEvent.start = startDate; matchingEvent.end = endDate; // Save the updated event final updateResult = await deviceCalendarPlugin.createOrUpdateEvent(matchingEvent); if (updateResult?.isSuccess == true) { log('Reminder updated successfully: ${updateResult?.data}'); "Reminder updated successfully on device calendar".showToast; } else { log('Failed to update reminder: ${updateResult?.errors}'); "Failed to update reminder on device calendar".showToast; } } else { // add event to calendar.. final eventToCreate = Event( writableCalendar.id, title: title, description: 'You have a reminder for a visit for work order $workOrderNumber', start: startDate, end: endDate, ); final createResult = await deviceCalendarPlugin.createOrUpdateEvent(eventToCreate); if (createResult?.isSuccess == true) { log('Reminder added successfully: ${createResult?.data}'); //TODO need to confirm to show the message to user... 'Reminder added to device calendar'.showToast; } else { log('Failed to add reminder: ${createResult?.errors}'); 'Failed to add reminder to device calendar'.showToast; } } } // static void addEventToCalendar({ // required BuildContext context, // required DateTime start, // required DateTime end, // required String title, // String? workOrderNumber, // }) async { // final deviceCalendarPlugin = DeviceCalendarPlugin(); // // // Retrieve current timezone // final currentLocation = getLocation(await FlutterTimezone.getLocalTimezone()); // setLocalLocation(currentLocation); // // // Retrieve calendars // final calendarsResult = await deviceCalendarPlugin.retrieveCalendars(); // // if (calendarsResult.data?.isEmpty ?? true) { // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text('No calendars available on the device.')), // ); // return; // } // // // Select a writable calendar // final writableCalendar = calendarsResult.data?.firstWhere( // (calendar) => calendar.isReadOnly == false, // orElse: () => Calendar(id: '', name: 'Default', isReadOnly: true), // ); // // if (writableCalendar == null || writableCalendar.isReadOnly==true) { // log('No writable calendar found.'); // return; // Handle the case where no writable calendar exists // } // // Convert start and end times // TZDateTime startDate = TZDateTime.from(start, currentLocation); // TZDateTime endDate = TZDateTime.from(end, currentLocation); // // // Create event // final eventToCreate = Event( // writableCalendar.id, // title: title, // description: 'You have a reminder for a visit for work order $workOrderNumber ', // start: startDate, // end: endDate, // ); // // // Create or update the event // final response = await deviceCalendarPlugin.createOrUpdateEvent(eventToCreate); // // if (response?.isSuccess == true) { // log('Event added successfully: ${response?.data}'); // } else { // log('Failed to add event: ${response?.errors}'); // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text('Failed to add event to calendar.')), // ); // } // } // static Future updateEventOnCalendar({ // required BuildContext context, // required String calendarId, // required int requestId, // required String updatedTitle, // required DateTime updatedStart, // required DateTime updatedEnd, // }) async { // final deviceCalendarPlugin = DeviceCalendarPlugin(); // // // Retrieve all events in the calendar // final eventsResult = await deviceCalendarPlugin.retrieveEvents( // calendarId, // RetrieveEventsParams(), // ); // // if (!(eventsResult.isSuccess && eventsResult.data != null)) { // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text('Failed to retrieve events.')), // ); // return; // } // // // Find the event with the matching requestId // final matchingEvent = eventsResult.data!.firstWhere( // (event) => event.eventId == requestId.toString(), // orElse: () => null, // ); // // if (matchingEvent == null) { // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text('Event not found for requestId: $requestId')), // ); // return; // } // // // Update the event's details // matchingEvent.title = updatedTitle; // matchingEvent.start = updatedStart; // matchingEvent.end = updatedEnd; // // // Save the updated event // final updateResult = await deviceCalendarPlugin.createOrUpdateEvent(matchingEvent); // // if (updateResult?.isSuccess == true) { // log('Event updated successfully: ${updateResult?.data}'); // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text('Event updated successfully.')), // ); // } else { // log('Failed to update event: ${updateResult?.errors}'); // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text('Failed to update event.')), // ); // } // } // static void addEventToCalendar({required BuildContext context, required DateTime start, required DateTime end,required String title,required int ? requestId}) async { // // // var currentLocation = getLocation(await FlutterTimezone.getLocalTimezone()); // final calendarsResult = await DeviceCalendarPlugin().retrieveCalendars(); // // if (calendarsResult.data?.isNotEmpty ?? false) { // setLocalLocation(currentLocation); // await Future.delayed(const Duration(seconds: 2)); // TZDateTime startDate = TZDateTime.from(start, currentLocation); // TZDateTime endDate = TZDateTime.from(end, currentLocation); // final calendarId = calendarsResult.data!.first.id; // Use the first calendar // // var eventToCreate = Event( // calendarId, // eventId: requestId.toString(), // title: title, // description: "You have a reminder for a visit", // start: startDate, // end: endDate, // ); // final response = await DeviceCalendarPlugin().createOrUpdateEvent(eventToCreate); // log("Response of adding calendar is ${response?.data}"); // } else { // // "No calendars available on the device.".showToast(); // // } // // setLocalLocation(currentLocation); // // await Future.delayed(const Duration(seconds: 2)); // // TZDateTime startDate = TZDateTime.from(start, currentLocation); // // TZDateTime endDate = TZDateTime.from(end, currentLocation); // // var eventToCreate = Event( // // '0', // // // eventId:requestId.toString(), // // title: title, // // description: "You have reminder for Visit", // // start: startDate, // // end: endDate, // // ); // // log('event to add i got is ${eventToCreate.toJson()}'); // // final response = await DeviceCalendarPlugin().createOrUpdateEvent(eventToCreate); // // log("response of adding calander is ${response?.data}"); // // // } // Map messageData = message.data; // if (messageData["notificationType"] != null && messageData["accept"] != null) { // if (messageData["notificationType"] == "arrivalConfirmation") { // if ((messageData["accept"].toString()) == "False") { // if(messageData["accept"]) // Navigator.pop(context, true); // } else { // Navigator.pop(context, false); // } // } // } static Future listenForApproval() async { log('listen for approval called..'); bool isVerified = false; Completer completer = Completer(); 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) { ConfirmArrivalNotificationModel notificationModel = ConfirmArrivalNotificationModel.fromJson(remoteMessage.toMap()); if (notificationModel.requestId != null && notificationModel.accept != null) { if (notificationModel.accept == 'True') { isVerified = true; completer.complete(isVerified); } else if (notificationModel.accept == 'False') { isVerified = false; completer.complete(isVerified); } } }, onError: (Object error) { log("onMessageReceivedStream:${error.toString()}"); completer.complete(false); // Handle any error }); } else { // Using Firebase FirebaseMessaging.onMessage.listen((RemoteMessage message) { log('message data received: ${message.data}'); ConfirmArrivalNotificationModel notificationModel = ConfirmArrivalNotificationModel.fromJson(message.data); if (notificationModel.requestId != null && notificationModel.accept != null) { if (notificationModel.accept == 'True') { isVerified = true; if (!completer.isCompleted) { completer.complete(isVerified); } } else if (notificationModel.accept == 'False') { isVerified = false; if (!completer.isCompleted) { completer.complete(isVerified); } } } }); } // Wait for the future to complete with the notification response return completer.future; } static Future isGoogleServicesAvailable() async { GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); String status = availability.toString().split('.').last; if (status == "success") { return true; } return false; } } class ConfirmArrivalNotificationModel { final String? transactionType; final String? requestId; final String? notificationType; final String? userId; final String? accept; ConfirmArrivalNotificationModel({ this.transactionType, this.requestId, this.notificationType, this.userId, this.accept, }); factory ConfirmArrivalNotificationModel.fromJson(Map json) { return ConfirmArrivalNotificationModel( transactionType: json['transactionType'], requestId: json['requestId'], notificationType: json['notificationType'], userId: json['userId'], accept: json['accept'], ); } // Convert an instance to a Map (e.g., for serialization) Map toJson() { return { 'transactionType': transactionType, 'requestId': requestId, 'notificationType': notificationType, 'userId': userId, 'accept': accept, }; } }