|
|
|
@ -3,20 +3,20 @@ package sushi.hardcore.droidfs.explorers
|
|
|
|
|
import android.app.Activity |
|
|
|
|
import android.content.Intent |
|
|
|
|
import android.net.Uri |
|
|
|
|
import android.os.Looper |
|
|
|
|
import android.view.Menu |
|
|
|
|
import android.view.MenuItem |
|
|
|
|
import android.view.View |
|
|
|
|
import android.view.WindowManager |
|
|
|
|
import android.widget.EditText |
|
|
|
|
import android.widget.Toast |
|
|
|
|
import androidx.appcompat.app.AppCompatActivity |
|
|
|
|
import androidx.documentfile.provider.DocumentFile |
|
|
|
|
import sushi.hardcore.droidfs.CameraActivity |
|
|
|
|
import sushi.hardcore.droidfs.OpenActivity |
|
|
|
|
import sushi.hardcore.droidfs.R |
|
|
|
|
import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter |
|
|
|
|
import sushi.hardcore.droidfs.util.* |
|
|
|
|
import sushi.hardcore.droidfs.file_operations.OperationFile |
|
|
|
|
import sushi.hardcore.droidfs.util.ExternalProvider |
|
|
|
|
import sushi.hardcore.droidfs.util.GocryptfsVolume |
|
|
|
|
import sushi.hardcore.droidfs.util.PathUtils |
|
|
|
|
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder |
|
|
|
|
import java.io.File |
|
|
|
|
|
|
|
|
@ -30,7 +30,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
private var usf_decrypt = false |
|
|
|
|
private var usf_share = false |
|
|
|
|
private var currentItemAction = ItemsActions.NONE |
|
|
|
|
private val itemsToProcess = ArrayList<ExplorerElement>() |
|
|
|
|
private val itemsToProcess = ArrayList<OperationFile>() |
|
|
|
|
override fun init() { |
|
|
|
|
setContentView(R.layout.activity_explorer) |
|
|
|
|
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false) |
|
|
|
@ -47,19 +47,17 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
if (fileName.isEmpty()) { |
|
|
|
|
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() |
|
|
|
|
} else { |
|
|
|
|
checkPathOverwrite(PathUtils.pathJoin(currentDirectoryPath, fileName), false)?.let { |
|
|
|
|
val handleID = gocryptfsVolume.openWriteMode(it) |
|
|
|
|
if (handleID == -1) { |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
val handleID = gocryptfsVolume.openWriteMode(fileName) //don't check overwrite because openWriteMode open in read-write (doesn't erase content) |
|
|
|
|
if (handleID == -1) { |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(R.string.file_creation_failed) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} else { |
|
|
|
|
gocryptfsVolume.closeFile(handleID) |
|
|
|
|
setCurrentPath(currentDirectoryPath) |
|
|
|
|
invalidateOptionsMenu() |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
gocryptfsVolume.closeFile(handleID) |
|
|
|
|
setCurrentPath(currentDirectoryPath) |
|
|
|
|
invalidateOptionsMenu() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -138,162 +136,137 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
super.onActivityResult(requestCode, resultCode, data) |
|
|
|
|
if (requestCode == PICK_FILES_REQUEST_CODE) { |
|
|
|
|
if (resultCode == Activity.RESULT_OK && data != null) { |
|
|
|
|
object : LoadingTask(this, R.string.loading_msg_import){ |
|
|
|
|
override fun doTask(activity: AppCompatActivity) { |
|
|
|
|
val uris: MutableList<Uri> = ArrayList() |
|
|
|
|
val singleUri = data.data |
|
|
|
|
if (singleUri == null) { //multiples choices |
|
|
|
|
val clipData = data.clipData |
|
|
|
|
if (clipData != null){ |
|
|
|
|
for (i in 0 until clipData.itemCount) { |
|
|
|
|
uris.add(clipData.getItemAt(i).uri) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
uris.add(singleUri) |
|
|
|
|
val uris: MutableList<Uri> = ArrayList() |
|
|
|
|
val singleUri = data.data |
|
|
|
|
if (singleUri == null) { //multiples choices |
|
|
|
|
val clipData = data.clipData |
|
|
|
|
if (clipData != null){ |
|
|
|
|
for (i in 0 until clipData.itemCount) { |
|
|
|
|
uris.add(clipData.getItemAt(i).uri) |
|
|
|
|
} |
|
|
|
|
Looper.prepare() |
|
|
|
|
if (importFilesFromUris(uris, this)) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.success_import) |
|
|
|
|
.setMessage(""" |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
uris.add(singleUri) |
|
|
|
|
} |
|
|
|
|
importFilesFromUris(uris){ failedItem -> |
|
|
|
|
if (failedItem == null){ |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.success_import) |
|
|
|
|
.setMessage(""" |
|
|
|
|
${getString(R.string.success_import_msg)} |
|
|
|
|
${getString(R.string.ask_for_wipe)} |
|
|
|
|
""".trimIndent()) |
|
|
|
|
.setPositiveButton(R.string.yes) { _, _ -> |
|
|
|
|
object : LoadingTask(activity, R.string.loading_msg_wipe){ |
|
|
|
|
override fun doTask(activity: AppCompatActivity) { |
|
|
|
|
var success = true |
|
|
|
|
for (uri in uris) { |
|
|
|
|
val errorMsg = Wiper.wipe(activity, uri) |
|
|
|
|
if (errorMsg != null) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.wipe_failed, errorMsg)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
success = false |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (success) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.wipe_successful) |
|
|
|
|
.setMessage(R.string.wipe_success_msg) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.setPositiveButton(R.string.yes) { _, _ -> |
|
|
|
|
fileOperationService.wipeUris(uris) { errorMsg -> |
|
|
|
|
runOnUiThread { |
|
|
|
|
if (errorMsg == null){ |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.wipe_successful) |
|
|
|
|
.setMessage(R.string.wipe_success_msg) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} else { |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.wipe_failed, errorMsg)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
.setNegativeButton(R.string.no, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
override fun doFinally(activity: AppCompatActivity){ |
|
|
|
|
setCurrentPath(currentDirectoryPath) |
|
|
|
|
} |
|
|
|
|
.setNegativeButton(R.string.no, null) |
|
|
|
|
.show() |
|
|
|
|
} else { |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.import_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { |
|
|
|
|
if (resultCode == Activity.RESULT_OK && data != null) { |
|
|
|
|
object : LoadingTask(this, R.string.loading_msg_export){ |
|
|
|
|
override fun doTask(activity: AppCompatActivity) { |
|
|
|
|
data.data?.let {uri -> |
|
|
|
|
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) |
|
|
|
|
DocumentFile.fromTreeUri(activity, uri)?.let { treeDocumentFile -> |
|
|
|
|
var failedItem: String? = null |
|
|
|
|
for (i in explorerAdapter.selectedItems) { |
|
|
|
|
val element = explorerAdapter.getItem(i) |
|
|
|
|
val fullPath = PathUtils.pathJoin(currentDirectoryPath, element.name) |
|
|
|
|
failedItem = if (element.isDirectory) { |
|
|
|
|
recursiveExportDirectory(fullPath, treeDocumentFile) |
|
|
|
|
} else { |
|
|
|
|
if (exportFileInto(fullPath, treeDocumentFile)) null else fullPath |
|
|
|
|
} |
|
|
|
|
if (failedItem != null) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.export_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (failedItem == null) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.success_export) |
|
|
|
|
.setMessage(R.string.success_export_msg) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
data.data?.let { uri -> |
|
|
|
|
fileOperationService.exportFiles(uri, explorerAdapter.selectedItems.map { i -> explorerElements[i] }){ failedItem -> |
|
|
|
|
runOnUiThread { |
|
|
|
|
if (failedItem == null){ |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.success_export) |
|
|
|
|
.setMessage(R.string.success_export_msg) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} else { |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.export_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
override fun doFinally(activity: AppCompatActivity) { |
|
|
|
|
unselectAll() |
|
|
|
|
} |
|
|
|
|
unselectAll() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (requestCode == PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) { |
|
|
|
|
if (resultCode == Activity.RESULT_OK && data != null) { |
|
|
|
|
object : LoadingTask(this, R.string.loading_msg_import){ |
|
|
|
|
override fun doTask(activity: AppCompatActivity) { |
|
|
|
|
val remoteSessionID = data.getIntExtra("sessionID", -1) |
|
|
|
|
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID) |
|
|
|
|
val path = data.getStringExtra("path") |
|
|
|
|
var failedItem: String? = null |
|
|
|
|
Looper.prepare() |
|
|
|
|
if (path == null) { |
|
|
|
|
val paths = data.getStringArrayListExtra("paths") |
|
|
|
|
val types = data.getIntegerArrayListExtra("types") |
|
|
|
|
if (types != null && paths != null){ |
|
|
|
|
for (i in paths.indices) { |
|
|
|
|
failedItem = if (types[i] == 0) { //directory |
|
|
|
|
recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath) |
|
|
|
|
} else { |
|
|
|
|
safeImportFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.pathJoin(currentDirectoryPath, File(paths[i]).name)) |
|
|
|
|
} |
|
|
|
|
if (failedItem != null) { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
val remoteSessionID = data.getIntExtra("sessionID", -1) |
|
|
|
|
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID) |
|
|
|
|
val path = data.getStringExtra("path") |
|
|
|
|
val operationFiles = ArrayList<OperationFile>() |
|
|
|
|
if (path == null){ //multiples elements |
|
|
|
|
val paths = data.getStringArrayListExtra("paths") |
|
|
|
|
val types = data.getIntegerArrayListExtra("types") |
|
|
|
|
if (types != null && paths != null){ |
|
|
|
|
for (i in paths.indices) { |
|
|
|
|
operationFiles.add( |
|
|
|
|
OperationFile.fromExplorerElement( |
|
|
|
|
ExplorerElement(File(paths[i]).name, types[i].toShort(), -1, -1, PathUtils.getParentPath(paths[i])) |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
if (types[i] == 0){ //directory |
|
|
|
|
remoteGocryptfsVolume.recursiveMapFiles(paths[i]).forEach { |
|
|
|
|
operationFiles.add(OperationFile.fromExplorerElement(it)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
failedItem = safeImportFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.pathJoin(currentDirectoryPath, File(path).name)) |
|
|
|
|
} |
|
|
|
|
if (failedItem == null) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.success_import) |
|
|
|
|
.setMessage(R.string.success_import_msg) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} else if (failedItem!!.isNotEmpty()){ |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.import_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
operationFiles.add( |
|
|
|
|
OperationFile.fromExplorerElement( |
|
|
|
|
ExplorerElement(File(path).name, 1, -1, -1, PathUtils.getParentPath(path)) |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
if (operationFiles.size > 0){ |
|
|
|
|
checkPathOverwrite(operationFiles, currentDirectoryPath) { items -> |
|
|
|
|
if (items == null) { |
|
|
|
|
remoteGocryptfsVolume.close() |
|
|
|
|
} else { |
|
|
|
|
fileOperationService.copyElements(items, remoteGocryptfsVolume){ failedItem -> |
|
|
|
|
runOnUiThread { |
|
|
|
|
if (failedItem == null){ |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.success_import) |
|
|
|
|
.setMessage(R.string.success_import_msg) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} else { |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.import_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
remoteGocryptfsVolume.close() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
remoteGocryptfsVolume.close() |
|
|
|
|
} |
|
|
|
|
override fun doFinally(activity: AppCompatActivity) { |
|
|
|
|
setCurrentPath(currentDirectoryPath) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
remoteGocryptfsVolume.close() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -344,7 +317,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
} |
|
|
|
|
R.id.cut -> { |
|
|
|
|
for (i in explorerAdapter.selectedItems){ |
|
|
|
|
itemsToProcess.add(explorerElements[i]) |
|
|
|
|
itemsToProcess.add(OperationFile.fromExplorerElement(explorerElements[i])) |
|
|
|
|
} |
|
|
|
|
currentItemAction = ItemsActions.MOVE |
|
|
|
|
unselectAll() |
|
|
|
@ -352,7 +325,12 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
} |
|
|
|
|
R.id.copy -> { |
|
|
|
|
for (i in explorerAdapter.selectedItems){ |
|
|
|
|
itemsToProcess.add(explorerElements[i]) |
|
|
|
|
itemsToProcess.add(OperationFile.fromExplorerElement(explorerElements[i])) |
|
|
|
|
if (explorerElements[i].isDirectory){ |
|
|
|
|
gocryptfsVolume.recursiveMapFiles(explorerElements[i].fullPath).forEach { |
|
|
|
|
itemsToProcess.add(OperationFile.fromExplorerElement(it)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
currentItemAction = ItemsActions.COPY |
|
|
|
|
unselectAll() |
|
|
|
@ -360,81 +338,45 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
} |
|
|
|
|
R.id.validate -> { |
|
|
|
|
if (currentItemAction == ItemsActions.COPY){ |
|
|
|
|
object : LoadingTask(this, R.string.loading_msg_copy){ |
|
|
|
|
override fun doTask(activity: AppCompatActivity) { |
|
|
|
|
var failedItem: String? = null |
|
|
|
|
Looper.prepare() |
|
|
|
|
for (element in itemsToProcess) { |
|
|
|
|
val dstPath = checkPathOverwrite(PathUtils.pathJoin(currentDirectoryPath, element.name), element.isDirectory) |
|
|
|
|
failedItem = if (dstPath == null){ |
|
|
|
|
"" |
|
|
|
|
} else { |
|
|
|
|
if (element.isDirectory) { |
|
|
|
|
recursiveCopyDirectory(element.fullPath, dstPath) |
|
|
|
|
} else { |
|
|
|
|
if (copyFile(element.fullPath, dstPath)) null else element.fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (failedItem != null){ |
|
|
|
|
if (failedItem.isNotEmpty()) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
checkPathOverwrite(itemsToProcess, currentDirectoryPath){ items -> |
|
|
|
|
items?.let { |
|
|
|
|
fileOperationService.copyElements(it.toMutableList() as ArrayList<OperationFile>){ failedItem -> |
|
|
|
|
runOnUiThread { |
|
|
|
|
if (failedItem != null){ |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString( |
|
|
|
|
R.string.copy_failed, |
|
|
|
|
failedItem |
|
|
|
|
)) |
|
|
|
|
.setMessage(getString(R.string.copy_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
Toast.makeText(this, R.string.copy_success, Toast.LENGTH_SHORT).show() |
|
|
|
|
} |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (failedItem == null) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(getString(R.string.copy_success)) |
|
|
|
|
.setMessage(getString(R.string.copy_success_msg)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
override fun doFinally(activity: AppCompatActivity) { |
|
|
|
|
cancelItemAction() |
|
|
|
|
unselectAll() |
|
|
|
|
setCurrentPath(currentDirectoryPath) |
|
|
|
|
} |
|
|
|
|
cancelItemAction() |
|
|
|
|
unselectAll() |
|
|
|
|
} |
|
|
|
|
} else if (currentItemAction == ItemsActions.MOVE){ |
|
|
|
|
object : LoadingTask(this, R.string.loading_msg_move){ |
|
|
|
|
override fun doTask(activity: AppCompatActivity) { |
|
|
|
|
Looper.prepare() |
|
|
|
|
val failedItem = moveElements(itemsToProcess, currentDirectoryPath) |
|
|
|
|
if (failedItem == null) { |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(getString(R.string.move_success)) |
|
|
|
|
.setMessage(getString(R.string.move_success_msg)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} |
|
|
|
|
} else if (failedItem.isNotEmpty()){ |
|
|
|
|
stopTask { |
|
|
|
|
ColoredAlertDialogBuilder(activity) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.move_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
mapFileForMove(itemsToProcess, itemsToProcess[0].explorerElement.parentPath) |
|
|
|
|
checkPathOverwrite(itemsToProcess, currentDirectoryPath){ items -> |
|
|
|
|
items?.let { |
|
|
|
|
fileOperationService.moveElements(it.toMutableList() as ArrayList<OperationFile>){ failedItem -> |
|
|
|
|
runOnUiThread { |
|
|
|
|
if (failedItem != null){ |
|
|
|
|
ColoredAlertDialogBuilder(this) |
|
|
|
|
.setTitle(R.string.error) |
|
|
|
|
.setMessage(getString(R.string.move_failed, failedItem)) |
|
|
|
|
.setPositiveButton(R.string.ok, null) |
|
|
|
|
.show() |
|
|
|
|
} else { |
|
|
|
|
Toast.makeText(this, R.string.move_success, Toast.LENGTH_SHORT).show() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
override fun doFinally(activity: AppCompatActivity) { |
|
|
|
|
cancelItemAction() |
|
|
|
|
unselectAll() |
|
|
|
|
setCurrentPath(currentDirectoryPath) |
|
|
|
|
} |
|
|
|
|
cancelItemAction() |
|
|
|
|
unselectAll() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
true |
|
|
|
@ -473,6 +415,24 @@ 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 |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
items.addAll(newItems) |
|
|
|
|
return items |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun cancelItemAction() { |
|
|
|
|
if (currentItemAction != ItemsActions.NONE){ |
|
|
|
|
currentItemAction = ItemsActions.NONE |
|
|
|
@ -489,220 +449,13 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun copyFile(srcPath: String, dstPath: String): Boolean { |
|
|
|
|
var success = true |
|
|
|
|
val originalHandleId = gocryptfsVolume.openReadMode(srcPath) |
|
|
|
|
if (originalHandleId != -1){ |
|
|
|
|
val newHandleId = gocryptfsVolume.openWriteMode(dstPath) |
|
|
|
|
if (newHandleId != -1){ |
|
|
|
|
var offset: Long = 0 |
|
|
|
|
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS) |
|
|
|
|
var length: Int |
|
|
|
|
while (gocryptfsVolume.readFile(originalHandleId, offset, ioBuffer).also { length = it } > 0) { |
|
|
|
|
val written = gocryptfsVolume.writeFile(newHandleId, offset, ioBuffer, length).toLong() |
|
|
|
|
if (written == length.toLong()) { |
|
|
|
|
offset += written |
|
|
|
|
} else { |
|
|
|
|
success = false |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
gocryptfsVolume.closeFile(newHandleId) |
|
|
|
|
} else { |
|
|
|
|
success = false |
|
|
|
|
} |
|
|
|
|
gocryptfsVolume.closeFile(originalHandleId) |
|
|
|
|
} else { |
|
|
|
|
success = false |
|
|
|
|
} |
|
|
|
|
return success |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun recursiveCopyDirectory(srcDirectoryPath: String, dstDirectoryPath: String): String? { |
|
|
|
|
val mappedElements = gocryptfsVolume.recursiveMapFiles(srcDirectoryPath) |
|
|
|
|
if (!gocryptfsVolume.pathExists(dstDirectoryPath)){ |
|
|
|
|
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) { |
|
|
|
|
return srcDirectoryPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (e in mappedElements) { |
|
|
|
|
val dstPath = checkPathOverwrite(PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath)), e.isDirectory) |
|
|
|
|
if (dstPath == null){ |
|
|
|
|
return "" |
|
|
|
|
} else { |
|
|
|
|
if (e.isDirectory) { |
|
|
|
|
if (!gocryptfsVolume.pathExists(dstPath)){ |
|
|
|
|
if (!gocryptfsVolume.mkdir(dstPath)){ |
|
|
|
|
return e.fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (!copyFile(e.fullPath, dstPath)) { |
|
|
|
|
return e.fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun moveDirectory(srcDirectoryPath: String, dstDirectoryPath: String): String? { |
|
|
|
|
if (!gocryptfsVolume.pathExists(dstDirectoryPath)) { |
|
|
|
|
if (!gocryptfsVolume.rename(srcDirectoryPath, dstDirectoryPath)) { |
|
|
|
|
return srcDirectoryPath |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
moveElements(gocryptfsVolume.listDir(srcDirectoryPath), dstDirectoryPath) |
|
|
|
|
gocryptfsVolume.rmdir(srcDirectoryPath) |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun moveElements(elements: List<ExplorerElement>, dstDirectoryPath: String): String? { |
|
|
|
|
for (element in elements){ |
|
|
|
|
val dstPath = checkPathOverwrite(PathUtils.pathJoin(dstDirectoryPath, element.name), element.isDirectory) |
|
|
|
|
if (dstPath == null){ |
|
|
|
|
return "" |
|
|
|
|
} else { |
|
|
|
|
if (element.isDirectory){ |
|
|
|
|
moveDirectory(element.fullPath, dstPath)?.let{ |
|
|
|
|
return it |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (!gocryptfsVolume.rename(element.fullPath, dstPath)){ |
|
|
|
|
return element.fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun importFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean { |
|
|
|
|
var success = true |
|
|
|
|
val srcHandleID = remoteGocryptfsVolume.openReadMode(srcPath) |
|
|
|
|
if (srcHandleID != -1) { |
|
|
|
|
val dstHandleID = gocryptfsVolume.openWriteMode(dstPath) |
|
|
|
|
if (dstHandleID != -1) { |
|
|
|
|
var length: Int |
|
|
|
|
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS) |
|
|
|
|
var offset: Long = 0 |
|
|
|
|
while (remoteGocryptfsVolume.readFile(srcHandleID, offset, ioBuffer).also { length = it } > 0) { |
|
|
|
|
val written = |
|
|
|
|
gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong() |
|
|
|
|
if (written == length.toLong()) { |
|
|
|
|
offset += length.toLong() |
|
|
|
|
} else { |
|
|
|
|
success = false |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
gocryptfsVolume.closeFile(dstHandleID) |
|
|
|
|
} |
|
|
|
|
remoteGocryptfsVolume.closeFile(srcHandleID) |
|
|
|
|
} |
|
|
|
|
return success |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun safeImportFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): String? { |
|
|
|
|
val checkedDstPath = checkPathOverwrite(dstPath, false) |
|
|
|
|
return if (checkedDstPath == null){ |
|
|
|
|
"" |
|
|
|
|
} else { |
|
|
|
|
if (importFileFromOtherVolume(remoteGocryptfsVolume, srcPath, checkedDstPath)) null else srcPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? { |
|
|
|
|
val mappedElements = remote_gocryptfsVolume.recursiveMapFiles(remote_directory_path) |
|
|
|
|
val dstDirectoryPath = checkPathOverwrite(PathUtils.pathJoin(outputPath, File(remote_directory_path).name), true) |
|
|
|
|
if (dstDirectoryPath == null){ |
|
|
|
|
return "" |
|
|
|
|
} else { |
|
|
|
|
if (!gocryptfsVolume.pathExists(dstDirectoryPath)) { |
|
|
|
|
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) { |
|
|
|
|
return remote_directory_path |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (e in mappedElements) { |
|
|
|
|
val dstPath = checkPathOverwrite(PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath)), e.isDirectory) |
|
|
|
|
if (dstPath == null){ |
|
|
|
|
return "" |
|
|
|
|
} else { |
|
|
|
|
if (e.isDirectory) { |
|
|
|
|
if (!gocryptfsVolume.pathExists(dstPath)){ |
|
|
|
|
if (!gocryptfsVolume.mkdir(dstPath)){ |
|
|
|
|
return e.fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, dstPath)) { |
|
|
|
|
return e.fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun exportFileInto(srcPath: String, treeDocumentFile: DocumentFile): Boolean { |
|
|
|
|
val outputStream = treeDocumentFile.createFile("*/*", File(srcPath).name)?.uri?.let { |
|
|
|
|
contentResolver.openOutputStream(it) |
|
|
|
|
} |
|
|
|
|
return if (outputStream != null){ |
|
|
|
|
gocryptfsVolume.exportFile(srcPath, outputStream) |
|
|
|
|
} else { |
|
|
|
|
false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun recursiveExportDirectory(plain_directory_path: String, treeDocumentFile: DocumentFile): String? { |
|
|
|
|
treeDocumentFile.createDirectory(File(plain_directory_path).name)?.let {childTree -> |
|
|
|
|
val explorerElements = gocryptfsVolume.listDir(plain_directory_path) |
|
|
|
|
for (e in explorerElements) { |
|
|
|
|
val fullPath = PathUtils.pathJoin(plain_directory_path, e.name) |
|
|
|
|
if (e.isDirectory) { |
|
|
|
|
val failedItem = recursiveExportDirectory(fullPath, childTree) |
|
|
|
|
failedItem?.let { return it } |
|
|
|
|
} else { |
|
|
|
|
if (!exportFileInto(fullPath, childTree)){ |
|
|
|
|
return fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
return treeDocumentFile.name |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun recursiveRemoveDirectory(plain_directory_path: String): String? { |
|
|
|
|
val explorerElements = gocryptfsVolume.listDir(plain_directory_path) |
|
|
|
|
for (e in explorerElements) { |
|
|
|
|
val fullPath = PathUtils.pathJoin(plain_directory_path, e.name) |
|
|
|
|
if (e.isDirectory) { |
|
|
|
|
val result = recursiveRemoveDirectory(fullPath) |
|
|
|
|
result?.let { return it } |
|
|
|
|
} else { |
|
|
|
|
if (!gocryptfsVolume.removeFile(fullPath)) { |
|
|
|
|
return fullPath |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return if (!gocryptfsVolume.rmdir(plain_directory_path)) { |
|
|
|
|
plain_directory_path |
|
|
|
|
} else { |
|
|
|
|
null |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun removeSelectedItems() { |
|
|
|
|
var failedItem: String? = null |
|
|
|
|
for (i in explorerAdapter.selectedItems) { |
|
|
|
|
val element = explorerAdapter.getItem(i) |
|
|
|
|
val fullPath = PathUtils.pathJoin(currentDirectoryPath, element.name) |
|
|
|
|
if (element.isDirectory) { |
|
|
|
|
val result = recursiveRemoveDirectory(fullPath) |
|
|
|
|
val result = gocryptfsVolume.recursiveRemoveDirectory(fullPath) |
|
|
|
|
result?.let{ failedItem = it } |
|
|
|
|
} else { |
|
|
|
|
if (!gocryptfsVolume.removeFile(fullPath)) { |
|
|
|
|