瀏覽代碼

Allow uploading document as multiple images instead of single PDF

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Álvaro Brey 2 年之前
父節點
當前提交
cd51c4f5df

+ 34 - 13
app/src/main/java/com/nextcloud/client/documentscan/DocumentScanActivity.kt

@@ -23,13 +23,13 @@
 package com.nextcloud.client.documentscan
 
 import android.os.Bundle
-import android.util.Log
 import android.view.Menu
 import android.view.MenuInflater
 import android.view.MenuItem
 import androidx.core.view.MenuProvider
 import androidx.lifecycle.ViewModelProvider
 import androidx.recyclerview.widget.GridLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.nextcloud.client.di.Injectable
 import com.nextcloud.client.di.ViewModelFactory
 import com.nextcloud.client.logger.Logger
@@ -128,17 +128,22 @@ class DocumentScanActivity : ToolbarActivity(), Injectable {
     }
 
     private fun handleState(state: DocumentScanViewModel.UIState) {
+        logger.d(TAG, "handleState: called with $state")
         when (state) {
-            is DocumentScanViewModel.UIState.NormalState -> {
-                val pageList = state.pageList
-                Log.d(
-                    TAG,
-                    "handleState: NormalState with ${pageList.size} pages, isProcessing: ${state.isProcessing}"
-                )
-                updateRecycler(pageList)
-                updateButtonsEnabled(state.isProcessing)
-                if (state.shouldRequestScan) {
-                    startPageScan()
+            is DocumentScanViewModel.UIState.BaseState -> when (state) {
+                is DocumentScanViewModel.UIState.NormalState -> {
+                    updateButtonsEnabled(true)
+                    val pageList = state.pageList
+                    updateRecycler(pageList)
+                    if (state.shouldRequestScan) {
+                        startPageScan()
+                    }
+                }
+                is DocumentScanViewModel.UIState.RequestExportState -> {
+                    updateButtonsEnabled(false)
+                    if (state.shouldRequestExportType) {
+                        showExportDialog()
+                    }
                 }
             }
             DocumentScanViewModel.UIState.DoneState -> {
@@ -147,6 +152,22 @@ class DocumentScanActivity : ToolbarActivity(), Injectable {
         }
     }
 
+    private fun showExportDialog() {
+        // TODO better dialog
+        MaterialAlertDialogBuilder(this)
+            .setTitle(R.string.document_scan_export_dialog_title)
+            .setPositiveButton(R.string.document_scan_export_dialog_pdf) { _, _ ->
+                viewModel.onExportTypeSelected(DocumentScanViewModel.ExportType.PDF)
+            }
+            .setNeutralButton(R.string.document_scan_export_dialog_images) { _, _ ->
+                viewModel.onExportTypeSelected(DocumentScanViewModel.ExportType.IMAGES)
+            }
+            .setNegativeButton(R.string.common_cancel) { _, _ ->
+                viewModel.onExportCanceled()
+            }
+            .show()
+    }
+
     private fun updateRecycler(pageList: List<String>) {
         if (binding.pagesRecycler.adapter == null) {
             binding.pagesRecycler.adapter = DocumentPageListAdapter()
@@ -154,8 +175,8 @@ class DocumentScanActivity : ToolbarActivity(), Injectable {
         (binding.pagesRecycler.adapter as? DocumentPageListAdapter)?.submitList(pageList)
     }
 
-    private fun updateButtonsEnabled(processing: Boolean) {
-        binding.fab.isEnabled = !processing
+    private fun updateButtonsEnabled(enabled: Boolean) {
+        binding.fab.isEnabled = enabled
     }
 
     private fun startPageScan() {

+ 93 - 22
app/src/main/java/com/nextcloud/client/documentscan/DocumentScanViewModel.kt

@@ -31,7 +31,12 @@ import com.nextcloud.client.account.CurrentAccountProvider
 import com.nextcloud.client.di.IoDispatcher
 import com.nextcloud.client.jobs.BackgroundJobManager
 import com.nextcloud.client.logger.Logger
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.files.services.FileUploader
+import com.owncloud.android.files.services.NameCollisionPolicy
+import com.owncloud.android.operations.UploadFileOperation
 import com.owncloud.android.ui.helpers.FileOperationsHelper
+import com.owncloud.android.utils.MimeType
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -51,15 +56,21 @@ class DocumentScanViewModel @Inject constructor(
     }
 
     sealed interface UIState {
-        data class NormalState(
-            val pageList: List<String> = emptyList(),
-            val isProcessing: Boolean = false,
-            val shouldRequestScan: Boolean = false
-        ) : UIState {
+        sealed class BaseState(val pageList: List<String>) : UIState {
             val isEmpty: Boolean
                 get() = pageList.isEmpty()
         }
 
+        class NormalState(
+            pageList: List<String> = emptyList(),
+            val shouldRequestScan: Boolean = false
+        ) : BaseState(pageList)
+
+        class RequestExportState(
+            pageList: List<String> = emptyList(),
+            val shouldRequestExportType: Boolean = true
+        ) : BaseState(pageList)
+
         object DoneState : UIState
     }
 
@@ -80,11 +91,10 @@ class DocumentScanViewModel @Inject constructor(
 
         viewModelScope.launch(ioDispatcher) {
             if (result != null) {
-                _uiState.postValue(state.copy(pageList = state.pageList, isProcessing = true))
                 val newPath = renameCapturedImage(result)
                 val pageList = state.pageList.toMutableList()
                 pageList.add(newPath)
-                _uiState.postValue(UIState.NormalState(pageList, isProcessing = false))
+                _uiState.postValue(UIState.NormalState(pageList))
             } else {
                 // TODO
             }
@@ -113,41 +123,102 @@ class DocumentScanViewModel @Inject constructor(
         val state = uiState.value
         require(state is UIState.NormalState)
 
-        _uiState.postValue(state.copy(shouldRequestScan = false))
+        _uiState.postValue(UIState.NormalState(state.pageList, shouldRequestScan = false))
     }
 
     fun onAddPageClicked() {
         val state = uiState.value
         require(state is UIState.NormalState)
         if (!state.shouldRequestScan) {
-            _uiState.postValue(state.copy(shouldRequestScan = true))
+            _uiState.postValue(UIState.NormalState(state.pageList, shouldRequestScan = true))
         }
     }
 
     fun onClickDone() {
-        // TODO dialog to choose pictures or PDF
+        val state = _uiState.value
+        if (state is UIState.BaseState && !state.isEmpty) {
+            _uiState.postValue(UIState.RequestExportState(state.pageList))
+        }
+    }
+
+    fun setUploadFolder(folder: String) {
+        this.uploadFolder = folder
+    }
+
+    fun onRequestTypeHandled() {
+        val state = _uiState.value
+        require(state is UIState.RequestExportState)
+        _uiState.postValue(UIState.RequestExportState(state.pageList, false))
+    }
+
+    fun onExportTypeSelected(exportType: ExportType) {
+        val state = _uiState.value
+        require(state is UIState.RequestExportState)
+        when (exportType) {
+            ExportType.PDF -> {
+                exportToPdf(state.pageList)
+            }
+            ExportType.IMAGES -> {
+                exportToImages(state.pageList)
+            }
+        }
+        _uiState.postValue(UIState.DoneState)
+    }
+
+    private fun exportToPdf(pageList: List<String>) {
         val genPath =
             getApplication<Application>().cacheDir.path + File.separator + FileOperationsHelper.getTimestampedFileName(
                 ".pdf"
             )
+        backgroundJobManager.startPdfGenerateAndUploadWork(
+            currentAccountProvider.user,
+            uploadFolder!!,
+            pageList,
+            genPath
+        )
+        // after job is started, finish the application.
+        _uiState.postValue(UIState.DoneState)
+    }
+
+    private fun exportToImages(pageList: List<String>) {
+        val uploadPaths = pageList.map {
+            uploadFolder + OCFile.PATH_SEPARATOR + File(it).name
+        }.toTypedArray()
+
+        val mimetypes = pageList.map {
+            MimeType.JPEG
+        }.toTypedArray()
+
+        FileUploader.uploadNewFile(
+            getApplication(),
+            currentAccountProvider.user,
+            pageList.toTypedArray(),
+            uploadPaths,
+            mimetypes,
+            FileUploader.LOCAL_BEHAVIOUR_DELETE,
+            true,
+            UploadFileOperation.CREATED_BY_USER,
+            false,
+            false,
+            NameCollisionPolicy.ASK_USER
+        )
+    }
+
+    fun onExportCanceled() {
         val state = _uiState.value
-        if (state is UIState.NormalState && !state.isEmpty && !state.isProcessing) {
-            backgroundJobManager.startPdfGenerateAndUploadWork(
-                currentAccountProvider.user,
-                uploadFolder!!,
-                state.pageList,
-                genPath
-            )
-            // after job is started, finish the application.
-            _uiState.postValue(UIState.DoneState)
+        if (state is UIState.BaseState) {
+            _uiState.postValue(UIState.NormalState(state.pageList))
         }
     }
 
-    fun setUploadFolder(folder: String) {
-        this.uploadFolder = folder
-    }
+    private
 
     companion object {
         private const val TAG = "DocumentScanViewModel"
     }
+
+    enum class ExportType {
+        PDF,
+        IMAGES
+    }
 }

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -1071,4 +1071,7 @@
     <string name="document_scan_pdf_generation_in_progress">Generating PDF...</string>
     <string name="error_starting_doc_scan">Error starting document scan</string>
     <string name="document_scan_pdf_generation_failed">PDF generation failed</string>
+    <string name="document_scan_export_dialog_title">Choose export type</string>
+    <string name="document_scan_export_dialog_pdf">PDF</string>
+    <string name="document_scan_export_dialog_images">Multiple images</string>
 </resources>