diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ae995740..d597ec1d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,12 +10,13 @@ - - + + - + + @@ -108,14 +109,20 @@ - - - + + + - - + + - + diff --git a/lib/helper/utils.dart b/lib/helper/utils.dart index 82d3f879..55d40f74 100644 --- a/lib/helper/utils.dart +++ b/lib/helper/utils.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:google_api_availability/google_api_availability.dart'; @@ -79,6 +80,13 @@ class Utils { return await Future.delayed(Duration(milliseconds: millis)); } + static void hapticFeedback() async { + bool canVibrate = await Vibrate.canVibrate; + if (canVibrate) { + Vibrate.feedback(FeedbackType.medium); + } + } + static bool isBeforeOrEqualCurrentTime(TimeOfDay t1, TimeOfDay t2) { return t1.hour < t2.hour || (t1.hour == t2.hour && t1.minute <= t2.minute); } @@ -428,45 +436,45 @@ class Utils { NfcManager.instance.startSession( onDiscovered: (NfcTag tag) async { - String identifier = ''; + String identifier = ''; - try { - if (Platform.isAndroid) { - // Try NfcA first (most common) - final nfcA = NfcAAndroid.from(tag); - if (nfcA != null) { - identifier = nfcA.tag.id.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); - } else { - // Fallback to NfcB - final nfcB = NfcBAndroid.from(tag); - if (nfcB != null) { - identifier = nfcB.tag.id.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + try { + if (Platform.isAndroid) { + // Try NfcA first (most common) + final nfcA = NfcAAndroid.from(tag); + if (nfcA != null) { + identifier = nfcA.tag.id.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + } else { + // Fallback to NfcB + final nfcB = NfcBAndroid.from(tag); + if (nfcB != null) { + identifier = nfcB.tag.id.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + } } - } - } else { - // For iOS, try MiFare first - final mifare = MiFareIos.from(tag); - if (mifare != null) { - identifier = mifare.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); } else { - // Fallback to Iso15693 for iOS - final iso15693 = Iso15693Ios.from(tag); - if (iso15693 != null) { - identifier = iso15693.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + // For iOS, try MiFare first + final mifare = MiFareIos.from(tag); + if (mifare != null) { + identifier = mifare.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + } else { + // Fallback to Iso15693 for iOS + final iso15693 = Iso15693Ios.from(tag); + if (iso15693 != null) { + identifier = iso15693.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + } } } + } catch (e) { + print('Error reading NFC: $e'); } - } catch (e) { - print('Error reading NFC: $e'); - } - try { - await NfcManager.instance.stopSession(); - } catch (e) { - print('Error stopping NFC session: $e'); - } - onRead(identifier); - }, + try { + await NfcManager.instance.stopSession(); + } catch (e) { + print('Error stopping NFC session: $e'); + } + onRead(identifier); + }, pollingOptions: {NfcPollingOption.iso14443}, ).catchError((err) { print('NFC session error: $err'); diff --git a/lib/new_views/swipe_module/utils/swipe_general_utils.dart b/lib/new_views/swipe_module/utils/swipe_general_utils.dart index 4eb96010..ee087dee 100644 --- a/lib/new_views/swipe_module/utils/swipe_general_utils.dart +++ b/lib/new_views/swipe_module/utils/swipe_general_utils.dart @@ -372,7 +372,7 @@ class SwipeGeneralUtils { UserProvider userProvider = Provider.of(context, listen: false); String qrCodeValue = await Navigator.of(context).push( - MaterialPageRoute(builder: (_) => ScanQr()), + MaterialPageRoute(builder: (_) => const ScanQr()), ) as String; if (qrCodeValue != null) { diff --git a/lib/views/widgets/qr/asset_scan_qr.dart b/lib/views/widgets/qr/asset_scan_qr.dart index 022cc559..10313de5 100644 --- a/lib/views/widgets/qr/asset_scan_qr.dart +++ b/lib/views/widgets/qr/asset_scan_qr.dart @@ -1,224 +1,6 @@ -//Todo delete after testing - -// import 'dart:async'; -// import 'dart:io'; -// -// import 'package:flutter/material.dart'; -// import 'package:fluttertoast/fluttertoast.dart'; -// import 'package:mobile_scanner/mobile_scanner.dart'; -// import 'package:provider/provider.dart'; -// import 'package:qr_code_scanner_plus/qr_code_scanner_plus.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/helper/utils.dart'; -// import 'package:test_sa/models/device/asset.dart'; -// import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; -// import 'package:test_sa/modules/tm_module/device_transfer/search_device_page.dart'; -// import 'package:test_sa/new_views/app_style/app_color.dart'; -// import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; -// -// import '../../../controllers/providers/api/devices_provider.dart'; -// import '../../../models/device/asset_search.dart'; -// import '../../../new_views/common_widgets/app_filled_button.dart'; -// -// class AssetScanQr extends StatefulWidget { -// static const String id = "/asset-scan-qr"; -// -// const AssetScanQr({Key? key, required this.title, this.multiSelection = false, this.enablePickManually = true}) : super(key: key); -// final String title; -// final bool multiSelection; -// final bool enablePickManually; -// -// @override -// _AssetScanQrState createState() => _AssetScanQrState(); -// } -// -// class _AssetScanQrState extends State { -// // Barcode result; -// // QRViewController? _controller; -// bool _scanDone = false; -// bool _scanning = false; -// final GlobalKey qrKey = GlobalKey(debugLabel: 'QR_scanner'); -// late AssetProvider _devicesProvider; -// -// MobileScannerController? controller; -// -// @override -// void initState() { -// // TODO: implement initState -// super.initState(); -// controller = MobileScannerController( -// detectionSpeed: DetectionSpeed.noDuplicates, -// formats: [], -// returnImage: false, -// torchEnabled: false, -// invertImage: false, -// autoZoom: true, -// ); -// } -// -// @override -// void dispose() { -// super.dispose(); -// controller?.dispose(); -// _devicesProvider.searchReset(); -// } -// -// _pickManually() async { -// controller?.pause(); -// await Navigator.push(context, MaterialPageRoute(builder: (context) => SearchDevicePage(multiSelection: widget.multiSelection))).then((value) => controller?.stop()); -// } -// -// _getDevice(String result, {bool isQr = false}) async { -// _devicesProvider.reset(); -// await _devicesProvider.getAssets( -// search: DeviceSearch(assetNo: result, assetSerialNumber: ""), -// isQr: isQr, -// ); -// return _devicesProvider.devices; -// } -// -// @override -// Widget build(BuildContext context) { -// _devicesProvider = Provider.of(context); -// return Scaffold( -// appBar: DefaultAppBar(title: widget.title), -// body: Column( -// children: [ -// Stack( -// children: [ -// MobileScanner( -// key: qrKey, -// tapToFocus: true, -// controller: controller, -// onDetect: (result) async { -// String? scanData = result.barcodes.first.rawValue; -// if (!_scanDone) { -// _scanDone = true; -// if (!widget.enablePickManually) { -// if (scanData != null) { -// Navigator.of(context).pop(Asset(assetNumber: scanData)); -// } else { -// _scanDone = false; -// } -// } else { -// setState(() { -// _scanning = true; -// }); -// final result = await _getDevice(scanData!, isQr: true); -// setState(() { -// _scanning = false; -// }); -// if (result.isNotEmpty) { -// if (widget.multiSelection) { -// Navigator.of(context).pop([result[0]]); -// } else { -// Navigator.of(context).pop(result[0]); -// } -// } else { -// Utils.showToast("No Item Found. Try Again", toastGravity: ToastGravity.CENTER); -// _scanDone = false; -// } -// } -// } -// }, -// ), -// -// // QRView( -// // key: qrKey, -// // onQRViewCreated: (QRViewController controller) { -// // setState(() { -// // _controller = controller; -// // }); -// // controller.scannedDataStream.listen((scanData) async { -// // if (!_scanDone) { -// // _scanDone = true; -// // if (!widget.enablePickManually) { -// // if (scanData.code != null) { -// // Navigator.of(context).pop(Asset(assetNumber: scanData.code!)); -// // } else { -// // _scanDone = false; -// // } -// // } else { -// // _controller?.pauseCamera(); -// // setState(() { -// // _scanning = true; -// // }); -// // final result = await _getDevice(scanData.code!, isQr: true); -// // setState(() { -// // _scanning = false; -// // }); -// // if (result.isNotEmpty) { -// // if (widget.multiSelection) { -// // Navigator.of(context).pop([result[0]]); -// // } else { -// // Navigator.of(context).pop(result[0]); -// // } -// // } else { -// // Utils.showToast("No Item Found. Try Again", toastGravity: ToastGravity.CENTER); -// // _controller?.resumeCamera(); -// // _scanDone = false; -// // } -// // } -// // } -// // }); -// // }, -// // ), -// Center( -// child: 'scan'.toSvgAsset( -// height: 283.toScreenHeight.toInt(), -// width: 283.toScreenWidth.toInt(), -// fit: BoxFit.fitHeight, -// ), -// ), -// if (_scanning) -// Container( -// width: 56.toScreenHeight, -// height: 56.toScreenHeight, -// padding: const EdgeInsets.all(8), -// decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.white), -// child: const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3), -// ).center -// ], -// ).expanded, -// if (widget.enablePickManually) -// FooterActionButton.footerContainer( -// context: context, -// child: AppFilledButton( -// label: context.translation.pickManually, -// onPressed: _pickManually, -// ), -// ), -// ], -// ), -// // bottomSheet: -// // 16.height, -// -// // Container( -// // height: 82.toScreenHeight, -// // color: Colors.white, -// // width: MediaQuery.of(context).size.width, -// // child: Center( -// // child: SizedBox( -// // height: 50.toScreenHeight, -// // width: 358.toScreenWidth, -// // child: AppFilledButton( -// // label: context.translation.pickManually, -// // onPressed: _pickManually, -// // ), -// // ), -// // ), -// // ) -// ); -// } -// } import 'dart:async'; import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:google_api_availability/google_api_availability.dart'; @@ -371,7 +153,6 @@ class _AssetScanQrState extends State { ); _scanDone = false; mobileController?.start(); - qrController?.resumeCamera(); } } @@ -380,9 +161,42 @@ class _AssetScanQrState extends State { controller.scannedDataStream.listen((scanData) async { if (_scanDone) return; - if (scanData.code == null || scanData.code!.isEmpty) return; - await _handleScan(scanData.code!); + final code = scanData.code; + if (code == null || code.isEmpty) return; + + _scanDone = true; + await qrController?.pauseCamera(); + + if (!widget.enablePickManually) { + Navigator.of(context).pop(Asset(assetNumber: code)); + return; + } + + setState(() { + _scanning = true; + }); + + final result = await _getDevice(code, isQr: true); + + setState(() { + _scanning = false; + }); + + if (result.isNotEmpty) { + if (widget.multiSelection) { + Navigator.of(context).pop([result[0]]); + } else { + Navigator.of(context).pop(result[0]); + } + } else { + Utils.showToast( + "No Item Found. Try Again", + toastGravity: ToastGravity.CENTER, + ); + _scanDone = false; + await qrController?.resumeCamera(); + } }); } diff --git a/lib/views/widgets/qr/scan_qr.dart b/lib/views/widgets/qr/scan_qr.dart index 0edec5ae..6e90674e 100644 --- a/lib/views/widgets/qr/scan_qr.dart +++ b/lib/views/widgets/qr/scan_qr.dart @@ -5,6 +5,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + import '../buttons/app_icon_button.dart'; class ScanQr extends StatefulWidget { @@ -26,6 +30,7 @@ class _ScanQrState extends State { @override void initState() { + // TODO: implement initState super.initState(); _initScanner(); } @@ -64,6 +69,7 @@ class _ScanQrState extends State { if (scanData.code == null || scanData.code!.isEmpty) return; _scanDone = true; + Utils.hapticFeedback(); Navigator.of(context).pop(scanData.code); }); } @@ -77,50 +83,47 @@ class _ScanQrState extends State { @override Widget build(BuildContext context) { - if (_isLoading) { - return const Scaffold( - body: Center(child: CircularProgressIndicator()), - ); - } - return Scaffold( - body: Stack( - children: [ - _isHuawei - ? QRView( - key: qrKey, - onQRViewCreated: _onQrViewCreated, - overlay: QrScannerOverlayShape( - borderColor: Colors.white, - borderRadius: 12, - borderLength: 30, - borderWidth: 4, - cutOutSize: MediaQuery.of(context).size.width * 0.7, - ), - ) - : MobileScanner( - controller: mobileController, - onDetect: (capture) { - if (_scanDone) return; - final barcodes = capture.barcodes; - if (barcodes.isEmpty) return; - final value = barcodes.first.rawValue; - if (value == null || value.isEmpty) return; - - _scanDone = true; - Navigator.of(context).pop(value); + body: _isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Stack( + children: [ + _isHuawei + ? QRView( + key: qrKey, + onQRViewCreated: _onQrViewCreated, + overlay: QrScannerOverlayShape( + borderColor: Colors.white, + borderRadius: 12, + borderLength: 30, + borderWidth: 4, + cutOutSize: MediaQuery.of(context).size.width * 0.7, + ), + ) + : MobileScanner( + controller: mobileController, + onDetect: (capture) { + if (_scanDone) return; + final barcodes = capture.barcodes; + if (barcodes.isEmpty) return; + final value = barcodes.first.rawValue; + if (value == null || value.isEmpty) return; + + _scanDone = true; + Utils.hapticFeedback(); + Navigator.of(context).pop(value); + }, + ), + _ScannerOverlay( + // scanDone: _scanDone, + onBack: () { + if (!_scanDone) _scanDone = true; + Navigator.of(context).pop(); }, + showSvgOverlay: !_isHuawei, ), - _ScannerOverlay( - // scanDone: _scanDone, - onBack: () { - if (!_scanDone) _scanDone = true; - Navigator.of(context).pop(); - }, - showSvgOverlay: !_isHuawei, - ), - ], - ), + ], + ), ); } } diff --git a/pubspec.lock b/pubspec.lock index 590577cc..5880d411 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -593,6 +593,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.2.0" + flutter_vibrate: + dependency: "direct main" + description: + name: flutter_vibrate + sha256: "6eea0e94dd7dc8c7872e994a1139d55a8640e32fd5898ff59ee8bd53b8dea3b3" + url: "https://pub.dev" + source: hosted + version: "1.4.0" flutter_web_plugins: dependency: transitive description: flutter @@ -750,14 +758,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.14.0+300" - huawei_scan: - dependency: "direct main" - description: - name: huawei_scan - sha256: "71bc8ce2e90fa07c2d82829ce928ebe3e69b3326fb55d2e7a68f62d9efdef6dd" - url: "https://pub.dev" - source: hosted - version: "2.12.0+304" image: dependency: transitive description: @@ -1941,4 +1941,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.11.0 <4.0.0" - flutter: ">=3.38.4" + flutter: ">=3.38.5" diff --git a/pubspec.yaml b/pubspec.yaml index eccb9416..d4b29e02 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -89,7 +89,7 @@ dependencies: pie_chart: ^5.4.0 badges: ^3.1.1 -# buttons_tabbar: ^1.1.2 + # buttons_tabbar: ^1.1.2 flutter_custom_month_picker: ^0.1.3 local_auth: ^2.3.0 google_api_availability: ^5.0.1 @@ -106,9 +106,9 @@ dependencies: audio_waveforms: ^1.3.0 signalr_netcore: ^1.4.4 ellipsized_text: ^2.0.0 - huawei_scan: ^2.12.0+304 - local_auth_darwin: ^1.4.0 + flutter_vibrate: ^1.4.0 + dev_dependencies: flutter_test: sdk: flutter