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

369 lines
17 KiB
Kotlin

package sushi.hardcore.droidfs.explorers
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.view.Menu
import android.view.MenuItem
import android.view.View
import kotlinx.android.synthetic.main.activity_explorer.*
import sushi.hardcore.droidfs.OpenActivity
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.ExternalProvider
import sushi.hardcore.droidfs.util.FilesUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog
import java.io.File
import java.util.*
class ExplorerActivity : ExplorerActivityRO() {
private val PICK_DIRECTORY_REQUEST_CODE = 1
private val PICK_FILES_REQUEST_CODE = 2
private val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3
private var usf_decrypt = false
private var usf_share = false
override fun init() {
setContentView(R.layout.activity_explorer)
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false)
usf_share = sharedPrefs.getBoolean("usf_share", false)
}
fun onClickAddFile(view: View?) {
fam_explorer.close(true)
val i = Intent(Intent.ACTION_OPEN_DOCUMENT)
i.type = "*/*"
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
i.addCategory(Intent.CATEGORY_OPENABLE)
startActivityForResult(i, PICK_FILES_REQUEST_CODE)
}
fun onClickAddFileFromOtherVolume(view: View?) {
fam_explorer.close(true)
val intent = Intent(this, OpenActivity::class.java)
intent.action = "pick"
startActivityForResult(intent, PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE)
}
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) {
val uris: MutableList<Uri> = ArrayList()
val single_uri = data.data
if (single_uri == 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(single_uri)
}
if (uris.isNotEmpty()){
var success = true
for (uri in uris) {
val dst_path = FilesUtils.path_join(current_path, FilesUtils.getFilenameFromURI(this, uri))
var `is` = contentResolver.openInputStream(uri)
if (`is` != null) {
success = gocryptfsVolume.import_file(`is`, dst_path)
}
if (!success) {
ColoredAlertDialog(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, uri))
.setPositiveButton(R.string.ok, null)
.show()
break
}
}
if (success) {
ColoredAlertDialog(this)
.setTitle(R.string.success_import)
.setMessage("""
${getString(R.string.success_import_msg)}
${getString(R.string.ask_for_wipe)}
""".trimIndent())
.setPositiveButton(R.string.yes) { _, _ ->
success = true
for (uri in uris) {
if (!Wiper.wipe(this, uri)) {
ColoredAlertDialog(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.wipe_failed, uri))
.setPositiveButton(R.string.ok, null)
.show()
success = false
break
}
}
if (success) {
ColoredAlertDialog(this)
.setTitle(R.string.wipe_successful)
.setMessage(R.string.wipe_success_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
.setNegativeButton(getString(R.string.no), null)
.show()
}
setCurrentPath(current_path)
}
}
} else if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
val uri = data.data
val output_dir = FilesUtils.getFullPathFromTreeUri(uri, this)
var failed_item: String? = null
for (i in explorer_adapter.selectedItems) {
val element = explorer_adapter.getItem(i)
val full_path = FilesUtils.path_join(current_path, element.name)
failed_item = if (element.isDirectory) {
recursive_export_directory(full_path, output_dir)
} else {
if (gocryptfsVolume.export_file(full_path, FilesUtils.path_join(output_dir, element.name))) null else full_path
}
if (failed_item != null) {
ColoredAlertDialog(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.export_failed, failed_item))
.setPositiveButton(R.string.ok, null)
.show()
break
}
}
if (failed_item == null) {
ColoredAlertDialog(this)
.setTitle(R.string.success_export)
.setMessage(R.string.success_export_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
explorer_adapter.unSelectAll()
invalidateOptionsMenu()
} else if (requestCode == PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
val remote_sessionID = data.getIntExtra("sessionID", -1)
val remote_gocryptfsVolume = GocryptfsVolume(remote_sessionID)
val path = data.getStringExtra("path")
var failed_item: String? = null
if (path == null) {
val paths = data.getStringArrayListExtra("paths")
val types = data.getIntegerArrayListExtra("types")
if (types != null && paths != null){
for (i in paths.indices) {
failed_item = if (types[i] == 0) { //directory
recursive_import_directory_from_other_volume(remote_gocryptfsVolume, paths[i], current_path)
} else {
if (import_file_from_other_volume(remote_gocryptfsVolume, paths[i], current_path)) null else paths[i]
}
if (failed_item != null) {
break
}
}
}
} else {
failed_item = if (import_file_from_other_volume(remote_gocryptfsVolume, path, current_path)) null else path
}
if (failed_item == null) {
ColoredAlertDialog(this)
.setTitle(R.string.success_import)
.setMessage(R.string.success_import_msg)
.setPositiveButton(R.string.ok, null)
.show()
} else {
ColoredAlertDialog(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failed_item))
.setPositiveButton(R.string.ok, null)
.show()
}
remote_gocryptfsVolume.close()
setCurrentPath(current_path)
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer, menu)
handle_menu_items(menu)
if (usf_share){
menu.findItem(R.id.explorer_menu_share).isVisible = false
}
val any_item_selected = explorer_adapter.selectedItems.isNotEmpty()
menu.findItem(R.id.explorer_menu_select_all).isVisible = any_item_selected
menu.findItem(R.id.explorer_menu_delete).isVisible = any_item_selected
menu.findItem(R.id.explorer_menu_decrypt).isVisible = any_item_selected && usf_decrypt
if (any_item_selected && usf_share){
var containsDir = false
for (i in explorer_adapter.selectedItems) {
if (explorer_elements[i].isDirectory) {
containsDir = true
break
}
}
if (!containsDir) {
menu.findItem(R.id.explorer_menu_share).isVisible = true
}
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.explorer_menu_select_all -> {
explorer_adapter.selectAll()
invalidateOptionsMenu()
true
}
R.id.explorer_menu_delete -> {
val size = explorer_adapter.selectedItems.size
val dialog = ColoredAlertDialog(this)
dialog.setTitle(R.string.warning)
dialog.setPositiveButton(R.string.ok) { _, _ -> remove_selected_items() }
dialog.setNegativeButton(R.string.cancel, null)
if (size > 1) {
dialog.setMessage(getString(R.string.multiple_delete_confirm, explorer_adapter.selectedItems.size.toString()))
} else {
dialog.setMessage(getString(R.string.single_delete_confirm, explorer_adapter.getItem(explorer_adapter.selectedItems[0]).name))
}
dialog.show()
true
}
R.id.explorer_menu_share -> {
val paths: MutableList<String> = ArrayList()
for (i in explorer_adapter.selectedItems) {
val e = explorer_elements[i]
paths.add(FilesUtils.path_join(current_path, e.name))
}
ExternalProvider.share(this, gocryptfsVolume, paths)
explorer_adapter.unSelectAll()
invalidateOptionsMenu()
true
}
R.id.explorer_menu_decrypt -> {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun import_file_from_other_volume(remote_gocryptfsVolume: GocryptfsVolume, full_path: String, output_dir: String): Boolean {
val output_path = FilesUtils.path_join(output_dir, File(full_path).name)
var success = true
val src_handleID = remote_gocryptfsVolume.open_read_mode(full_path)
if (src_handleID != -1) {
val dst_handleID = gocryptfsVolume.open_write_mode(output_path)
if (dst_handleID != -1) {
var length: Int
val io_buffer = ByteArray(GocryptfsVolume.DefaultBS)
var offset: Long = 0
while (remote_gocryptfsVolume.read_file(src_handleID, offset, io_buffer).also { length = it } > 0){
val written = gocryptfsVolume.write_file(dst_handleID, offset, io_buffer, length).toLong()
if (written == length.toLong()) {
offset += length.toLong()
} else {
success = false
break
}
}
gocryptfsVolume.close_file(dst_handleID)
}
remote_gocryptfsVolume.close_file(src_handleID)
}
return success
}
private fun recursive_import_directory_from_other_volume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, output_dir: String): String? {
val directory_path = FilesUtils.path_join(output_dir, File(remote_directory_path).name)
if (!gocryptfsVolume.path_exists(directory_path)) {
if (!gocryptfsVolume.mkdir(directory_path)) {
return directory_path
}
}
val explorer_elements = remote_gocryptfsVolume.list_dir(remote_directory_path)
for (e in explorer_elements) {
val full_path = FilesUtils.path_join(remote_directory_path, e.name)
if (e.isDirectory) {
val failed_item = recursive_import_directory_from_other_volume(remote_gocryptfsVolume, full_path, directory_path)
failed_item?.let { return it }
} else {
if (!import_file_from_other_volume(remote_gocryptfsVolume, full_path, directory_path)) {
return full_path
}
}
}
return null
}
private fun recursive_export_directory(plain_directory_path: String, output_dir: String?): String? {
if (File(FilesUtils.path_join(output_dir, plain_directory_path)).mkdir()) {
val explorer_elements = gocryptfsVolume.list_dir(plain_directory_path)
for (e in explorer_elements) {
val full_path = FilesUtils.path_join(plain_directory_path, e.name)
if (e.isDirectory) {
val failed_item = recursive_export_directory(full_path, output_dir)
failed_item?.let { return it }
} else {
if (!gocryptfsVolume.export_file(full_path, FilesUtils.path_join(output_dir, full_path))) {
return full_path
}
}
}
return null
}
return output_dir
}
private fun recursive_remove_directory(plain_directory_path: String): String? {
val explorer_elements = gocryptfsVolume.list_dir(plain_directory_path)
for (e in explorer_elements) {
val full_path = FilesUtils.path_join(plain_directory_path, e.name)
if (e.isDirectory) {
val result = recursive_remove_directory(full_path)
result?.let { return it }
} else {
if (!gocryptfsVolume.remove_file(full_path)) {
return full_path
}
}
}
return if (!gocryptfsVolume.rmdir(plain_directory_path)) {
plain_directory_path
} else {
null
}
}
private fun remove_selected_items() {
var failed_item: String? = null
for (i in explorer_adapter.selectedItems) {
val element = explorer_adapter.getItem(i)
val full_path = FilesUtils.path_join(current_path, element.name)
if (element.isDirectory) {
val result = recursive_remove_directory(full_path)
result?.let{ failed_item = it }
} else {
if (!gocryptfsVolume.remove_file(full_path)) {
failed_item = full_path
}
}
if (failed_item != null) {
ColoredAlertDialog(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.remove_failed, failed_item))
.setPositiveButton(R.string.ok, null)
.show()
break
}
}
explorer_adapter.unSelectAll()
invalidateOptionsMenu()
setCurrentPath(current_path) //refresh
}
}