浏览代码

Move optional appscan feature to a separate module to avoid duplicating code between variants

This now uses a AppScanOptionalFeature injected interface that is satisfied by each module,
reducing the duplicated code to a minimum

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Álvaro Brey 2 年之前
父节点
当前提交
1917198305
共有 29 个文件被更改,包括 417 次插入379 次删除
  1. 4 4
      app/build.gradle
  2. 34 0
      app/src/generic/java/com/nextcloud/client/di/VariantModule.kt
  3. 0 42
      app/src/generic/java/com/owncloud/android/ui/activity/AppScanActivity.kt
  4. 37 0
      app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt
  5. 0 80
      app/src/gplay/java/com/owncloud/android/ui/activity/AppScanActivity.kt
  6. 37 0
      app/src/huawei/java/com/nextcloud/client/di/VariantModule.kt
  7. 0 77
      app/src/huawei/java/com/owncloud/android/ui/activity/AppScanActivity.kt
  8. 1 0
      app/src/main/java/com/nextcloud/client/di/AppComponent.java
  9. 45 0
      app/src/main/java/com/nextcloud/client/documentscan/AppScanOptionalFeature.kt
  10. 10 7
      app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt
  11. 0 40
      app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
  12. 1 2
      app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java
  13. 8 4
      app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java
  14. 6 1
      app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialogFragment.kt
  15. 0 1
      app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  16. 0 1
      app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt
  17. 37 0
      app/src/qa/java/com/nextcloud/client/di/VariantModule.kt
  18. 0 77
      app/src/qa/java/com/owncloud/android/ui/activity/AppScanActivity.kt
  19. 34 0
      app/src/versionDev/java/com/nextcloud/client/di/VariantModule.kt
  20. 0 41
      app/src/versionDev/java/com/owncloud/android/ui/activity/AppScanActivity.kt
  21. 1 0
      appscan/.gitignore
  22. 41 0
      appscan/build.gradle
  23. 0 0
      appscan/consumer-rules.pro
  24. 21 0
      appscan/proguard-rules.pro
  25. 29 0
      appscan/src/main/AndroidManifest.xml
  26. 67 0
      appscan/src/main/java/com/nextcloud/appscan/AppScanActivity.kt
  27. 1 2
      appscan/src/main/java/com/nextcloud/appscan/ScanPageContract.kt
  28. 2 0
      build.gradle
  29. 1 0
      settings.gradle

+ 4 - 4
app/build.gradle

@@ -244,7 +244,7 @@ dependencies {
     implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     implementation 'com.google.android.material:material:1.8.0'
     implementation 'com.jakewharton:disklrucache:2.0.2'
-    implementation 'androidx.appcompat:appcompat:1.6.0'
+    implementation "androidx.appcompat:appcompat:$appCompatVersion"
     implementation 'androidx.webkit:webkit:1.5.0'
     implementation 'androidx.cardview:cardview:1.0.0'
     implementation 'androidx.exifinterface:exifinterface:1.3.5'
@@ -276,9 +276,9 @@ dependencies {
     implementation "com.github.nextcloud-deps.hwsecurity:hwsecurity-fido2:$fidoVersion"
 
     // document scanner not available on FDroid (generic) due to OpenCV binaries
-    gplayImplementation "com.github.zynkware:Document-Scanning-Android-SDK:$documentScannerVersion"
-    huaweiImplementation "com.github.zynkware:Document-Scanning-Android-SDK:$documentScannerVersion"
-    qaImplementation "com.github.zynkware:Document-Scanning-Android-SDK:$documentScannerVersion"
+    gplayImplementation project(':appscan')
+    huaweiImplementation project(':appscan')
+    qaImplementation project(':appscan')
 
     spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0'
     spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.7'

+ 34 - 0
app/src/generic/java/com/nextcloud/client/di/VariantModule.kt

@@ -0,0 +1,34 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2919 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.di
+
+import com.nextcloud.client.documentscan.AppScanOptionalFeature
+import dagger.Module
+import dagger.Provides
+import dagger.Reusable
+
+@Module
+internal class VariantModule {
+    @Provides
+    @Reusable
+    fun scanOptionalFeature(): AppScanOptionalFeature {
+        return AppScanOptionalFeature.Stub
+    }
+}

+ 0 - 42
app/src/generic/java/com/owncloud/android/ui/activity/AppScanActivity.kt

@@ -1,42 +0,0 @@
-/*
- *
- * 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 com.owncloud.android.lib.common.utils.Log_OC
-
-class AppScanActivity {
-    // stub
-    companion object {
-        private val TAG = AppScanActivity::class.simpleName
-
-        @JvmStatic
-        val enabled: Boolean = false
-
-        @JvmStatic
-        fun scanFromCamera(activity: Activity, requestcode: Int) {
-            // stub
-            Log_OC.w(TAG, "scanFromCamera called in stub implementation")
-        }
-    }
-}

+ 37 - 0
app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt

@@ -0,0 +1,37 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2919 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.di
+
+import com.nextcloud.appscan.ScanPageContract
+import com.nextcloud.client.documentscan.AppScanOptionalFeature
+import dagger.Module
+import dagger.Provides
+import dagger.Reusable
+
+@Module
+internal class VariantModule {
+    @Provides
+    @Reusable
+    fun scanOptionalFeature(): AppScanOptionalFeature {
+        return object : AppScanOptionalFeature() {
+            override fun getScanContract() = ScanPageContract()
+        }
+    }
+}

+ 0 - 80
app/src/gplay/java/com/owncloud/android/ui/activity/AppScanActivity.kt

@@ -1,80 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Álvaro Brey Vilas
- * Copyright (C) 2022 Álvaro Brey Vilas
- * Copyright (C) 2022 Nextcloud GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.activity
-
-import android.Manifest
-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.owncloud.android.utils.PermissionUtil
-import com.zynksoftware.documentscanner.ScanActivity
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.model.ScannerResults
-import com.zynksoftware.documentscanner.ui.DocumentScanner
-
-@Suppress("unused")
-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(
-            EXTRA_FILE,
-            scannerResults.transformedImageFile?.absolutePath ?: scannerResults.croppedImageFile?.absolutePath
-        )
-
-        setResult(Activity.RESULT_OK, intent)
-        finish()
-    }
-
-    override fun onClose() {
-        finish()
-    }
-
-    companion object {
-        @JvmStatic
-        val enabled: Boolean = true
-
-        const val EXTRA_FILE = "file"
-
-        @JvmStatic
-        fun scanFromCamera(activity: Activity, requestCode: Int) {
-            DocumentScanner.init(activity)
-            val scanIntent = Intent(activity, AppScanActivity::class.java)
-            if (PermissionUtil.checkSelfPermission(activity, Manifest.permission.CAMERA)) {
-                activity.startActivityForResult(scanIntent, requestCode)
-            } else {
-                PermissionUtil.requestCameraPermission(activity, PermissionUtil.PERMISSIONS_SCAN_DOCUMENT)
-            }
-        }
-    }
-}

+ 37 - 0
app/src/huawei/java/com/nextcloud/client/di/VariantModule.kt

@@ -0,0 +1,37 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2919 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.di
+
+import com.nextcloud.appscan.ScanPageContract
+import com.nextcloud.client.documentscan.AppScanOptionalFeature
+import dagger.Module
+import dagger.Provides
+import dagger.Reusable
+
+@Module
+internal class VariantModule {
+    @Provides
+    @Reusable
+    fun scanOptionalFeature(): AppScanOptionalFeature {
+        return object : AppScanOptionalFeature() {
+            override fun getScanContract() = ScanPageContract()
+        }
+    }
+}

+ 0 - 77
app/src/huawei/java/com/owncloud/android/ui/activity/AppScanActivity.kt

@@ -1,77 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Álvaro Brey Vilas
- * Copyright (C) 2022 Álvaro Brey Vilas
- * Copyright (C) 2022 Nextcloud GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.activity
-
-import android.Manifest
-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.owncloud.android.utils.PermissionUtil
-import com.zynksoftware.documentscanner.ScanActivity
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.model.ScannerResults
-import com.zynksoftware.documentscanner.ui.DocumentScanner
-
-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()
-    }
-
-    companion object {
-        @JvmStatic
-        val enabled: Boolean = true
-
-        @JvmStatic
-        fun scanFromCamera(activity: Activity, requestCode: Int) {
-            DocumentScanner.init(activity)
-            val scanIntent = Intent(activity, AppScanActivity::class.java)
-            if (PermissionUtil.checkSelfPermission(activity, Manifest.permission.CAMERA)) {
-                activity.startActivityForResult(scanIntent, requestCode)
-            } else {
-                PermissionUtil.requestCameraPermission(activity, PermissionUtil.PERMISSIONS_SCAN_DOCUMENT)
-            }
-        }
-    }
-}

+ 1 - 0
app/src/main/java/com/nextcloud/client/di/AppComponent.java

@@ -55,6 +55,7 @@ import dagger.android.support.AndroidSupportInjectionModule;
     ThemeModule.class,
     DatabaseModule.class,
     DispatcherModule.class,
+    VariantModule.class
 })
 @Singleton
 public interface AppComponent {

+ 45 - 0
app/src/main/java/com/nextcloud/client/documentscan/AppScanOptionalFeature.kt

@@ -0,0 +1,45 @@
+/*
+ * Nextcloud Android client application
+ *
+ *  @author Álvaro Brey
+ *  Copyright (C) 2023 Álvaro Brey
+ *  Copyright (C) 2023 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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.nextcloud.client.documentscan
+
+import androidx.activity.result.contract.ActivityResultContract
+
+abstract class AppScanOptionalFeature {
+    /**
+     * Check [isAvailable] before calling this method.
+     */
+    abstract fun getScanContract(): ActivityResultContract<Unit, String?>
+    open val isAvailable: Boolean = true
+
+    /**
+     * Use this in variants where the feature is not available
+     */
+    @Suppress("unused") // used only in some variants
+    object Stub : AppScanOptionalFeature() {
+        override fun getScanContract(): ActivityResultContract<Unit, String?> {
+            throw UnsupportedOperationException("Document scan is not available")
+        }
+
+        override val isAvailable = false
+    }
+}

+ 10 - 7
app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt

@@ -26,6 +26,7 @@ import android.os.Bundle
 import android.view.Menu
 import android.view.MenuInflater
 import android.view.MenuItem
+import androidx.activity.result.ActivityResultLauncher
 import androidx.appcompat.app.AlertDialog
 import androidx.core.view.MenuProvider
 import androidx.lifecycle.ViewModelProvider
@@ -39,7 +40,6 @@ import com.owncloud.android.databinding.ActivityDocumentScanBinding
 import com.owncloud.android.databinding.DialogScanExportTypeBinding
 import com.owncloud.android.ui.activity.ToolbarActivity
 import com.owncloud.android.utils.theme.ViewThemeUtils
-import com.zynksoftware.documentscanner.ui.DocumentScanner
 import javax.inject.Inject
 
 class DocumentScanActivity : ToolbarActivity(), Injectable {
@@ -53,17 +53,22 @@ class DocumentScanActivity : ToolbarActivity(), Injectable {
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var appScanOptionalFeature: AppScanOptionalFeature
+
     lateinit var binding: ActivityDocumentScanBinding
 
     lateinit var viewModel: DocumentScanViewModel
 
-    private val scanPage = registerForActivityResult(ScanPageContract()) { result ->
-        viewModel.onScanPageResult(result)
-    }
+    private var scanPage: ActivityResultLauncher<Unit>? = null
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        scanPage = registerForActivityResult(appScanOptionalFeature.getScanContract()) { result ->
+            viewModel.onScanPageResult(result)
+        }
+
         val folder = intent.extras?.getString(EXTRA_FOLDER)
         require(folder != null) { "Folder must be provided for upload" }
 
@@ -72,8 +77,6 @@ class DocumentScanActivity : ToolbarActivity(), Injectable {
 
         setupViews()
 
-        DocumentScanner.init(this) // TODO this should go back to AppScanActivity, it needs the lib!
-
         observeState()
     }
 
@@ -203,7 +206,7 @@ class DocumentScanActivity : ToolbarActivity(), Injectable {
     private fun startPageScan() {
         logger.d(TAG, "startPageScan() called")
         viewModel.onScanRequestHandled()
-        scanPage.launch(Unit)
+        scanPage!!.launch(Unit)
     }
 
     companion object {

+ 0 - 40
app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -430,15 +430,6 @@ public class FileDisplayActivity extends FileActivity
                     // toggle on is save since this is the only scenario this code gets accessed
                 }
                 break;
-            case PermissionUtil.PERMISSIONS_SCAN_DOCUMENT:
-                // If request is cancelled, result arrays are empty.
-                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                    // permission was granted
-                    AppScanActivity.scanFromCamera(
-                        this,
-                        FileDisplayActivity.REQUEST_CODE__UPLOAD_SCAN_DOC_FROM_CAMERA);
-                }
-                break;
             case PermissionUtil.PERMISSIONS_CAMERA:
                 // If request is cancelled, result arrays are empty.
                 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@@ -854,37 +845,6 @@ 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)) {
-            // TODO replace with upload PDF from DocumentScanActivity
-
-            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;

+ 1 - 2
app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java

@@ -490,8 +490,7 @@ 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 ||
-                requestCode == FileDisplayActivity.REQUEST_CODE__UPLOAD_SCAN_DOC_FROM_CAMERA) {
+            if (requestCode == FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA) {
                 data.putExtra(EXTRA_CHOSEN_FILES, new String[]{filesToUpload[0]});
                 setResult(RESULT_OK_AND_DELETE, data);
 

+ 8 - 4
app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java

@@ -28,6 +28,7 @@ import com.google.gson.Gson;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.device.DeviceInfo;
 import com.nextcloud.client.di.Injectable;
+import com.nextcloud.client.documentscan.AppScanOptionalFeature;
 import com.nextcloud.utils.EditorUtils;
 import com.owncloud.android.R;
 import com.owncloud.android.databinding.FileListActionsBottomSheetCreatorBinding;
@@ -38,7 +39,6 @@ import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.Creator;
 import com.owncloud.android.lib.common.DirectEditing;
 import com.owncloud.android.lib.resources.status.OCCapability;
-import com.owncloud.android.ui.activity.AppScanActivity;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.utils.MimeTypeUtil;
 import com.owncloud.android.utils.theme.ThemeUtils;
@@ -59,6 +59,8 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog implements In
     private final ViewThemeUtils viewThemeUtils;
     private final EditorUtils editorUtils;
 
+    private final AppScanOptionalFeature appScanOptionalFeature;
+
 
     public OCFileListBottomSheetDialog(FileActivity fileActivity,
                                        OCFileListBottomSheetActions actions,
@@ -67,7 +69,8 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog implements In
                                        OCFile file,
                                        ThemeUtils themeUtils,
                                        ViewThemeUtils viewThemeUtils,
-                                       EditorUtils editorUtils) {
+                                       EditorUtils editorUtils,
+                                       AppScanOptionalFeature appScanOptionalFeature) {
         super(fileActivity);
         this.actions = actions;
         this.fileActivity = fileActivity;
@@ -77,6 +80,7 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog implements In
         this.themeUtils = themeUtils;
         this.viewThemeUtils = viewThemeUtils;
         this.editorUtils = editorUtils;
+        this.appScanOptionalFeature = appScanOptionalFeature;
     }
 
     @Override
@@ -189,12 +193,12 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog implements In
             dismiss();
         });
 
-        if(AppScanActivity.getEnabled()) {
+        if (appScanOptionalFeature.isAvailable()) {
             binding.menuScanDocUpload.setOnClickListener(v -> {
                 actions.scanDocUpload();
                 dismiss();
             });
-        }else {
+        } else {
             binding.menuScanDocUpload.setVisibility(View.GONE);
         }
 

+ 6 - 1
app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialogFragment.kt

@@ -27,6 +27,7 @@ import androidx.fragment.app.DialogFragment
 import com.nextcloud.client.account.User
 import com.nextcloud.client.device.DeviceInfo
 import com.nextcloud.client.di.Injectable
+import com.nextcloud.client.documentscan.AppScanOptionalFeature
 import com.nextcloud.utils.EditorUtils
 import com.owncloud.android.datamodel.OCFile
 import com.owncloud.android.ui.activity.FileActivity
@@ -51,6 +52,9 @@ class OCFileListBottomSheetDialogFragment(
     @Inject
     lateinit var editorUtils: EditorUtils
 
+    @Inject
+    lateinit var appScanOptionalFeature: AppScanOptionalFeature
+
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         return OCFileListBottomSheetDialog(
             fileActivity,
@@ -60,7 +64,8 @@ class OCFileListBottomSheetDialogFragment(
             file,
             themeUtils,
             viewThemeUtils,
-            editorUtils
+            editorUtils,
+            appScanOptionalFeature
         )
     }
 }

+ 0 - 1
app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -77,7 +77,6 @@ import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
 import com.owncloud.android.lib.resources.files.SearchRemoteOperation;
 import com.owncloud.android.lib.resources.files.ToggleFavoriteRemoteOperation;
 import com.owncloud.android.lib.resources.status.OCCapability;
-import com.owncloud.android.ui.activity.AppScanActivity;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FolderPickerActivity;

+ 0 - 1
app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt

@@ -52,7 +52,6 @@ object PermissionUtil {
     const val PERMISSIONS_CAMERA = 5
     const val PERMISSIONS_READ_CALENDAR_AUTOMATIC = 6
     const val PERMISSIONS_WRITE_CALENDAR = 7
-    const val PERMISSIONS_SCAN_DOCUMENT = 6
 
     const val REQUEST_CODE_MANAGE_ALL_FILES = 19203
 

+ 37 - 0
app/src/qa/java/com/nextcloud/client/di/VariantModule.kt

@@ -0,0 +1,37 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2919 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.di
+
+import com.nextcloud.appscan.ScanPageContract
+import com.nextcloud.client.documentscan.AppScanOptionalFeature
+import dagger.Module
+import dagger.Provides
+import dagger.Reusable
+
+@Module
+internal class VariantModule {
+    @Provides
+    @Reusable
+    fun scanOptionalFeature(): AppScanOptionalFeature {
+        return object : AppScanOptionalFeature() {
+            override fun getScanContract() = ScanPageContract()
+        }
+    }
+}

+ 0 - 77
app/src/qa/java/com/owncloud/android/ui/activity/AppScanActivity.kt

@@ -1,77 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Álvaro Brey Vilas
- * Copyright (C) 2022 Álvaro Brey Vilas
- * Copyright (C) 2022 Nextcloud GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.activity
-
-import android.Manifest
-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.owncloud.android.utils.PermissionUtil
-import com.zynksoftware.documentscanner.ScanActivity
-import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
-import com.zynksoftware.documentscanner.model.ScannerResults
-import com.zynksoftware.documentscanner.ui.DocumentScanner
-
-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()
-    }
-
-    companion object {
-        @JvmStatic
-        val enabled: Boolean = true
-
-        @JvmStatic
-        fun scanFromCamera(activity: Activity, requestCode: Int) {
-            DocumentScanner.init(activity)
-            val scanIntent = Intent(activity, AppScanActivity::class.java)
-            if (PermissionUtil.checkSelfPermission(activity, Manifest.permission.CAMERA)) {
-                activity.startActivityForResult(scanIntent, requestCode)
-            } else {
-                PermissionUtil.requestCameraPermission(activity, PermissionUtil.PERMISSIONS_SCAN_DOCUMENT)
-            }
-        }
-    }
-}

+ 34 - 0
app/src/versionDev/java/com/nextcloud/client/di/VariantModule.kt

@@ -0,0 +1,34 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2919 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.di
+
+import com.nextcloud.client.documentscan.AppScanOptionalFeature
+import dagger.Module
+import dagger.Provides
+import dagger.Reusable
+
+@Module
+internal class VariantModule {
+    @Provides
+    @Reusable
+    fun scanOptionalFeature(): AppScanOptionalFeature {
+        return AppScanOptionalFeature.Stub
+    }
+}

+ 0 - 41
app/src/versionDev/java/com/owncloud/android/ui/activity/AppScanActivity.kt

@@ -1,41 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Álvaro Brey Vilas
- * Copyright (C) 2022 Álvaro Brey Vilas
- * Copyright (C) 2022 Nextcloud GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU 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 com.owncloud.android.lib.common.utils.Log_OC
-
-class AppScanActivity {
-    // stub
-    companion object {
-        private val TAG = AppScanActivity::class.simpleName
-
-        @JvmStatic
-        val enabled: Boolean = false
-
-        @JvmStatic
-        fun scanFromCamera(activity: Activity, requestcode: Int) {
-            // stub
-            Log_OC.w(TAG, "scanFromCamera called in stub implementation")
-        }
-    }
-}

+ 1 - 0
appscan/.gitignore

@@ -0,0 +1 @@
+/build

+ 41 - 0
appscan/build.gradle

@@ -0,0 +1,41 @@
+buildscript {
+    dependencies {
+        classpath "com.android.tools.build:gradle:$androidPluginVersion"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+    namespace 'com.nextcloud.appscan'
+    compileSdk 33
+
+    defaultConfig {
+        minSdk 21
+        targetSdk 33
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+}
+
+dependencies {
+    implementation "androidx.appcompat:appcompat:$appCompatVersion"
+    implementation "com.github.zynkware:Document-Scanning-Android-SDK:$documentScannerVersion"
+}

+ 0 - 0
appscan/consumer-rules.pro


+ 21 - 0
appscan/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 29 - 0
appscan/src/main/AndroidManifest.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Nextcloud Android client application
+  ~
+  ~  @author Álvaro Brey
+  ~  Copyright (C) 2023 Álvaro Brey
+  ~  Copyright (C) 2023 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
+  ~ License as published by the Free Software Foundation; either
+  ~ version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+  ~
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application>
+        <activity android:name=".AppScanActivity" />
+    </application>
+
+</manifest>

+ 67 - 0
appscan/src/main/java/com/nextcloud/appscan/AppScanActivity.kt

@@ -0,0 +1,67 @@
+/*
+ * Nextcloud Android client application
+ *
+ *  @author Álvaro Brey
+ *  Copyright (C) 2023 Álvaro Brey
+ *  Copyright (C) 2023 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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.nextcloud.appscan
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import com.zynksoftware.documentscanner.ScanActivity
+import com.zynksoftware.documentscanner.model.DocumentScannerErrorModel
+import com.zynksoftware.documentscanner.model.ScannerResults
+import com.zynksoftware.documentscanner.ui.DocumentScanner
+
+@Suppress("unused")
+class AppScanActivity : ScanActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        DocumentScanner.init(this)
+        addFragmentContentLayout()
+    }
+
+    override fun onError(error: DocumentScannerErrorModel) {
+        // TODO pass this from app somehow?
+    }
+
+    override fun onSuccess(scannerResults: ScannerResults) {
+        val intent = Intent()
+
+        intent.putExtra(
+            EXTRA_FILE,
+            scannerResults.transformedImageFile?.absolutePath ?: scannerResults.croppedImageFile?.absolutePath
+        )
+
+        setResult(Activity.RESULT_OK, intent)
+        finish()
+    }
+
+    override fun onClose() {
+        finish()
+    }
+
+    companion object {
+        @JvmStatic
+        val enabled: Boolean = true
+
+        const val EXTRA_FILE = "file"
+    }
+}

+ 1 - 2
app/src/main/java/com/nextcloud/client/documentscan/ScanPageContract.kt → appscan/src/main/java/com/nextcloud/appscan/ScanPageContract.kt

@@ -20,13 +20,12 @@
  *
  */
 
-package com.nextcloud.client.documentscan
+package com.nextcloud.appscan
 
 import android.app.Activity
 import android.content.Context
 import android.content.Intent
 import androidx.activity.result.contract.ActivityResultContract
-import com.owncloud.android.ui.activity.AppScanActivity
 
 class ScanPageContract : ActivityResultContract<Unit, String?>() {
     override fun createIntent(context: Context, input: Unit): Intent {

+ 2 - 0
build.gradle

@@ -1,5 +1,7 @@
 buildscript {
     ext {
+        androidPluginVersion = '7.4.0'
+        appCompatVersion = '1.6.0'
         jacoco_version = '0.8.8'
         kotlin_version = '1.7.22'
         androidxTestVersion = "1.4.0"

+ 1 - 0
settings.gradle

@@ -1,6 +1,7 @@
 rootProject.name = 'Nextcloud'
 
 include ':app'
+include ':appscan'
 
 //includeBuild('../android-common') {
 //    dependencySubstitution {