import 'dart:io'; import 'package:flutter/material.dart'; import 'package:sino_med_cloud/core/utils/logger.dart'; import 'package:sino_med_cloud/core/utils/toast_utils.dart'; import 'package:sino_med_cloud/l10n/app_localizations.dart'; import 'package:sino_med_cloud/media/media.dart'; /// 媒体功能测试页面 class MediaTestPage extends StatefulWidget { const MediaTestPage({super.key}); @override State createState() => _MediaTestPageState(); } class _MediaTestPageState extends State { File? _selectedImage; File? _cameraImage; bool _isLoading = false; /// 获取当前选中的图片(优先使用相册/系统相机,其次使用专业相机) File? get _currentImage => _selectedImage ?? _cameraImage; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( title: Text(l10n.mediaTest), elevation: 0, ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 测试按钮区域 _buildTestButtons(), const SizedBox(height: 24), // 图片显示区域 _buildImageDisplay(), const SizedBox(height: 24), ], ), ), ); } /// 构建测试按钮区域 Widget _buildTestButtons() { final l10n = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( l10n.quickTest, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), // 从相册选择 ElevatedButton.icon( onPressed: _isLoading ? null : _testPickFromGallery, icon: const Icon(Icons.photo_library), label: Text(l10n.pickFromGallery), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 12), // 使用系统相机拍照 ElevatedButton.icon( onPressed: _isLoading ? null : _testPickFromCamera, icon: const Icon(Icons.camera_alt), label: Text(l10n.pickFromCamera), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 24), Text( l10n.professionalCameraTest, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), // 专业相机拍照 ElevatedButton.icon( onPressed: _isLoading ? null : _testProfessionalCamera, icon: const Icon(Icons.camera), label: Text(l10n.useProfessionalCamera), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 24), Text( l10n.imageProcessingTest, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), // 获取图片信息 ElevatedButton.icon( onPressed: _isLoading || _currentImage == null ? null : _testGetImageInfo, icon: const Icon(Icons.info_outline), label: Text(l10n.getImageInfo), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 12), // 去除 EXIF ElevatedButton.icon( onPressed: _isLoading || _currentImage == null ? null : _testRemoveExif, icon: const Icon(Icons.security), label: Text(l10n.removeExif), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 12), // 压缩图片 ElevatedButton.icon( onPressed: _isLoading || _currentImage == null ? null : _testCompressImage, icon: const Icon(Icons.compress), label: Text(l10n.compressImage), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 12), // 清除图片 OutlinedButton.icon( onPressed: _isLoading ? null : _clearImages, icon: const Icon(Icons.clear), label: Text(l10n.clearAllImages), ), ], ); } /// 构建图片显示区域 Widget _buildImageDisplay() { final l10n = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.selectedImage, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), if (_selectedImage == null && _cameraImage == null) Container( height: 200, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( l10n.noImage, style: const TextStyle( color: Colors.grey, fontSize: 16, ), ), ), ) else Column( children: [ if (_selectedImage != null) ...[ Text( l10n.galleryOrSystemCamera, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), const SizedBox(height: 8), _buildImagePreview(_selectedImage!), const SizedBox(height: 16), ], if (_cameraImage != null) ...[ Text( l10n.professionalCamera, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), const SizedBox(height: 8), _buildImagePreview(_cameraImage!), ], ], ), ], ); } /// 构建图片预览 Widget _buildImagePreview(File imageFile) { return Container( constraints: const BoxConstraints(maxHeight: 400), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey[300]!), ), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.file( imageFile, fit: BoxFit.contain, errorBuilder: (context, error, stackTrace) { final l10n = AppLocalizations.of(context)!; return Container( height: 200, color: Colors.grey[200], child: Center( child: Text(l10n.imageLoadFailed), ), ); }, ), ), ); } /// 测试从相册选择 Future _testPickFromGallery() async { setState(() { _isLoading = true; }); try { AppLogger.d('开始测试从相册选择图片...'); final file = await MediaService.pickFromGallery(); if (file != null) { if (!mounted) return; final l10n = AppLocalizations.of(context)!; setState(() { _selectedImage = file; _cameraImage = null; }); ToastUtils.showSuccess(l10n.galleryPickSuccess); AppLogger.d('相册选择成功: ${file.path}'); } else { if (!mounted) return; final l10n = AppLocalizations.of(context)!; ToastUtils.show(l10n.userCancelledSelection); } } on MediaException catch (e) { AppLogger.e('相册选择失败', e); ToastUtils.showError(e.message); } catch (e) { AppLogger.e('相册选择异常', e); ToastUtils.showError('相册选择失败: ${e.toString()}'); } finally { setState(() { _isLoading = false; }); } } /// 测试使用系统相机拍照 Future _testPickFromCamera() async { setState(() { _isLoading = true; }); try { AppLogger.d('开始测试使用系统相机拍照...'); final file = await MediaService.pickFromCamera(); if (file != null) { if (!mounted) return; final l10n = AppLocalizations.of(context)!; setState(() { _selectedImage = file; _cameraImage = null; }); ToastUtils.showSuccess(l10n.cameraPhotoSuccess); AppLogger.d('拍照成功: ${file.path}'); } else { if (!mounted) return; final l10n = AppLocalizations.of(context)!; ToastUtils.show(l10n.userCancelledPhoto); } } on MediaException catch (e) { AppLogger.e('拍照失败', e); ToastUtils.showError(e.message); } catch (e) { AppLogger.e('拍照异常', e); ToastUtils.showError('拍照失败: ${e.toString()}'); } finally { setState(() { _isLoading = false; }); } } /// 测试专业相机 Future _testProfessionalCamera() async { setState(() { _isLoading = true; }); CameraService? cameraService; try { AppLogger.d('开始测试专业相机...'); cameraService = CameraService(); // 初始化相机 await cameraService.init(); ToastUtils.show('相机初始化成功,请拍照'); // 显示拍照对话框 if (!mounted) return; final l10n = AppLocalizations.of(context)!; final shouldTakePhoto = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(l10n.professionalCamera), content: Text(l10n.cameraReady), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text(l10n.cancel), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), child: Text(l10n.takePhoto), ), ], ), ); if (shouldTakePhoto == true && mounted) { final file = await cameraService.takePicture(); if (file != null) { setState(() { _cameraImage = file; _selectedImage = null; }); ToastUtils.showSuccess(l10n.professionalCameraSuccess); AppLogger.d('专业相机拍照成功: ${file.path}'); } } } on MediaException catch (e) { AppLogger.e('专业相机失败', e); ToastUtils.showError(e.message); } catch (e) { AppLogger.e('专业相机异常', e); ToastUtils.showError('专业相机失败: ${e.toString()}'); } finally { await cameraService?.dispose(); setState(() { _isLoading = false; }); } } /// 测试获取图片信息 Future _testGetImageInfo() async { final currentImage = _currentImage; if (currentImage == null) return; setState(() { _isLoading = true; }); try { AppLogger.d('开始获取图片信息...'); final info = await MediaService.getImageInfo(currentImage); final l10n = AppLocalizations.of(context)!; final sizeInBytes = info['size'] as int; final sizeInKB = (sizeInBytes / 1024).toStringAsFixed(2); final infoText = '${l10n.imageWidth}: ${info['width']}${l10n.pixels}\n' '${l10n.imageHeight}: ${info['height']}${l10n.pixels}\n' '${l10n.imageSize}: $sizeInKB ${l10n.kilobytes}\n' '${l10n.imageFormat}: ${info['format']}'; if (!mounted) return; // 使用 Alert 弹窗显示图片信息 showDialog( context: context, builder: (context) => AlertDialog( title: Text(l10n.imageInfo), content: Text( infoText, style: const TextStyle( fontSize: 14, fontFamily: 'monospace', ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text(l10n.confirm), ), ], ), ); ToastUtils.showSuccess(l10n.getImageInfoSuccess); AppLogger.d('图片信息: $info'); } on MediaException catch (e) { AppLogger.e('获取图片信息失败', e); ToastUtils.showError(e.message); } catch (e) { AppLogger.e('获取图片信息异常', e); ToastUtils.showError('获取图片信息失败: ${e.toString()}'); } finally { setState(() { _isLoading = false; }); } } /// 测试去除 EXIF Future _testRemoveExif() async { final currentImage = _currentImage; if (currentImage == null) return; setState(() { _isLoading = true; }); try { AppLogger.d('开始去除 EXIF...'); final cleanedFile = await MediaService.removeExif(currentImage); if (!mounted) return; final l10n = AppLocalizations.of(context)!; setState(() { // 更新对应的图片变量 if (_selectedImage != null) { _selectedImage = cleanedFile; } else if (_cameraImage != null) { _cameraImage = cleanedFile; } }); ToastUtils.showSuccess(l10n.removeExifSuccess); AppLogger.d('EXIF 去除成功: ${cleanedFile.path}'); } on MediaException catch (e) { AppLogger.e('去除 EXIF 失败', e); ToastUtils.showError(e.message); } catch (e) { AppLogger.e('去除 EXIF 异常', e); ToastUtils.showError('去除 EXIF 失败: ${e.toString()}'); } finally { setState(() { _isLoading = false; }); } } /// 测试压缩图片 Future _testCompressImage() async { final currentImage = _currentImage; if (currentImage == null) return; setState(() { _isLoading = true; }); try { AppLogger.d('开始压缩图片...'); if (!mounted) return; final l10n = AppLocalizations.of(context)!; final originalSize = await currentImage.length(); final compressedFile = await MediaService.compressImage(currentImage); final compressedSize = await compressedFile.length(); setState(() { // 更新对应的图片变量 if (_selectedImage != null) { _selectedImage = compressedFile; } else if (_cameraImage != null) { _cameraImage = compressedFile; } }); final originalSizeKB = (originalSize / 1024).toStringAsFixed(2); final compressedSizeKB = (compressedSize / 1024).toStringAsFixed(2); final compressionRatio = ((1 - compressedSize / originalSize) * 100).toStringAsFixed(1); final compressInfo = '${l10n.originalSize}: $originalSizeKB ${l10n.kilobytes}\n' '${l10n.compressedSize}: $compressedSizeKB ${l10n.kilobytes}\n' '${l10n.compressionRatio}: $compressionRatio%'; if (!mounted) return; // 使用 Alert 弹窗显示压缩信息 showDialog( context: context, builder: (context) => AlertDialog( title: Text(l10n.compressImageSuccess), content: Text( compressInfo, style: const TextStyle( fontSize: 14, fontFamily: 'monospace', ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text(l10n.confirm), ), ], ), ); ToastUtils.showSuccess(l10n.compressImageSuccess); AppLogger.d('图片压缩成功: ${compressedFile.path}'); } on MediaException catch (e) { AppLogger.e('压缩图片失败', e); ToastUtils.showError(e.message); } catch (e) { AppLogger.e('压缩图片异常', e); ToastUtils.showError('压缩图片失败: ${e.toString()}'); } finally { setState(() { _isLoading = false; }); } } /// 清除所有图片 void _clearImages() { final l10n = AppLocalizations.of(context)!; setState(() { _selectedImage = null; _cameraImage = null; }); ToastUtils.show(l10n.clearAllImagesSuccess); } }