Fixed: The Tabs history is not restoring in custom apps.

* This happened because we moved the file scanning logic to the IO thread, and inside that, we were also opening the ZIM file in the reader on the IO thread, and then restoring the tabs (which is now done on the Dispatcher’s main thread instead of the UI main thread). Because of this, the WebView was loading freely and saving new history in the database, which cleared the previous history. As a result, the tabs were not restoring.
* Now, we are not saving new WebView history while restoring tabs, which avoids this issue in a multi-threading environment.
* Made `restoreViewStateOnValidWebViewHistory` and `restoreViewStateOnInvalidWebViewHistory` suspend functions so they can run in the same scope from where we are opening and restoring tabs.
This commit is contained in:
MohitMaliFtechiz 2025-09-05 22:19:24 +05:30
parent de54d36355
commit daf5d51bc2
3 changed files with 42 additions and 29 deletions

View File

@ -110,6 +110,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
if (zimFileUri.isNotEmpty()) {
tryOpeningZimFile(zimFileUri)
} else {
isWebViewHistoryRestoring = true
val restoreOrigin =
if (searchItemTitle.isNotEmpty()) FromSearchScreen else FromExternalLaunch
manageExternalLaunchAndRestoringViewState(restoreOrigin)
@ -224,7 +225,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
}
}
override fun restoreViewStateOnInvalidWebViewHistory() {
override suspend fun restoreViewStateOnInvalidWebViewHistory() {
Log.d(TAG_KIWIX, "Kiwix normal start, no zimFile loaded last time -> display home page")
exitBook()
}
@ -242,7 +243,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
* @param restoreOrigin Indicates whether the restoration is triggered from an external launch or the search screen.
* @param onComplete Callback to be invoked upon completion of the restoration process.
*/
override fun restoreViewStateOnValidWebViewHistory(
override suspend fun restoreViewStateOnValidWebViewHistory(
webViewHistoryItemList: List<WebViewHistoryItem>,
currentTab: Int,
restoreOrigin: RestoreOrigin,
@ -250,29 +251,27 @@ class KiwixReaderFragment : CoreReaderFragment() {
) {
when (restoreOrigin) {
FromExternalLaunch -> {
coreReaderLifeCycleScope?.launch {
if (!isAdded) return@launch
val settings =
activity?.getSharedPreferences(SharedPreferenceUtil.PREF_KIWIX_MOBILE, 0)
val zimReaderSource = fromDatabaseValue(settings?.getString(TAG_CURRENT_FILE, null))
if (zimReaderSource?.canOpenInLibkiwix() == true) {
if (zimReaderContainer?.zimReaderSource == null) {
openZimFile(zimReaderSource, isFromManageExternalLaunch = true)
Log.d(
TAG_KIWIX,
"Kiwix normal start, Opened last used zimFile: -> ${zimReaderSource.toDatabase()}"
)
} else {
zimReaderContainer?.zimFileReader?.let(::setUpBookmarks)
}
restoreTabs(webViewHistoryItemList, currentTab, onComplete)
} else {
readerScreenState.value.snackBarHostState.snack(
requireActivity().getString(string.zim_not_opened),
lifecycleScope = lifecycleScope
if (!isAdded) return
val settings =
activity?.getSharedPreferences(SharedPreferenceUtil.PREF_KIWIX_MOBILE, 0)
val zimReaderSource = fromDatabaseValue(settings?.getString(TAG_CURRENT_FILE, null))
if (zimReaderSource?.canOpenInLibkiwix() == true) {
if (zimReaderContainer?.zimReaderSource == null) {
openZimFile(zimReaderSource, isFromManageExternalLaunch = true)
Log.d(
TAG_KIWIX,
"Kiwix normal start, Opened last used zimFile: -> ${zimReaderSource.toDatabase()}"
)
exitBook() // hide the options for zim file to avoid unexpected UI behavior
} else {
zimReaderContainer?.zimFileReader?.let(::setUpBookmarks)
}
restoreTabs(webViewHistoryItemList, currentTab, onComplete)
} else {
readerScreenState.value.snackBarHostState.snack(
requireActivity().getString(string.zim_not_opened),
lifecycleScope = lifecycleScope
)
exitBook() // hide the options for zim file to avoid unexpected UI behavior
}
}

View File

@ -172,6 +172,7 @@ import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import javax.inject.Inject
import kotlin.concurrent.Volatile
import kotlin.math.max
const val SEARCH_ITEM_TITLE_KEY = "searchItemTitle"
@ -224,6 +225,8 @@ abstract class CoreReaderFragment :
protected var currentWebViewIndex by mutableStateOf(0)
private var currentTtsWebViewIndex = 0
private var isFirstTimeMainPageLoaded = true
@Volatile
private var isFromManageExternalLaunch = false
private val savingTabsMutex = Mutex()
private var searchItemToOpen: SearchItemToOpen? = null
@ -340,6 +343,8 @@ abstract class CoreReaderFragment :
*/
private var pendingIntent: Intent? = null
@Volatile var isWebViewHistoryRestoring = false
private var storagePermissionForNotesLauncher: ActivityResultLauncher<String>? =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
@ -2333,7 +2338,9 @@ abstract class CoreReaderFragment :
showProgressBarWithProgress(progress)
if (progress == 100) {
hideProgressBar()
saveTabStates()
if (!isWebViewHistoryRestoring) {
saveTabStates()
}
Log.d(TAG_KIWIX, "Loaded URL: " + getCurrentWebView()?.url)
}
(webView.context as AppCompatActivity).invalidateOptionsMenu()
@ -2434,6 +2441,7 @@ abstract class CoreReaderFragment :
restoreViewStateOnInvalidWebViewHistory()
// handle the pending intent if any present.
handlePendingIntent()
isWebViewHistoryRestoring = false
return
}
restoreViewStateOnValidWebViewHistory(
@ -2446,11 +2454,14 @@ abstract class CoreReaderFragment :
// to open the specified item, then sets `searchItemToOpen` to null to prevent
// any unexpected behavior on future calls. Similarly, if `findInPageTitle` is set,
// it invokes `findInPage` and resets `findInPageTitle` to null.
isWebViewHistoryRestoring = false
searchItemToOpen?.let(::openSearchItem)
searchItemToOpen = null
findInPageTitle?.let(::findInPage)
findInPageTitle = null
handlePendingIntent()
// When the restoration completes than save the tabs history.
saveTabStates()
}
}.onFailure {
Log.e(
@ -2460,6 +2471,7 @@ abstract class CoreReaderFragment :
restoreViewStateOnInvalidWebViewHistory()
// handle the pending intent if any present.
handlePendingIntent()
isWebViewHistoryRestoring = false
}
}
@ -2487,7 +2499,7 @@ abstract class CoreReaderFragment :
* @Warning: This method restores tabs state in new launches, do not modify it
* unless it is explicitly mentioned in the issue you're fixing.
*/
protected fun restoreTabs(
protected suspend fun restoreTabs(
webViewHistoryItemList: List<WebViewHistoryItem>,
currentTab: Int,
onComplete: () -> Unit
@ -2612,7 +2624,7 @@ abstract class CoreReaderFragment :
* KiwixReaderFragment.restoreViewStateOnValidWebViewHistory) to ensure consistent behavior
* when handling valid webViewHistory scenarios.
*/
protected abstract fun restoreViewStateOnValidWebViewHistory(
protected abstract suspend fun restoreViewStateOnValidWebViewHistory(
webViewHistoryItemList: List<WebViewHistoryItem>,
currentTab: Int,
restoreOrigin: RestoreOrigin,
@ -2627,7 +2639,7 @@ abstract class CoreReaderFragment :
* KiwixReaderFragment.restoreViewStateOnInvalidWebViewHistory) to ensure consistent behavior
* when handling invalid JSON scenarios.
*/
abstract fun restoreViewStateOnInvalidWebViewHistory()
abstract suspend fun restoreViewStateOnInvalidWebViewHistory()
}
enum class RestoreOrigin {

View File

@ -185,6 +185,7 @@ class CustomReaderFragment : CoreReaderFragment() {
// See https://github.com/kiwix/kiwix-android/issues/3541
zimReaderContainer?.zimFileReader?.let(::setUpBookmarks)
} else {
isWebViewHistoryRestoring = true
coreReaderLifeCycleScope?.launch {
openObbOrZim(true)
}
@ -197,7 +198,7 @@ class CustomReaderFragment : CoreReaderFragment() {
* due to the absence of any history records. In this case, it navigates to the homepage of the
* ZIM file, as custom apps are expected to have the ZIM file readily available.
*/
override fun restoreViewStateOnInvalidWebViewHistory() {
override suspend fun restoreViewStateOnInvalidWebViewHistory() {
openHomeScreen()
}
@ -205,7 +206,7 @@ class CustomReaderFragment : CoreReaderFragment() {
* Restores the view state when the webViewHistory data is valid.
* This method restores the tabs with webView pages history.
*/
override fun restoreViewStateOnValidWebViewHistory(
override suspend fun restoreViewStateOnValidWebViewHistory(
webViewHistoryItemList: List<WebViewHistoryItem>,
currentTab: Int,
// Unused in custom apps as there is only one ZIM file that is already set.
@ -430,6 +431,7 @@ class CustomReaderFragment : CoreReaderFragment() {
super.onResume()
if (appSettingsLaunched) {
appSettingsLaunched = false
isWebViewHistoryRestoring = true
coreReaderLifeCycleScope?.launch {
openObbOrZim(true)
}