diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt index 2dcb640bda..9dd9a47f8b 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt @@ -31,6 +31,7 @@ import android.view.View.VISIBLE import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.drawerlayout.widget.DrawerLayout +import androidx.lifecycle.lifecycleScope import com.google.android.material.bottomnavigation.BottomNavigationView import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.R @@ -165,29 +166,31 @@ class KiwixReaderFragment : CoreReaderFragment() { } override fun hideTabSwitcher() { - actionBar?.let { actionBar -> - actionBar.setDisplayShowTitleEnabled(true) - toolbar?.let { activity?.setupDrawerToggle(it, true) } + lifecycleScope.launch { + actionBar?.let { actionBar -> + actionBar.setDisplayShowTitleEnabled(true) + toolbar?.let { activity?.setupDrawerToggle(it, true) } - setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) - closeAllTabsButton?.setImageDrawableCompat(drawable.ic_close_black_24dp) - if (tabSwitcherRoot?.visibility == View.VISIBLE) { - tabSwitcherRoot?.visibility = GONE - startAnimation(tabSwitcherRoot, anim.slide_up) - progressBar?.visibility = View.GONE - progressBar?.progress = 0 - contentFrame?.visibility = View.VISIBLE - } - mainMenu?.showWebViewOptions(true) - if (webViewList.isEmpty()) { - exitBook() - } else { - // Reset the top margin of web views to 0 to remove any previously set margin - // This ensures that the web views are displayed without any additional - // top margin for kiwix main app. - setTopMarginToWebViews(0) - selectTab(currentWebViewIndex) + closeAllTabsButton?.setImageDrawableCompat(drawable.ic_close_black_24dp) + if (tabSwitcherRoot?.visibility == View.VISIBLE) { + tabSwitcherRoot?.visibility = GONE + startAnimation(tabSwitcherRoot, anim.slide_up) + progressBar?.visibility = View.GONE + progressBar?.progress = 0 + contentFrame?.visibility = View.VISIBLE + } + mainMenu?.showWebViewOptions(true) + if (webViewList.isEmpty()) { + exitBook() + } else { + // Reset the top margin of web views to 0 to remove any previously set margin + // This ensures that the web views are displayed without any additional + // top margin for kiwix main app. + setTopMarginToWebViews(0) + selectTab(currentWebViewIndex) + } } } } @@ -229,7 +232,7 @@ class KiwixReaderFragment : CoreReaderFragment() { } } - override fun restoreViewStateOnInvalidJSON() { + override suspend fun restoreViewStateOnInvalidJSON() { Log.d(TAG_KIWIX, "Kiwix normal start, no zimFile loaded last time -> display home page") exitBook() } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index 9a1142926e..81c86a2b2f 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -1398,7 +1398,7 @@ abstract class CoreReaderFragment : mainMenu?.showBookSpecificMenuItems() } - protected fun exitBook() { + protected suspend fun exitBook() { showNoBookOpenViews() bottomToolbar?.visibility = View.GONE actionBar?.title = getString(R.string.reader) @@ -1408,7 +1408,7 @@ abstract class CoreReaderFragment : closeZimBook() } - fun closeZimBook() { + suspend fun closeZimBook() { zimReaderContainer?.setZimReaderSource(null) } @@ -1428,29 +1428,31 @@ abstract class CoreReaderFragment : } private fun restoreDeletedTab(index: Int) { - if (webViewList.isEmpty()) { - reopenBook() - } - tempWebViewForUndo?.let { - if (tabSwitcherRoot?.visibility == View.GONE) { - // Remove the top margin from the webView when the tabSwitcher is not visible. - // We have added this margin in `TabsAdapter` to not show the top margin in tabs. - // `tempWebViewForUndo` saved with that margin so before showing it to the `contentFrame` - // We need to set full width and height for properly showing the content of webView. - it.layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT - ) + lifecycleScope.launch { + if (webViewList.isEmpty()) { + reopenBook() } - zimReaderContainer?.setZimReaderSource(tempZimSourceForUndo) - webViewList.add(index, it) - tabsAdapter?.notifyDataSetChanged() - snackBarRoot?.let { root -> - Snackbar.make(root, R.string.tab_restored, Snackbar.LENGTH_SHORT).show() + tempWebViewForUndo?.let { + if (tabSwitcherRoot?.visibility == View.GONE) { + // Remove the top margin from the webView when the tabSwitcher is not visible. + // We have added this margin in `TabsAdapter` to not show the top margin in tabs. + // `tempWebViewForUndo` saved with that margin so before showing it to the `contentFrame` + // We need to set full width and height for properly showing the content of webView. + it.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + ) + } + zimReaderContainer?.setZimReaderSource(tempZimSourceForUndo) + webViewList.add(index, it) + tabsAdapter?.notifyDataSetChanged() + snackBarRoot?.let { root -> + Snackbar.make(root, R.string.tab_restored, Snackbar.LENGTH_SHORT).show() + } + setUpWithTextToSpeech(it) + updateBottomToolbarVisibility() + safelyAddWebView(it) } - setUpWithTextToSpeech(it) - updateBottomToolbarVisibility() - safelyAddWebView(it) } } @@ -1861,18 +1863,20 @@ abstract class CoreReaderFragment : } private fun restoreDeletedTabs() { - if (tempWebViewListForUndo.isNotEmpty()) { - zimReaderContainer?.setZimReaderSource(tempZimSourceForUndo) - webViewList.addAll(tempWebViewListForUndo) - tabsAdapter?.notifyDataSetChanged() - snackBarRoot?.let { root -> - Snackbar.make(root, R.string.tabs_restored, Snackbar.LENGTH_SHORT).show() + lifecycleScope.launch { + if (tempWebViewListForUndo.isNotEmpty()) { + zimReaderContainer?.setZimReaderSource(tempZimSourceForUndo) + webViewList.addAll(tempWebViewListForUndo) + tabsAdapter?.notifyDataSetChanged() + snackBarRoot?.let { root -> + Snackbar.make(root, R.string.tabs_restored, Snackbar.LENGTH_SHORT).show() + } + reopenBook() + showTabSwitcher() + setUpWithTextToSpeech(tempWebViewListForUndo.last()) + updateBottomToolbarVisibility() + safelyAddWebView(tempWebViewListForUndo.last()) } - reopenBook() - showTabSwitcher() - setUpWithTextToSpeech(tempWebViewListForUndo.last()) - updateBottomToolbarVisibility() - safelyAddWebView(tempWebViewListForUndo.last()) } } @@ -2496,7 +2500,7 @@ abstract class CoreReaderFragment : private fun isInvalidJson(jsonString: String?): Boolean = jsonString == null || jsonString == "[]" - protected fun manageExternalLaunchAndRestoringViewState( + protected suspend fun manageExternalLaunchAndRestoringViewState( restoreOrigin: RestoreOrigin = FromExternalLaunch ) { val settings = requireActivity().getSharedPreferences( @@ -2637,7 +2641,7 @@ abstract class CoreReaderFragment : * KiwixReaderFragment.restoreViewStateOnInvalidJSON) to ensure consistent behavior * when handling invalid JSON scenarios. */ - abstract fun restoreViewStateOnInvalidJSON() + abstract suspend fun restoreViewStateOnInvalidJSON() } enum class RestoreOrigin { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt index cb8990da66..d41e526548 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt @@ -19,7 +19,9 @@ package org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope import io.reactivex.processors.PublishProcessor +import kotlinx.coroutines.launch import org.json.JSONArray import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent @@ -51,36 +53,38 @@ data class ShowOpenNoteDialog( ShowNoteDialog, { effects.offer(OpenPage(page, zimReaderContainer)) }, { - val item = page as NoteListItem - // Check if toDatabase is not null, and then set it in zimReaderContainer. - // For custom apps, we are currently using fileDescriptor, and they only have a single file in them, - // which is already set in zimReaderContainer, so there's no need to set it again. - item.zimReaderSource?.toDatabase().let { - val currentZimReaderSource = zimReaderContainer.zimReaderSource - if (!activity.isCustomApp()) { - zimReaderContainer.setZimReaderSource(item.zimReaderSource) - } - if (zimReaderContainer.zimReaderSource != currentZimReaderSource) { - // if current zim file is not the same set the main page of that zim file - // so that when we go back it properly loads the article, and do nothing if the - // zim file is same because there might be multiple tabs opened. - val settings = activity.getSharedPreferences( - SharedPreferenceUtil.PREF_KIWIX_MOBILE, - 0 - ) - val editor = settings.edit() - val urls = JSONArray() - val positions = JSONArray() - urls.put(CONTENT_PREFIX + zimReaderContainer.mainPage) - positions.put(0) - editor.putString(TAG_CURRENT_FILE, zimReaderContainer.zimReaderSource?.toDatabase()) - editor.putString(TAG_CURRENT_ARTICLES, "$urls") - editor.putString(TAG_CURRENT_POSITIONS, "$positions") - editor.putInt(TAG_CURRENT_TAB, 0) - editor.apply() + activity.lifecycleScope.launch { + val item = page as NoteListItem + // Check if toDatabase is not null, and then set it in zimReaderContainer. + // For custom apps, we are currently using fileDescriptor, and they only have a single file in them, + // which is already set in zimReaderContainer, so there's no need to set it again. + item.zimReaderSource?.toDatabase().let { + val currentZimReaderSource = zimReaderContainer.zimReaderSource + if (!activity.isCustomApp()) { + zimReaderContainer.setZimReaderSource(item.zimReaderSource) + } + if (zimReaderContainer.zimReaderSource != currentZimReaderSource) { + // if current zim file is not the same set the main page of that zim file + // so that when we go back it properly loads the article, and do nothing if the + // zim file is same because there might be multiple tabs opened. + val settings = activity.getSharedPreferences( + SharedPreferenceUtil.PREF_KIWIX_MOBILE, + 0 + ) + val editor = settings.edit() + val urls = JSONArray() + val positions = JSONArray() + urls.put(CONTENT_PREFIX + zimReaderContainer.mainPage) + positions.put(0) + editor.putString(TAG_CURRENT_FILE, zimReaderContainer.zimReaderSource?.toDatabase()) + editor.putString(TAG_CURRENT_ARTICLES, "$urls") + editor.putString(TAG_CURRENT_POSITIONS, "$positions") + editor.putInt(TAG_CURRENT_TAB, 0) + editor.apply() + } } + effects.offer(OpenNote(item.noteFilePath, item.zimUrl, item.title)) } - effects.offer(OpenNote(item.noteFilePath, item.zimUrl, item.title)) } ) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt index c0bdefc03b..7c8ebaed9f 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt @@ -32,15 +32,14 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F field = value } - fun setZimReaderSource(zimReaderSource: ZimReaderSource?) { + suspend fun setZimReaderSource(zimReaderSource: ZimReaderSource?) { if (zimReaderSource == zimFileReader?.zimReaderSource) { return } - zimFileReader = runBlocking { + zimFileReader = if (zimReaderSource?.exists() == true && zimReaderSource.canOpenInLibkiwix()) zimFileReaderFactory.create(zimReaderSource) else null - } } fun getPageUrlFromTitle(title: String) = zimFileReader?.getPageUrlFrom(title) 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 d6d353f491..e0c270ba63 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 @@ -30,6 +30,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.core.net.toUri import androidx.drawerlayout.widget.DrawerLayout +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.R.dimen @@ -70,27 +71,29 @@ class CustomReaderFragment : CoreReaderFragment() { @Suppress("NestedBlockDepth") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - if (enforcedLanguage()) { - return - } - - if (isAdded) { - setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) - if (BuildConfig.DISABLE_SIDEBAR) { - val toolbarToc = - activity?.findViewById(org.kiwix.kiwixmobile.core.R.id.bottom_toolbar_toc) - toolbarToc?.isEnabled = false + lifecycleScope.launch { + if (enforcedLanguage()) { + return@launch } - with(activity as AppCompatActivity) { - supportActionBar?.setDisplayHomeAsUpEnabled(true) - toolbar?.let(::setUpDrawerToggle) - } - loadPageFromNavigationArguments() - if (BuildConfig.DISABLE_EXTERNAL_LINK) { - // If "external links" are disabled in a custom app, - // this sets the shared preference to not show the external link popup - // when opening external links. - sharedPreferenceUtil?.putPrefExternalLinkPopup(false) + + if (isAdded) { + setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + if (BuildConfig.DISABLE_SIDEBAR) { + val toolbarToc = + activity?.findViewById(org.kiwix.kiwixmobile.core.R.id.bottom_toolbar_toc) + toolbarToc?.isEnabled = false + } + with(activity as AppCompatActivity) { + supportActionBar?.setDisplayHomeAsUpEnabled(true) + toolbar?.let(::setUpDrawerToggle) + } + loadPageFromNavigationArguments() + if (BuildConfig.DISABLE_EXTERNAL_LINK) { + // If "external links" are disabled in a custom app, + // this sets the shared preference to not show the external link popup + // when opening external links. + sharedPreferenceUtil?.putPrefExternalLinkPopup(false) + } } } } @@ -135,7 +138,7 @@ class CustomReaderFragment : CoreReaderFragment() { super.setTabSwitcherVisibility(visibility) } - private fun loadPageFromNavigationArguments() { + private suspend fun loadPageFromNavigationArguments() { val args = CustomReaderFragmentArgs.fromBundle(requireArguments()) if (args.pageUrl.isNotEmpty()) { loadUrlWithCurrentWebview(args.pageUrl) @@ -154,7 +157,7 @@ class CustomReaderFragment : CoreReaderFragment() { * due to invalid or corrupted data. In this case, it opens the homepage of the zim file, * as custom apps always have the zim file available. */ - override fun restoreViewStateOnInvalidJSON() { + override suspend fun restoreViewStateOnInvalidJSON() { openHomeScreen() }