mirror of
https://github.com/nextcloud/android.git
synced 2024-11-21 12:45:32 +03:00
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:
parent
ee000f7b4e
commit
9e7a9b410f
15 changed files with 205 additions and 7 deletions
|
@ -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'
|
||||
|
||||
|
|
9
drawable_resources/ic_scan_document.svg
Normal file
9
drawable_resources/ic_scan_document.svg
Normal 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 |
|
@ -334,6 +334,11 @@ public class DialogFragmentIT extends AbstractIT {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanDocUpload() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showTemplate(Creator creator, String headline) {
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -183,6 +183,11 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
|
|||
dismiss();
|
||||
});
|
||||
|
||||
binding.menuScanDocUpload.setOnClickListener(v -> {
|
||||
actions.scanDocUpload();
|
||||
dismiss();
|
||||
});
|
||||
|
||||
binding.menuUploadFiles.setOnClickListener(v -> {
|
||||
actions.uploadFiles();
|
||||
dismiss();
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
13
src/main/res/drawable/ic_scan_document.xml
Normal file
13
src/main/res/drawable/ic_scan_document.xml
Normal 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>
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue