// Dart imports: import 'dart:io'; import 'dart:io' as file; // Flutter imports: import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; // Package imports: import 'package:file_picker/file_picker.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:http/http.dart'; import 'package:invoiceninja_flutter/main_app.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/utils/dialogs.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; // Project imports: import 'package:invoiceninja_flutter/utils/platforms.dart'; // ignore: unused_import import 'package:invoiceninja_flutter/utils/web_stub.dart' if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart'; import 'package:share_plus/share_plus.dart'; Future?> pickFiles({ String? fileIndex, FileType? fileType, List? allowedExtensions, bool allowMultiple = true, }) async { if (kIsWeb || isDesktopOS()) { return _pickFiles( fileIndex: fileIndex, fileType: fileType, allowedExtensions: allowedExtensions, allowMultiple: allowMultiple, ); } else { PermissionStatus? status; if (Platform.isIOS) { if (fileType == FileType.image) { status = await Permission.photos.request(); } else { status = await Permission.storage.request(); } } else if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt >= 33) { status = await Permission.photos.request(); } else { status = await Permission.storage.request(); } } if (status == PermissionStatus.granted) { return _pickFiles( fileIndex: fileIndex, fileType: fileType, allowedExtensions: allowedExtensions, allowMultiple: allowMultiple, ); } else { openAppSettings(); return null; } } } Future?> _pickFiles({ String? fileIndex, FileType? fileType, List? allowedExtensions, required bool allowMultiple, }) async { final result = await FilePicker.platform.pickFiles( type: fileType ?? FileType.custom, allowedExtensions: fileType == FileType.image ? [] : allowedExtensions ?? [], allowCompression: true, withData: true, allowMultiple: allowMultiple, ); if (result != null && result.files.isNotEmpty) { final multipartFiles = []; for (var index = 0; index < result.files.length; index++) { final file = result.files[index]; multipartFiles.add(MultipartFile.fromBytes( allowMultiple ? 'documents[$index]' : fileIndex!, file.bytes!, filename: file.name)); } return multipartFiles; } return null; } void saveDownloadedFile(Uint8List data, String fileName) async { if (kIsWeb) { WebUtils.downloadBinaryFile(fileName, data); } else { final directory = await getAppDownloadDirectory(); if (directory != null) { String filePath = '$directory/${file.Platform.pathSeparator}$fileName'; if (file.File(filePath).existsSync()) { final extension = fileName.split('.').last; final timestamp = DateTime.now().millisecondsSinceEpoch; filePath = filePath.replaceFirst('.$extension', '_$timestamp.$extension'); } await File(filePath).writeAsBytes(data); if (isDesktopOS()) { showToast(AppLocalization.of(navigatorKey.currentContext!)! .fileSavedInPath .replaceFirst(':path', directory)); } else { await Share.shareXFiles([XFile(filePath)]); } } } } Future getAppDownloadDirectory() async { var path = ''; final store = StoreProvider.of(navigatorKey.currentContext!); final state = store.state; if (state.prefState.donwloadsFolder.isNotEmpty) { path = state.prefState.donwloadsFolder; } else { final directory = await (isDesktopOS() ? getDownloadsDirectory() : getApplicationDocumentsDirectory()); if (directory == null) { return null; } path = directory.path; } if (!Directory(path).existsSync()) { showErrorDialog( message: AppLocalization.of(navigatorKey.currentContext!)! .downloadsFolderDoesNotExist .replaceFirst(':value', path)); return null; } return path; }