diff --git a/build.gradle b/build.gradle
index 8c2cc635bc..b7b88363f0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -274,6 +274,8 @@ dependencies {
implementation "com.github.cotechde.hwsecurity:hwsecurity-fido:$fidoVersion"
implementation "com.github.cotechde.hwsecurity:hwsecurity-fido2:$fidoVersion"
+ implementation 'com.github.zynkware:Document-Scanning-Android-SDK:1.0.1'
+
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0'
spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.7'
diff --git a/drawable_resources/ic_scan_document.svg b/drawable_resources/ic_scan_document.svg
new file mode 100644
index 0000000000..5d8d876202
--- /dev/null
+++ b/drawable_resources/ic_scan_document.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png b/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png
index 7b351248b4..11fb3fb7b2 100644
Binary files a/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png and b/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png differ
diff --git a/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java b/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java
index d11c82cea2..b554918c46 100644
--- a/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java
+++ b/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java
@@ -334,6 +334,11 @@ public class DialogFragmentIT extends AbstractIT {
}
+ @Override
+ public void scanDocUpload() {
+
+ }
+
@Override
public void showTemplate(Creator creator, String headline) {
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index fd15977a44..3a65dc9468 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -117,6 +117,11 @@
android:name="android.app.searchable"
android:resource="@xml/users_and_groups_searchable" />
+
diff --git a/src/main/java/com/owncloud/android/ui/activity/AppScanActivity.kt b/src/main/java/com/owncloud/android/ui/activity/AppScanActivity.kt
new file mode 100644
index 0000000000..6d80172b13
--- /dev/null
+++ b/src/main/java/com/owncloud/android/ui/activity/AppScanActivity.kt
@@ -0,0 +1,59 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2022 Tobias Kaminsky
+ * Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.owncloud.android.ui.activity
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import com.owncloud.android.R
+import com.owncloud.android.utils.DisplayUtils
+import com.zynksoftware.documentscanner.ScanActivity
+import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
+import com.zynksoftware.documentscanner.model.ScannerResults
+
+class AppScanActivity : ScanActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ addFragmentContentLayout()
+ }
+
+ override fun onError(error: DocumentScannerErrorModel) {
+ DisplayUtils.showSnackMessage(this, R.string.error_starting_scan_doc)
+ }
+
+ override fun onSuccess(scannerResults: ScannerResults) {
+ val intent = Intent()
+
+ intent.putExtra(
+ "file",
+ scannerResults.transformedImageFile?.absolutePath ?: scannerResults.croppedImageFile?.absolutePath
+ )
+
+ setResult(Activity.RESULT_OK, intent)
+ finish()
+ }
+
+ override fun onClose() {
+ finish()
+ }
+}
diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
index 6342d3bfe8..20ee39908d 100644
--- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
+++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
@@ -192,6 +192,7 @@ public class FileDisplayActivity extends FileActivity
public static final int REQUEST_CODE__MOVE_FILES = REQUEST_CODE__LAST_SHARED + 3;
public static final int REQUEST_CODE__COPY_FILES = REQUEST_CODE__LAST_SHARED + 4;
public static final int REQUEST_CODE__UPLOAD_FROM_CAMERA = REQUEST_CODE__LAST_SHARED + 5;
+ public static final int REQUEST_CODE__UPLOAD_SCAN_DOC_FROM_CAMERA = REQUEST_CODE__LAST_SHARED + 6;
protected static final long DELAY_TO_REQUEST_REFRESH_OPERATION_LATER = DELAY_TO_REQUEST_OPERATIONS_LATER + 350;
@@ -916,6 +917,35 @@ public class FileDisplayActivity extends FileActivity
}
}
}, new String[]{FileOperationsHelper.createImageFile(getActivity()).getAbsolutePath()}).execute();
+ } else if (requestCode == REQUEST_CODE__UPLOAD_SCAN_DOC_FROM_CAMERA &&
+ (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_DELETE)) {
+ Uri fileUri = Uri.parse(data.getStringExtra("file"));
+
+ new CheckAvailableSpaceTask(new CheckAvailableSpaceTask.CheckAvailableSpaceListener() {
+ @Override
+ public void onCheckAvailableSpaceStart() {
+ Log_OC.d(this, "onCheckAvailableSpaceStart");
+ }
+
+ @Override
+ public void onCheckAvailableSpaceFinish(boolean hasEnoughSpaceAvailable, String... filesToUpload) {
+ Log_OC.d(this, "onCheckAvailableSpaceFinish");
+
+ if (hasEnoughSpaceAvailable) {
+ File file = new File(filesToUpload[0]);
+ File renamedFile = new File(file.getParent() + PATH_SEPARATOR + FileOperationsHelper.getCapturedImageName());
+
+ if (!file.renameTo(renamedFile)) {
+ DisplayUtils.showSnackMessage(getActivity(), "Fail to upload taken image!");
+ return;
+ }
+
+ requestUploadOfFilesFromFileSystem(renamedFile.getParentFile().getAbsolutePath(),
+ new String[]{renamedFile.getAbsolutePath()},
+ FileUploader.LOCAL_BEHAVIOUR_DELETE);
+ }
+ }
+ }, new String[]{fileUri.getPath()}).execute();
} else if (requestCode == REQUEST_CODE__MOVE_FILES && resultCode == RESULT_OK) {
exitSelectionMode();
final Intent fData = data;
diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java
index 9b28bbf227..50766642d1 100644
--- a/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java
+++ b/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java
@@ -499,7 +499,8 @@ public class UploadFilesActivity extends DrawerActivity implements LocalFileList
// return the list of files (success)
Intent data = new Intent();
- if (requestCode == FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA) {
+ if (requestCode == FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA ||
+ requestCode == FileDisplayActivity.REQUEST_CODE__UPLOAD_SCAN_DOC_FROM_CAMERA) {
data.putExtra(EXTRA_CHOSEN_FILES, new String[]{filesToUpload[0]});
setResult(RESULT_OK_AND_DELETE, data);
diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java
index 769a4c6cca..97b3f9f327 100644
--- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java
+++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java
@@ -62,6 +62,11 @@ public interface OCFileListBottomSheetActions {
*/
void directCameraUpload();
+ /**
+ * offers scanning document upload to the current folder.
+ */
+ void scanDocUpload();
+
/**
* open template selection for creator @link Creator
*/
diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java
index a070ac699c..84be01db91 100644
--- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java
+++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java
@@ -183,6 +183,11 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
dismiss();
});
+ binding.menuScanDocUpload.setOnClickListener(v -> {
+ actions.scanDocUpload();
+ dismiss();
+ });
+
binding.menuUploadFiles.setOnClickListener(v -> {
actions.uploadFiles();
dismiss();
diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
index e82810b122..917bcb9574 100644
--- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
+++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
@@ -42,6 +42,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.PopupMenu;
+import android.widget.Toast;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
@@ -478,6 +479,21 @@ public class OCFileListFragment extends ExtendedListFragment implements
}
}
+ @Override
+ public void scanDocUpload() {
+ FileDisplayActivity fileDisplayActivity = (FileDisplayActivity) getActivity();
+
+ if (fileDisplayActivity != null) {
+ fileDisplayActivity.getFileOperationsHelper()
+ .scanFromCamera(fileDisplayActivity, FileDisplayActivity.REQUEST_CODE__UPLOAD_SCAN_DOC_FROM_CAMERA);
+ } else {
+ Toast.makeText(getContext(),
+ getString(R.string.error_starting_direct_camera_upload),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+
@Override
public void uploadFiles() {
UploadFilesActivity.startUploadActivityForResult(
diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
index c59926c7fa..dda5b44447 100755
--- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
+++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
@@ -37,6 +37,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -68,6 +69,7 @@ import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.services.OperationsService;
+import com.owncloud.android.ui.activity.AppScanActivity;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.ExternalSiteWebView;
import com.owncloud.android.ui.activity.FileActivity;
@@ -84,6 +86,7 @@ import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.PermissionUtil;
import com.owncloud.android.utils.UriUtils;
+import com.zynksoftware.documentscanner.ui.DocumentScanner;
import org.greenrobot.eventbus.EventBus;
@@ -92,7 +95,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -123,9 +126,9 @@ public class FileOperationsHelper {
private static final String FILE_EXTENSION_WEBLOC = "webloc";
public static final int SINGLE_LINK_SIZE = 1;
- private FileActivity fileActivity;
- private CurrentAccountProvider currentAccount;
- private ConnectivityService connectivityService;
+ private final FileActivity fileActivity;
+ private final CurrentAccountProvider currentAccount;
+ private final ConnectivityService connectivityService;
/// Identifier of operation in progress which result shouldn't be lost
private long mWaitingForOpId = Long.MAX_VALUE;
@@ -145,7 +148,7 @@ public class FileOperationsHelper {
InputStreamReader fr = null;
BufferedReader br = null;
try {
- fr = new InputStreamReader(new FileInputStream(storagePath), Charset.forName("UTF-8"));
+ fr = new InputStreamReader(new FileInputStream(storagePath), StandardCharsets.UTF_8);
br = new BufferedReader(fr);
String line;
@@ -1072,6 +1075,19 @@ public class FileOperationsHelper {
}
}
+ public void scanFromCamera(Activity activity, int requestCode) {
+ DocumentScanner.Configuration configuration = new DocumentScanner.Configuration();
+ configuration.setImageType(Bitmap.CompressFormat.PNG);
+ DocumentScanner.INSTANCE.init(activity, configuration);
+
+ Intent scanIntent = new Intent(activity, AppScanActivity.class);
+ if (PermissionUtil.checkSelfPermission(activity, Manifest.permission.CAMERA)) {
+ activity.startActivityForResult(scanIntent, requestCode);
+ } else {
+ PermissionUtil.requestCameraPermission(activity);
+ }
+ }
+
public static File createImageFile(Activity activity) {
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
diff --git a/src/main/res/drawable/ic_scan_document.xml b/src/main/res/drawable/ic_scan_document.xml
new file mode 100644
index 0000000000..780fa2153f
--- /dev/null
+++ b/src/main/res/drawable/ic_scan_document.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml b/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml
index d11602e4e8..06440c2e51 100644
--- a/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml
+++ b/src/main/res/layout/file_list_actions_bottom_sheet_fragment.xml
@@ -125,6 +125,36 @@
+
+
+
+
+
+
+
+ android:background="@color/list_divider_background" />
Add to %1$s
Upload files
Upload from camera
+ Scan document from camera
Upload content from other apps
Create new folder
Virus detected. Upload cannot be completed!
@@ -803,6 +804,7 @@
Close
Failed to load details
Error starting camera
+ Error using document scanning
source folder is read-only; file will only be uploaded
kept in original folder, as it is readonly
Login via QR code