Merge pull request #4164 from kiwix/Fixes#4163

Fixed: Application crash caused by "Input dispatching timed out" while retrieving storageDeviceList information in the online library.
This commit is contained in:
Kelson 2025-01-06 20:38:00 +01:00 committed by GitHub
commit 1e5bc58f15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 110 additions and 88 deletions

View File

@ -40,6 +40,7 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils import eu.mhutti1.utils.storage.StorageDeviceUtils
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.BuildConfig import org.kiwix.kiwixmobile.BuildConfig
@ -115,6 +116,7 @@ class KiwixMainActivity : CoreMainActivity() {
NavController.OnDestinationChangedListener { _, _, _ -> NavController.OnDestinationChangedListener { _, _, _ ->
actionMode?.finish() actionMode?.finish()
} }
private val storageDeviceList = arrayListOf<StorageDevice>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
cachedComponent.inject(this) cachedComponent.inject(this)
@ -142,7 +144,7 @@ class KiwixMainActivity : CoreMainActivity() {
private suspend fun migrateInternalToPublicAppDirectory() { private suspend fun migrateInternalToPublicAppDirectory() {
if (!sharedPreferenceUtil.prefIsAppDirectoryMigrated) { if (!sharedPreferenceUtil.prefIsAppDirectoryMigrated) {
val storagePath = StorageDeviceUtils.getWritableStorage(this) val storagePath = getStorageDeviceList()
.getOrNull(sharedPreferenceUtil.storagePosition) .getOrNull(sharedPreferenceUtil.storagePosition)
?.name ?.name
storagePath?.let { storagePath?.let {
@ -152,6 +154,23 @@ class KiwixMainActivity : CoreMainActivity() {
} }
} }
/**
* Fetches the storage device list once in the main activity and reuses it across all fragments.
* This is necessary because retrieving the storage device list, especially on devices with large SD cards,
* is a resource-intensive operation. Performing this operation repeatedly in fragments can negatively
* affect the user experience, as it takes time and can block the UI.
*
* If a fragment is destroyed and we need to retrieve the device list again, performing the operation
* repeatedly leads to inefficiency. To optimize this, we fetch the storage device list once and reuse
* it in all fragments, thereby reducing redundant processing and improving performance.
*/
suspend fun getStorageDeviceList(): List<StorageDevice> {
if (storageDeviceList.isEmpty()) {
storageDeviceList.addAll(StorageDeviceUtils.getWritableStorage(this))
}
return storageDeviceList
}
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
if (::activityKiwixMainBinding.isInitialized) { if (::activityKiwixMainBinding.isInitialized) {

View File

@ -33,7 +33,6 @@ import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import eu.mhutti1.utils.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE import eu.mhutti1.utils.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils
import eu.mhutti1.utils.storage.StorageSelectDialog import eu.mhutti1.utils.storage.StorageSelectDialog
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -53,6 +52,7 @@ import org.kiwix.kiwixmobile.core.utils.INTERNAL_SELECT_POSITION
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.zimManager.Fat32Checker import org.kiwix.kiwixmobile.zimManager.Fat32Checker
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.Companion.FOUR_GIGABYTES_IN_KILOBYTES import org.kiwix.kiwixmobile.zimManager.Fat32Checker.Companion.FOUR_GIGABYTES_IN_KILOBYTES
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
@ -83,9 +83,6 @@ class CopyMoveFileHandler @Inject constructor(
var shouldValidateZimFile: Boolean = false var shouldValidateZimFile: Boolean = false
private var fileSystemDisposable: Disposable? = null private var fileSystemDisposable: Disposable? = null
private lateinit var fragmentManager: FragmentManager private lateinit var fragmentManager: FragmentManager
val storageDeviceList by lazy {
StorageDeviceUtils.getWritableStorage(activity)
}
private val copyMoveTitle: String by lazy { private val copyMoveTitle: String by lazy {
if (isMoveOperation) { if (isMoveOperation) {
@ -111,18 +108,23 @@ class CopyMoveFileHandler @Inject constructor(
this.shouldValidateZimFile = shouldValidateZimFile this.shouldValidateZimFile = shouldValidateZimFile
this.fragmentManager = fragmentManager this.fragmentManager = fragmentManager
setSelectedFileAndUri(uri, documentFile) setSelectedFileAndUri(uri, documentFile)
if (sharedPreferenceUtil.shouldShowStorageSelectionDialog && storageDeviceList.size > 1) { if (getStorageDeviceList().isEmpty()) {
showPreparingCopyMoveDialog()
}
if (sharedPreferenceUtil.shouldShowStorageSelectionDialog && getStorageDeviceList().size > 1) {
// Show dialog to select storage if more than one storage device is available, and user // Show dialog to select storage if more than one storage device is available, and user
// have not configured the storage yet. // have not configured the storage yet.
hidePreparingCopyMoveDialog()
showCopyMoveDialog(true) showCopyMoveDialog(true)
} else { } else {
if (storageDeviceList.size == 1) { if (getStorageDeviceList().size == 1) {
// If only internal storage is currently available, set shouldShowStorageSelectionDialog // If only internal storage is currently available, set shouldShowStorageSelectionDialog
// to true. This allows the storage configuration dialog to be shown again if the // to true. This allows the storage configuration dialog to be shown again if the
// user removes an external storage device (like an SD card) and then reinserts it. // user removes an external storage device (like an SD card) and then reinserts it.
// This ensures they are prompted to configure storage settings upon SD card reinsertion. // This ensures they are prompted to configure storage settings upon SD card reinsertion.
sharedPreferenceUtil.shouldShowStorageSelectionDialog = true sharedPreferenceUtil.shouldShowStorageSelectionDialog = true
} }
hidePreparingCopyMoveDialog()
if (validateZimFileCanCopyOrMove()) { if (validateZimFileCanCopyOrMove()) {
showCopyMoveDialog() showCopyMoveDialog()
} }
@ -142,17 +144,15 @@ class CopyMoveFileHandler @Inject constructor(
lifecycleScope = coroutineScope lifecycleScope = coroutineScope
} }
fun showStorageSelectDialog() = StorageSelectDialog() fun showStorageSelectDialog(storageDeviceList: List<StorageDevice>) =
.apply { StorageSelectDialog()
onSelectAction = ::copyMoveZIMFileInSelectedStorage .apply {
titleSize = STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE onSelectAction = ::copyMoveZIMFileInSelectedStorage
setStorageDeviceList(storageDeviceList) titleSize = STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
setShouldShowCheckboxSelected(false) setStorageDeviceList(storageDeviceList)
} setShouldShowCheckboxSelected(false)
.show( }
fragmentManager, .show(fragmentManager, activity.getString(R.string.choose_storage_to_copy_move_zim_file))
activity.getString(R.string.choose_storage_to_copy_move_zim_file)
)
fun copyMoveZIMFileInSelectedStorage(storageDevice: StorageDevice) { fun copyMoveZIMFileInSelectedStorage(storageDevice: StorageDevice) {
lifecycleScope?.launch { lifecycleScope?.launch {
@ -259,19 +259,23 @@ class CopyMoveFileHandler @Inject constructor(
fun performCopyOperation(showStorageSelectionDialog: Boolean = false) { fun performCopyOperation(showStorageSelectionDialog: Boolean = false) {
isMoveOperation = false isMoveOperation = false
if (showStorageSelectionDialog) { lifecycleScope?.launch {
showStorageSelectDialog() if (showStorageSelectionDialog) {
} else { showStorageSelectDialog(getStorageDeviceList())
copyZimFileToPublicAppDirectory() } else {
copyZimFileToPublicAppDirectory()
}
} }
} }
fun performMoveOperation(showStorageSelectionDialog: Boolean = false) { fun performMoveOperation(showStorageSelectionDialog: Boolean = false) {
isMoveOperation = true isMoveOperation = true
if (showStorageSelectionDialog) { lifecycleScope?.launch {
showStorageSelectDialog() if (showStorageSelectionDialog) {
} else { showStorageSelectDialog(getStorageDeviceList())
moveZimFileToPublicAppDirectory() } else {
moveZimFileToPublicAppDirectory()
}
} }
} }
@ -538,6 +542,9 @@ class CopyMoveFileHandler @Inject constructor(
} }
} }
suspend fun getStorageDeviceList() =
(activity as KiwixMainActivity).getStorageDeviceList()
fun dispose() { fun dispose() {
fileSystemDisposable?.dispose() fileSystemDisposable?.dispose()
setFileCopyMoveCallback(null) setFileCopyMoveCallback(null)

View File

@ -58,7 +58,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import eu.mhutti1.utils.storage.Bytes import eu.mhutti1.utils.storage.Bytes
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils
import eu.mhutti1.utils.storage.StorageSelectDialog import eu.mhutti1.utils.storage.StorageSelectDialog
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
@ -99,6 +98,7 @@ import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDis
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.databinding.FragmentDestinationLibraryBinding import org.kiwix.kiwixmobile.databinding.FragmentDestinationLibraryBinding
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.zimManager.MAX_PROGRESS import org.kiwix.kiwixmobile.zimManager.MAX_PROGRESS
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions
@ -135,9 +135,6 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
private val zimManageViewModel by lazy { private val zimManageViewModel by lazy {
requireActivity().viewModel<ZimManageViewModel>(viewModelFactory) requireActivity().viewModel<ZimManageViewModel>(viewModelFactory)
} }
private val storageDeviceList by lazy {
StorageDeviceUtils.getWritableStorage(requireActivity())
}
private var storagePermissionLauncher: ActivityResultLauncher<Array<String>>? = private var storagePermissionLauncher: ActivityResultLauncher<Array<String>>? =
registerForActivityResult( registerForActivityResult(
@ -665,17 +662,22 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
message, message,
requireActivity().findViewById(R.id.bottom_nav_view), requireActivity().findViewById(R.id.bottom_nav_view),
string.download_change_storage, string.download_change_storage,
::showStorageSelectDialog {
lifecycleScope.launch {
showStorageSelectDialog((requireActivity() as KiwixMainActivity).getStorageDeviceList())
}
}
) )
} }
private fun showStorageSelectDialog() = StorageSelectDialog() private fun showStorageSelectDialog(storageDeviceList: List<StorageDevice>) =
.apply { StorageSelectDialog()
onSelectAction = ::storeDeviceInPreferences .apply {
setStorageDeviceList(storageDeviceList) onSelectAction = ::storeDeviceInPreferences
setShouldShowCheckboxSelected(true) setStorageDeviceList(storageDeviceList)
} setShouldShowCheckboxSelected(true)
.show(parentFragmentManager, getString(string.pref_storage)) }
.show(parentFragmentManager, getString(string.pref_storage))
private fun storeDeviceInPreferences( private fun storeDeviceInPreferences(
storageDevice: StorageDevice storageDevice: StorageDevice

View File

@ -53,7 +53,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.mhutti1.utils.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE import eu.mhutti1.utils.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils
import eu.mhutti1.utils.storage.StorageSelectDialog import eu.mhutti1.utils.storage.StorageSelectDialog
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.R
@ -93,6 +92,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly
import org.kiwix.kiwixmobile.core.zim_manager.NetworkState import org.kiwix.kiwixmobile.core.zim_manager.NetworkState
import org.kiwix.kiwixmobile.databinding.FragmentDestinationDownloadBinding import org.kiwix.kiwixmobile.databinding.FragmentDestinationDownloadBinding
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryAdapter import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryAdapter
@ -116,9 +116,6 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
private val zimManageViewModel by lazy { private val zimManageViewModel by lazy {
requireActivity().viewModel<ZimManageViewModel>(viewModelFactory) requireActivity().viewModel<ZimManageViewModel>(viewModelFactory)
} }
private val storageDeviceList by lazy {
StorageDeviceUtils.getWritableStorage(requireActivity())
}
@VisibleForTesting @VisibleForTesting
fun getOnlineLibraryList() = libraryAdapter.items fun getOnlineLibraryList() = libraryAdapter.items
@ -550,8 +547,8 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
else -> if (sharedPreferenceUtil.showStorageOption) { else -> if (sharedPreferenceUtil.showStorageOption) {
// Show the storage selection dialog for configuration if there is an SD card available. // Show the storage selection dialog for configuration if there is an SD card available.
if (storageDeviceList.size > 1) { if (getStorageDeviceList().size > 1) {
showStorageSelectDialog() showStorageSelectDialog(getStorageDeviceList())
} else { } else {
// If only internal storage is available, proceed with the ZIM file download directly. // If only internal storage is available, proceed with the ZIM file download directly.
// Displaying a configuration dialog is unnecessary in this case. // Displaying a configuration dialog is unnecessary in this case.
@ -575,7 +572,11 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
""".trimIndent(), """.trimIndent(),
requireActivity().findViewById(R.id.bottom_nav_view), requireActivity().findViewById(R.id.bottom_nav_view),
string.download_change_storage, string.download_change_storage,
::showStorageSelectDialog {
lifecycleScope.launch {
showStorageSelectDialog(getStorageDeviceList())
}
}
) )
} }
) )
@ -588,14 +589,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
} }
} }
private fun showStorageSelectDialog() = StorageSelectDialog() private fun showStorageSelectDialog(storageDeviceList: List<StorageDevice>) =
.apply { StorageSelectDialog()
onSelectAction = ::storeDeviceInPreferences .apply {
titleSize = STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE onSelectAction = ::storeDeviceInPreferences
setStorageDeviceList(storageDeviceList) titleSize = STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
setShouldShowCheckboxSelected(false) setStorageDeviceList(storageDeviceList)
} setShouldShowCheckboxSelected(false)
.show(parentFragmentManager, getString(string.choose_storage_to_download_book)) }
.show(parentFragmentManager, getString(string.choose_storage_to_download_book))
private fun clickOnBookItem() { private fun clickOnBookItem() {
if (!requireActivity().isManageExternalStoragePermissionGranted(sharedPreferenceUtil)) { if (!requireActivity().isManageExternalStoragePermissionGranted(sharedPreferenceUtil)) {
@ -615,4 +617,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
) )
} }
} }
private suspend fun getStorageDeviceList() =
(activity as KiwixMainActivity).getStorageDeviceList()
} }

View File

@ -26,11 +26,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.extensions.getFreeSpace import org.kiwix.kiwixmobile.core.extensions.getFreeSpace
@ -44,11 +39,11 @@ import org.kiwix.kiwixmobile.core.settings.StorageRadioButtonPreference
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_EXTERNAL_STORAGE import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_EXTERNAL_STORAGE
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_INTERNAL_STORAGE import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_INTERNAL_STORAGE
import org.kiwix.kiwixmobile.main.KiwixMainActivity
const val PREF_STORAGE_PROGRESSBAR = "storage_progressbar" const val PREF_STORAGE_PROGRESSBAR = "storage_progressbar"
class KiwixPrefsFragment : CorePrefsFragment() { class KiwixPrefsFragment : CorePrefsFragment() {
private var storageDisposable: Disposable? = null
private var storageDeviceList: List<StorageDevice> = listOf() private var storageDeviceList: List<StorageDevice> = listOf()
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -65,20 +60,13 @@ class KiwixPrefsFragment : CorePrefsFragment() {
return@setStorage return@setStorage
} }
showHideProgressBarWhileFetchingStorageInfo(true) showHideProgressBarWhileFetchingStorageInfo(true)
storageDisposable = lifecycleScope.launch {
Flowable.fromCallable { StorageDeviceUtils.getWritableStorage(requireActivity()) } storageDeviceList = (requireActivity() as KiwixMainActivity).getStorageDeviceList()
.subscribeOn(Schedulers.io()) showHideProgressBarWhileFetchingStorageInfo(false)
.observeOn(AndroidSchedulers.mainThread()) showInternalStoragePreferece()
.subscribe( showExternalPreferenceIfAvailable()
{ storageList -> setUpStoragePreference(it)
storageDeviceList = storageList }
showHideProgressBarWhileFetchingStorageInfo(false)
showInternalStoragePreferece()
showExternalPreferenceIfAvailable()
setUpStoragePreference(it)
},
Throwable::printStackTrace
)
} }
} }
@ -157,12 +145,6 @@ class KiwixPrefsFragment : CorePrefsFragment() {
preferenceCategory?.isVisible = true preferenceCategory?.isVisible = true
} }
override fun onDestroyView() {
storageDisposable?.dispose()
storageDisposable = null
super.onDestroyView()
}
companion object { companion object {
const val PREF_MANAGE_EXTERNAL_STORAGE_PERMISSION = const val PREF_MANAGE_EXTERNAL_STORAGE_PERMISSION =
"pref_manage_external_storage" "pref_manage_external_storage"

View File

@ -22,6 +22,7 @@ import android.app.Activity
import android.net.Uri import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import eu.mhutti1.utils.storage.StorageDevice
import io.mockk.Runs import io.mockk.Runs
import io.mockk.clearAllMocks import io.mockk.clearAllMocks
import io.mockk.coEvery import io.mockk.coEvery
@ -207,9 +208,11 @@ class CopyMoveFileHandlerTest {
@Test @Test
fun showStorageConfigureDialogAtFirstLaunch() = runBlocking { fun showStorageConfigureDialogAtFirstLaunch() = runBlocking {
fileHandler = spyk(fileHandler) fileHandler = spyk(fileHandler)
every { fileHandler.showStorageSelectDialog() } just Runs val storageDeviceList = listOf<StorageDevice>(mockk(), mockk())
every { fileHandler.showStorageSelectDialog(storageDeviceList) } just Runs
every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns true every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns true
every { fileHandler.storageDeviceList } returns listOf(mockk(), mockk()) coEvery { fileHandler.getStorageDeviceList() } returns storageDeviceList
val positiveButtonClickSlot = slot<() -> Unit>() val positiveButtonClickSlot = slot<() -> Unit>()
every { every {
alertDialogShower.show( alertDialogShower.show(
@ -221,14 +224,15 @@ class CopyMoveFileHandlerTest {
fileHandler.showMoveFileToPublicDirectoryDialog(fragmentManager = fragmentManager) fileHandler.showMoveFileToPublicDirectoryDialog(fragmentManager = fragmentManager)
coEvery { fileHandler.validateZimFileCanCopyOrMove() } returns true coEvery { fileHandler.validateZimFileCanCopyOrMove() } returns true
positiveButtonClickSlot.captured.invoke() positiveButtonClickSlot.captured.invoke()
verify { fileHandler.showStorageSelectDialog() } testDispatcher.scheduler.advanceUntilIdle()
coVerify { fileHandler.showStorageSelectDialog(storageDeviceList) }
} }
@Test @Test
fun shouldNotShowStorageConfigureDialogWhenThereIsOnlyInternalAvailable() = runBlocking { fun shouldNotShowStorageConfigureDialogWhenThereIsOnlyInternalAvailable() = runBlocking {
fileHandler = spyk(fileHandler) fileHandler = spyk(fileHandler)
every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns true every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns true
every { fileHandler.storageDeviceList } returns listOf(mockk()) coEvery { fileHandler.getStorageDeviceList() } returns listOf(mockk())
val positiveButtonClickSlot = slot<() -> Unit>() val positiveButtonClickSlot = slot<() -> Unit>()
every { every {
alertDialogShower.show( alertDialogShower.show(
@ -240,14 +244,14 @@ class CopyMoveFileHandlerTest {
coEvery { fileHandler.validateZimFileCanCopyOrMove() } returns true coEvery { fileHandler.validateZimFileCanCopyOrMove() } returns true
fileHandler.showMoveFileToPublicDirectoryDialog(fragmentManager = fragmentManager) fileHandler.showMoveFileToPublicDirectoryDialog(fragmentManager = fragmentManager)
positiveButtonClickSlot.captured.invoke() positiveButtonClickSlot.captured.invoke()
verify(exactly = 0) { fileHandler.showStorageSelectDialog() } verify(exactly = 0) { fileHandler.showStorageSelectDialog(listOf(mockk())) }
} }
@Test @Test
fun showDirectlyCopyMoveDialogAfterFirstLaunch() = runBlocking { fun showDirectlyCopyMoveDialogAfterFirstLaunch() = runBlocking {
fileHandler = spyk(fileHandler) fileHandler = spyk(fileHandler)
every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns false every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns false
every { fileHandler.storageDeviceList } returns listOf(mockk(), mockk()) coEvery { fileHandler.getStorageDeviceList() } returns listOf(mockk(), mockk())
coEvery { fileHandler.validateZimFileCanCopyOrMove() } returns true coEvery { fileHandler.validateZimFileCanCopyOrMove() } returns true
prepareFileSystemAndFileForMockk() prepareFileSystemAndFileForMockk()
every { alertDialogShower.show(any(), any(), any()) } just Runs every { alertDialogShower.show(any(), any(), any()) } just Runs
@ -267,7 +271,7 @@ class CopyMoveFileHandlerTest {
val positiveButtonClickSlot = slot<() -> Unit>() val positiveButtonClickSlot = slot<() -> Unit>()
val negativeButtonClickSlot = slot<() -> Unit>() val negativeButtonClickSlot = slot<() -> Unit>()
fileHandler = spyk(fileHandler) fileHandler = spyk(fileHandler)
every { fileHandler.storageDeviceList } returns listOf(mockk(), mockk()) coEvery { fileHandler.getStorageDeviceList() } returns listOf(mockk(), mockk())
every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns false every { sharedPreferenceUtil.shouldShowStorageSelectionDialog } returns false
every { every {
alertDialogShower.show( alertDialogShower.show(

View File

@ -21,6 +21,8 @@ package eu.mhutti1.utils.storage
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.os.Environment import android.os.Environment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import java.io.File import java.io.File
import java.io.FileFilter import java.io.FileFilter
@ -28,8 +30,9 @@ import java.io.RandomAccessFile
object StorageDeviceUtils { object StorageDeviceUtils {
@JvmStatic @JvmStatic
fun getWritableStorage(context: Context) = suspend fun getWritableStorage(context: Context) = withContext(Dispatchers.IO) {
validate(externalMediaFilesDirsDevices(context), true) validate(externalMediaFilesDirsDevices(context), true)
}
@JvmStatic @JvmStatic
fun getReadableStorage(context: Context): List<StorageDevice> { fun getReadableStorage(context: Context): List<StorageDevice> {

View File

@ -55,7 +55,7 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:indeterminate="false" android:indeterminate="false"
android:max="100" android:max="100"
android:progress="50" android:progress="0"
android:progressDrawable="@drawable/progress_bar_state" android:progressDrawable="@drawable/progress_bar_state"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/radioButton" app:layout_constraintStart_toEndOf="@id/radioButton"