import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/devices_provider.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/models/device/predict_device_model.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/views/widgets/equipment/predict_device_list.dart'; enum _ImagePickSource { camera, gallery, files } class CopilotHelper { static Future pickImageFromCamera(BuildContext context, {bool pickMode = false}) { return _pickImage(context, pickMode: pickMode, allowGallery: false); } static Future pickImageForInventorySession(BuildContext context, {bool pickMode = true}) { return _pickImage(context, pickMode: pickMode, allowGallery: true); } static Future _pickImage(BuildContext context, {bool pickMode = false, bool allowGallery = false}) async { _ImagePickSource source = _ImagePickSource.camera; if (allowGallery) { final _ImagePickSource? selected = await _showImageSourceSheet(context); if (selected == null) return null; if (!context.mounted) return null; source = selected; } if (source == _ImagePickSource.files) { return _pickFromFiles(context, pickMode: pickMode); } if (source == _ImagePickSource.gallery) { return openCamera(context, pickMode: pickMode, source: ImageSource.gallery); } final status = await Permission.camera.status; if (status.isGranted) { return openCamera(context, pickMode: pickMode, source: ImageSource.camera); } final permission = await Permission.camera.request(); if (permission.isGranted) { return openCamera(context, pickMode: pickMode, source: ImageSource.camera); } if (permission.isPermanentlyDenied) { await openAppSettings(); return null; } debugPrint('Camera permission denied'); return null; } static Future _pickFromFiles(BuildContext context, {bool pickMode = false}) async { final FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.image, allowMultiple: false, ); final String? path = result?.files.single.path; if (path == null) return null; final File imageFile = File(path); debugPrint('CopilotHelper: picked image extension = ${imageFile.path.split('.').last}'); if (!context.mounted) return null; return uploadImage(imageFile, context, pickMode: pickMode); } static Future<_ImagePickSource?> _showImageSourceSheet(BuildContext context) { return showModalBottomSheet<_ImagePickSource>( context: context, builder: (BuildContext context) { Widget sourceCard({required String icon, required String label, required VoidCallback onTap}) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColor.background(context), borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColor.borderColor(context), width: 2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ icon.toSvgAsset(width: 24, height: 24, color: AppColor.icon2Color(context)), label.bodyText2(context).custom(color: AppColor.labelTextStyleColor(context)), ], ), ).onPress(onTap); } List listItems = [ sourceCard( icon: 'camera_icon', label: '${context.translation.open}\n${context.translation.camera}', onTap: () => Navigator.of(context).pop(_ImagePickSource.camera), ), sourceCard( icon: 'gallery_icon', label: '${context.translation.open}\n${context.translation.gallery}', onTap: () => Navigator.of(context).pop(_ImagePickSource.gallery), ), sourceCard( icon: 'file_icon', label: '${context.translation.open}\n${context.translation.files}', onTap: () => Navigator.of(context).pop(_ImagePickSource.files), ), ]; return SafeArea( top: false, child: Container( width: double.infinity, color: AppColor.bottomSheetColor(context), padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ 'Scan Asset'.addTranslation.heading4(context), GridView.builder( padding: const EdgeInsets.only(top: 16, bottom: 0), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 16, mainAxisSpacing: 16), itemCount: listItems.length, itemBuilder: (context, index) => listItems[index], ), ], ), ), ); }, ); } static Future openCamera(BuildContext context, {bool pickMode = false, ImageSource source = ImageSource.camera}) async { final XFile? pickedFile = await ImagePicker().pickImage( source: source, imageQuality: 70, ); if (pickedFile == null) { return null; } final File imageFile = File(pickedFile.path); debugPrint('CopilotHelper: picked image extension = ${imageFile.path.split('.').last}'); if (!context.mounted) return null; return uploadImage(imageFile, context, pickMode: pickMode); } static Future uploadImage(File imageFile, BuildContext context, {bool pickMode = false}) async { try { showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); AssetProvider assetProvider = Provider.of(context, listen: false); PredictDeviceResponse? response = await assetProvider.predictDevice(imageFile); Navigator.pop(context); if (response != null && response.success == true) { final List predictedAssetList = response.predictedAsset ?? []; if (predictedAssetList.isNotEmpty) { return Navigator.push( context, MaterialPageRoute(builder: (context) => PredictDeviceView(predictedAsset: predictedAssetList, pickMode: pickMode)), ); } else { context.showInfoDialog("No Predicted asset found"); } } else { context.showInfoDialog("Failed to recognize the asset. Please try again."); } } catch (e) { debugPrint('Upload error: $e'); } return null; } }