Fix move operation
This commit is contained in:
parent
b6b8bba666
commit
cba1418417
@ -344,22 +344,29 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
} else if (currentItemAction == ItemsActions.MOVE){
|
||||
mapFileForMove(itemsToProcess, itemsToProcess[0].explorerElement.parentPath)
|
||||
checkPathOverwrite(itemsToProcess, currentDirectoryPath){ items ->
|
||||
items?.let {
|
||||
lifecycleScope.launch {
|
||||
val failedItem = fileOperationService.moveElements(it.toMutableList() as ArrayList<OperationFile>)
|
||||
if (failedItem == null) {
|
||||
Toast.makeText(this@ExplorerActivity, R.string.move_success, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
CustomAlertDialogBuilder(this@ExplorerActivity, themeValue)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.move_failed, failedItem))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
setCurrentPath(currentDirectoryPath)
|
||||
itemsToProcess.forEach {
|
||||
it.dstPath = PathUtils.pathJoin(currentDirectoryPath, it.explorerElement.name)
|
||||
it.overwriteConfirmed = false // reset the field in case of a previous cancelled move
|
||||
}
|
||||
val toMove = ArrayList<OperationFile>(itemsToProcess.size)
|
||||
val toClean = ArrayList<ExplorerElement>()
|
||||
prepareFilesForMove(
|
||||
itemsToProcess,
|
||||
toMove,
|
||||
toClean,
|
||||
) {
|
||||
lifecycleScope.launch {
|
||||
val failedItem = fileOperationService.moveElements(toMove, toClean)
|
||||
if (failedItem == null) {
|
||||
Toast.makeText(this@ExplorerActivity, R.string.move_success, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
CustomAlertDialogBuilder(this@ExplorerActivity, themeValue)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.move_failed, failedItem))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
setCurrentPath(currentDirectoryPath)
|
||||
}
|
||||
cancelItemAction()
|
||||
invalidateOptionsMenu()
|
||||
@ -403,22 +410,85 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapFileForMove(items: ArrayList<OperationFile>, srcDirectoryPath: String): ArrayList<OperationFile> {
|
||||
val newItems = ArrayList<OperationFile>()
|
||||
items.forEach {
|
||||
if (it.explorerElement.isDirectory){
|
||||
if (gocryptfsVolume.pathExists(PathUtils.pathJoin(currentDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, it.explorerElement.fullPath)))){
|
||||
newItems.addAll(
|
||||
mapFileForMove(
|
||||
gocryptfsVolume.listDir(it.explorerElement.fullPath).map { e -> OperationFile.fromExplorerElement(e) } as ArrayList<OperationFile>,
|
||||
srcDirectoryPath
|
||||
/**
|
||||
* Ask the user what to do if an item would overwrite another item in case of a move.
|
||||
*
|
||||
* All [OperationFile] must have a non-null [dstPath][OperationFile.dstPath].
|
||||
*/
|
||||
private fun checkMoveOverwrite(items: List<OperationFile>, callback: (List<OperationFile>?) -> Unit) {
|
||||
for (item in items) {
|
||||
if (gocryptfsVolume.pathExists(item.dstPath!!) && !item.overwriteConfirmed) {
|
||||
CustomAlertDialogBuilder(this, themeValue)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(
|
||||
getString(
|
||||
if (item.explorerElement.isDirectory) {
|
||||
R.string.dir_overwrite_question
|
||||
} else {
|
||||
R.string.file_overwrite_question
|
||||
},
|
||||
item.dstPath!!
|
||||
)
|
||||
)
|
||||
.setPositiveButton(R.string.yes) {_, _ ->
|
||||
item.overwriteConfirmed = true
|
||||
checkMoveOverwrite(items, callback)
|
||||
}
|
||||
.setNegativeButton(R.string.no) { _, _ ->
|
||||
with(EditTextDialog(this, R.string.enter_new_name) {
|
||||
item.dstPath = PathUtils.pathJoin(PathUtils.getParentPath(item.dstPath!!), it)
|
||||
checkMoveOverwrite(items, callback)
|
||||
}) {
|
||||
setSelectedText(item.explorerElement.name)
|
||||
show()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
return
|
||||
}
|
||||
}
|
||||
callback(items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for destination overwriting in case of a move operation.
|
||||
*
|
||||
* If the user decides to merge the content of a folder, the function recursively tests all
|
||||
* children of the source folder to see if they will overwrite.
|
||||
*
|
||||
* The items to be moved are stored in [toMove]. We also need to keep track of the merged
|
||||
* folders to delete them after the move. These folders are stored in [toClean].
|
||||
*/
|
||||
private fun prepareFilesForMove(
|
||||
items: List<OperationFile>,
|
||||
toMove: ArrayList<OperationFile>,
|
||||
toClean: ArrayList<ExplorerElement>,
|
||||
onReady: () -> Unit
|
||||
) {
|
||||
checkMoveOverwrite(items) { checkedItems ->
|
||||
checkedItems?.let {
|
||||
for (item in checkedItems) {
|
||||
if (!item.overwriteConfirmed || !item.explorerElement.isDirectory) {
|
||||
toMove.add(item)
|
||||
}
|
||||
}
|
||||
val toCheck = mutableListOf<OperationFile>()
|
||||
for (item in checkedItems) {
|
||||
if (item.overwriteConfirmed && item.explorerElement.isDirectory) {
|
||||
val children = gocryptfsVolume.listDir(item.explorerElement.fullPath)
|
||||
toCheck.addAll(children.map {
|
||||
OperationFile(it, PathUtils.pathJoin(item.dstPath!!, it.name))
|
||||
})
|
||||
toClean.add(item.explorerElement)
|
||||
}
|
||||
}
|
||||
if (toCheck.isEmpty()) {
|
||||
onReady()
|
||||
} else {
|
||||
prepareFilesForMove(toCheck, toMove, toClean, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
items.addAll(newItems)
|
||||
return items
|
||||
}
|
||||
|
||||
private fun cancelItemAction() {
|
||||
|
@ -183,32 +183,26 @@ class FileOperationService : Service() {
|
||||
waitForTask(notification, task).failedItem
|
||||
}
|
||||
|
||||
suspend fun moveElements(items: ArrayList<OperationFile>): String? = coroutineScope {
|
||||
val notification = showNotification(R.string.file_op_move_msg, items.size)
|
||||
val task = async {
|
||||
suspend fun moveElements(toMove: List<OperationFile>, toClean: List<ExplorerElement>): String? = coroutineScope {
|
||||
val notification = showNotification(R.string.file_op_move_msg, toMove.size)
|
||||
val task = async(Dispatchers.IO) {
|
||||
val total = toMove.size+toClean.size
|
||||
var failedItem: String? = null
|
||||
withContext(Dispatchers.IO) {
|
||||
val mergedFolders = ArrayList<String>()
|
||||
for (i in 0 until items.size) {
|
||||
if (items[i].explorerElement.isDirectory && gocryptfsVolume.pathExists(items[i].dstPath!!)) { //folder will be merged
|
||||
mergedFolders.add(items[i].explorerElement.fullPath)
|
||||
} else {
|
||||
if (!gocryptfsVolume.rename(items[i].explorerElement.fullPath, items[i].dstPath!!)) {
|
||||
failedItem = items[i].explorerElement.fullPath
|
||||
break
|
||||
} else {
|
||||
updateNotificationProgress(notification, i+1, items.size)
|
||||
}
|
||||
}
|
||||
for ((i, item) in toMove.withIndex()) {
|
||||
if (!gocryptfsVolume.rename(item.explorerElement.fullPath, item.dstPath!!)) {
|
||||
failedItem = item.explorerElement.fullPath
|
||||
break
|
||||
} else {
|
||||
updateNotificationProgress(notification, i+1, total)
|
||||
}
|
||||
if (failedItem == null) {
|
||||
for (i in 0 until mergedFolders.size) {
|
||||
if (!gocryptfsVolume.rmdir(mergedFolders[i])) {
|
||||
failedItem = mergedFolders[i]
|
||||
break
|
||||
} else {
|
||||
updateNotificationProgress(notification, items.size-(mergedFolders.size-i), items.size)
|
||||
}
|
||||
}
|
||||
if (failedItem == null) {
|
||||
for ((i, folder) in toClean.asReversed().withIndex()) {
|
||||
if (!gocryptfsVolume.rmdir(folder.fullPath)) {
|
||||
failedItem = folder.fullPath
|
||||
break
|
||||
} else {
|
||||
updateNotificationProgress(notification, toMove.size+i+1, total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user