import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_nfc_kit/flutter_nfc_kit.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_api_availability/google_api_availability.dart'; import 'package:huawei_location/huawei_location.dart'; import 'package:intl/intl.dart'; import 'package:nfc_manager/nfc_manager.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.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/main.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart'; import 'package:test_sa/new_views/swipe_module/dialoge/nfc_reader_sheet.dart'; // import 'package:test_sa/new_views/swipe_module/dialoge/nfc_reader_sheet.dart'; import 'package:test_sa/new_views/swipe_module/enums/swipe_type.dart'; import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart'; import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart'; import 'package:test_sa/new_views/swipe_module/utils/location_utils.dart'; import 'package:test_sa/views/widgets/qr/scan_qr.dart'; import 'package:wifi_iot/wifi_iot.dart'; class SwipeGeneralUtils { SwipeGeneralUtils._(); static SwipeGeneralUtils instance = SwipeGeneralUtils._(); static bool _isLoadingVisible = false; static bool get isLoading => _isLoadingVisible; void markFakeAttendance(dynamic sourceName, String lat, String long, {required BuildContext context}) async { showLoading(context); try { hideLoading(navigatorKey.currentState!.overlay!.context); confirmDialog(navigatorKey.currentState!.overlay!.context, "Fake Location)"); } catch (ex) { log('$ex'); hideLoading(context); //handleException(ex, context, null); } } void showLoading(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) { _isLoadingVisible = true; showDialog( context: context, barrierColor: Colors.black.withOpacity(0.5), useRootNavigator: false, builder: (BuildContext context) => const AppLazyLoading(), ).then((value) { _isLoadingVisible = false; }); }); } void hideLoading(BuildContext context) { if (_isLoadingVisible) { _isLoadingVisible = false; Navigator.of(context).pop(); } _isLoadingVisible = false; } static Future getStringFromPrefs(String key) async { SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getString(key) ?? ""; } void confirmDialog(cxt, String message, {VoidCallback? onTap}) { showDialog( context: cxt, builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap), ); } void showErrorDialog({String? message, required BuildContext context}) { showDialog( context: context, builder: (context) => ConfirmDialog(message: message, title: 'Error', onTap: () => Navigator.pop(context)), ); } void showMDialog(context, {Widget? child, Color? backgroundColor, bool isDismissable = true, bool isBusniessCard = false}) async { return showDialog( context: context, barrierDismissible: isDismissable, builder: (context) { return Dialog( shape: isBusniessCard ? const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(15.0), ), ) : null, backgroundColor: backgroundColor, child: child, ); }, ); } Widget attendanceTypeCard(String title, String icon, bool isEnabled, VoidCallback onPress, BuildContext context) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isEnabled ? context.isDark ? AppColor.neutral60 : Colors.white : AppColor.background(context), borderRadius: BorderRadius.circular(18), border: Border.all(color: context.isDark ? AppColor.neutral60 : Colors.white70, width: 2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ icon.toSvgAsset( color: isEnabled ? context.isDark ? AppColor.neutral30 : AppColor.neutral50 : Colors.grey.withOpacity(0.5)), title.heading5(context).custom( color: isEnabled ? context.isDark ? AppColor.neutral30 : AppColor.neutral50 : Colors.grey.withOpacity(0.5)), ], ), ).onPress( () { if (!isEnabled) return; onPress(); }, ); } //huawei permission part.... void getHuaweiCurrentLocation({SwipeTypeEnum? attendanceType, required BuildContext context}) async { try { showLoading(context); FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService(); LocationRequest locationRequest = LocationRequest(); locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; locationRequest.interval = 500; List locationRequestList = [locationRequest]; LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); late StreamSubscription _streamSubscription; int requestCode = (await (locationService.requestLocationUpdates(locationRequest)))!; _streamSubscription = locationService.onLocationData!.listen( (Location location) async { hideLoading(context); await locationService.removeLocationUpdates(requestCode); handleSwipeOperation(swipeType: attendanceType!, context: context, lat: location.latitude ?? 0, long: location.longitude ?? 0); requestCode = 0; _streamSubscription.cancel(); }, ); } catch (error) { log("HUAWEI LOCATION ERROR!!!!!"); log('$error'); hideLoading(context); // handleException(error, context, null); } } Future requestPermissions() async { var result = await [ Permission.location, ].request(); return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); } Future checkHuaweiLocationPermission({ required SwipeTypeEnum attendanceType, required BuildContext context, }) async { try { // Check if location service is enabled final isEnabled = await LocationUtilities.isEnabledAsync(); if (!isEnabled) { showDialog( context: context, builder: (cxt) => ConfirmDialog( message: "You need to enable location services to mark attendance", onTap: () async { Navigator.pop(cxt); await Geolocator.openLocationSettings(); }, ), ); return; } // Check permission bool hasPermission = await LocationUtilities.havePermissionAsync(); // Request if not granted if (!hasPermission) { hasPermission = await requestPermissions(); } if (!hasPermission) { showDialog( context: context, builder: (cxt) => ConfirmDialog( message: "You need to give location permission to mark attendance", onTap: () { Navigator.pop(cxt); }, ), ); return; } // All good → get location getHuaweiCurrentLocation( attendanceType: attendanceType, context: context, ); } catch (e) { debugPrint("❌ Huawei location error: $e"); showDialog( context: context, builder: (cxt) => ConfirmDialog( message: "Something went wrong. Please try again.", onTap: () { Navigator.pop(cxt); }, ), ); } } // void checkHuaweiLocationPermission({required SwipeTypeEnum attendanceType, required BuildContext context}) async { // // Permission_Handler permissionHandler = PermissionHandler(); // LocationUtilities.isEnabled((bool isEnabled) async { // if (isEnabled) { // LocationUtilities.havePermission((bool permission) async { // if (permission) { // getHuaweiCurrentLocation(attendanceType: attendanceType, context: context); // } else { // bool has = await requestPermissions(); // if (has) { // getHuaweiCurrentLocation(attendanceType: attendanceType, context: context); // } else { // showDialog( // context: context, // builder: (BuildContext cxt) => ConfirmDialog( // message: "You need to give location permission to mark attendance", // onTap: () { // Navigator.pop(context); // }, // ), // ); // } // } // }); // } else { // showDialog( // context: context, // builder: (BuildContext cxt) => ConfirmDialog( // message: "You need to enable location services to mark attendance", // onTap: () async { // Navigator.pop(context); // await Geolocator.openLocationSettings(); // }, // ), // ); // } // }); // // // if (await permissionHandler.hasLocationPermission()) { // // getHuaweiCurrentLocation(attendanceType); // // } else { // // bool has = await requestPermissions(); // // if (has) { // // getHuaweiCurrentLocation(attendanceType); // // } else { // // showDialog( // // context: context, // // builder: (BuildContext cxt) => ConfirmDialog( // // message: "You need to give location permission to mark attendance", // // onTap: () { // // Navigator.pop(context); // // }, // // ), // // ); // // } // // } // } void handleSwipeOperation({required SwipeTypeEnum swipeType, required double lat, required double long, required BuildContext context}) { switch (swipeType) { case SwipeTypeEnum.NFC: log('showing nfc sheet'); handleNfcAttendance(latitude: lat, longitude: long, context: context); return; case SwipeTypeEnum.QR: performQrCodeAttendance(latitude: lat, longitude: long, context: context); return; case SwipeTypeEnum.Wifi: performWifiAttendance(latitude: lat, long: long, context: context); return; } } String formatTimeOnly(DateTime dateTime) { return DateFormat.Hms().format(dateTime); } Future performQrCodeAttendance({double? latitude, double? longitude, required BuildContext context}) async { UserProvider userProvider = Provider.of(context, listen: false); String qrCodeValue = await Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ScanQr()), ) as String; showLoading(context); try { final swipeModel = Swipe( swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(), value: qrCodeValue, latitude: latitude, longitude: longitude, ); await userProvider.makeSwipe(model: swipeModel).then((swipeResponse) { if (swipeResponse.isSuccess == true) { hideLoading(context); Navigator.pushNamed(context, SwipeSuccessView.routeName); } else { hideLoading(context); showDialog( barrierDismissible: true, context: context, builder: (cxt) => ConfirmDialog( message: swipeResponse.message ?? "", onTap: () { Navigator.pop(context); }, onCloseTap: () {}, ), ); } }); } catch (ex) { log('$ex'); hideLoading(context); //this need to confirm where it comes.. // handleException(ex, context, null); } } // if (Platform.isAndroid) { // // Android: show custom bottom sheet with your scan/done UI // showNfcReader(context, onNcfScan: (String? nfcId) async { // if (nfcId == null || nfcId.isEmpty) return; // // try { // await _processNfcAttendance(nfcId, latitude, longitude, context); // } finally { // _nfcScanning = false; // } // }); // } // dart // Add this field to the class (near the top, e.g. after the constructor) bool _nfcScanning = false; // dart Future handleNfcAttendance({ double? latitude = 0, double? longitude = 0, required BuildContext context, }) async { // if (_nfcScanning) return; // _nfcScanning = true; try { var availability = await FlutterNfcKit.nfcAvailability; if (availability != NFCAvailability.available) { "NFC is not available on this device".showToast; return; } if (Platform.isAndroid) { try { showNfcReader(context, onNcfScan: (String? nfcId) async { log('nfc id from sheet: $nfcId'); if (nfcId == null || nfcId.isEmpty) return; try { await _processNfcAttendance(nfcId, latitude, longitude, context); } catch (e) { // handle per-scan error if needed } }); } catch (e) { debugPrint('Error showing NFC reader sheet: $e'); } } else { try { NFCTag tag = await FlutterNfcKit.poll( timeout: const Duration(seconds: 20), iosAlertMessage: "Hold your card near the phone", iosMultipleTagMessage: "Multiple cards detected. Please scan only one.", ); String nfcId = tag.id.toUpperCase(); await FlutterNfcKit.finish(); await _processNfcAttendance(nfcId, latitude, longitude, context); } catch (e) { if (!e.toString().contains("cancelled")) { "Failed to read NFC card".showToast; // ScaffoldMessenger.of(context).showSnackBar( // const SnackBar(content: Text("Failed to read NFC card")), // ); } try { await FlutterNfcKit.finish(); } catch (_) {} } } } catch (e) { debugPrint("NFC error: $e"); } finally { _nfcScanning = false; } } // Future handleNfcAttendance({double? latitude = 0, double? longitude = 0, required BuildContext context}) async { // bool _nfcScanning = false; // // UserProvider _userProvider = Provider.of(context,listen:false); // // if (Platform.isIOS) { // readNFc(onRead: (String nfcId) async { // await _processNfcAttendance(nfcId, latitude, longitude, context); // }); // } else { // showNfcReader(context, onNcfScan: (String? nfcId) async { // await _processNfcAttendance(nfcId ?? '', latitude, longitude, context); // }); // } // } Future _processNfcAttendance( String nfcId, double? latitude, double? longitude, BuildContext context, ) async { showLoading(context); try { final swipeModel = Swipe( swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId, latitude: latitude, longitude: longitude, ); UserProvider userProvider = Provider.of(context, listen: false); final swipeResponse = await userProvider.makeSwipe(model: swipeModel); if (swipeResponse.isSuccess == true) { hideLoading(context); Navigator.pushNamed(context, SwipeSuccessView.routeName); } else { hideLoading(context); showErrorDialog(message: swipeResponse.message ?? "Unexpected error occurred", context: context); } } catch (errSwipeGeneralUtilsor) { hideLoading(context); } } Future handleSwipe({ required SwipeTypeEnum swipeType, required bool isEnable, required BuildContext context, }) async { try { if (Platform.isAndroid && !(await isGoogleServicesAvailable())) { checkHuaweiLocationPermission( attendanceType: swipeType, context: context, ); return; } // Check if location is enabled final isEnabled = await LocationUtilities.isEnabledAsync(); if (!isEnabled) { showInfoDialog( message: "You need to enable location services to mark attendance", onTap: () async => await Geolocator.openLocationSettings(), ); return; } // Check permission final hasPermission = await LocationUtilities.havePermissionAsync(); if (!hasPermission) { showInfoDialog( message: "You need to give location permission to mark attendance", onTap: () async => await Geolocator.openAppSettings(), ); return; } // Show loader showLoading(context); // Get location await LocationUtilities.getCurrentLocation( (Position position, bool isMocked) { hideLoading(context); if (isMocked) { markFakeAttendance( swipeType.name, position.latitude.toString(), position.longitude.toString(), context: context, ); } else { handleSwipeOperation( swipeType: swipeType, lat: position.latitude, long: position.longitude, context: context, ); } }, (String error) { hideLoading(context); confirmDialog( context, error.isNotEmpty ? error : "Unable to determine your location. Please try again.", ); }, context, ); } catch (e) { hideLoading(context); confirmDialog( context, "Something went wrong. Please try again.", ); debugPrint("❌ handleSwipe error: $e"); } } // void handleSwipe({required SwipeTypeEnum swipeType, required bool isEnable, required BuildContext context}) async { // if (Platform.isAndroid && !(await isGoogleServicesAvailable())) { // checkHuaweiLocationPermission(attendanceType: swipeType, context: context); // } else { // LocationUtilities.isEnabled((bool isEnabled) { // if (isEnabled) { // LocationUtilities.havePermission((bool permission) { // if (permission) { // showLoading(context); // LocationUtilities.getCurrentLocation( // (Position position, bool isMocked) { // if (isMocked) { // hideLoading(context); // markFakeAttendance(swipeType.name, position.latitude.toString(), position.longitude.toString() ?? "", context: context); // } else { // hideLoading(context); // handleSwipeOperation(swipeType: swipeType, lat: position.latitude, long: position.longitude, context: context); // } // }, // () { // hideLoading(context); // confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); // }, // context, // ); // } else { // showInfoDialog( // message: "You need to give location permission to mark attendance", // onTap: () async { // await Geolocator.openAppSettings(); // }); // } // }); // } else { // showInfoDialog( // message: "You need to enable location services to mark attendance", // onTap: () async { // await Geolocator.openLocationSettings(); // }); // } // }); // } // } void showInfoDialog({required String message, VoidCallback? onTap}) { showDialog( context: navigatorKey.currentState!.overlay!.context, builder: (BuildContext cxt) => ConfirmDialog( message: message, onTap: () async { Navigator.pop(navigatorKey.currentState!.overlay!.context); onTap!(); }, ), ); } List availableAttendanceMethodList({required BuildContext context, required UserProvider userProvider, required bool isNfcSupported}) { List availableMethods = []; if (userProvider.user!.enableNFC!) { availableMethods.add(attendanceTypeCard(SwipeTypeEnum.NFC.name, 'nfc_icon', isNfcSupported, () { Navigator.pop(context); handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: userProvider.user!.enableNFC!, context: navigatorKey.currentState!.overlay!.context); }, context)); } if (userProvider.user!.enableQR!) { availableMethods.add(attendanceTypeCard(SwipeTypeEnum.QR.name, 'qr', userProvider.user!.enableQR!, () { Navigator.pop(context); handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: userProvider.user!.enableQR!, context: navigatorKey.currentState!.overlay!.context); }, context)); } if (kDebugMode) { userProvider.user!.enableWifi = true; } if (userProvider.user!.enableWifi!) { availableMethods.add(attendanceTypeCard(SwipeTypeEnum.Wifi.name, 'wifi_icon', userProvider.user!.enableWifi!, () { Navigator.pop(context); handleSwipe(swipeType: SwipeTypeEnum.Wifi, isEnable: userProvider.user!.enableWifi!, context: navigatorKey.currentState!.overlay!.context); }, context)); } return availableMethods; } void showSwipeTypeBottomSheetSheet({required bool isNfcSupported}) { BuildContext context = navigatorKey.currentState!.overlay!.context; UserProvider _userProvider = Provider.of(context, listen: false); showModalBottomSheet( context: context, useSafeArea: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), ), backgroundColor: Theme.of(context).scaffoldBackgroundColor, clipBehavior: Clip.antiAliasWithSaveLayer, builder: (BuildContext context) => Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom + MediaQuery.of(context).padding.bottom, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ context.translation.markAttendance.heading4(context).custom(color: context.isDark ? AppColor.white50 : AppColor.white936), 8.height, context.translation.selectMethodToMarkAttendance.bodyText2(context).custom(color: AppColor.neutral120), 12.height, GridView( padding: const EdgeInsets.all(0), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12), children: availableAttendanceMethodList(context: context, userProvider: _userProvider, isNfcSupported: isNfcSupported)), ], ).paddingAll(16), ), ); } //older code.. void readNFc({Function(String)? onRead}) { NfcManager.instance.startSession( onDiscovered: (NfcTag tag) async { String identifier = ''; try { final dynamic tagDynamic = tag; final Map tagData = Map.from(tagDynamic.data as Map); if (Platform.isAndroid) { if (tagData.containsKey('nfca')) { final nfcaData = tagData['nfca'] as Map; if (nfcaData.containsKey('identifier')) { final List idBytes = List.from(nfcaData['identifier'] as List); identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); } } } else { if (tagData.containsKey('mifare')) { final mifareData = tagData['mifare'] as Map; if (mifareData.containsKey('identifier')) { final List idBytes = List.from(mifareData['identifier'] as List); identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); } } } } catch (e) { print('Error reading NFC: $e'); } NfcManager.instance.stopSession(); onRead!(identifier); }, pollingOptions: {NfcPollingOption.iso14443}, ).catchError((err) { print(err); }); } //HUAWEI DECISION MAKING Future isGoogleServicesAvailable() async { GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); String status = availability.toString().split('.').last; if (status == "success") { return true; } return false; } Future performWifiAttendance({required double latitude, required double long, required BuildContext context}) async { String ssId = String.fromCharCodes(base64Decode("SE1HLU1PSEVNTQ==")); String password = String.fromCharCodes(base64Decode("TTBoZW1tQDEyMTI=")); showLoading(context); bool isConnected = await WiFiForIoTPlugin.connect(ssId, password: password, joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); if (Platform.isIOS) { if (await WiFiForIoTPlugin.getSSID() == ssId) { isConnected = true; } else { isConnected = false; } } UserProvider userProvider = Provider.of(context, listen: false); if (isConnected) { await WiFiForIoTPlugin.forceWifiUsage(true); await Future.delayed(const Duration(seconds: 6)); try { final swipeModel = Swipe(swipeTypeValue: SwipeTypeEnum.Wifi.getIntFromSwipeTypeEnum(), value: '', latitude: latitude, longitude: long); final swipeResponse = await userProvider.makeSwipe(model: swipeModel); await closeWifiRequest(); if (swipeResponse.isSuccess == true) { hideLoading(context); Navigator.pushNamed(context, SwipeSuccessView.routeName); } else { hideLoading(context); await Future.delayed(const Duration(milliseconds: 250)); showErrorDialog(message: swipeResponse.message ?? "Unexpected error occurred", context: context); } } catch (errSwipeGeneralUtilsor) { hideLoading(context); await closeWifiRequest(); } } else { // if (userProvider.) { hideLoading(context); confirmDialog(context, "Come Near HMG Wifi"); // } else { // await closeWifiRequest(); // } await closeWifiRequest(); } } Future closeWifiRequest() async { if (Platform.isAndroid) { await WiFiForIoTPlugin.forceWifiUsage(false); } return await WiFiForIoTPlugin.disconnect(); } }