diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt index cac04b1ef..de9043513 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt @@ -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() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt index cb008c401..6bf9fc4d4 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt @@ -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. diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt index ac9da5f0d..8f4401082 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt @@ -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 = obbFiles(), diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt index 5e3c83691..d892632e0 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt @@ -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) + } } } } diff --git a/custom/src/test/java/org/kiwix/kiwixmobile/custom/download/main/CustomFileValidatorTest.kt b/custom/src/test/java/org/kiwix/kiwixmobile/custom/download/main/CustomFileValidatorTest.kt index 8aae3c338..ce234f58e 100644 --- a/custom/src/test/java/org/kiwix/kiwixmobile/custom/download/main/CustomFileValidatorTest.kt +++ b/custom/src/test/java/org/kiwix/kiwixmobile/custom/download/main/CustomFileValidatorTest.kt @@ -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() val zimFile = mockk() 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() 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() 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() 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() mockZimFiles(arrayOf(zimFile), "ZIM")