diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt
index 86b789315..05b93c4e8 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt
@@ -41,7 +41,7 @@ import org.junit.Rule
import org.junit.Test
import org.kiwix.kiwixmobile.BaseActivityTest
import org.kiwix.kiwixmobile.R
-import org.kiwix.kiwixmobile.core.main.CoreReaderFragment
+import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt
index 4cb0510ae..4768d6877 100644
--- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt
+++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt
@@ -55,6 +55,7 @@ import androidx.compose.ui.unit.dp
import org.kiwix.kiwixmobile.R.string
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
+import org.kiwix.kiwixmobile.core.main.reader.CONTENT_LOADING_PROGRESSBAR_TESTING_TAG
import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixButton
@@ -78,7 +79,6 @@ import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
const val NO_FILE_TEXT_TESTING_TAG = "noFileTextTestingTag"
const val DOWNLOAD_BUTTON_TESTING_TAG = "downloadButtonTestingTag"
const val BOOK_LIST_TESTING_TAG = "bookListTestingTag"
-const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTestingTag"
const val SELECT_FILE_BUTTON_TESTING_TAG = "selectFileButtonTestingTag"
@OptIn(ExperimentalMaterial3Api::class)
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 5f5f0f94b..d5566eacc 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
@@ -50,11 +50,11 @@ import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
-import org.kiwix.kiwixmobile.core.main.CoreReaderFragment
+import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
import org.kiwix.kiwixmobile.core.main.CoreWebViewClient
-import org.kiwix.kiwixmobile.core.main.RestoreOrigin
-import org.kiwix.kiwixmobile.core.main.RestoreOrigin.FromExternalLaunch
-import org.kiwix.kiwixmobile.core.main.RestoreOrigin.FromSearchScreen
+import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin
+import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromExternalLaunch
+import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromSearchScreen
import org.kiwix.kiwixmobile.core.main.ToolbarScrollingKiwixWebView
import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt
similarity index 97%
rename from core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt
rename to core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt
index a71785914..05e4f6c81 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*
*/
-package org.kiwix.kiwixmobile.core.main
+package org.kiwix.kiwixmobile.core.main.reader
import android.Manifest
import android.Manifest.permission.POST_NOTIFICATIONS
@@ -131,13 +131,33 @@ import org.kiwix.kiwixmobile.core.extensions.setToolTipWithContentDescription
import org.kiwix.kiwixmobile.core.extensions.showFullScreenMode
import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.extensions.toast
+import org.kiwix.kiwixmobile.core.main.AddNoteDialog
+import org.kiwix.kiwixmobile.core.main.CompatFindActionModeCallback
+import org.kiwix.kiwixmobile.core.main.CoreMainActivity
+import org.kiwix.kiwixmobile.core.main.CoreSearchWidget
+import org.kiwix.kiwixmobile.core.main.CoreWebViewClient
+import org.kiwix.kiwixmobile.core.main.DarkModeViewPainter
+import org.kiwix.kiwixmobile.core.main.DocumentParser
import org.kiwix.kiwixmobile.core.main.DocumentParser.SectionsListener
+import org.kiwix.kiwixmobile.core.main.FIND_IN_PAGE_SEARCH_STRING
+import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnInitSucceedListener
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnSpeakingListener
+import org.kiwix.kiwixmobile.core.main.KiwixWebView
+import org.kiwix.kiwixmobile.core.main.MainMenu
import org.kiwix.kiwixmobile.core.main.MainMenu.MenuClickListener
-import org.kiwix.kiwixmobile.core.main.RestoreOrigin.FromExternalLaunch
+import org.kiwix.kiwixmobile.core.main.MainRepositoryActions
+import org.kiwix.kiwixmobile.core.main.OnSwipeTouchListener
+import org.kiwix.kiwixmobile.core.main.ServiceWorkerUninitialiser
+import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter
+import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromExternalLaunch
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.TableClickListener
+import org.kiwix.kiwixmobile.core.main.TabsAdapter
+import org.kiwix.kiwixmobile.core.main.ToolbarScrollingKiwixWebView
+import org.kiwix.kiwixmobile.core.main.UNINITIALISER_ADDRESS
+import org.kiwix.kiwixmobile.core.main.WebViewCallback
+import org.kiwix.kiwixmobile.core.main.WebViewProvider
import org.kiwix.kiwixmobile.core.navigateToAppSettings
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
import org.kiwix.kiwixmobile.core.page.history.NavigationHistoryClickListener
@@ -824,9 +844,9 @@ abstract class CoreReaderFragment :
// the unwanted blank space caused by the toolbar.
setTopMarginToWebViews(-requireActivity().getToolbarHeight())
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- bottomToolbar?.visibility = View.GONE
- contentFrame?.visibility = View.GONE
- progressBar?.visibility = View.GONE
+ bottomToolbar?.visibility = GONE
+ contentFrame?.visibility = GONE
+ progressBar?.visibility = GONE
backToTopButton?.hide()
setTabSwitcherVisibility(VISIBLE)
startAnimation(tabSwitcherRoot, R.anim.slide_down)
@@ -906,10 +926,10 @@ abstract class CoreReaderFragment :
)
tabSwitcherRoot?.let {
if (it.isVisible) {
- setTabSwitcherVisibility(View.GONE)
+ setTabSwitcherVisibility(GONE)
startAnimation(it, R.anim.slide_up)
- progressBar?.visibility = View.VISIBLE
- contentFrame?.visibility = View.VISIBLE
+ progressBar?.visibility = VISIBLE
+ contentFrame?.visibility = VISIBLE
}
}
progressBar?.hide()
@@ -1058,7 +1078,7 @@ abstract class CoreReaderFragment :
@Suppress("ReturnCount", "NestedBlockDepth")
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
when {
- tabSwitcherRoot?.visibility == View.VISIBLE -> {
+ tabSwitcherRoot?.visibility == VISIBLE -> {
selectTab(
if (currentWebViewIndex < webViewList.size) {
currentWebViewIndex
@@ -1168,7 +1188,7 @@ abstract class CoreReaderFragment :
override fun onSpeakingStarted() {
requireActivity().runOnUiThread {
mainMenu?.onTextToSpeechStartedTalking()
- ttsControls?.visibility = View.VISIBLE
+ ttsControls?.visibility = VISIBLE
setActionAndStartTTSService(ACTION_PAUSE_OR_RESUME_TTS, false)
}
}
@@ -1176,7 +1196,7 @@ abstract class CoreReaderFragment :
override fun onSpeakingEnded() {
requireActivity().runOnUiThread {
mainMenu?.onTextToSpeechStoppedTalking()
- ttsControls?.visibility = View.GONE
+ ttsControls?.visibility = GONE
pauseTTSButton?.setText(R.string.tts_pause)
setActionAndStartTTSService(ACTION_STOP_TTS)
}
@@ -1469,15 +1489,15 @@ abstract class CoreReaderFragment :
private fun reopenBook() {
hideNoBookOpenViews()
- contentFrame?.visibility = View.VISIBLE
+ contentFrame?.visibility = VISIBLE
mainMenu?.showBookSpecificMenuItems()
}
protected fun exitBook(shouldCloseZimBook: Boolean = true) {
showNoBookOpenViews()
- bottomToolbar?.visibility = View.GONE
+ bottomToolbar?.visibility = GONE
actionBar?.title = getString(R.string.reader)
- contentFrame?.visibility = View.GONE
+ contentFrame?.visibility = GONE
hideProgressBar()
mainMenu?.hideBookSpecificMenuItems()
if (shouldCloseZimBook) {
@@ -1501,7 +1521,7 @@ abstract class CoreReaderFragment :
protected fun hideProgressBar() {
progressBar?.apply {
- visibility = View.GONE
+ visibility = GONE
hide()
}
}
@@ -1511,7 +1531,7 @@ abstract class CoreReaderFragment :
reopenBook()
}
tempWebViewForUndo?.let {
- if (tabSwitcherRoot?.visibility == View.GONE) {
+ if (tabSwitcherRoot?.visibility == 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`
@@ -1610,7 +1630,7 @@ abstract class CoreReaderFragment :
if (requireActivity().hasNotificationPermission(sharedPreferenceUtil)) {
ttsControls?.let { ttsControls ->
when (ttsControls.visibility) {
- View.GONE -> {
+ GONE -> {
if (isBackToTopEnabled) {
backToTopButton?.hide()
}
@@ -1622,7 +1642,7 @@ abstract class CoreReaderFragment :
}
}
- View.VISIBLE -> {
+ VISIBLE -> {
if (isBackToTopEnabled) {
backToTopButton?.show()
}
@@ -1663,14 +1683,14 @@ abstract class CoreReaderFragment :
}
override fun onHomeMenuClicked() {
- if (tabSwitcherRoot?.visibility == View.VISIBLE) {
+ if (tabSwitcherRoot?.visibility == VISIBLE) {
hideTabSwitcher()
}
createNewTab()
}
override fun onTabMenuClicked() {
- if (tabSwitcherRoot?.visibility == View.VISIBLE) {
+ if (tabSwitcherRoot?.visibility == VISIBLE) {
hideTabSwitcher()
selectTab(currentWebViewIndex)
} else {
@@ -1751,9 +1771,9 @@ abstract class CoreReaderFragment :
@Suppress("MagicNumber")
protected open fun openFullScreen() {
(requireActivity() as CoreMainActivity).disableDrawer(false)
- toolbarContainer?.visibility = View.GONE
- bottomToolbar?.visibility = View.GONE
- exitFullscreenButton?.visibility = View.VISIBLE
+ toolbarContainer?.visibility = GONE
+ bottomToolbar?.visibility = GONE
+ exitFullscreenButton?.visibility = VISIBLE
exitFullscreenButton?.background?.alpha = 153
val window = requireActivity().window
window.decorView.showFullScreenMode(window)
@@ -1769,9 +1789,9 @@ abstract class CoreReaderFragment :
toolbar?.let(::setUpDrawerToggle)
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
sharedPreferenceUtil?.putPrefFullScreen(false)
- toolbarContainer?.visibility = View.VISIBLE
+ toolbarContainer?.visibility = VISIBLE
updateBottomToolbarVisibility()
- exitFullscreenButton?.visibility = View.GONE
+ exitFullscreenButton?.visibility = GONE
exitFullscreenButton?.background?.alpha = 255
val window = requireActivity().window
window.decorView.closeFullScreenMode(window)
@@ -1801,7 +1821,7 @@ abstract class CoreReaderFragment :
// Show content if there is `Open Library` button showing
// and we are opening the ZIM file
hideNoBookOpenViews()
- contentFrame?.visibility = View.VISIBLE
+ contentFrame?.visibility = VISIBLE
openAndSetInContainer(zimReaderSource)
updateTitle()
} else {
@@ -2014,13 +2034,13 @@ abstract class CoreReaderFragment :
// opens home screen when user closes all tabs
protected fun showNoBookOpenViews() {
- noOpenBookButton?.visibility = View.VISIBLE
- noOpenBookText?.visibility = View.VISIBLE
+ noOpenBookButton?.visibility = VISIBLE
+ noOpenBookText?.visibility = VISIBLE
}
private fun hideNoBookOpenViews() {
- noOpenBookButton?.visibility = View.GONE
- noOpenBookText?.visibility = View.GONE
+ noOpenBookButton?.visibility = GONE
+ noOpenBookText?.visibility = GONE
}
@Suppress("MagicNumber")
@@ -2101,7 +2121,7 @@ abstract class CoreReaderFragment :
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
val rightAndLeftMargin = requireActivity().resources.getDimensionPixelSize(
- org.kiwix.kiwixmobile.core.R.dimen.activity_horizontal_margin
+ R.dimen.activity_horizontal_margin
)
setMargins(
rightAndLeftMargin,
@@ -2188,11 +2208,11 @@ abstract class CoreReaderFragment :
private fun updateBottomToolbarVisibility() {
bottomToolbar?.let {
if (urlIsValid() &&
- tabSwitcherRoot?.visibility != View.VISIBLE && !isInFullScreenMode()
+ tabSwitcherRoot?.visibility != VISIBLE && !isInFullScreenMode()
) {
- it.visibility = View.VISIBLE
+ it.visibility = VISIBLE
} else {
- it.visibility = View.GONE
+ it.visibility = GONE
}
}
}
@@ -2332,7 +2352,7 @@ abstract class CoreReaderFragment :
}
private fun contentUrl(articleUrl: String?): String =
- "${ZimFileReader.CONTENT_PREFIX}$articleUrl".toUri().toString()
+ "${CONTENT_PREFIX}$articleUrl".toUri().toString()
private fun redirectOrOriginal(contentUrl: String): String {
zimReaderContainer?.let {
@@ -2722,13 +2742,13 @@ abstract class CoreReaderFragment :
if (it > 200) {
if (
(backToTopButton?.isGone == true || backToTopButton?.isInvisible == true) &&
- ttsControls?.visibility == View.GONE
+ ttsControls?.visibility == GONE
) {
backToTopButton?.show()
}
} else {
backToTopButton?.isVisible
- if (backToTopButton?.visibility == View.VISIBLE) {
+ if (backToTopButton?.visibility == VISIBLE) {
backToTopButton?.hide()
}
}
@@ -2739,7 +2759,7 @@ abstract class CoreReaderFragment :
override fun webViewLongClick(url: String) {
var handleEvent = false
when {
- url.startsWith(ZimFileReader.CONTENT_PREFIX) -> {
+ url.startsWith(CONTENT_PREFIX) -> {
// This is my web site, so do not override; let my WebView load the page
handleEvent = true
}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt
new file mode 100644
index 000000000..04b09ddee
--- /dev/null
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt
@@ -0,0 +1,284 @@
+/*
+ * Kiwix Android
+ * Copyright (c) 2025 Kiwix
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.kiwix.kiwixmobile.core.main.reader
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.BottomAppBar
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.FloatingActionButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import org.kiwix.kiwixmobile.core.R
+import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
+import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
+import org.kiwix.kiwixmobile.core.ui.components.KiwixButton
+import org.kiwix.kiwixmobile.core.ui.components.KiwixSnackbarHost
+import org.kiwix.kiwixmobile.core.ui.components.ProgressBarStyle
+import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
+import org.kiwix.kiwixmobile.core.ui.models.IconItem
+import org.kiwix.kiwixmobile.core.ui.models.IconItem.Drawable
+import org.kiwix.kiwixmobile.core.ui.models.toPainter
+import org.kiwix.kiwixmobile.core.ui.theme.Black
+import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme
+import org.kiwix.kiwixmobile.core.ui.theme.White
+import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
+import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
+import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE
+import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_LAYOUT_HEIGHT
+import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TTS_BUTTONS_CONTROL_ALPHA
+
+const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTestingTag"
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Suppress("ComposableLambdaParameterNaming")
+@Composable
+fun ReaderScreen(
+ state: ReaderScreenState,
+ actionMenuItems: List,
+ navigationIcon: @Composable () -> Unit
+) {
+ KiwixDialogTheme {
+ Scaffold(
+ snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) },
+ topBar = { KiwixAppBar(R.string.note, navigationIcon, actionMenuItems) },
+ floatingActionButton = { BackToTopFab(state) }
+ ) { paddingValues ->
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ ShowProgressBarIfZIMFilePageIsLoading(state)
+ if (state.isNoBookOpenInReader) {
+ NoBookOpenView(state.onOpenLibraryButtonClicked)
+ }
+ }
+ TtsControls(state)
+ ShowFullScreenView(state)
+ ShowDonationLayout(state)
+ }
+ }
+ }
+}
+
+@Composable
+private fun ShowFullScreenView(state: ReaderScreenState) {
+ if (state.fullScreenItem.first) {
+ state.fullScreenItem.second
+ }
+}
+
+@Composable
+private fun ShowProgressBarIfZIMFilePageIsLoading(state: ReaderScreenState) {
+ if (state.pageLoadingItem.first) {
+ ContentLoadingProgressBar(
+ modifier = Modifier.testTag(CONTENT_LOADING_PROGRESSBAR_TESTING_TAG),
+ progressBarStyle = ProgressBarStyle.HORIZONTAL,
+ progress = state.pageLoadingItem.second
+ )
+ }
+}
+
+@Composable
+private fun NoBookOpenView(
+ onOpenLibraryButtonClicked: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = FOUR_DP)
+ .verticalScroll(rememberScrollState()),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = stringResource(R.string.no_open_book),
+ style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Medium),
+ textAlign = TextAlign.Center
+ )
+ Spacer(modifier = Modifier.height(EIGHT_DP))
+ KiwixButton(
+ buttonText = stringResource(R.string.open_library),
+ clickListener = onOpenLibraryButtonClicked
+ )
+ }
+}
+
+@Composable
+private fun BoxScope.TtsControls(state: ReaderScreenState) {
+ if (state.showTtsControls) {
+ Row(modifier = Modifier.align(Alignment.TopCenter)) {
+ Button(
+ onClick = state.onPauseTtsClick,
+ modifier = Modifier
+ .weight(1f)
+ .alpha(TTS_BUTTONS_CONTROL_ALPHA)
+ ) {
+ Text(
+ text = stringResource(R.string.tts_pause),
+ fontWeight = FontWeight.Bold
+ )
+ }
+ Spacer(modifier = Modifier.width(FOUR_DP))
+ Button(
+ onClick = state.onStopTtsClick,
+ modifier = Modifier
+ .weight(1f)
+ .alpha(TTS_BUTTONS_CONTROL_ALPHA)
+ ) {
+ Text(
+ text = stringResource(R.string.stop),
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun BackToTopFab(state: ReaderScreenState) {
+ if (state.showBackToTopButton) {
+ FloatingActionButton(
+ onClick = state.backToTopButtonClick,
+ modifier = Modifier,
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ shape = FloatingActionButtonDefaults.smallShape
+ ) {
+ Icon(
+ painter = Drawable(R.drawable.action_find_previous).toPainter(),
+ contentDescription = stringResource(R.string.pref_back_to_top),
+ tint = White
+ )
+ }
+ }
+}
+
+@Composable
+private fun BottomAppBarOfReaderScreen(
+ onBookmarkClick: () -> Unit,
+ onBackClick: () -> Unit,
+ onHomeClick: () -> Unit,
+ onForwardClick: () -> Unit,
+ onTocClick: () -> Unit
+) {
+ BottomAppBar(
+ containerColor = Black,
+ contentColor = White
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(READER_BOTTOM_APP_BAR_LAYOUT_HEIGHT),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ // Bookmark Icon
+ BottomAppBarButtonIcon(
+ onBookmarkClick,
+ Drawable(R.drawable.ic_bookmark_border_24dp),
+ stringResource(R.string.bookmarks)
+ )
+ // Back Icon(for going to previous page)
+ BottomAppBarButtonIcon(
+ onBackClick,
+ Drawable(R.drawable.ic_keyboard_arrow_left_24dp),
+ stringResource(R.string.go_to_previous_page)
+ )
+ // Home Icon(to open the home page of ZIM file)
+ BottomAppBarButtonIcon(
+ onHomeClick,
+ Drawable(R.drawable.action_home),
+ stringResource(R.string.menu_home)
+ )
+ // Forward Icon(for going to next page)
+ BottomAppBarButtonIcon(
+ onForwardClick,
+ Drawable(R.drawable.ic_keyboard_arrow_right_24dp),
+ stringResource(R.string.go_to_next_page)
+ )
+ // Toggle Icon(to open the table of content in right side bar)
+ BottomAppBarButtonIcon(
+ onTocClick,
+ Drawable(R.drawable.ic_toc_24dp),
+ stringResource(R.string.table_of_contents)
+ )
+ }
+ }
+}
+
+@Composable
+private fun BottomAppBarButtonIcon(
+ onClick: () -> Unit,
+ buttonIcon: IconItem,
+ contentDescription: String
+) {
+ IconButton(onClick = onClick) {
+ Icon(
+ buttonIcon.toPainter(),
+ contentDescription,
+ modifier = Modifier.size(READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE)
+ )
+ }
+}
+
+@Composable
+private fun BoxScope.ShowDonationLayout(state: ReaderScreenState) {
+ if (state.shouldShowDonationPopup) {
+ Box(
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ .fillMaxWidth()
+ ) {
+ // TODO create donation popup layout.
+ }
+ }
+}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreenState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreenState.kt
new file mode 100644
index 000000000..751a6cbda
--- /dev/null
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreenState.kt
@@ -0,0 +1,76 @@
+/*
+ * Kiwix Android
+ * Copyright (c) 2025 Kiwix
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.kiwix.kiwixmobile.core.main.reader
+
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.ui.platform.ComposeView
+
+/**
+ * Represents the UI state for the Reader Screen.
+ *
+ * This data class encapsulates all UI-related states in a single object,
+ * reducing complexity in the Fragment.
+ */
+data class ReaderScreenState(
+ /**
+ * Handles snack bar messages and displays.
+ */
+ val snackBarHostState: SnackbarHostState,
+ /**
+ * Manages the showing of "No open book" message and button.
+ */
+ val isNoBookOpenInReader: Boolean,
+ /**
+ * Handles when open library button clicks.
+ */
+ val onOpenLibraryButtonClicked: () -> Unit,
+ /**
+ * Manages the showing of "ProgressBar" when ZIM file page is loading.
+ *
+ * A [Pair] containing:
+ * - [Boolean]: Whether page is loading.
+ * - [Int]: progress of page loading.
+ */
+ val pageLoadingItem: Pair,
+ /**
+ * Manages the showing of "Donation" layout.
+ */
+ val shouldShowDonationPopup: Boolean,
+ /**
+ * Manages the showing of "Full screen view".
+ *
+ * A [Pair] containing:
+ * - [Boolean]: Whether to show/hide full screen mode.
+ * - [ComposeView]: full screen view.
+ */
+ val fullScreenItem: Pair,
+ /**
+ * Manages the showing of "BackToTop" fab button.
+ */
+ val showBackToTopButton: Boolean,
+ /**
+ * Handles the click of "BackToTop" fab button.
+ */
+ val backToTopButtonClick: () -> Unit,
+ val showFullscreenButton: Boolean = false,
+ val onExitFullscreenClick: () -> Unit = {},
+ val showTtsControls: Boolean = false,
+ val onPauseTtsClick: () -> Unit = {},
+ val onStopTtsClick: () -> Unit = {},
+)
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt
index 6f63a8337..d4445616f 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/effects/OpenSearchItem.kt
@@ -25,7 +25,7 @@ import kotlinx.parcelize.Parcelize
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.setNavigationResultOnCurrent
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
-import org.kiwix.kiwixmobile.core.main.SEARCH_ITEM_TITLE_KEY
+import org.kiwix.kiwixmobile.core.main.reader.SEARCH_ITEM_TITLE_KEY
import org.kiwix.kiwixmobile.core.reader.addContentPrefix
import org.kiwix.kiwixmobile.core.search.SearchListItem
import org.kiwix.kiwixmobile.core.utils.TAG_FILE_SEARCHED
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt
index 068223553..e65eb5229 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt
@@ -179,5 +179,9 @@ object ComposeDimens {
// Settings screen dimes
val STORAGE_LOADING_PROGRESS_BAR_SIZE = 40.dp
val CATEGORY_TITLE_TEXT_SIZE = 14.sp
- val PREFERENCE_TITLE_TEXT_SIZE = 18.sp
+
+ // Reader screen dimes
+ val READER_BOTTOM_APP_BAR_LAYOUT_HEIGHT = 48.dp
+ val READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE = 30.dp
+ const val TTS_BUTTONS_CONTROL_ALPHA = 0.6f
}
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 723ca336a..1a5eecbe2 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
@@ -37,9 +37,9 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.browserIntent
import org.kiwix.kiwixmobile.core.extensions.getResizedDrawable
import org.kiwix.kiwixmobile.core.extensions.isFileExist
-import org.kiwix.kiwixmobile.core.main.CoreReaderFragment
+import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
import org.kiwix.kiwixmobile.core.main.MainMenu
-import org.kiwix.kiwixmobile.core.main.RestoreOrigin
+import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin
import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.utils.LanguageUtils