DroidFS/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt

746 lines
35 KiB
Kotlin
Raw Normal View History

2020-07-17 16:35:39 +02:00
package sushi.hardcore.droidfs.explorers
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Looper
2020-07-17 16:35:39 +02:00
import android.view.Menu
import android.view.MenuItem
import android.view.View
2020-07-19 21:46:29 +02:00
import android.view.WindowManager
import android.widget.EditText
import android.widget.Toast
2020-07-28 22:25:10 +02:00
import androidx.appcompat.app.AppCompatActivity
2020-08-29 20:48:12 +02:00
import androidx.documentfile.provider.DocumentFile
2020-08-05 14:06:54 +02:00
import sushi.hardcore.droidfs.CameraActivity
2020-07-17 16:35:39 +02:00
import sushi.hardcore.droidfs.OpenActivity
import sushi.hardcore.droidfs.R
2020-08-05 14:06:54 +02:00
import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter
2020-07-28 22:25:10 +02:00
import sushi.hardcore.droidfs.util.*
2020-07-27 16:20:52 +02:00
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
2020-07-17 16:35:39 +02:00
import java.io.File
2020-07-19 21:46:29 +02:00
class ExplorerActivity : BaseExplorerActivity() {
2020-08-09 14:34:42 +02:00
companion object {
private const val PICK_DIRECTORY_REQUEST_CODE = 1
private const val PICK_FILES_REQUEST_CODE = 2
private const val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3
private enum class ItemsActions {NONE, COPY, MOVE}
}
2020-07-17 16:35:39 +02:00
private var usf_decrypt = false
private var usf_share = false
2020-08-09 14:34:42 +02:00
private var currentItemAction = ItemsActions.NONE
private val itemsToProcess = ArrayList<ExplorerElement>()
2020-07-17 16:35:39 +02:00
override fun init() {
setContentView(R.layout.activity_explorer)
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false)
usf_share = sharedPrefs.getBoolean("usf_share", false)
}
override fun onExplorerItemLongClick(position: Int) {
2020-08-09 14:34:42 +02:00
cancelItemAction()
explorerAdapter.onItemLongClick(position)
invalidateOptionsMenu()
}
2020-07-19 21:46:29 +02:00
private fun createNewFile(fileName: String){
if (fileName.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else {
2020-08-09 14:34:42 +02:00
checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, fileName), false)?.let {
val handleID = gocryptfsVolume.openWriteMode(it)
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()
}
2020-07-19 21:46:29 +02:00
}
}
}
2020-08-05 14:06:54 +02:00
fun onClickFAB(view: View) {
2020-08-09 14:34:42 +02:00
if (currentItemAction != ItemsActions.NONE){
2020-08-05 14:06:54 +02:00
openDialogCreateFolder()
} else {
val adapter = IconTextDialogAdapter(this)
adapter.items = listOf(
listOf("importFromOtherVolumes", R.string.import_from_other_volume, R.drawable.icon_transfert),
listOf("importFiles", R.string.import_files, R.drawable.icon_encrypt),
listOf("createFile", R.string.new_file, R.drawable.icon_file_unknown),
listOf("createFolder", R.string.mkdir, R.drawable.icon_folder),
listOf("takePhoto", R.string.take_photo, R.drawable.icon_camera)
)
ColoredAlertDialogBuilder(this)
.setSingleChoiceItems(adapter, -1){ thisDialog, which ->
when (adapter.getItem(which)){
"importFromOtherVolumes" -> {
val intent = Intent(this, OpenActivity::class.java)
intent.action = "pick"
intent.putExtra("sessionID", gocryptfsVolume.sessionID)
isStartingActivity = true
2020-08-05 14:06:54 +02:00
startActivityForResult(intent, PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE)
}
"importFiles" -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.addCategory(Intent.CATEGORY_OPENABLE)
isStartingActivity = true
2020-08-05 14:06:54 +02:00
startActivityForResult(intent, PICK_FILES_REQUEST_CODE)
}
"createFile" -> {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView)
.setTitle(getString(R.string.enter_file_name))
.setPositiveButton(R.string.ok) { _, _ ->
val fileName = dialogEditText.text.toString()
createNewFile(fileName)
}
.setNegativeButton(R.string.cancel, null)
.create()
dialogEditText.setOnEditorActionListener { _, _, _ ->
val fileName = dialogEditText.text.toString()
dialog.dismiss()
createNewFile(fileName)
true
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
}
"createFolder" -> {
openDialogCreateFolder()
}
"takePhoto" -> {
val intent = Intent(this, CameraActivity::class.java)
intent.putExtra("path", currentDirectoryPath)
intent.putExtra("sessionID", gocryptfsVolume.sessionID)
isStartingActivity = true
2020-08-05 14:06:54 +02:00
startActivity(intent)
}
}
thisDialog.dismiss()
}
.setTitle(getString(R.string.fab_dialog_title))
.setNegativeButton(R.string.cancel, null)
.show()
2020-07-19 21:46:29 +02:00
}
2020-07-17 16:35:39 +02:00
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_FILES_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
2020-07-28 22:25:10 +02:00
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)
2020-07-17 16:35:39 +02:00
}
Looper.prepare()
var success = false
2020-07-28 22:25:10 +02:00
for (uri in uris) {
2020-08-09 14:34:42 +02:00
val dstPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false)
if (dstPath == null){
2020-07-28 22:25:10 +02:00
break
} else {
contentResolver.openInputStream(uri)?.let {
success = gocryptfsVolume.importFile(it, dstPath)
}
if (!success) {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, uri))
.setPositiveButton(R.string.ok, null)
.show()
}
break
}
2020-07-28 22:25:10 +02:00
}
2020-07-17 16:35:39 +02:00
}
2020-07-28 22:25:10 +02:00
if (success) {
stopTask {
ColoredAlertDialogBuilder(activity)
.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) {
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()
}
}
}
}
}
2020-08-12 15:14:38 +02:00
.setNegativeButton(R.string.no, null)
2020-07-17 16:35:39 +02:00
.show()
2020-07-28 22:25:10 +02:00
}
2020-07-17 16:35:39 +02:00
}
}
2020-07-28 22:25:10 +02:00
override fun doFinally(activity: AppCompatActivity){
setCurrentPath(currentDirectoryPath)
2020-07-17 16:35:39 +02:00
}
}
}
} else if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
2020-07-28 22:25:10 +02:00
object : LoadingTask(this, R.string.loading_msg_export){
override fun doTask(activity: AppCompatActivity) {
2020-08-29 20:48:12 +02:00
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.path_join(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()
}
2020-07-28 22:25:10 +02:00
}
}
}
2020-07-17 16:35:39 +02:00
}
2020-07-28 22:25:10 +02:00
override fun doFinally(activity: AppCompatActivity) {
unselectAll()
2020-07-17 16:35:39 +02:00
}
}
}
} else if (requestCode == PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
2020-07-28 22:25:10 +02:00
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()
2020-07-28 22:25:10 +02:00
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.path_join(currentDirectoryPath, File(paths[i]).name))
2020-07-28 22:25:10 +02:00
}
if (failedItem != null) {
break
}
}
2020-07-17 16:35:39 +02:00
}
2020-07-28 22:25:10 +02:00
} else {
failedItem = safeImportFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.path_join(currentDirectoryPath, File(path).name))
2020-07-28 22:25:10 +02:00
}
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()){
2020-07-28 22:25:10 +02:00
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
2020-07-17 16:35:39 +02:00
}
}
2020-07-28 22:25:10 +02:00
remoteGocryptfsVolume.close()
}
override fun doFinally(activity: AppCompatActivity) {
setCurrentPath(currentDirectoryPath)
2020-07-17 16:35:39 +02:00
}
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer, menu)
2020-08-09 14:34:42 +02:00
if (currentItemAction != ItemsActions.NONE) {
menu.findItem(R.id.validate).isVisible = true
menu.findItem(R.id.close).isVisible = false
} else {
handleMenuItems(menu)
if (usf_share){
menu.findItem(R.id.share).isVisible = false
2020-07-17 16:35:39 +02:00
}
val anyItemSelected = explorerAdapter.selectedItems.isNotEmpty()
menu.findItem(R.id.select_all).isVisible = anyItemSelected
menu.findItem(R.id.delete).isVisible = anyItemSelected
menu.findItem(R.id.copy).isVisible = anyItemSelected
2020-08-09 14:34:42 +02:00
menu.findItem(R.id.cut).isVisible = anyItemSelected
menu.findItem(R.id.decrypt).isVisible = anyItemSelected && usf_decrypt
if (anyItemSelected && usf_share){
var containsDir = false
for (i in explorerAdapter.selectedItems) {
if (explorerElements[i].isDirectory) {
containsDir = true
break
}
}
if (!containsDir) {
menu.findItem(R.id.share).isVisible = true
}
2020-07-17 16:35:39 +02:00
}
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
2020-08-09 14:34:42 +02:00
cancelItemAction()
super.onOptionsItemSelected(item)
}
R.id.select_all -> {
explorerAdapter.selectAll()
2020-07-17 16:35:39 +02:00
invalidateOptionsMenu()
true
}
2020-08-09 14:34:42 +02:00
R.id.cut -> {
for (i in explorerAdapter.selectedItems){
itemsToProcess.add(explorerElements[i])
}
currentItemAction = ItemsActions.MOVE
unselectAll()
true
}
R.id.copy -> {
for (i in explorerAdapter.selectedItems){
2020-08-09 14:34:42 +02:00
itemsToProcess.add(explorerElements[i])
}
2020-08-09 14:34:42 +02:00
currentItemAction = ItemsActions.COPY
unselectAll()
true
}
R.id.validate -> {
2020-08-09 14:34:42 +02:00
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.path_join(currentDirectoryPath, element.name), element.isDirectory)
failedItem = if (dstPath == null){
""
} else {
2020-08-09 14:34:42 +02:00
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)
.setTitle(R.string.error)
.setMessage(getString(
R.string.copy_failed,
failedItem
))
.setPositiveButton(R.string.ok, null)
.show()
}
}
break
}
}
2020-08-09 14:34:42 +02:00
if (failedItem == null) {
stopTask {
ColoredAlertDialogBuilder(activity)
2020-08-09 14:34:42 +02:00
.setTitle(getString(R.string.copy_success))
.setMessage(getString(R.string.copy_success_msg))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
2020-08-09 14:34:42 +02:00
override fun doFinally(activity: AppCompatActivity) {
cancelItemAction()
unselectAll()
setCurrentPath(currentDirectoryPath)
}
}
2020-08-09 14:34:42 +02:00
} 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()
}
}
}
override fun doFinally(activity: AppCompatActivity) {
cancelItemAction()
unselectAll()
setCurrentPath(currentDirectoryPath)
}
}
}
true
}
R.id.delete -> {
val size = explorerAdapter.selectedItems.size
2020-07-27 16:20:52 +02:00
val dialog = ColoredAlertDialogBuilder(this)
2020-07-17 16:35:39 +02:00
dialog.setTitle(R.string.warning)
dialog.setPositiveButton(R.string.ok) { _, _ -> removeSelectedItems() }
2020-07-17 16:35:39 +02:00
dialog.setNegativeButton(R.string.cancel, null)
if (size > 1) {
dialog.setMessage(getString(R.string.multiple_delete_confirm, explorerAdapter.selectedItems.size.toString()))
2020-07-17 16:35:39 +02:00
} else {
dialog.setMessage(getString(R.string.single_delete_confirm, explorerAdapter.getItem(explorerAdapter.selectedItems[0]).name))
2020-07-17 16:35:39 +02:00
}
dialog.show()
true
}
R.id.share -> {
2020-07-17 16:35:39 +02:00
val paths: MutableList<String> = ArrayList()
for (i in explorerAdapter.selectedItems) {
2020-08-05 14:06:54 +02:00
paths.add(explorerElements[i].fullPath)
2020-07-17 16:35:39 +02:00
}
isStartingActivity = true
2020-07-17 16:35:39 +02:00
ExternalProvider.share(this, gocryptfsVolume, paths)
2020-07-28 22:25:10 +02:00
unselectAll()
2020-07-17 16:35:39 +02:00
true
}
R.id.decrypt -> {
2020-07-17 16:35:39 +02:00
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
isStartingActivity = true
2020-07-17 16:35:39 +02:00
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
true
}
else -> super.onOptionsItemSelected(item)
}
}
2020-08-09 14:34:42 +02:00
private fun cancelItemAction() {
if (currentItemAction != ItemsActions.NONE){
currentItemAction = ItemsActions.NONE
itemsToProcess.clear()
}
}
2020-08-04 11:44:29 +02:00
override fun onBackPressed() {
2020-08-09 14:34:42 +02:00
if (currentItemAction != ItemsActions.NONE) {
cancelItemAction()
2020-08-04 11:44:29 +02:00
invalidateOptionsMenu()
} else {
super.onBackPressed()
}
}
private fun copyFile(srcPath: String, dstPath: String): Boolean {
var success = true
2020-08-01 16:43:48 +02:00
val originalHandleId = gocryptfsVolume.openReadMode(srcPath)
if (originalHandleId != -1){
2020-08-01 16:43:48 +02:00
val newHandleId = gocryptfsVolume.openWriteMode(dstPath)
if (newHandleId != -1){
var offset: Long = 0
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
var length: Int
2020-08-01 16:43:48 +02:00
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
}
}
2020-08-01 16:43:48 +02:00
gocryptfsVolume.closeFile(newHandleId)
} else {
success = false
}
2020-08-01 16:43:48 +02:00
gocryptfsVolume.closeFile(originalHandleId)
} else {
success = false
}
return success
}
2020-08-09 14:34:42 +02:00
private fun recursiveCopyDirectory(srcDirectoryPath: String, dstDirectoryPath: String): String? {
2020-08-01 16:43:48 +02:00
val mappedElements = gocryptfsVolume.recursiveMapFiles(srcDirectoryPath)
2020-08-09 14:34:42 +02:00
if (!gocryptfsVolume.pathExists(dstDirectoryPath)){
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
2020-08-09 14:34:42 +02:00
return srcDirectoryPath
}
}
for (e in mappedElements) {
2020-08-09 14:34:42 +02:00
val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath)), e.isDirectory)
if (dstPath == null){
return ""
} else {
2020-08-09 14:34:42 +02:00
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
}
2020-08-09 14:34:42 +02:00
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.path_join(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 {
2020-07-17 16:35:39 +02:00
var success = true
val srcHandleID = remoteGocryptfsVolume.openReadMode(srcPath)
if (srcHandleID != -1) {
2020-08-01 16:43:48 +02:00
val dstHandleID = gocryptfsVolume.openWriteMode(dstPath)
if (dstHandleID != -1) {
2020-07-17 16:35:39 +02:00
var length: Int
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
2020-07-17 16:35:39 +02:00
var offset: Long = 0
2020-08-09 14:34:42 +02:00
while (remoteGocryptfsVolume.readFile(srcHandleID, offset, ioBuffer).also { length = it } > 0) {
val written =
gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong()
2020-07-17 16:35:39 +02:00
if (written == length.toLong()) {
offset += length.toLong()
} else {
success = false
break
}
}
2020-08-01 16:43:48 +02:00
gocryptfsVolume.closeFile(dstHandleID)
2020-07-17 16:35:39 +02:00
}
remoteGocryptfsVolume.closeFile(srcHandleID)
2020-07-17 16:35:39 +02:00
}
return success
}
private fun safeImportFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): String? {
2020-08-09 14:34:42 +02:00
val checkedDstPath = checkPathOverwrite(dstPath, false)
return if (checkedDstPath == null){
""
} else {
2020-08-09 14:34:42 +02:00
if (importFileFromOtherVolume(remoteGocryptfsVolume, srcPath, checkedDstPath)) null else srcPath
}
}
private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? {
2020-08-29 20:48:12 +02:00
val mappedElements = remote_gocryptfsVolume.recursiveMapFiles(remote_directory_path)
2020-08-09 14:34:42 +02:00
val dstDirectoryPath = checkPathOverwrite(PathUtils.path_join(outputPath, File(remote_directory_path).name), true)
if (dstDirectoryPath == null){
return ""
} else {
if (!gocryptfsVolume.pathExists(dstDirectoryPath)) {
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
return remote_directory_path
}
2020-08-09 14:34:42 +02:00
}
for (e in mappedElements) {
val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath)), e.isDirectory)
if (dstPath == null){
return ""
} else {
2020-08-09 14:34:42 +02:00
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
}
}
2020-07-17 16:35:39 +02:00
}
}
}
return null
}
2020-08-29 20:48:12 +02:00
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? {
2020-09-05 11:37:47 +02:00
treeDocumentFile.createDirectory(File(plain_directory_path).name)?.let {childTree ->
2020-08-01 16:43:48 +02:00
val explorerElements = gocryptfsVolume.listDir(plain_directory_path)
for (e in explorerElements) {
val fullPath = PathUtils.path_join(plain_directory_path, e.name)
2020-07-17 16:35:39 +02:00
if (e.isDirectory) {
2020-08-29 20:48:12 +02:00
val failedItem = recursiveExportDirectory(fullPath, childTree)
failedItem?.let { return it }
2020-07-17 16:35:39 +02:00
} else {
2020-08-29 20:48:12 +02:00
if (!exportFileInto(fullPath, childTree)){
return fullPath
2020-07-17 16:35:39 +02:00
}
}
}
return null
}
2020-08-29 20:48:12 +02:00
return treeDocumentFile.name
2020-07-17 16:35:39 +02:00
}
private fun recursiveRemoveDirectory(plain_directory_path: String): String? {
2020-08-01 16:43:48 +02:00
val explorerElements = gocryptfsVolume.listDir(plain_directory_path)
for (e in explorerElements) {
val fullPath = PathUtils.path_join(plain_directory_path, e.name)
2020-07-17 16:35:39 +02:00
if (e.isDirectory) {
val result = recursiveRemoveDirectory(fullPath)
2020-07-17 16:35:39 +02:00
result?.let { return it }
} else {
2020-08-01 16:43:48 +02:00
if (!gocryptfsVolume.removeFile(fullPath)) {
return fullPath
2020-07-17 16:35:39 +02:00
}
}
}
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.path_join(currentDirectoryPath, element.name)
2020-07-17 16:35:39 +02:00
if (element.isDirectory) {
val result = recursiveRemoveDirectory(fullPath)
result?.let{ failedItem = it }
2020-07-17 16:35:39 +02:00
} else {
2020-08-01 16:43:48 +02:00
if (!gocryptfsVolume.removeFile(fullPath)) {
failedItem = fullPath
2020-07-17 16:35:39 +02:00
}
}
if (failedItem != null) {
2020-07-27 16:20:52 +02:00
ColoredAlertDialogBuilder(this)
2020-07-17 16:35:39 +02:00
.setTitle(R.string.error)
.setMessage(getString(R.string.remove_failed, failedItem))
2020-07-17 16:35:39 +02:00
.setPositiveButton(R.string.ok, null)
.show()
break
}
}
2020-07-28 22:25:10 +02:00
unselectAll()
setCurrentPath(currentDirectoryPath) //refresh
2020-07-17 16:35:39 +02:00
}
}