Migrated the Reader screen to jetpack compose.

* Created the `ReaderScreen` for compose UI.
* Created the `ReaderScreenState` to manage the state of UI.
This commit is contained in:
MohitMaliFtechiz 2025-06-13 00:29:52 +05:30
parent 11d1c161c6
commit 39baa985fb
9 changed files with 432 additions and 48 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
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
}

View File

@ -0,0 +1,284 @@
/*
* Kiwix Android
* Copyright (c) 2025 Kiwix <android.kiwix.org>
* 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 <http://www.gnu.org/licenses/>.
*
*/
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<ActionMenuItem>,
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.
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Kiwix Android
* Copyright (c) 2025 Kiwix <android.kiwix.org>
* 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 <http://www.gnu.org/licenses/>.
*
*/
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<Boolean, Int>,
/**
* 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<Boolean, ComposeView>,
/**
* 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 = {},
)

View File

@ -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

View File

@ -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
}

View File

@ -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