2020-07-17 16:35:39 +02:00
|
|
|
package sushi.hardcore.droidfs.explorers
|
|
|
|
|
2020-12-29 17:05:02 +01:00
|
|
|
import android.content.ComponentName
|
|
|
|
import android.content.Context
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.content.Intent
|
2020-12-29 17:05:02 +01:00
|
|
|
import android.content.ServiceConnection
|
2020-11-03 17:22:09 +01:00
|
|
|
import android.net.Uri
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.os.Bundle
|
2020-12-29 17:05:02 +01:00
|
|
|
import android.os.IBinder
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.view.Menu
|
|
|
|
import android.view.MenuItem
|
|
|
|
import android.view.View
|
2022-04-09 15:09:14 +02:00
|
|
|
import android.widget.ImageButton
|
2021-06-11 20:23:54 +02:00
|
|
|
import android.widget.TextView
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.widget.Toast
|
2021-11-09 11:12:09 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
2020-08-04 11:44:29 +02:00
|
|
|
import androidx.lifecycle.ViewModel
|
|
|
|
import androidx.lifecycle.ViewModelProvider
|
2022-04-20 15:17:33 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2022-04-09 15:09:14 +02:00
|
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
2021-11-10 19:45:10 +01:00
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
2021-06-11 20:23:54 +02:00
|
|
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
2022-06-29 14:47:50 +02:00
|
|
|
import kotlinx.coroutines.*
|
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
|
2022-03-23 16:35:13 +01:00
|
|
|
import sushi.hardcore.droidfs.ConstValues.isAudio
|
|
|
|
import sushi.hardcore.droidfs.ConstValues.isImage
|
|
|
|
import sushi.hardcore.droidfs.ConstValues.isPDF
|
|
|
|
import sushi.hardcore.droidfs.ConstValues.isText
|
|
|
|
import sushi.hardcore.droidfs.ConstValues.isVideo
|
2020-07-17 16:35:39 +02:00
|
|
|
import sushi.hardcore.droidfs.R
|
|
|
|
import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter
|
2020-08-05 14:06:54 +02:00
|
|
|
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
|
2021-06-26 12:09:29 +02:00
|
|
|
import sushi.hardcore.droidfs.content_providers.ExternalProvider
|
|
|
|
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
|
|
|
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
2020-12-29 17:05:02 +01:00
|
|
|
import sushi.hardcore.droidfs.file_operations.OperationFile
|
2022-02-18 15:53:48 +01:00
|
|
|
import sushi.hardcore.droidfs.file_viewers.*
|
2022-06-18 21:13:16 +02:00
|
|
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
|
|
|
import sushi.hardcore.droidfs.filesystems.Stat
|
2022-09-30 21:22:37 +02:00
|
|
|
import sushi.hardcore.droidfs.util.IntentUtils
|
2020-08-05 14:06:54 +02:00
|
|
|
import sushi.hardcore.droidfs.util.PathUtils
|
2021-11-09 11:12:09 +01:00
|
|
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
2022-03-24 20:08:23 +01:00
|
|
|
import sushi.hardcore.droidfs.widgets.EditTextDialog
|
2020-07-17 16:35:39 +02:00
|
|
|
|
2022-04-17 15:52:34 +02:00
|
|
|
open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listener {
|
2020-08-04 11:44:29 +02:00
|
|
|
private lateinit var sortOrderEntries: Array<String>
|
|
|
|
private lateinit var sortOrderValues: Array<String>
|
2021-04-04 12:31:49 +02:00
|
|
|
private var foldersFirst = true
|
2021-07-10 12:02:56 +02:00
|
|
|
private var mapFolders = true
|
2020-08-04 11:44:29 +02:00
|
|
|
private var currentSortOrderIndex = 0
|
2022-06-18 21:13:16 +02:00
|
|
|
protected lateinit var encryptedVolume: EncryptedVolume
|
2020-07-26 21:16:06 +02:00
|
|
|
private lateinit var volumeName: String
|
2020-08-04 11:44:29 +02:00
|
|
|
private lateinit var explorerViewModel: ExplorerViewModel
|
|
|
|
protected var currentDirectoryPath: String = ""
|
|
|
|
set(value) {
|
|
|
|
field = value
|
|
|
|
explorerViewModel.currentDirectoryPath = value
|
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
protected lateinit var fileOperationService: FileOperationService
|
2022-06-29 14:47:50 +02:00
|
|
|
protected val taskScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
2020-07-26 21:16:06 +02:00
|
|
|
protected lateinit var explorerElements: MutableList<ExplorerElement>
|
|
|
|
protected lateinit var explorerAdapter: ExplorerElementAdapter
|
2020-08-05 14:06:54 +02:00
|
|
|
private var isCreating = true
|
2020-08-08 14:09:34 +02:00
|
|
|
protected var isStartingActivity = false
|
2020-07-17 16:35:39 +02:00
|
|
|
private var usf_open = false
|
2020-10-23 13:21:14 +02:00
|
|
|
protected var usf_keep_open = false
|
2022-04-09 15:09:14 +02:00
|
|
|
private lateinit var linearLayoutManager: LinearLayoutManager
|
|
|
|
private var isUsingListLayout = true
|
|
|
|
private lateinit var layoutIcon: ImageButton
|
2021-06-11 20:23:54 +02:00
|
|
|
private lateinit var titleText: TextView
|
2021-11-10 19:45:10 +01:00
|
|
|
private lateinit var recycler_view_explorer: RecyclerView
|
2021-06-11 20:23:54 +02:00
|
|
|
private lateinit var refresher: SwipeRefreshLayout
|
|
|
|
private lateinit var textDirEmpty: TextView
|
|
|
|
private lateinit var currentPathText: TextView
|
2022-04-10 17:08:13 +02:00
|
|
|
private lateinit var numberOfFilesText: TextView
|
|
|
|
private lateinit var numberOfFoldersText: TextView
|
2021-06-11 20:23:54 +02:00
|
|
|
private lateinit var totalSizeText: TextView
|
|
|
|
|
2020-07-17 16:35:39 +02:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
usf_open = sharedPrefs.getBoolean("usf_open", false)
|
2020-10-23 13:21:14 +02:00
|
|
|
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
|
2020-07-26 21:16:06 +02:00
|
|
|
volumeName = intent.getStringExtra("volume_name") ?: ""
|
2022-09-30 21:22:37 +02:00
|
|
|
encryptedVolume = IntentUtils.getParcelableExtra(intent, "volume")!!
|
2020-08-04 11:44:29 +02:00
|
|
|
sortOrderEntries = resources.getStringArray(R.array.sort_orders_entries)
|
|
|
|
sortOrderValues = resources.getStringArray(R.array.sort_orders_values)
|
2021-04-04 12:31:49 +02:00
|
|
|
foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
2021-07-10 12:02:56 +02:00
|
|
|
mapFolders = sharedPrefs.getBoolean("map_folders", true)
|
2022-03-23 16:35:13 +01:00
|
|
|
currentSortOrderIndex = resources.getStringArray(R.array.sort_orders_values).indexOf(sharedPrefs.getString(ConstValues.SORT_ORDER_KEY, "name"))
|
2020-07-17 16:35:39 +02:00
|
|
|
init()
|
2021-11-10 19:45:10 +01:00
|
|
|
recycler_view_explorer = findViewById(R.id.recycler_view_explorer)
|
2021-06-11 20:23:54 +02:00
|
|
|
refresher = findViewById(R.id.refresher)
|
|
|
|
textDirEmpty = findViewById(R.id.text_dir_empty)
|
|
|
|
currentPathText = findViewById(R.id.current_path_text)
|
2022-04-10 17:08:13 +02:00
|
|
|
numberOfFilesText = findViewById(R.id.number_of_files_text)
|
|
|
|
numberOfFoldersText = findViewById(R.id.number_of_folders_text)
|
2021-06-11 20:23:54 +02:00
|
|
|
totalSizeText = findViewById(R.id.total_size_text)
|
2022-03-05 12:51:02 +01:00
|
|
|
supportActionBar?.apply {
|
|
|
|
setDisplayShowCustomEnabled(true)
|
|
|
|
setCustomView(R.layout.action_bar)
|
|
|
|
titleText = customView.findViewById(R.id.title_text)
|
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
title = ""
|
2022-04-17 15:52:34 +02:00
|
|
|
setVolumeNameTitle()
|
2021-11-11 15:05:33 +01:00
|
|
|
explorerAdapter = ExplorerElementAdapter(
|
|
|
|
this,
|
|
|
|
if (sharedPrefs.getBoolean("thumbnails", true)) {
|
2022-06-18 21:13:16 +02:00
|
|
|
encryptedVolume
|
2021-11-11 15:05:33 +01:00
|
|
|
} else {
|
|
|
|
null
|
|
|
|
},
|
2022-04-17 15:52:34 +02:00
|
|
|
this,
|
2022-03-23 16:35:13 +01:00
|
|
|
sharedPrefs.getLong(ConstValues.THUMBNAIL_MAX_SIZE_KEY, ConstValues.DEFAULT_THUMBNAIL_MAX_SIZE)*1000,
|
2021-11-11 15:05:33 +01:00
|
|
|
)
|
2022-04-09 15:09:14 +02:00
|
|
|
explorerViewModel = ViewModelProvider(this).get(ExplorerViewModel::class.java)
|
2020-08-04 11:44:29 +02:00
|
|
|
currentDirectoryPath = explorerViewModel.currentDirectoryPath
|
2020-07-26 21:16:06 +02:00
|
|
|
setCurrentPath(currentDirectoryPath)
|
2022-04-09 15:09:14 +02:00
|
|
|
linearLayoutManager = LinearLayoutManager(this@BaseExplorerActivity)
|
|
|
|
recycler_view_explorer.adapter = explorerAdapter
|
|
|
|
isUsingListLayout = sharedPrefs.getBoolean("useListLayout", true)
|
|
|
|
layoutIcon = findViewById(R.id.layout_icon)
|
|
|
|
setRecyclerViewLayout()
|
|
|
|
layoutIcon.setOnClickListener {
|
|
|
|
isUsingListLayout = !isUsingListLayout
|
|
|
|
setRecyclerViewLayout()
|
|
|
|
recycler_view_explorer.recycledViewPool.clear()
|
|
|
|
with (sharedPrefs.edit()) {
|
|
|
|
putBoolean("useListLayout", isUsingListLayout)
|
|
|
|
apply()
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
refresher.setOnRefreshListener {
|
2020-07-26 21:16:06 +02:00
|
|
|
setCurrentPath(currentDirectoryPath)
|
2020-07-17 16:35:39 +02:00
|
|
|
refresher.isRefreshing = false
|
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
bindFileOperationService()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2020-08-04 11:44:29 +02:00
|
|
|
class ExplorerViewModel: ViewModel() {
|
2022-06-18 21:13:16 +02:00
|
|
|
var currentDirectoryPath = "/"
|
2020-08-04 11:44:29 +02:00
|
|
|
}
|
|
|
|
|
2022-04-09 15:09:14 +02:00
|
|
|
private fun setRecyclerViewLayout() {
|
|
|
|
layoutIcon.setImageResource(if (isUsingListLayout) {
|
|
|
|
recycler_view_explorer.layoutManager = linearLayoutManager
|
|
|
|
explorerAdapter.isUsingListLayout = true
|
2022-04-10 17:08:13 +02:00
|
|
|
R.drawable.icon_view_grid
|
2022-04-09 15:09:14 +02:00
|
|
|
} else {
|
|
|
|
val displayMetrics = resources.displayMetrics
|
|
|
|
val columnsNumber = (displayMetrics.widthPixels / displayMetrics.density / 200 + 0.5).toInt()
|
|
|
|
recycler_view_explorer.layoutManager = GridLayoutManager(this, columnsNumber)
|
|
|
|
explorerAdapter.isUsingListLayout = false
|
2022-04-10 17:08:13 +02:00
|
|
|
R.drawable.icon_view_list
|
2022-04-09 15:09:14 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-07-17 16:35:39 +02:00
|
|
|
protected open fun init() {
|
2022-09-30 21:22:37 +02:00
|
|
|
setContentView(R.layout.activity_explorer)
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2020-12-29 17:05:02 +01:00
|
|
|
protected open fun bindFileOperationService(){
|
|
|
|
Intent(this, FileOperationService::class.java).also {
|
|
|
|
bindService(it, object : ServiceConnection {
|
|
|
|
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
|
|
|
val binder = service as FileOperationService.LocalBinder
|
|
|
|
fileOperationService = binder.getService()
|
2022-06-18 21:13:16 +02:00
|
|
|
binder.setEncryptedVolume(encryptedVolume)
|
2020-12-29 17:05:02 +01:00
|
|
|
}
|
|
|
|
override fun onServiceDisconnected(arg0: ComponentName) {
|
|
|
|
|
|
|
|
}
|
|
|
|
}, Context.BIND_AUTO_CREATE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 21:11:14 +01:00
|
|
|
private fun startFileViewer(cls: Class<*>, filePath: String){
|
|
|
|
val intent = Intent(this, cls).apply {
|
|
|
|
putExtra("path", filePath)
|
2022-06-18 21:13:16 +02:00
|
|
|
putExtra("volume", encryptedVolume)
|
2021-03-17 21:11:14 +01:00
|
|
|
putExtra("sortOrder", sortOrderValues[currentSortOrderIndex])
|
2020-08-04 11:44:29 +02:00
|
|
|
}
|
2020-08-08 14:09:34 +02:00
|
|
|
isStartingActivity = true
|
2020-07-17 16:35:39 +02:00
|
|
|
startActivity(intent)
|
|
|
|
}
|
|
|
|
|
2020-09-05 11:37:47 +02:00
|
|
|
private fun openWithExternalApp(fullPath: String){
|
|
|
|
isStartingActivity = true
|
2022-06-18 21:13:16 +02:00
|
|
|
ExternalProvider.open(this, themeValue, encryptedVolume, fullPath)
|
2020-09-05 11:37:47 +02:00
|
|
|
}
|
|
|
|
|
2021-10-19 11:24:20 +02:00
|
|
|
private fun showOpenAsDialog(path: String) {
|
|
|
|
val adapter = OpenAsDialogAdapter(this, usf_open)
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2021-10-19 11:24:20 +02:00
|
|
|
.setSingleChoiceItems(adapter, -1) { dialog, which ->
|
|
|
|
when (adapter.getItem(which)) {
|
|
|
|
"image" -> startFileViewer(ImageViewer::class.java, path)
|
|
|
|
"video" -> startFileViewer(VideoPlayer::class.java, path)
|
|
|
|
"audio" -> startFileViewer(AudioPlayer::class.java, path)
|
2022-02-18 15:53:48 +01:00
|
|
|
"pdf" -> startFileViewer(PdfViewer::class.java, path)
|
2021-10-19 11:24:20 +02:00
|
|
|
"text" -> startFileViewer(TextEditor::class.java, path)
|
|
|
|
"external" -> if (usf_open) {
|
|
|
|
openWithExternalApp(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dialog.dismiss()
|
|
|
|
}
|
|
|
|
.setTitle(getString(R.string.open_as) + ':')
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
|
2022-04-17 15:52:34 +02:00
|
|
|
private fun setVolumeNameTitle() {
|
|
|
|
titleText.text = getString(R.string.volume, volumeName)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSelectionChanged(size: Int) {
|
|
|
|
if (size == 0) {
|
|
|
|
setVolumeNameTitle()
|
|
|
|
} else {
|
|
|
|
titleText.text = getString(R.string.elements_selected, size, explorerElements.count { !it.isParentFolder })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onExplorerElementClick(position: Int) {
|
2020-07-26 21:16:06 +02:00
|
|
|
if (explorerAdapter.selectedItems.isEmpty()) {
|
2022-03-05 12:51:02 +01:00
|
|
|
val fullPath = explorerElements[position].fullPath
|
|
|
|
when {
|
|
|
|
explorerElements[position].isDirectory -> {
|
|
|
|
setCurrentPath(fullPath)
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
explorerElements[position].isParentFolder -> {
|
|
|
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
|
|
|
}
|
|
|
|
isImage(fullPath) -> {
|
|
|
|
startFileViewer(ImageViewer::class.java, fullPath)
|
|
|
|
}
|
|
|
|
isVideo(fullPath) -> {
|
|
|
|
startFileViewer(VideoPlayer::class.java, fullPath)
|
|
|
|
}
|
|
|
|
isText(fullPath) -> {
|
|
|
|
startFileViewer(TextEditor::class.java, fullPath)
|
|
|
|
}
|
|
|
|
isPDF(fullPath) -> {
|
|
|
|
startFileViewer(PdfViewer::class.java, fullPath)
|
|
|
|
}
|
|
|
|
isAudio(fullPath) -> {
|
|
|
|
startFileViewer(AudioPlayer::class.java, fullPath)
|
|
|
|
}
|
|
|
|
else -> showOpenAsDialog(fullPath)
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 20:48:12 +02:00
|
|
|
invalidateOptionsMenu()
|
2020-08-01 15:39:00 +02:00
|
|
|
}
|
|
|
|
|
2022-04-17 15:52:34 +02:00
|
|
|
override fun onExplorerElementLongClick(position: Int) {
|
2020-07-17 16:35:39 +02:00
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
|
2022-04-09 19:20:20 +02:00
|
|
|
protected fun unselectAll(notifyChange: Boolean = true) {
|
|
|
|
explorerAdapter.unSelectAll(notifyChange)
|
2020-07-28 22:25:10 +02:00
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
|
2022-04-10 22:08:33 +02:00
|
|
|
private fun displayExplorerElements(totalSize: Long) {
|
|
|
|
totalSizeText.text = getString(R.string.total_size, PathUtils.formatSize(totalSize))
|
2021-11-11 20:14:48 +01:00
|
|
|
synchronized(this) {
|
|
|
|
ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
|
|
|
|
}
|
2022-04-09 19:20:20 +02:00
|
|
|
unselectAll(false)
|
2021-11-11 20:14:48 +01:00
|
|
|
explorerAdapter.explorerElements = explorerElements
|
2020-07-26 21:16:06 +02:00
|
|
|
val sharedPrefsEditor = sharedPrefs.edit()
|
2022-03-23 16:35:13 +01:00
|
|
|
sharedPrefsEditor.putString(ConstValues.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
|
2020-07-26 21:16:06 +02:00
|
|
|
sharedPrefsEditor.apply()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2022-03-06 14:59:45 +01:00
|
|
|
private fun recursiveSetSize(directory: ExplorerElement) {
|
2022-06-18 21:13:16 +02:00
|
|
|
for (child in encryptedVolume.readDir(directory.fullPath) ?: return) {
|
2022-03-06 14:59:45 +01:00
|
|
|
if (child.isDirectory) {
|
|
|
|
recursiveSetSize(child)
|
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
directory.stat.size += child.stat.size
|
2022-03-06 14:59:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-10 17:08:13 +02:00
|
|
|
private fun displayNumberOfElements(textView: TextView, stringIdSingular: Int, stringIdPlural: Int, count: Int) {
|
|
|
|
with(textView) {
|
|
|
|
visibility = if (count == 0) {
|
|
|
|
View.GONE
|
|
|
|
} else {
|
|
|
|
text = if (count == 1) {
|
|
|
|
getString(stringIdSingular)
|
|
|
|
} else {
|
|
|
|
getString(stringIdPlural, count)
|
|
|
|
}
|
|
|
|
View.VISIBLE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 20:14:48 +01:00
|
|
|
protected fun setCurrentPath(path: String, onDisplayed: (() -> Unit)? = null) {
|
2021-06-26 12:09:29 +02:00
|
|
|
synchronized(this) {
|
2022-06-18 21:13:16 +02:00
|
|
|
explorerElements = encryptedVolume.readDir(path) ?: return
|
|
|
|
if (path != "/") {
|
2021-11-11 20:14:48 +01:00
|
|
|
explorerElements.add(
|
|
|
|
0,
|
2022-06-18 21:13:16 +02:00
|
|
|
ExplorerElement("..", Stat.parentFolderStat(), parentPath = currentDirectoryPath)
|
2021-11-11 20:14:48 +01:00
|
|
|
)
|
|
|
|
}
|
2021-06-26 12:09:29 +02:00
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
textDirEmpty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.GONE
|
2020-07-26 21:16:06 +02:00
|
|
|
currentDirectoryPath = path
|
2021-06-11 20:23:54 +02:00
|
|
|
currentPathText.text = getString(R.string.location, currentDirectoryPath)
|
2022-04-10 17:08:13 +02:00
|
|
|
displayNumberOfElements(numberOfFilesText, R.string.one_file, R.string.multiple_files, explorerElements.count { it.isRegularFile })
|
|
|
|
displayNumberOfElements(numberOfFoldersText, R.string.one_folder, R.string.multiple_folders, explorerElements.count { it.isDirectory })
|
2021-11-11 20:14:48 +01:00
|
|
|
if (mapFolders) {
|
2022-04-20 15:17:33 +02:00
|
|
|
lifecycleScope.launch {
|
2021-07-10 12:02:56 +02:00
|
|
|
var totalSize: Long = 0
|
2022-04-20 15:17:33 +02:00
|
|
|
withContext(Dispatchers.IO) {
|
|
|
|
synchronized(this@BaseExplorerActivity) {
|
|
|
|
for (element in explorerElements) {
|
|
|
|
if (element.isDirectory) {
|
|
|
|
recursiveSetSize(element)
|
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
totalSize += element.stat.size
|
2020-08-11 20:40:14 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-05 14:06:54 +02:00
|
|
|
}
|
2022-04-20 15:17:33 +02:00
|
|
|
displayExplorerElements(totalSize)
|
|
|
|
onDisplayed?.invoke()
|
|
|
|
}
|
2021-11-11 20:14:48 +01:00
|
|
|
} else {
|
2022-06-18 21:13:16 +02:00
|
|
|
displayExplorerElements(explorerElements.filter { !it.isParentFolder }.sumOf { it.stat.size })
|
2021-11-11 20:14:48 +01:00
|
|
|
onDisplayed?.invoke()
|
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun askCloseVolume() {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
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()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBackPressed() {
|
2020-07-26 21:16:06 +02:00
|
|
|
if (explorerAdapter.selectedItems.isEmpty()) {
|
2020-08-01 15:39:00 +02:00
|
|
|
val parentPath = PathUtils.getParentPath(currentDirectoryPath)
|
2020-07-26 21:16:06 +02:00
|
|
|
if (parentPath == currentDirectoryPath) {
|
2020-07-17 16:35:39 +02:00
|
|
|
askCloseVolume()
|
|
|
|
} else {
|
2020-08-01 15:39:00 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-04 12:31:49 +02:00
|
|
|
private fun createFolder(folderName: String){
|
|
|
|
if (folderName.isEmpty()) {
|
2020-07-17 16:35:39 +02:00
|
|
|
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
|
|
|
|
} else {
|
2022-06-18 21:13:16 +02:00
|
|
|
if (!encryptedVolume.mkdir(PathUtils.pathJoin(currentDirectoryPath, folderName))) {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-07-17 16:35:39 +02:00
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.error_mkdir)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
} else {
|
2020-07-26 21:16:06 +02:00
|
|
|
setCurrentPath(currentDirectoryPath)
|
2020-07-17 16:35:39 +02:00
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 14:06:54 +02:00
|
|
|
protected fun openDialogCreateFolder() {
|
2022-03-24 20:08:23 +01:00
|
|
|
EditTextDialog(this, R.string.enter_folder_name) {
|
|
|
|
createFolder(it)
|
|
|
|
}.show()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2020-12-29 17:05:02 +01:00
|
|
|
protected fun checkPathOverwrite(items: ArrayList<OperationFile>, dstDirectoryPath: String, callback: (ArrayList<OperationFile>?) -> Unit) {
|
2022-06-18 21:13:16 +02:00
|
|
|
val srcDirectoryPath = items[0].parentPath
|
2020-12-29 17:05:02 +01:00
|
|
|
var ready = true
|
|
|
|
for (i in 0 until items.size) {
|
|
|
|
val testDstPath: String
|
|
|
|
if (items[i].dstPath == null){
|
2022-06-18 21:13:16 +02:00
|
|
|
testDstPath = PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, items[i].srcPath))
|
|
|
|
if (encryptedVolume.pathExists(testDstPath)) {
|
2020-12-29 17:05:02 +01:00
|
|
|
ready = false
|
|
|
|
} else {
|
|
|
|
items[i].dstPath = testDstPath
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
testDstPath = items[i].dstPath!!
|
2022-06-18 21:13:16 +02:00
|
|
|
if (encryptedVolume.pathExists(testDstPath) && !items[i].overwriteConfirmed) {
|
2020-12-29 17:05:02 +01:00
|
|
|
ready = false
|
|
|
|
}
|
2020-08-07 19:25:16 +02:00
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
if (!ready){
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-08-07 19:25:16 +02:00
|
|
|
.setTitle(R.string.warning)
|
2022-06-18 21:13:16 +02:00
|
|
|
.setMessage(getString(
|
|
|
|
if (items[i].isDirectory) {
|
|
|
|
R.string.dir_overwrite_question
|
|
|
|
} else {
|
|
|
|
R.string.file_overwrite_question
|
|
|
|
}, testDstPath
|
|
|
|
))
|
2020-12-29 17:05:02 +01:00
|
|
|
.setPositiveButton(R.string.yes) {_, _ ->
|
|
|
|
items[i].dstPath = testDstPath
|
|
|
|
items[i].overwriteConfirmed = true
|
|
|
|
checkPathOverwrite(items, dstDirectoryPath, callback)
|
|
|
|
}
|
2020-08-07 19:25:16 +02:00
|
|
|
.setNegativeButton(R.string.no) { _, _ ->
|
2022-03-24 20:08:23 +01:00
|
|
|
with(EditTextDialog(this, R.string.enter_new_name) {
|
2022-06-18 21:13:16 +02:00
|
|
|
items[i].dstPath = PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, items[i].parentPath), it)
|
|
|
|
if (items[i].isDirectory) {
|
2022-03-24 20:08:23 +01:00
|
|
|
for (j in 0 until items.size){
|
2022-06-18 21:13:16 +02:00
|
|
|
if (PathUtils.isChildOf(items[j].srcPath, items[i].srcPath)) {
|
|
|
|
items[j].dstPath = PathUtils.pathJoin(items[i].dstPath!!, PathUtils.getRelativePath(items[i].srcPath, items[j].srcPath))
|
2020-12-29 19:10:07 +01:00
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
}
|
2022-03-24 20:08:23 +01:00
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
checkPathOverwrite(items, dstDirectoryPath, callback)
|
2022-03-24 20:08:23 +01:00
|
|
|
}) {
|
2022-06-18 21:13:16 +02:00
|
|
|
setSelectedText(items[i].name)
|
2022-03-24 20:08:23 +01:00
|
|
|
setOnCancelListener{
|
|
|
|
callback(null)
|
|
|
|
}
|
|
|
|
show()
|
2020-08-07 19:25:16 +02:00
|
|
|
}
|
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
.setOnCancelListener{
|
|
|
|
callback(null)
|
|
|
|
}
|
|
|
|
.show()
|
|
|
|
break
|
2020-08-07 19:25:16 +02:00
|
|
|
}
|
|
|
|
}
|
2020-12-29 17:05:02 +01:00
|
|
|
if (ready){
|
|
|
|
callback(items)
|
|
|
|
}
|
2020-08-07 19:25:16 +02:00
|
|
|
}
|
|
|
|
|
2020-12-29 17:05:02 +01:00
|
|
|
protected fun importFilesFromUris(uris: List<Uri>, callback: (String?) -> Unit) {
|
|
|
|
val items = ArrayList<OperationFile>()
|
2020-11-03 17:22:09 +01:00
|
|
|
for (uri in uris) {
|
2020-12-29 17:05:02 +01:00
|
|
|
val fileName = PathUtils.getFilenameFromURI(this, uri)
|
|
|
|
if (fileName == null) {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-11-03 17:22:09 +01:00
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(getString(R.string.error_retrieving_filename, uri))
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
2020-12-29 17:05:02 +01:00
|
|
|
items.clear()
|
2020-11-03 17:22:09 +01:00
|
|
|
break
|
|
|
|
} else {
|
2022-06-18 21:13:16 +02:00
|
|
|
items.add(OperationFile(PathUtils.pathJoin(currentDirectoryPath, fileName), Stat.S_IFREG))
|
2020-12-29 17:05:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (items.size > 0) {
|
|
|
|
checkPathOverwrite(items, currentDirectoryPath) { checkedItems ->
|
|
|
|
checkedItems?.let {
|
2022-06-29 14:47:50 +02:00
|
|
|
taskScope.launch {
|
2022-04-20 15:17:33 +02:00
|
|
|
val taskResult = fileOperationService.importFilesFromUris(checkedItems.map { it.dstPath!! }, uris)
|
|
|
|
if (taskResult.cancelled) {
|
|
|
|
setCurrentPath(currentDirectoryPath)
|
|
|
|
} else {
|
|
|
|
callback(taskResult.failedItem)
|
2020-11-03 17:22:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 14:06:54 +02:00
|
|
|
protected fun rename(old_name: String, new_name: String){
|
2020-07-17 16:35:39 +02:00
|
|
|
if (new_name.isEmpty()) {
|
|
|
|
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
|
|
|
|
} else {
|
2022-06-18 21:13:16 +02:00
|
|
|
if (!encryptedVolume.rename(PathUtils.pathJoin(currentDirectoryPath, old_name), PathUtils.pathJoin(currentDirectoryPath, new_name))) {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
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 {
|
2021-11-11 20:14:48 +01:00
|
|
|
setCurrentPath(currentDirectoryPath) {
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-09 11:12:09 +01:00
|
|
|
private fun setMenuIconTint(menu: Menu, iconColor: Int, menuItemId: Int, drawableId: Int) {
|
|
|
|
menu.findItem(menuItemId)?.let {
|
|
|
|
it.icon = ContextCompat.getDrawable(this, drawableId)?.apply {
|
|
|
|
setTint(iconColor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-01 13:50:37 +02:00
|
|
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
2020-08-01 15:39:00 +02:00
|
|
|
menu.findItem(R.id.rename).isVisible = false
|
2021-10-19 11:24:20 +02:00
|
|
|
menu.findItem(R.id.open_as)?.isVisible = false
|
2020-07-17 16:35:39 +02:00
|
|
|
if (usf_open){
|
2020-08-01 15:39:00 +02:00
|
|
|
menu.findItem(R.id.external_open)?.isVisible = false
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2020-08-01 15:39:00 +02:00
|
|
|
val noItemSelected = explorerAdapter.selectedItems.isEmpty()
|
2022-01-11 20:18:02 +01:00
|
|
|
val iconColor = ContextCompat.getColor(this, R.color.neutralIconTint)
|
2021-11-09 11:12:09 +01:00
|
|
|
setMenuIconTint(menu, iconColor, R.id.sort, R.drawable.icon_sort)
|
|
|
|
setMenuIconTint(menu, iconColor, R.id.decrypt, R.drawable.icon_decrypt)
|
|
|
|
setMenuIconTint(menu, iconColor, R.id.share, R.drawable.icon_share)
|
2020-08-01 15:39:00 +02:00
|
|
|
menu.findItem(R.id.sort).isVisible = noItemSelected
|
|
|
|
menu.findItem(R.id.close).isVisible = noItemSelected
|
2022-03-05 12:51:02 +01:00
|
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(!noItemSelected)
|
|
|
|
if (!noItemSelected) {
|
2020-08-01 15:39:00 +02:00
|
|
|
if (explorerAdapter.selectedItems.size == 1) {
|
|
|
|
menu.findItem(R.id.rename).isVisible = true
|
2022-04-09 19:20:20 +02:00
|
|
|
if (explorerElements[explorerAdapter.selectedItems.first()].isRegularFile) {
|
2021-10-19 11:24:20 +02:00
|
|
|
menu.findItem(R.id.open_as)?.isVisible = true
|
|
|
|
if (usf_open) {
|
|
|
|
menu.findItem(R.id.external_open)?.isVisible = true
|
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-01 13:50:37 +02:00
|
|
|
return super.onCreateOptionsMenu(menu)
|
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
|
|
|
|
}
|
2020-08-01 15:39:00 +02:00
|
|
|
R.id.sort -> {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-07-17 16:35:39 +02:00
|
|
|
.setTitle(R.string.sort_order)
|
2022-01-18 20:42:13 +01:00
|
|
|
.setSingleChoiceItems(sortOrderEntries, currentSortOrderIndex) { dialog, which ->
|
2020-08-04 11:44:29 +02:00
|
|
|
currentSortOrderIndex = which
|
2020-07-26 21:16:06 +02:00
|
|
|
setCurrentPath(currentDirectoryPath)
|
2020-07-17 16:35:39 +02:00
|
|
|
dialog.dismiss()
|
2020-07-27 16:20:52 +02:00
|
|
|
}
|
2020-12-19 19:55:54 +01:00
|
|
|
.setNegativeButton(R.string.cancel, null)
|
2020-07-27 16:20:52 +02:00
|
|
|
.show()
|
2020-07-17 16:35:39 +02:00
|
|
|
true
|
|
|
|
}
|
2020-08-01 15:39:00 +02:00
|
|
|
R.id.rename -> {
|
2022-04-09 19:20:20 +02:00
|
|
|
val oldName = explorerElements[explorerAdapter.selectedItems.first()].name
|
2022-03-24 20:08:23 +01:00
|
|
|
with(EditTextDialog(this, R.string.rename_title) {
|
|
|
|
rename(oldName, it)
|
|
|
|
}) {
|
2022-04-18 14:49:35 +02:00
|
|
|
setSelectedText(oldName)
|
2022-03-24 20:08:23 +01:00
|
|
|
show()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
2021-10-19 11:24:20 +02:00
|
|
|
R.id.open_as -> {
|
2022-04-09 19:20:20 +02:00
|
|
|
showOpenAsDialog(
|
|
|
|
PathUtils.pathJoin(
|
|
|
|
currentDirectoryPath,
|
|
|
|
explorerElements[explorerAdapter.selectedItems.first()].name
|
|
|
|
)
|
|
|
|
)
|
2021-10-19 11:24:20 +02:00
|
|
|
true
|
|
|
|
}
|
2020-08-01 15:39:00 +02:00
|
|
|
R.id.external_open -> {
|
2020-07-17 16:35:39 +02:00
|
|
|
if (usf_open){
|
2022-04-09 19:20:20 +02:00
|
|
|
openWithExternalApp(
|
|
|
|
PathUtils.pathJoin(
|
|
|
|
currentDirectoryPath,
|
|
|
|
explorerElements[explorerAdapter.selectedItems.first()].name
|
|
|
|
)
|
|
|
|
)
|
2020-07-28 22:25:10 +02:00
|
|
|
unselectAll()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
2020-08-01 15:39:00 +02:00
|
|
|
R.id.close -> {
|
2020-07-17 16:35:39 +02:00
|
|
|
askCloseVolume()
|
|
|
|
true
|
|
|
|
}
|
|
|
|
else -> super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-08 14:09:34 +02:00
|
|
|
protected open fun closeVolumeOnUserExit() {
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
protected open fun closeVolumeOnDestroy() {
|
2022-06-29 14:47:50 +02:00
|
|
|
taskScope.cancel()
|
2022-06-18 21:13:16 +02:00
|
|
|
if (!encryptedVolume.isClosed()) {
|
|
|
|
encryptedVolume.close()
|
2020-08-08 14:09:34 +02:00
|
|
|
}
|
|
|
|
RestrictedFileProvider.wipeAll(this) //additional security
|
|
|
|
}
|
|
|
|
|
2020-07-17 16:35:39 +02:00
|
|
|
override fun onDestroy() {
|
|
|
|
super.onDestroy()
|
|
|
|
if (!isChangingConfigurations) { //activity won't be recreated
|
|
|
|
closeVolumeOnDestroy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-08 14:09:34 +02:00
|
|
|
override fun onPause() {
|
|
|
|
super.onPause()
|
2020-08-29 20:48:12 +02:00
|
|
|
if (!isChangingConfigurations){
|
|
|
|
if (isStartingActivity){
|
|
|
|
isStartingActivity = false
|
2020-10-23 13:21:14 +02:00
|
|
|
} else if (!usf_keep_open){
|
2020-08-29 20:48:12 +02:00
|
|
|
finish()
|
|
|
|
}
|
2020-08-08 14:09:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-17 16:35:39 +02:00
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
2020-08-05 14:06:54 +02:00
|
|
|
if (isCreating){
|
|
|
|
isCreating = false
|
|
|
|
} else {
|
2022-06-18 21:13:16 +02:00
|
|
|
if (encryptedVolume.isClosed()) {
|
2020-08-08 14:09:34 +02:00
|
|
|
finish()
|
|
|
|
} else {
|
|
|
|
isStartingActivity = false
|
2022-04-20 15:17:33 +02:00
|
|
|
ExternalProvider.removeFilesAsync(this)
|
2020-12-20 12:27:48 +01:00
|
|
|
setCurrentPath(currentDirectoryPath)
|
2020-08-08 14:09:34 +02:00
|
|
|
}
|
2020-08-05 14:06:54 +02:00
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2020-08-11 20:40:14 +02:00
|
|
|
}
|