import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:open_filex/open_filex.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:path_provider/path_provider.dart'; import 'android_download_helper.dart'; import 'download_result.dart'; final Dio _dio = Dio(); Future ensureStorageAccessImpl() async { if (Platform.isAndroid) { await Permission.notification.request(); final sdkInt = await androidSdkInt(); if (sdkInt != null && sdkInt < 29) { final storage = await Permission.storage.request(); return storage.isGranted; } return true; } return true; } Future downloadEcardImpl({ required String url, required String fileName, Map? headers, }) async { if (Platform.isIOS) { return _downloadOnIos( url: url, fileName: fileName, headers: headers, ); } try { final hasPermission = await ensureStorageAccessImpl(); if (!hasPermission) { return DownloadResult.failure( 'Storage permission required', ); } final safeFileName = _sanitizeFileName(fileName); final tempDir = await getTemporaryDirectory(); final tempPath = '${tempDir.path}/$safeFileName'; final tempFile = File(tempPath); if (await tempFile.exists()) { await tempFile.delete(); } await _dio.download( url, tempPath, options: Options( headers: headers, responseType: ResponseType.bytes, ), deleteOnError: true, ); if (!await tempFile.exists()) { return DownloadResult.failure('Download failed'); } final bytes = await tempFile.readAsBytes(); if (bytes.isEmpty) { await tempFile.delete(); return DownloadResult.failure('Download failed'); } final savedPath = await savePdfToAndroidDownloads( fileName: safeFileName, bytes: bytes, ); await tempFile.delete(); return DownloadResult.success( savedPath: savedPath, message: 'File saved under Download/$safeFileName', ); } on FileSystemException { return DownloadResult.failure('Storage permission issue'); } on DioException catch (e) { final hasStatusCode = e.response?.statusCode != null; final message = hasStatusCode ? 'Download failed (${e.response!.statusCode})' : 'Download failed'; return DownloadResult.failure(message); } on PlatformException catch (e) { return DownloadResult.failure(e.message ?? 'Download failed'); } catch (_) { return DownloadResult.failure('Download failed'); } } Future _downloadOnIos({ required String url, required String fileName, Map? headers, }) async { try { final safeFileName = _sanitizeFileName(fileName); final documentsDir = await getApplicationDocumentsDirectory(); final downloadDir = Directory('${documentsDir.path}/ecards'); if (!await downloadDir.exists()) { await downloadDir.create(recursive: true); } final filePath = '${downloadDir.path}/$safeFileName'; final file = File(filePath); if (await file.exists()) { await file.delete(); } final response = await http.get(Uri.parse(url), headers: headers); if (response.statusCode != 200) { return DownloadResult.failure( 'Download failed (${response.statusCode})', ); } await file.writeAsBytes(response.bodyBytes, flush: true); if (!await file.exists() || await file.length() == 0) { return DownloadResult.failure('Download failed'); } try { await OpenFilex.open(filePath); } catch (_) { // File is saved even if the preview cannot be opened. } return DownloadResult.success( savedPath: filePath, message: 'E-Card downloaded', ); } catch (_) { return DownloadResult.failure('Download failed'); } } String _sanitizeFileName(String fileName) { final replaced = fileName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_').trim(); if (replaced.isEmpty) return 'ecard.pdf'; return replaced.toLowerCase().endsWith('.pdf') ? replaced : '$replaced.pdf'; }