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