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

360 lines
15 KiB
Kotlin
Raw Normal View History

2020-07-17 16:35:39 +02:00
package sushi.hardcore.droidfs.explorers
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.widget.AdapterView.OnItemClickListener
import android.widget.AdapterView.OnItemLongClickListener
import android.widget.EditText
import android.widget.Toast
import com.github.clans.fab.FloatingActionMenu
2020-07-19 21:46:29 +02:00
import kotlinx.android.synthetic.main.activity_explorer_base.*
2020-07-17 16:35:39 +02:00
import kotlinx.android.synthetic.main.explorer_info_bar.*
import kotlinx.android.synthetic.main.toolbar.*
2020-07-21 15:05:05 +02:00
import sushi.hardcore.droidfs.BaseActivity
2020-07-17 16:35:39 +02:00
import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.ConstValues.Companion.isAudio
import sushi.hardcore.droidfs.ConstValues.Companion.isImage
import sushi.hardcore.droidfs.ConstValues.Companion.isText
import sushi.hardcore.droidfs.ConstValues.Companion.isVideo
import sushi.hardcore.droidfs.R
2020-07-27 16:20:52 +02:00
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
2020-07-17 16:35:39 +02:00
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter
import sushi.hardcore.droidfs.file_viewers.AudioPlayer
import sushi.hardcore.droidfs.file_viewers.ImageViewer
import sushi.hardcore.droidfs.file_viewers.TextEditor
import sushi.hardcore.droidfs.file_viewers.VideoPlayer
import sushi.hardcore.droidfs.provider.RestrictedFileProvider
2020-07-17 16:35:39 +02:00
import sushi.hardcore.droidfs.util.ExternalProvider
import sushi.hardcore.droidfs.util.PathUtils
2020-07-17 16:35:39 +02:00
import sushi.hardcore.droidfs.util.GocryptfsVolume
2020-07-27 16:20:52 +02:00
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
2020-07-17 16:35:39 +02:00
import java.util.*
2020-07-21 15:05:05 +02:00
open class BaseExplorerActivity : BaseActivity() {
private lateinit var sortModesEntries: Array<String>
private lateinit var sortModesValues: Array<String>
private var currentSortModeIndex = 0
2020-07-17 16:35:39 +02:00
protected lateinit var gocryptfsVolume: GocryptfsVolume
private lateinit var volumeName: String
protected var currentDirectoryPath = ""
protected lateinit var explorerElements: MutableList<ExplorerElement>
protected lateinit var explorerAdapter: ExplorerElementAdapter
2020-07-17 16:35:39 +02:00
private var usf_open = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
usf_open = sharedPrefs.getBoolean("usf_open", false)
val intent = intent
volumeName = intent.getStringExtra("volume_name") ?: ""
2020-07-17 16:35:39 +02:00
val sessionID = intent.getIntExtra("sessionID", -1)
gocryptfsVolume = GocryptfsVolume(sessionID)
sortModesEntries = resources.getStringArray(R.array.sort_orders_entries)
sortModesValues = resources.getStringArray(R.array.sort_orders_values)
currentSortModeIndex = resources.getStringArray(R.array.sort_orders_values).indexOf(sharedPrefs.getString(ConstValues.sort_order_key, "name"))
2020-07-17 16:35:39 +02:00
init()
setSupportActionBar(toolbar)
title = ""
title_text.text = getString(R.string.volume, volumeName)
explorerAdapter = ExplorerElementAdapter(this)
setCurrentPath(currentDirectoryPath)
list_explorer.adapter = explorerAdapter
2020-07-17 16:35:39 +02:00
list_explorer.onItemClickListener = OnItemClickListener { _, _, position, _ -> onExplorerItemClick(position) }
list_explorer.onItemLongClickListener = OnItemLongClickListener { _, _, position, _ -> onExplorerItemLongClick(position); true }
2020-07-17 16:35:39 +02:00
refresher.setOnRefreshListener {
setCurrentPath(currentDirectoryPath)
2020-07-17 16:35:39 +02:00
refresher.isRefreshing = false
}
}
protected open fun init() {
2020-07-19 21:46:29 +02:00
setContentView(R.layout.activity_explorer_base)
2020-07-17 16:35:39 +02:00
}
private fun startFileViewer(cls: Class<*>, filePath: String){
val intent = Intent(this, cls)
intent.putExtra("path", filePath)
intent.putExtra("sessionID", gocryptfsVolume.sessionID)
startActivity(intent)
}
protected open fun onExplorerItemClick(position: Int) {
val wasSelecting = explorerAdapter.selectedItems.isNotEmpty()
explorerAdapter.onItemClick(position)
if (explorerAdapter.selectedItems.isEmpty()) {
2020-07-17 16:35:39 +02:00
if (!wasSelecting) {
val fullPath = explorerElements[position].getFullPath()
2020-07-17 16:35:39 +02:00
when {
explorerElements[position].isDirectory -> {
setCurrentPath(fullPath)
2020-07-17 16:35:39 +02:00
}
explorerElements[position].isParentFolder -> {
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
2020-07-17 16:35:39 +02:00
}
isImage(fullPath) -> {
startFileViewer(ImageViewer::class.java, fullPath)
2020-07-17 16:35:39 +02:00
}
isVideo(fullPath) -> {
startFileViewer(VideoPlayer::class.java, fullPath)
2020-07-17 16:35:39 +02:00
}
isText(fullPath) -> {
startFileViewer(TextEditor::class.java, fullPath)
2020-07-17 16:35:39 +02:00
}
isAudio(fullPath) -> {
startFileViewer(AudioPlayer::class.java, fullPath)
2020-07-17 16:35:39 +02:00
}
else -> {
val adapter = OpenAsDialogAdapter(this)
2020-07-27 16:20:52 +02:00
ColoredAlertDialogBuilder(this)
.setSingleChoiceItems(adapter, -1){ dialog, which ->
when (adapter.getItem(which)){
"image" -> startFileViewer(ImageViewer::class.java, fullPath)
"video" -> startFileViewer(VideoPlayer::class.java, fullPath)
"audio" -> startFileViewer(AudioPlayer::class.java, fullPath)
"text" -> startFileViewer(TextEditor::class.java, fullPath)
}
dialog.dismiss()
}
2020-07-17 16:35:39 +02:00
.setTitle(getString(R.string.open_as))
.setNegativeButton(R.string.cancel, null)
.create()
2020-07-27 16:20:52 +02:00
.show()
2020-07-17 16:35:39 +02:00
}
}
} else {
invalidateOptionsMenu()
2020-07-17 16:35:39 +02:00
}
}
}
protected open fun onExplorerItemLongClick(position: Int) {
explorerAdapter.onItemLongClick(position)
2020-07-17 16:35:39 +02:00
invalidateOptionsMenu()
}
2020-07-28 22:25:10 +02:00
protected fun unselectAll(){
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
}
2020-07-18 18:44:53 +02:00
private fun sortExplorerElements() {
when (sortModesValues[currentSortModeIndex]) {
2020-07-17 16:35:39 +02:00
"name" -> {
explorerElements.sortWith(Comparator { o1, o2 -> o1.name.compareTo(o2.name) })
2020-07-17 16:35:39 +02:00
}
"size" -> {
explorerElements.sortWith(Comparator { o1, o2 -> (o1.size - o2.size).toInt() })
2020-07-17 16:35:39 +02:00
}
"date" -> {
explorerElements.sortWith(Comparator { o1, o2 -> o1.mTime.compareTo(o2.mTime) })
2020-07-17 16:35:39 +02:00
}
"name_desc" -> {
explorerElements.sortWith(Comparator { o1, o2 -> o2.name.compareTo(o1.name) })
2020-07-17 16:35:39 +02:00
}
"size_desc" -> {
explorerElements.sortWith(Comparator { o1, o2 -> (o2.size - o1.size).toInt() })
2020-07-17 16:35:39 +02:00
}
"date_desc" -> {
explorerElements.sortWith(Comparator { o1, o2 -> o2.mTime.compareTo(o1.mTime) })
2020-07-17 16:35:39 +02:00
}
}
val sharedPrefsEditor = sharedPrefs.edit()
sharedPrefsEditor.putString(ConstValues.sort_order_key, sortModesValues[currentSortModeIndex])
sharedPrefsEditor.apply()
2020-07-17 16:35:39 +02:00
}
protected fun setCurrentPath(path: String) {
2020-08-01 16:43:48 +02:00
explorerElements = gocryptfsVolume.listDir(path)
text_dir_empty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.INVISIBLE
2020-07-18 18:44:53 +02:00
sortExplorerElements()
2020-07-17 16:35:39 +02:00
if (path.isNotEmpty()) { //not root
explorerElements.add(0, ExplorerElement("..", (-1).toShort(), -1, -1, currentDirectoryPath))
2020-07-17 16:35:39 +02:00
}
explorerAdapter.setExplorerElements(explorerElements)
currentDirectoryPath = path
current_path_text.text = getString(R.string.location, currentDirectoryPath)
total_size_text.text = getString(R.string.total_size, PathUtils.formatSize(explorerAdapter.currentDirectoryTotalSize))
2020-07-17 16:35:39 +02:00
}
private fun askCloseVolume() {
2020-07-27 16:20:52 +02:00
ColoredAlertDialogBuilder(this)
2020-07-17 16:35:39 +02:00
.setTitle(R.string.warning)
.setMessage(R.string.ask_close_volume)
.setPositiveButton(R.string.ok) { _, _ -> closeVolumeOnUserExit() }
.setNegativeButton(R.string.cancel, null)
.show()
}
protected open fun closeVolumeOnUserExit() {
finish()
}
protected open fun closeVolumeOnDestroy() {
gocryptfsVolume.close()
RestrictedFileProvider.wipeAll(this) //additional security
2020-07-17 16:35:39 +02:00
}
override fun onBackPressed() {
if (explorerAdapter.selectedItems.isEmpty()) {
val parentPath = PathUtils.getParentPath(currentDirectoryPath)
if (parentPath == currentDirectoryPath) {
2020-07-17 16:35:39 +02:00
askCloseVolume()
} else {
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
2020-07-17 16:35:39 +02:00
}
} else {
2020-07-28 22:25:10 +02:00
unselectAll()
2020-07-17 16:35:39 +02:00
}
}
2020-07-18 18:44:53 +02:00
private fun createFolder(folder_name: String){
2020-07-17 16:35:39 +02:00
if (folder_name.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else {
if (!gocryptfsVolume.mkdir(PathUtils.path_join(currentDirectoryPath, folder_name))) {
2020-07-27 16:20:52 +02:00
ColoredAlertDialogBuilder(this)
2020-07-17 16:35:39 +02:00
.setTitle(R.string.error)
.setMessage(R.string.error_mkdir)
.setPositiveButton(R.string.ok, null)
.show()
} else {
setCurrentPath(currentDirectoryPath)
2020-07-17 16:35:39 +02:00
invalidateOptionsMenu()
}
}
}
fun onClickAddFolder(view: View?) {
findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true)
2020-07-19 21:46:29 +02:00
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
2020-07-27 16:20:52 +02:00
val dialog = ColoredAlertDialogBuilder(this)
2020-07-19 21:46:29 +02:00
.setView(dialogEditTextView)
2020-07-17 16:35:39 +02:00
.setTitle(R.string.enter_folder_name)
.setPositiveButton(R.string.ok) { _, _ ->
2020-07-19 21:46:29 +02:00
val folderName = dialogEditText.text.toString()
createFolder(folderName)
2020-07-17 16:35:39 +02:00
}
.setNegativeButton(R.string.cancel, null)
.create()
2020-07-19 21:46:29 +02:00
dialogEditText.setOnEditorActionListener { _, _, _ ->
val folderName = dialogEditText.text.toString()
2020-07-17 16:35:39 +02:00
dialog.dismiss()
2020-07-19 21:46:29 +02:00
createFolder(folderName)
2020-07-17 16:35:39 +02:00
true
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
}
fun rename(old_name: String, new_name: String){
if (new_name.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else {
if (!gocryptfsVolume.rename(PathUtils.path_join(currentDirectoryPath, old_name), PathUtils.path_join(currentDirectoryPath, new_name))) {
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.rename_failed, old_name))
.setPositiveButton(R.string.ok, null)
.show()
} else {
setCurrentPath(currentDirectoryPath)
2020-07-17 16:35:39 +02:00
invalidateOptionsMenu()
}
}
}
2020-07-18 18:44:53 +02:00
fun handleMenuItems(menu: Menu){
menu.findItem(R.id.rename).isVisible = false
2020-07-17 16:35:39 +02:00
if (usf_open){
menu.findItem(R.id.external_open)?.isVisible = false
2020-07-17 16:35:39 +02:00
}
val noItemSelected = explorerAdapter.selectedItems.isEmpty()
menu.findItem(R.id.sort).isVisible = noItemSelected
menu.findItem(R.id.close).isVisible = noItemSelected
if (noItemSelected){
2020-07-17 16:35:39 +02:00
toolbar.navigationIcon = null
} else {
toolbar.setNavigationIcon(R.drawable.icon_arrow_back)
if (explorerAdapter.selectedItems.size == 1) {
menu.findItem(R.id.rename).isVisible = true
if (usf_open && explorerElements[explorerAdapter.selectedItems[0]].isRegularFile) {
menu.findItem(R.id.external_open)?.isVisible = true
2020-07-17 16:35:39 +02:00
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
2020-07-28 22:25:10 +02:00
unselectAll()
2020-07-17 16:35:39 +02:00
true
}
R.id.sort -> {
2020-07-27 16:20:52 +02:00
ColoredAlertDialogBuilder(this)
2020-07-17 16:35:39 +02:00
.setTitle(R.string.sort_order)
2020-07-27 16:20:52 +02:00
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortModesEntries), currentSortModeIndex) { dialog, which ->
currentSortModeIndex = which
setCurrentPath(currentDirectoryPath)
2020-07-17 16:35:39 +02:00
dialog.dismiss()
2020-07-27 16:20:52 +02:00
}
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.show()
2020-07-17 16:35:39 +02:00
true
}
R.id.rename -> {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val oldName = explorerElements[explorerAdapter.selectedItems[0]].name
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
dialogEditText.setText(oldName)
dialogEditText.selectAll()
2020-07-27 16:20:52 +02:00
val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView)
2020-07-17 16:35:39 +02:00
.setTitle(R.string.rename_title)
.setPositiveButton(R.string.ok) { _, _ ->
val newName = dialogEditText.text.toString()
rename(oldName, newName)
2020-07-17 16:35:39 +02:00
}
.setNegativeButton(R.string.cancel, null)
.create()
dialogEditText.setOnEditorActionListener { _, _, _ ->
val newName = dialogEditText.text.toString()
2020-07-17 16:35:39 +02:00
dialog.dismiss()
rename(oldName, newName)
2020-07-17 16:35:39 +02:00
true
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
true
}
R.id.external_open -> {
2020-07-17 16:35:39 +02:00
if (usf_open){
ExternalProvider.open(this, gocryptfsVolume, PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
2020-07-28 22:25:10 +02:00
unselectAll()
2020-07-17 16:35:39 +02:00
}
true
}
R.id.close -> {
2020-07-17 16:35:39 +02:00
askCloseVolume()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onDestroy() {
super.onDestroy()
if (!isChangingConfigurations) { //activity won't be recreated
closeVolumeOnDestroy()
}
}
override fun onResume() {
super.onResume()
ExternalProvider.removeFiles(this)
2020-07-17 16:35:39 +02:00
}
}