Directory loading indicator

This commit is contained in:
Matéo Duparc 2024-01-13 23:19:22 +01:00
parent f4e47c1827
commit b4635dc2e0
Signed by untrusted user: hardcoresushi
GPG Key ID: AFE384344A45E13A
2 changed files with 47 additions and 31 deletions

View File

@ -11,10 +11,12 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -23,10 +25,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.yield
import sushi.hardcore.droidfs.BaseActivity import sushi.hardcore.droidfs.BaseActivity
import sushi.hardcore.droidfs.Constants import sushi.hardcore.droidfs.Constants
import sushi.hardcore.droidfs.EncryptedFileProvider import sushi.hardcore.droidfs.EncryptedFileProvider
@ -69,6 +74,7 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
protected lateinit var fileOperationService: FileOperationService protected lateinit var fileOperationService: FileOperationService
protected val activityScope = MainScope() protected val activityScope = MainScope()
private var directoryLoadingTask: Job? = null
protected lateinit var explorerElements: MutableList<ExplorerElement> protected lateinit var explorerElements: MutableList<ExplorerElement>
protected lateinit var explorerAdapter: ExplorerElementAdapter protected lateinit var explorerAdapter: ExplorerElementAdapter
protected lateinit var app: VolumeManagerApp protected lateinit var app: VolumeManagerApp
@ -79,6 +85,7 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
private lateinit var titleText: TextView private lateinit var titleText: TextView
private lateinit var recycler_view_explorer: RecyclerView private lateinit var recycler_view_explorer: RecyclerView
private lateinit var refresher: SwipeRefreshLayout private lateinit var refresher: SwipeRefreshLayout
private lateinit var loader: ProgressBar
private lateinit var textDirEmpty: TextView private lateinit var textDirEmpty: TextView
private lateinit var currentPathText: TextView private lateinit var currentPathText: TextView
private lateinit var numberOfFilesText: TextView private lateinit var numberOfFilesText: TextView
@ -101,6 +108,7 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
init() init()
recycler_view_explorer = findViewById(R.id.recycler_view_explorer) recycler_view_explorer = findViewById(R.id.recycler_view_explorer)
refresher = findViewById(R.id.refresher) refresher = findViewById(R.id.refresher)
loader = findViewById(R.id.loader)
textDirEmpty = findViewById(R.id.text_dir_empty) textDirEmpty = findViewById(R.id.text_dir_empty)
currentPathText = findViewById(R.id.current_path_text) currentPathText = findViewById(R.id.current_path_text)
numberOfFilesText = findViewById(R.id.number_of_files_text) numberOfFilesText = findViewById(R.id.number_of_files_text)
@ -312,17 +320,15 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
private fun displayExplorerElements() { private fun displayExplorerElements() {
synchronized(this) { ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
}
unselectAll(false) unselectAll(false)
loader.isVisible = false
recycler_view_explorer.isVisible = true
explorerAdapter.explorerElements = explorerElements explorerAdapter.explorerElements = explorerElements
val sharedPrefsEditor = sharedPrefs.edit()
sharedPrefsEditor.putString(Constants.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
sharedPrefsEditor.apply()
} }
private fun recursiveSetSize(directory: ExplorerElement) { private suspend fun recursiveSetSize(directory: ExplorerElement) {
yield()
for (child in encryptedVolume.readDir(directory.fullPath) ?: return) { for (child in encryptedVolume.readDir(directory.fullPath) ?: return) {
if (child.isDirectory) { if (child.isDirectory) {
recursiveSetSize(child) recursiveSetSize(child)
@ -346,15 +352,16 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
} }
protected fun setCurrentPath(path: String, onDisplayed: (() -> Unit)? = null) { protected fun setCurrentPath(path: String, onDisplayed: (() -> Unit)? = null) = lifecycleScope.launch {
synchronized(this) { directoryLoadingTask?.cancelAndJoin()
explorerElements = encryptedVolume.readDir(path) ?: return recycler_view_explorer.isVisible = false
if (path != "/") { loader.isVisible = true
explorerElements.add( explorerElements = encryptedVolume.readDir(path) ?: return@launch
0, if (path != "/") {
ExplorerElement("..", Stat.parentFolderStat(), parentPath = currentDirectoryPath) explorerElements.add(
) 0,
} ExplorerElement("..", Stat.parentFolderStat(), parentPath = currentDirectoryPath)
)
} }
textDirEmpty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.GONE textDirEmpty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.GONE
currentDirectoryPath = path currentDirectoryPath = path
@ -362,22 +369,19 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
displayNumberOfElements(numberOfFilesText, R.string.one_file, R.string.multiple_files, explorerElements.count { it.isRegularFile }) 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 }) displayNumberOfElements(numberOfFoldersText, R.string.one_folder, R.string.multiple_folders, explorerElements.count { it.isDirectory })
if (mapFolders) { if (mapFolders) {
lifecycleScope.launch { var totalSize: Long = 0
var totalSize: Long = 0 directoryLoadingTask = launch(Dispatchers.IO) {
withContext(Dispatchers.IO) { for (element in explorerElements) {
synchronized(this@BaseExplorerActivity) { if (element.isDirectory) {
for (element in explorerElements) { recursiveSetSize(element)
if (element.isDirectory) {
recursiveSetSize(element)
}
totalSize += element.stat.size
}
} }
totalSize += element.stat.size
} }
displayExplorerElements()
totalSizeText.text = getString(R.string.total_size, PathUtils.formatSize(totalSize))
onDisplayed?.invoke()
} }
directoryLoadingTask!!.join()
displayExplorerElements()
totalSizeText.text = getString(R.string.total_size, PathUtils.formatSize(totalSize))
onDisplayed?.invoke()
} else { } else {
displayExplorerElements() displayExplorerElements()
totalSizeText.text = getString( totalSizeText.text = getString(
@ -607,7 +611,13 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
.setTitle(R.string.sort_order) .setTitle(R.string.sort_order)
.setSingleChoiceItems(sortOrderEntries, currentSortOrderIndex) { dialog, which -> .setSingleChoiceItems(sortOrderEntries, currentSortOrderIndex) { dialog, which ->
currentSortOrderIndex = which currentSortOrderIndex = which
displayExplorerElements() // displayExplorerElements must not be called if directoryLoadingTask is active
if (directoryLoadingTask?.isActive != true) {
displayExplorerElements()
}
val sharedPrefsEditor = sharedPrefs.edit()
sharedPrefsEditor.putString(Constants.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
sharedPrefsEditor.apply()
dialog.dismiss() dialog.dismiss()
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)

View File

@ -10,6 +10,12 @@
android:text="@string/dir_empty" android:text="@string/dir_empty"
android:visibility="gone"/> android:visibility="gone"/>
<ProgressBar
android:id="@+id/loader"
android:layout_centerInParent="true"
android:layout_width="40dp"
android:layout_height="40dp"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresher" android:id="@+id/refresher"
android:layout_width="match_parent" android:layout_width="match_parent"