Offload file discovery for copy in coroutine

This commit is contained in:
Matéo Duparc 2023-05-11 21:20:19 +02:00
parent cdf98a7190
commit 393c458495
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
2 changed files with 63 additions and 50 deletions

View File

@ -373,10 +373,10 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
}.show() }.show()
} }
protected fun checkPathOverwrite(items: ArrayList<OperationFile>, dstDirectoryPath: String, callback: (ArrayList<OperationFile>?) -> Unit) { protected fun checkPathOverwrite(items: List<OperationFile>, dstDirectoryPath: String, callback: (List<OperationFile>?) -> Unit) {
val srcDirectoryPath = items[0].parentPath val srcDirectoryPath = items[0].parentPath
var ready = true var ready = true
for (i in 0 until items.size) { for (i in items.indices) {
val testDstPath: String val testDstPath: String
if (items[i].dstPath == null){ if (items[i].dstPath == null){
testDstPath = PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, items[i].srcPath)) testDstPath = PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, items[i].srcPath))
@ -410,7 +410,7 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
with(EditTextDialog(this, R.string.enter_new_name) { with(EditTextDialog(this, R.string.enter_new_name) {
items[i].dstPath = PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, items[i].parentPath), it) items[i].dstPath = PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, items[i].parentPath), it)
if (items[i].isDirectory) { if (items[i].isDirectory) {
for (j in 0 until items.size){ for (j in items.indices) {
if (PathUtils.isChildOf(items[j].srcPath, items[i].srcPath)) { if (PathUtils.isChildOf(items[j].srcPath, items[i].srcPath)) {
items[j].dstPath = PathUtils.pathJoin(items[i].dstPath!!, PathUtils.getRelativePath(items[i].srcPath, items[j].srcPath)) items[j].dstPath = PathUtils.pathJoin(items[i].dstPath!!, PathUtils.getRelativePath(items[i].srcPath, items[j].srcPath))
} }

View File

@ -9,9 +9,11 @@ import android.widget.Toast
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.lifecycleScope
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import sushi.hardcore.droidfs.CameraActivity import sushi.hardcore.droidfs.CameraActivity
import sushi.hardcore.droidfs.LoadingTask
import sushi.hardcore.droidfs.MainActivity import sushi.hardcore.droidfs.MainActivity
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter
@ -37,45 +39,29 @@ class ExplorerActivity : BaseExplorerActivity() {
val srcVolumeId = resultIntent.getIntExtra("volumeId", -1) val srcVolumeId = resultIntent.getIntExtra("volumeId", -1)
val srcEncryptedVolume = app.volumeManager.getVolume(srcVolumeId)!! val srcEncryptedVolume = app.volumeManager.getVolume(srcVolumeId)!!
val path = resultIntent.getStringExtra("path") val path = resultIntent.getStringExtra("path")
val operationFiles = ArrayList<OperationFile>()
if (path == null){ //multiples elements if (path == null){ //multiples elements
val paths = resultIntent.getStringArrayListExtra("paths") val paths = resultIntent.getStringArrayListExtra("paths")
val types = resultIntent.getIntegerArrayListExtra("types") val types = resultIntent.getIntegerArrayListExtra("types")
if (types != null && paths != null){ if (types != null && paths != null){
for (i in paths.indices) { object : LoadingTask<List<OperationFile>>(this, theme, R.string.discovering_files) {
operationFiles.add( override suspend fun doTask(): List<OperationFile> {
OperationFile(paths[i], types[i]) val operationFiles = ArrayList<OperationFile>()
) for (i in paths.indices) {
if (types[i] == Stat.S_IFDIR) { operationFiles.add(OperationFile(paths[i], types[i]))
srcEncryptedVolume.recursiveMapFiles(paths[i])?.forEach { if (types[i] == Stat.S_IFDIR) {
operationFiles.add(OperationFile.fromExplorerElement(it)) srcEncryptedVolume.recursiveMapFiles(paths[i])?.forEach {
operationFiles.add(OperationFile.fromExplorerElement(it))
}
}
} }
return operationFiles
} }
}.startTask(lifecycleScope) { operationFiles ->
importFilesFromVolume(srcVolumeId, operationFiles)
} }
} }
} else { } else {
operationFiles.add( importFilesFromVolume(srcVolumeId, arrayListOf(OperationFile(path, Stat.S_IFREG)))
OperationFile(path, Stat.S_IFREG)
)
}
if (operationFiles.size > 0){
checkPathOverwrite(operationFiles, currentDirectoryPath) { items ->
if (items != null) {
// stop loading thumbnails while writing files
explorerAdapter.loadThumbnails = false
activityScope.launch {
onTaskResult(
fileOperationService.copyElements(
volumeId,
items,
srcVolumeId
), R.string.import_failed, R.string.success_import
)
explorerAdapter.loadThumbnails = true
setCurrentPath(currentDirectoryPath)
}
}
}
} }
} }
} }
@ -120,6 +106,27 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
private fun importFilesFromVolume(srcVolumeId: Int, operationFiles: List<OperationFile>) {
checkPathOverwrite(operationFiles, currentDirectoryPath) { items ->
if (items != null) {
// stop loading thumbnails while writing files
explorerAdapter.loadThumbnails = false
activityScope.launch {
onTaskResult(
fileOperationService.copyElements(
volumeId,
items,
srcVolumeId
), R.string.import_failed, R.string.success_import
)
explorerAdapter.loadThumbnails = true
setCurrentPath(currentDirectoryPath)
}
}
}
}
private fun onImportComplete(urisToWipe: List<Uri>, rootFile: DocumentFile? = null) { private fun onImportComplete(urisToWipe: List<Uri>, rootFile: DocumentFile? = null) {
CustomAlertDialogBuilder(this, theme) CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.success_import) .setTitle(R.string.success_import)
@ -289,11 +296,6 @@ class ExplorerActivity : BaseExplorerActivity() {
R.id.copy -> { R.id.copy -> {
for (i in explorerAdapter.selectedItems){ for (i in explorerAdapter.selectedItems){
itemsToProcess.add(OperationFile.fromExplorerElement(explorerElements[i])) itemsToProcess.add(OperationFile.fromExplorerElement(explorerElements[i]))
if (explorerElements[i].isDirectory){
encryptedVolume.recursiveMapFiles(explorerElements[i].fullPath)?.forEach {
itemsToProcess.add(OperationFile.fromExplorerElement(it))
}
}
} }
currentItemAction = ItemsActions.COPY currentItemAction = ItemsActions.COPY
unselectAll() unselectAll()
@ -301,20 +303,31 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
R.id.validate -> { R.id.validate -> {
if (currentItemAction == ItemsActions.COPY){ if (currentItemAction == ItemsActions.COPY){
checkPathOverwrite(itemsToProcess, currentDirectoryPath) { object : LoadingTask<List<OperationFile>>(this, theme, R.string.discovering_files) {
// copying before being cleared override suspend fun doTask(): List<OperationFile> {
it?.toMutableList()?.let { items -> val items = itemsToProcess.toMutableList()
activityScope.launch { itemsToProcess.filter { it.isDirectory }.forEach { dir ->
onTaskResult( encryptedVolume.recursiveMapFiles(dir.srcPath)?.forEach {
fileOperationService.copyElements(volumeId, items), items.add(OperationFile.fromExplorerElement(it))
R.string.copy_failed, }
R.string.copy_success,
)
setCurrentPath(currentDirectoryPath)
} }
return items
}
}.startTask(lifecycleScope) { items ->
checkPathOverwrite(items, currentDirectoryPath) {
it?.let { checkedItems ->
activityScope.launch {
onTaskResult(
fileOperationService.copyElements(volumeId, checkedItems),
R.string.copy_failed,
R.string.copy_success,
)
setCurrentPath(currentDirectoryPath)
}
}
cancelItemAction()
invalidateOptionsMenu()
} }
cancelItemAction()
invalidateOptionsMenu()
} }
} else if (currentItemAction == ItemsActions.MOVE){ } else if (currentItemAction == ItemsActions.MOVE){
itemsToProcess.forEach { itemsToProcess.forEach {