mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-22 20:24:03 -04:00
Fixed: Wikimedar application crash caused by Input Dispatching Timed Out.
* Moved the file-scanning logic to the IO thread, allowing smooth directory scanning or ZIM file preparation from the asset directory while keeping the main thread free. * Refactored `CustomFileValidatorTest` to align with this change. * Corrected the ZIM URL in `testZimFileRendering` test. * Improved the `manageExternalLaunchAndRestoringViewState` for thread safety.
This commit is contained in:
parent
b827cfe542
commit
683c35d5ac
@ -272,7 +272,7 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
||||
Request.Builder()
|
||||
.url(
|
||||
URI.create(
|
||||
"https://download.kiwix.org/zim/wikipedia_fr_climate_change_mini.zim"
|
||||
"https://download.kiwix.org/zim/wikipedia_fr_climate-change_mini.zim"
|
||||
).toURL()
|
||||
).build()
|
||||
|
||||
|
@ -2487,17 +2487,16 @@ abstract class CoreReaderFragment :
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
protected suspend fun manageExternalLaunchAndRestoringViewState(
|
||||
restoreOrigin: RestoreOrigin = FromExternalLaunch,
|
||||
dispatchersToGetWebViewHistoryFromDatabase: CoroutineDispatcher = Dispatchers.IO
|
||||
) {
|
||||
val settings = requireActivity().getSharedPreferences(
|
||||
SharedPreferenceUtil.PREF_KIWIX_MOBILE,
|
||||
0
|
||||
)
|
||||
val currentTab = safelyGetCurrentTab(settings)
|
||||
try {
|
||||
runCatching {
|
||||
val settings = requireActivity().getSharedPreferences(
|
||||
SharedPreferenceUtil.PREF_KIWIX_MOBILE,
|
||||
0
|
||||
)
|
||||
val currentTab = safelyGetCurrentTab(settings)
|
||||
val webViewHistoryList = withContext(dispatchersToGetWebViewHistoryFromDatabase) {
|
||||
// perform database operation on IO thread.
|
||||
repositoryActions?.loadWebViewPagesHistory().orEmpty()
|
||||
@ -2524,10 +2523,10 @@ abstract class CoreReaderFragment :
|
||||
findInPageTitle = null
|
||||
handlePendingIntent()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}.onFailure {
|
||||
Log.e(
|
||||
TAG_KIWIX,
|
||||
"Could not restore tabs. Original exception = ${e.printStackTrace()}"
|
||||
"Could not restore tabs. Original exception = ${it.printStackTrace()}"
|
||||
)
|
||||
restoreViewStateOnInvalidWebViewHistory()
|
||||
// handle the pending intent if any present.
|
||||
|
@ -23,6 +23,9 @@ import android.content.ContextWrapper
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.AssetFileDescriptor
|
||||
import android.content.res.AssetManager
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.kiwix.kiwixmobile.core.utils.files.Log
|
||||
import org.kiwix.kiwixmobile.custom.main.ValidationState.HasBothFiles
|
||||
import org.kiwix.kiwixmobile.custom.main.ValidationState.HasFile
|
||||
@ -32,13 +35,18 @@ import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class CustomFileValidator @Inject constructor(private val context: Context) {
|
||||
fun validate(onFilesFound: (ValidationState) -> Unit, onNoFilesFound: () -> Unit) =
|
||||
suspend fun validate(
|
||||
onFilesFound: suspend (ValidationState) -> Unit,
|
||||
onNoFilesFound: suspend () -> Unit,
|
||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) = withContext(dispatcher) {
|
||||
when (val installationState = detectInstallationState()) {
|
||||
is HasBothFiles,
|
||||
is HasFile -> onFilesFound(installationState)
|
||||
|
||||
HasNothing -> onNoFilesFound()
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectInstallationState(
|
||||
obbFiles: List<File> = obbFiles(),
|
||||
|
@ -20,17 +20,17 @@ package org.kiwix.kiwixmobile.custom.main
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.core.net.toUri
|
||||
import androidx.navigation.NavOptions
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.core.extensions.browserIntent
|
||||
import org.kiwix.kiwixmobile.core.extensions.isFileExist
|
||||
@ -80,10 +80,6 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
|
||||
if (isAdded) {
|
||||
enableLeftDrawer()
|
||||
with(activity as AppCompatActivity) {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
enableLeftDrawer()
|
||||
}
|
||||
loadPageFromNavigationArguments()
|
||||
if (BuildConfig.DISABLE_EXTERNAL_LINK) {
|
||||
// If "external links" are disabled in a custom app,
|
||||
@ -185,7 +181,9 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
// See https://github.com/kiwix/kiwix-android/issues/3541
|
||||
zimReaderContainer?.zimFileReader?.let(::setUpBookmarks)
|
||||
} else {
|
||||
openObbOrZim(true)
|
||||
coreReaderLifeCycleScope?.launch {
|
||||
openObbOrZim(true)
|
||||
}
|
||||
}
|
||||
requireArguments().clear()
|
||||
}
|
||||
@ -247,12 +245,13 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
* @param shouldManageExternalLaunch Indicates whether to manage external launch and
|
||||
* restore the view state after opening the file. Default is false.
|
||||
*/
|
||||
private fun openObbOrZim(shouldManageExternalLaunch: Boolean = false) {
|
||||
@Suppress("InjectDispatcher")
|
||||
private suspend fun openObbOrZim(shouldManageExternalLaunch: Boolean = false) {
|
||||
customFileValidator.validate(
|
||||
onFilesFound = {
|
||||
coreReaderLifeCycleScope?.launch {
|
||||
when (it) {
|
||||
is ValidationState.HasFile -> {
|
||||
when (it) {
|
||||
is ValidationState.HasFile -> {
|
||||
withContext(Dispatchers.Main) {
|
||||
openZimFile(
|
||||
ZimReaderSource(
|
||||
file = it.file,
|
||||
@ -276,31 +275,34 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
manageExternalLaunchAndRestoringViewState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is ValidationState.HasBothFiles -> {
|
||||
it.zimFile.delete()
|
||||
is ValidationState.HasBothFiles -> {
|
||||
it.zimFile.delete()
|
||||
withContext(Dispatchers.Main) {
|
||||
openZimFile(ZimReaderSource(it.obbFile), true, shouldManageExternalLaunch)
|
||||
if (shouldManageExternalLaunch) {
|
||||
// Open the previous loaded pages after ZIM file loads.
|
||||
manageExternalLaunchAndRestoringViewState()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
},
|
||||
onNoFilesFound = {
|
||||
if (sharedPreferenceUtil?.prefIsTest == false) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
delay(OPENING_DOWNLOAD_SCREEN_DELAY)
|
||||
withContext(Dispatchers.Main) {
|
||||
val navOptions = NavOptions.Builder()
|
||||
.setPopUpTo(CustomDestination.Reader.route, true)
|
||||
.build()
|
||||
(requireActivity() as CoreMainActivity).navigate(
|
||||
(activity as? CoreMainActivity)?.navigate(
|
||||
CustomDestination.Downloads.route,
|
||||
navOptions
|
||||
)
|
||||
}, OPENING_DOWNLOAD_SCREEN_DELAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -424,7 +426,9 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
super.onResume()
|
||||
if (appSettingsLaunched) {
|
||||
appSettingsLaunched = false
|
||||
openObbOrZim()
|
||||
coreReaderLifeCycleScope?.launch {
|
||||
openObbOrZim(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import android.content.res.AssetManager
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkConstructor
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Assertions.fail
|
||||
@ -48,7 +49,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onFilesFound when both OBB and ZIM files are found`() {
|
||||
fun `validate should call onFilesFound when both OBB and ZIM files are found`() = runTest {
|
||||
val obbFile = mockk<File>()
|
||||
val zimFile = mockk<File>()
|
||||
mockZimFiles(arrayOf(obbFile), "obb")
|
||||
@ -65,7 +66,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onFilesFound when only OBB file is found`() {
|
||||
fun `validate should call onFilesFound when only OBB file is found`() = runTest {
|
||||
val obbFile = mockk<File>()
|
||||
mockZimFiles(arrayOf(obbFile), "obb")
|
||||
mockZimFiles(arrayOf(), "zim")
|
||||
@ -80,7 +81,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onFilesFound when only ZIM file is found`() {
|
||||
fun `validate should call onFilesFound when only ZIM file is found`() = runTest {
|
||||
val zimFile = mockk<File>()
|
||||
mockZimFiles(arrayOf(), "obb")
|
||||
mockZimFiles(arrayOf(zimFile), "zim")
|
||||
@ -95,7 +96,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onNoFilesFound when no OBB or ZIM files are found`() {
|
||||
fun `validate should call onNoFilesFound when no OBB or ZIM files are found`() = runTest {
|
||||
mockZimFiles(arrayOf(), extension = "zim")
|
||||
mockZimFiles(arrayOf(), extension = "obb")
|
||||
|
||||
@ -106,7 +107,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onNoFilesFound when directories are null`() {
|
||||
fun `validate should call onNoFilesFound when directories are null`() = runTest {
|
||||
mockZimFiles(null, "zim")
|
||||
mockZimFiles(null, "obb")
|
||||
|
||||
@ -117,7 +118,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onNoFilesFound when no matching files are found`() {
|
||||
fun `validate should call onNoFilesFound when no matching files are found`() = runTest {
|
||||
val textFile = mockk<File>()
|
||||
mockZimFiles(arrayOf(textFile), "txt")
|
||||
|
||||
@ -128,7 +129,7 @@ class CustomFileValidatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate should call onFilesFound for case insensitive file extensions`() {
|
||||
fun `validate should call onFilesFound for case insensitive file extensions`() = runTest {
|
||||
val zimFile = mockk<File>()
|
||||
mockZimFiles(arrayOf(zimFile), "ZIM")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user