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.
194 lines
7.3 KiB
Dart
194 lines
7.3 KiB
Dart
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<PredictedAsset?> pickImageFromCamera(BuildContext context, {bool pickMode = false}) {
|
|
return _pickImage(context, pickMode: pickMode, allowGallery: false);
|
|
}
|
|
|
|
static Future<PredictedAsset?> pickImageForInventorySession(BuildContext context, {bool pickMode = true}) {
|
|
return _pickImage(context, pickMode: pickMode, allowGallery: true);
|
|
}
|
|
|
|
static Future<PredictedAsset?> _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<PredictedAsset?> _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<Widget> 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<PredictedAsset?> 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<PredictedAsset?> uploadImage(File imageFile, BuildContext context, {bool pickMode = false}) async {
|
|
try {
|
|
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
|
|
AssetProvider assetProvider = Provider.of<AssetProvider>(context, listen: false);
|
|
|
|
PredictDeviceResponse? response = await assetProvider.predictDevice(imageFile);
|
|
Navigator.pop(context);
|
|
if (response != null && response.success == true) {
|
|
final List<PredictedAsset> predictedAssetList = response.predictedAsset ?? [];
|
|
if (predictedAssetList.isNotEmpty) {
|
|
return Navigator.push<PredictedAsset?>(
|
|
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;
|
|
}
|
|
}
|