Add scan document feature

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
Signed-off-by: thelittlefireman <thelittlefireman@users.noreply.github.com>
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
thelittlefireman 2021-03-28 18:45:44 +02:00 committed by tobiasKaminsky
parent ee000f7b4e
commit 9e7a9b410f
No known key found for this signature in database
GPG key ID: 0E00D4D47D0C5AF7
15 changed files with 205 additions and 7 deletions

View file

@ -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'

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" height="100%" version="1.1"
viewBox="0 0 24 24" width="100%" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
<g transform="matrix(1.1,0,0,1.1,-1.2,-1.2)">
<path style="fill-rule:nonzero;"
d="M22,20.333C22,21.248 21.248,22 20.333,22L17,22L17,20.333L20.333,20.333L20.333,17L22,17L22,20.333ZM3.667,22C2.752,22 2,21.248 2,20.333L2,17L3.667,17L3.667,20.333L7,20.333L7,22L3.667,22ZM6.953,5.5C6.695,5.5 6.493,5.704 6.493,5.964L6.493,18.036C6.493,18.296 6.695,18.5 6.953,18.5L17.056,18.5C17.313,18.5 17.515,18.296 17.515,18.036L17.515,8.286L14.76,5.5L6.953,5.5ZM8.33,15.714L12.004,15.714L12.004,16.643L8.33,16.643L8.33,15.714ZM8.33,12.929L15.678,12.929L15.678,13.857L8.33,13.857L8.33,12.929ZM8.33,10.143L12.923,10.143L12.923,11.071L8.33,11.071L8.33,10.143ZM8.33,7.357L13.841,7.357L13.841,8.286L8.33,8.286L8.33,7.357ZM2,3.667C2,2.752 2.752,2 3.667,2L7,2L7,3.667L3.667,3.667L3.667,7L2,7L2,3.667ZM20.333,2C21.248,2 22,2.752 22,3.667L22,7L20.333,7L20.333,3.667L17,3.667L17,2L20.333,2Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -334,6 +334,11 @@ public class DialogFragmentIT extends AbstractIT {
}
@Override
public void scanDocUpload() {
}
@Override
public void showTemplate(Creator creator, String headline) {

View file

@ -117,6 +117,11 @@
android:name="android.app.searchable"
android:resource="@xml/users_and_groups_searchable" />
</activity>
<activity
android:name=".ui.activity.AppScanActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.NoActionBar"
tools:ignore="LockedOrientationActivity" />
<activity
android:name=".ui.activity.ManageAccountsActivity"
android:exported="false" />

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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()
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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
*/

View file

@ -183,6 +183,11 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
dismiss();
});
binding.menuScanDocUpload.setOnClickListener(v -> {
actions.scanDocUpload();
dismiss();
});
binding.menuUploadFiles.setOnClickListener(v -> {
actions.uploadFiles();
dismiss();

View file

@ -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(

View file

@ -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);

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24"
tools:ignore="VectorRaster">
<path
android:fillColor="#FF000000"
android:fillType="nonZero"
android:pathData="M23,21.1663C23,22.1728 22.1728,23 21.1663,23L17.5,23L17.5,21.1663L21.1663,21.1663L21.1663,17.5L23,17.5L23,21.1663ZM2.8337,23C1.8272,23 1,22.1728 1,21.1663L1,17.5L2.8337,17.5L2.8337,21.1663L6.5,21.1663L6.5,23L2.8337,23ZM6.4483,4.85C6.1645,4.85 5.9423,5.0744 5.9423,5.3604L5.9423,18.6396C5.9423,18.9256 6.1645,19.15 6.4483,19.15L17.5616,19.15C17.8443,19.15 18.0665,18.9256 18.0665,18.6396L18.0665,7.9146L15.036,4.85L6.4483,4.85ZM7.963,16.0854L12.0044,16.0854L12.0044,17.1073L7.963,17.1073L7.963,16.0854ZM7.963,13.0219L16.0458,13.0219L16.0458,14.0427L7.963,14.0427L7.963,13.0219ZM7.963,9.9573L13.0153,9.9573L13.0153,10.9781L7.963,10.9781L7.963,9.9573ZM7.963,6.8927L14.0251,6.8927L14.0251,7.9146L7.963,7.9146L7.963,6.8927ZM1,2.8337C1,1.8272 1.8272,1 2.8337,1L6.5,1L6.5,2.8337L2.8337,2.8337L2.8337,6.5L1,6.5L1,2.8337ZM21.1663,1C22.1728,1 23,1.8272 23,2.8337L23,6.5L21.1663,6.5L21.1663,2.8337L17.5,2.8337L17.5,1L21.1663,1Z" />
</vector>

View file

@ -125,6 +125,36 @@
</LinearLayout>
<LinearLayout
android:id="@+id/menu_scan_doc_upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_half_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_scan_doc_upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_scan_document"
app:tint="@color/primary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/standard_margin"
android:text="@string/upload_scan_doc_upload"
android:textColor="@color/text_color"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
@ -133,7 +163,7 @@
android:layout_marginTop="@dimen/standard_half_margin"
android:layout_marginEnd="@dimen/standard_margin"
android:layout_marginBottom="@dimen/standard_half_margin"
android:background="@color/list_divider_background"/>
android:background="@color/list_divider_background" />
<LinearLayout
android:id="@+id/menu_mkdir"

View file

@ -724,6 +724,7 @@
<string name="add_to_cloud">Add to %1$s</string>
<string name="upload_files">Upload files</string>
<string name="upload_direct_camera_upload">Upload from camera</string>
<string name="upload_scan_doc_upload">Scan document from camera</string>
<string name="upload_content_from_other_apps">Upload content from other apps</string>
<string name="create_new_folder">Create new folder</string>
<string name="uploads_view_upload_status_virus_detected">Virus detected. Upload cannot be completed!</string>
@ -803,6 +804,7 @@
<string name="battery_optimization_close">Close</string>
<string name="file_details_no_content">Failed to load details</string>
<string name="error_starting_direct_camera_upload">Error starting camera</string>
<string name="error_starting_scan_doc">Error using document scanning</string>
<string name="uploader_upload_files_behaviour_not_writable">source folder is read-only; file will only be uploaded</string>
<string name="auto_upload_file_behaviour_kept_in_folder">kept in original folder, as it is readonly</string>
<string name="scanQR_description">Login via QR code</string>