diff --git a/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadFiles.kt b/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadFiles.kt index 512acc25..c944db51 100644 --- a/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadFiles.kt +++ b/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadFiles.kt @@ -28,7 +28,7 @@ data class ActionUploadFiles( private val repository = OfflineRepository() override suspend fun execute(context: Context): Entry { - val result = ContentPickerFragment.pickItems(context, MIME_TYPES) + val result = ContentPickerFragment.pickItems(context, MIME_TYPES, entry.isMultiple) if (result.isNotEmpty()) { when (entry.uploadServer) { UploadServerType.UPLOAD_TO_TASK, UploadServerType.UPLOAD_TO_PROCESS -> { diff --git a/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadMedia.kt b/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadMedia.kt index 26827a17..494bc69f 100644 --- a/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadMedia.kt +++ b/actions/src/main/kotlin/com/alfresco/content/actions/ActionUploadMedia.kt @@ -25,7 +25,7 @@ data class ActionUploadMedia( private val repository = OfflineRepository() override suspend fun execute(context: Context): Entry { - val result = ContentPickerFragment.pickItems(context, MIME_TYPES) + val result = ContentPickerFragment.pickItems(context, MIME_TYPES, entry.isMultiple) if (result.isNotEmpty()) { when (entry.uploadServer) { UploadServerType.UPLOAD_TO_TASK, UploadServerType.UPLOAD_TO_PROCESS -> { diff --git a/common/src/main/kotlin/com/alfresco/content/ContentPickerFragment.kt b/common/src/main/kotlin/com/alfresco/content/ContentPickerFragment.kt index cbbe5a6e..0875ee27 100644 --- a/common/src/main/kotlin/com/alfresco/content/ContentPickerFragment.kt +++ b/common/src/main/kotlin/com/alfresco/content/ContentPickerFragment.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine class ContentPickerFragment : Fragment() { private lateinit var requestLauncher: ActivityResultLauncher> + private lateinit var requestLauncherSingle: ActivityResultLauncher> private var onResult: CancellableContinuation>? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -18,12 +19,20 @@ class ContentPickerFragment : Fragment() { requestLauncher = registerForActivityResult(GetMultipleContents()) { onResult?.resume(it, null) } + + requestLauncherSingle = registerForActivityResult(GetSingleContent()) { + onResult?.resume(it, null) + } } - private suspend fun pickItems(mimeTypes: Array): List = + private suspend fun pickItems(mimeTypes: Array, isMultiple: Boolean): List = suspendCancellableCoroutine { continuation -> onResult = continuation - requestLauncher.launch(mimeTypes) + if (!isMultiple) { + requestLauncherSingle.launch(mimeTypes) + } else { + requestLauncher.launch(mimeTypes) + } } companion object { @@ -32,11 +41,12 @@ class ContentPickerFragment : Fragment() { suspend fun pickItems( context: Context, mimeTypes: Array, + isMultiple: Boolean = false, ): List = withFragment( context, TAG, - { it.pickItems(mimeTypes) }, + { it.pickItems(mimeTypes, isMultiple) }, { ContentPickerFragment() }, ) } diff --git a/common/src/main/kotlin/com/alfresco/content/GetSingleContent.kt b/common/src/main/kotlin/com/alfresco/content/GetSingleContent.kt new file mode 100644 index 00000000..d5333625 --- /dev/null +++ b/common/src/main/kotlin/com/alfresco/content/GetSingleContent.kt @@ -0,0 +1,69 @@ +package com.alfresco.content + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.activity.result.contract.ActivityResultContract +import androidx.annotation.CallSuper + +/** + * An ActivityResultContract similar to + * [androidx.activity.result.contract.ActivityResultContracts.GetSingleContents] + * that allows specifying multiple mimeTypes. + */ +class GetSingleContent : ActivityResultContract, List>() { + + @CallSuper + override fun createIntent(context: Context, input: Array): Intent { + return Intent(Intent.ACTION_GET_CONTENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType("*/*") + .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) + .putExtra(Intent.EXTRA_MIME_TYPES, input) + } + + override fun parseResult(resultCode: Int, intent: Intent?): List { + return if (intent == null || resultCode != Activity.RESULT_OK) { + emptyList() + } else { + getClipDataUris(intent) + } + } + + companion object { + const val MAX_FILE_SIZE = 100 + + /** + * returns true if file exceed the 100mb length otherwise false + */ + fun isFileSizeExceed(length: Long): Boolean { + val fileLength = length.div(1024L).div(1024L) + return fileLength > MAX_FILE_SIZE.minus(1).toLong() + } + + fun getClipDataUris(intent: Intent): List { + // Use a LinkedHashSet to maintain any ordering that may be + // present in the ClipData + val resultSet = LinkedHashSet() + + val intentData = intent.data + if (intentData != null) { + resultSet.add(intentData) + } + + val clipData = intent.clipData + if (clipData == null && resultSet.isEmpty()) { + return emptyList() + } else if (clipData != null) { + for (i in 0 until clipData.itemCount) { + val uri = clipData.getItemAt(i).uri + if (uri != null) { + resultSet.add(uri) + } + } + } + return ArrayList(resultSet) + } + } +} diff --git a/data/objectbox-models/default.json b/data/objectbox-models/default.json index 68f72010..55e381c8 100644 --- a/data/objectbox-models/default.json +++ b/data/objectbox-models/default.json @@ -5,7 +5,7 @@ "entities": [ { "id": "1:3882697123829827748", - "lastPropertyId": "33:8784310188115192790", + "lastPropertyId": "34:9044184444890640140", "name": "Entry", "properties": [ { @@ -158,6 +158,11 @@ "id": "33:8784310188115192790", "name": "observerID", "type": 9 + }, + { + "id": "34:9044184444890640140", + "name": "isMultiple", + "type": 1 } ], "relations": [] diff --git a/data/objectbox-models/default.json.bak b/data/objectbox-models/default.json.bak index f6e247b0..68f72010 100644 --- a/data/objectbox-models/default.json.bak +++ b/data/objectbox-models/default.json.bak @@ -5,7 +5,7 @@ "entities": [ { "id": "1:3882697123829827748", - "lastPropertyId": "32:675229831295035009", + "lastPropertyId": "33:8784310188115192790", "name": "Entry", "properties": [ { @@ -153,6 +153,11 @@ "id": "32:675229831295035009", "name": "canDelete", "type": 1 + }, + { + "id": "33:8784310188115192790", + "name": "observerID", + "type": 9 } ], "relations": [] diff --git a/data/src/main/kotlin/com/alfresco/content/data/Entry.kt b/data/src/main/kotlin/com/alfresco/content/data/Entry.kt index 0f3ae661..76cb8df1 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/Entry.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/Entry.kt @@ -91,6 +91,7 @@ data class Entry( val isReadOnly: Boolean = false, var isSelectedForMultiSelection: Boolean = false, var observerID: String = "", + var isMultiple: Boolean = false, ) : ParentEntry(), Parcelable { val isSynced: Boolean @@ -448,8 +449,8 @@ data class Entry( /** * return the default Workflow content entry obj */ - fun defaultWorkflowEntry(id: String?, fieldId: String = ""): Entry { - return Entry(uploadServer = UploadServerType.UPLOAD_TO_PROCESS, parentId = id, observerID = fieldId) + fun defaultWorkflowEntry(id: String?, fieldId: String = "", isMultiple: Boolean = false): Entry { + return Entry(uploadServer = UploadServerType.UPLOAD_TO_PROCESS, parentId = id, observerID = fieldId, isMultiple = isMultiple) } fun withSelectedEntries(entries: List): Entry { diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/fragments/ProcessBaseFragment.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/fragments/ProcessBaseFragment.kt index 6042b3a3..5302d0d2 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/fragments/ProcessBaseFragment.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/fragments/ProcessBaseFragment.kt @@ -26,7 +26,8 @@ abstract class ProcessBaseFragment : Fragment(), DeleteContentListener { internal fun showCreateSheet(state: ProcessAttachFilesViewState, observerID: String) { AnalyticsManager().taskEvent(EventName.UploadProcessAttachment) - CreateActionsSheet.with(Entry.defaultWorkflowEntry(observerID, state.parent.field.id)).show(childFragmentManager, null) + val field = state.parent.field + CreateActionsSheet.with(Entry.defaultWorkflowEntry(observerID, field.id, field.params?.multiple ?: false)).show(childFragmentManager, null) } /**