mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 08:07:59 -06:00
Added possibility to upload documents from file system
This commit is contained in:
@@ -46,5 +46,6 @@ enum ErrorCode {
|
|||||||
createSavedViewError,
|
createSavedViewError,
|
||||||
deleteSavedViewError,
|
deleteSavedViewError,
|
||||||
requestTimedOut,
|
requestTimedOut,
|
||||||
storagePathAlreadyExists;
|
storagePathAlreadyExists,
|
||||||
|
unsupportedFileFormat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ import 'package:intl/date_symbol_data_local.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class DocumentUploadPage extends StatefulWidget {
|
class DocumentUploadPage extends StatefulWidget {
|
||||||
final Uint8List pdfBytes;
|
final Uint8List fileBytes;
|
||||||
|
|
||||||
const DocumentUploadPage({
|
const DocumentUploadPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.pdfBytes,
|
required this.fileBytes,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -190,7 +191,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
|||||||
_isUploadLoading = true;
|
_isUploadLoading = true;
|
||||||
});
|
});
|
||||||
await BlocProvider.of<DocumentsCubit>(context).addDocument(
|
await BlocProvider.of<DocumentsCubit>(context).addDocument(
|
||||||
widget.pdfBytes,
|
widget.fileBytes,
|
||||||
_formKey.currentState?.value[fkFileName],
|
_formKey.currentState?.value[fkFileName],
|
||||||
onConsumptionFinished: (document) {
|
onConsumptionFinished: (document) {
|
||||||
ScaffoldMessenger.of(rootScaffoldKey.currentContext!).showSnackBar(
|
ScaffoldMessenger.of(rootScaffoldKey.currentContext!).showSnackBar(
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ import 'dart:io';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:edge_detection/edge_detection.dart';
|
import 'package:edge_detection/edge_detection.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:mime/mime.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||||
|
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
@@ -12,6 +16,7 @@ import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart'
|
|||||||
import 'package:paperless_mobile/features/scan/view/document_upload_page.dart';
|
import 'package:paperless_mobile/features/scan/view/document_upload_page.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/widgets/grid_image_item_widget.dart';
|
import 'package:paperless_mobile/features/scan/view/widgets/grid_image_item_widget.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:pdf/pdf.dart';
|
import 'package:pdf/pdf.dart';
|
||||||
import 'package:pdf/widgets.dart' as pw;
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
@@ -25,6 +30,14 @@ class ScannerPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _ScannerPageState extends State<ScannerPage>
|
class _ScannerPageState extends State<ScannerPage>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
|
static const _supportedExtensions = [
|
||||||
|
'pdf',
|
||||||
|
'png',
|
||||||
|
'tiff',
|
||||||
|
'gif',
|
||||||
|
'jpg',
|
||||||
|
'jpeg'
|
||||||
|
];
|
||||||
late final AnimationController _fabPulsingController;
|
late final AnimationController _fabPulsingController;
|
||||||
late final Animation _animation;
|
late final Animation _animation;
|
||||||
@override
|
@override
|
||||||
@@ -109,18 +122,8 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _export(BuildContext context) async {
|
void _export(BuildContext context) async {
|
||||||
final pw.Document doc = pw.Document();
|
final doc = _buildDocumentFromImageFiles(
|
||||||
|
BlocProvider.of<DocumentScannerCubit>(context).state);
|
||||||
for (var element in BlocProvider.of<DocumentScannerCubit>(context).state) {
|
|
||||||
final img = pw.MemoryImage(element.readAsBytesSync());
|
|
||||||
doc.addPage(
|
|
||||||
pw.Page(
|
|
||||||
pageFormat:
|
|
||||||
PdfPageFormat(img.width!.toDouble(), img.height!.toDouble()),
|
|
||||||
build: (context) => pw.Image(img),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final bytes = await doc.save();
|
final bytes = await doc.save();
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@@ -128,7 +131,7 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
value: getIt<DocumentsCubit>(),
|
value: getIt<DocumentsCubit>(),
|
||||||
child: LabelBlocProvider(
|
child: LabelBlocProvider(
|
||||||
child: DocumentUploadPage(
|
child: DocumentUploadPage(
|
||||||
pdfBytes: bytes,
|
fileBytes: bytes,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -144,10 +147,27 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
}
|
}
|
||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Column(
|
||||||
S.of(context).documentScannerPageEmptyStateText,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
textAlign: TextAlign.center,
|
children: [
|
||||||
|
Text(
|
||||||
|
S.of(context).documentScannerPageEmptyStateText,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child:
|
||||||
|
Text(S.of(context).documentScannerPageAddScanButtonLabel),
|
||||||
|
onPressed: () => _openDocumentScanner(context),
|
||||||
|
),
|
||||||
|
Text(S.of(context).documentScannerPageOrText),
|
||||||
|
TextButton(
|
||||||
|
child: Text(S
|
||||||
|
.of(context)
|
||||||
|
.documentScannerPageUploadFromThisDeviceButtonLabel),
|
||||||
|
onPressed: _onUploadFromFilesystem,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -185,4 +205,53 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
Permission.camera.request();
|
Permission.camera.request();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onUploadFromFilesystem() async {
|
||||||
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
|
type: FileType.custom,
|
||||||
|
allowedExtensions: _supportedExtensions,
|
||||||
|
withData: true,
|
||||||
|
);
|
||||||
|
if (result?.files.single.path != null) {
|
||||||
|
File file = File(result!.files.single.path!);
|
||||||
|
|
||||||
|
final mimeType = lookupMimeType(file.path) ?? '';
|
||||||
|
late Uint8List fileBytes;
|
||||||
|
if (mimeType.startsWith('image')) {
|
||||||
|
fileBytes = await _buildDocumentFromImageFiles([file]).save();
|
||||||
|
} else {
|
||||||
|
// pdf
|
||||||
|
fileBytes = file.readAsBytesSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => BlocProvider.value(
|
||||||
|
value: getIt<DocumentsCubit>(),
|
||||||
|
child: LabelBlocProvider(
|
||||||
|
child: DocumentUploadPage(
|
||||||
|
fileBytes: fileBytes,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pw.Document _buildDocumentFromImageFiles(List<File> files) {
|
||||||
|
final doc = pw.Document();
|
||||||
|
for (final file in files) {
|
||||||
|
final img = pw.MemoryImage(file.readAsBytesSync());
|
||||||
|
doc.addPage(
|
||||||
|
pw.Page(
|
||||||
|
pageFormat:
|
||||||
|
PdfPageFormat(img.width!.toDouble(), img.height!.toDouble()),
|
||||||
|
build: (context) => pw.Image(img),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,9 @@
|
|||||||
"documentScannerPageTitle": "Scanner",
|
"documentScannerPageTitle": "Scanner",
|
||||||
"documentScannerPageResetButtonTooltipText": "Alle scans löschen",
|
"documentScannerPageResetButtonTooltipText": "Alle scans löschen",
|
||||||
"documentScannerPageUploadButtonTooltip": "Dokument hochladen",
|
"documentScannerPageUploadButtonTooltip": "Dokument hochladen",
|
||||||
|
"documentScannerPageAddScanButtonLabel": "Scanne ein Dokument",
|
||||||
|
"documentScannerPageOrText": "oder",
|
||||||
|
"documentScannerPageUploadFromThisDeviceButtonLabel": "Lade ein Dokument von diesem Gerät hoch",
|
||||||
"addTagPageTitle": "Neuer Tag",
|
"addTagPageTitle": "Neuer Tag",
|
||||||
"addCorrespondentPageTitle": "Neuer Korrespondent",
|
"addCorrespondentPageTitle": "Neuer Korrespondent",
|
||||||
"addDocumentTypePageTitle": "Neuer Dokumententyp",
|
"addDocumentTypePageTitle": "Neuer Dokumententyp",
|
||||||
@@ -182,4 +185,5 @@
|
|||||||
"referencedDocumentsReadOnlyHintText": "Dies ist eine schreibgeschützte Ansicht! Dokumente können nicht bearbeitet oder entfernt werden.",
|
"referencedDocumentsReadOnlyHintText": "Dies ist eine schreibgeschützte Ansicht! Dokumente können nicht bearbeitet oder entfernt werden.",
|
||||||
"editLabelPageConfirmDeletionDialogTitle": "Löschen bestätigen",
|
"editLabelPageConfirmDeletionDialogTitle": "Löschen bestätigen",
|
||||||
"editLabelPageDeletionDialogText": "Dieser Kennzeichner wird von Dokumenten referenziert. Durch das Löschen dieses Kennzeichners werden alle Referenzen entfernt. Fortfahren?"
|
"editLabelPageDeletionDialogText": "Dieser Kennzeichner wird von Dokumenten referenziert. Durch das Löschen dieses Kennzeichners werden alle Referenzen entfernt. Fortfahren?"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,9 @@
|
|||||||
"documentScannerPageTitle": "Scan",
|
"documentScannerPageTitle": "Scan",
|
||||||
"documentScannerPageResetButtonTooltipText": "Delete all scans",
|
"documentScannerPageResetButtonTooltipText": "Delete all scans",
|
||||||
"documentScannerPageUploadButtonTooltip": "Upload to Paperless",
|
"documentScannerPageUploadButtonTooltip": "Upload to Paperless",
|
||||||
|
"documentScannerPageAddScanButtonLabel": "Scan a document",
|
||||||
|
"documentScannerPageOrText": "or",
|
||||||
|
"documentScannerPageUploadFromThisDeviceButtonLabel": "Upload a document from this device",
|
||||||
"addTagPageTitle": "New Tag",
|
"addTagPageTitle": "New Tag",
|
||||||
"addCorrespondentPageTitle": "New Correspondent",
|
"addCorrespondentPageTitle": "New Correspondent",
|
||||||
"addDocumentTypePageTitle": "New Document Type",
|
"addDocumentTypePageTitle": "New Document Type",
|
||||||
|
|||||||
@@ -823,7 +823,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ dependencies:
|
|||||||
flutter_native_splash: ^2.2.11
|
flutter_native_splash: ^2.2.11
|
||||||
share_plus: ^6.2.0
|
share_plus: ^6.2.0
|
||||||
introduction_screen: ^3.0.2
|
introduction_screen: ^3.0.2
|
||||||
|
mime: ^1.0.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user