You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
531 lines
19 KiB
Dart
531 lines
19 KiB
Dart
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<String> 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<ServiceRequestDetailProvider>(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<void> 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<String, dynamic> 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<bool?> listenForApproval() async {
|
|
log('listen for approval called..');
|
|
bool isVerified = false;
|
|
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) {
|
|
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<bool> 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<String, dynamic> 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<String, dynamic> toJson() {
|
|
return {
|
|
'transactionType': transactionType,
|
|
'requestId': requestId,
|
|
'notificationType': notificationType,
|
|
'userId': userId,
|
|
'accept': accept,
|
|
};
|
|
}
|
|
}
|