mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
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:
parent
11d1c161c6
commit
39baa985fb
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
@ -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 = {},
|
||||
)
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user