From 3ebb37d5cc561b4b2f7288277b2dbbb230ce9ff0 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 24 Jun 2025 01:23:18 +0530 Subject: [PATCH] Refactored the TOC button functionality to handle cases where custom apps are configured to disable it. * Fixed: Missing bottom margin in custom apps. * Fixed: Reader's bottom app bar not appearing after closing or selecting a tab. * Fixed: Menu not showing in the toolbar when the application is freshly launched in custom apps. * Refactored the scroll behavior of the toolbar and bottom app bar to sync with WebView scrolling in the Compose UI. --- .../library/local/LocalLibraryScreen.kt | 8 +- .../library/online/OnlineLibraryScreen.kt | 8 +- .../destination/reader/KiwixReaderFragment.kt | 18 ++- .../kiwixmobile/webserver/ZimHostScreen.kt | 15 +- .../main/res/navigation/kiwix_nav_graph.xml | 3 +- core/detekt_baseline.xml | 2 +- .../core/extensions/TextViewExtensions.kt | 30 ---- .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 2 +- .../core/main/AddNoteDialogScreen.kt | 8 +- .../core/main/KiwixWebChromeClient.kt | 3 +- .../kiwixmobile/core/main/KiwixWebView.kt | 4 +- .../core/main/ToolbarScrollingKiwixWebView.kt | 135 +++++++++--------- .../core/main/reader/CoreReaderFragment.kt | 46 +++--- .../core/main/reader/ReaderMenuState.kt | 2 +- .../core/main/reader/ReaderScreen.kt | 70 ++++++--- .../core/main/reader/ReaderScreenState.kt | 6 +- .../history/NavigationHistoryDialogScreen.kt | 8 +- .../core/ui/components/KiwixAppBar.kt | 4 +- .../kiwixmobile/core/utils/ComposeDimens.kt | 2 + .../kiwixmobile/core/utils/DimenUtils.kt | 9 ++ .../VideoEnabledWebChromeClient.kt | 18 +-- .../custom/main/CustomReaderFragment.kt | 21 ++- 22 files changed, 235 insertions(+), 187 deletions(-) delete mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/extensions/TextViewExtensions.kt 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 07f2e58f7..fac91b17e 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 @@ -95,10 +95,10 @@ fun LocalLibraryScreen( snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) }, topBar = { KiwixAppBar( - stringResource(R.string.library), - navigationIcon, - state.actionMenuItems, - scrollBehavior + title = stringResource(R.string.library), + navigationIcon = navigationIcon, + actionMenuItems = state.actionMenuItems, + topAppBarScrollBehavior = scrollBehavior ) }, floatingActionButton = { SelectFileButton(fabButtonClick) }, diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt index bc214225c..e0a1e717d 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt @@ -100,10 +100,10 @@ fun OnlineLibraryScreen( snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) }, topBar = { KiwixAppBar( - stringResource(string.download), - navigationIcon, - actionMenuItems, - scrollBehavior, + title = stringResource(string.download), + navigationIcon = navigationIcon, + actionMenuItems = actionMenuItems, + topAppBarScrollBehavior = scrollBehavior, searchBar = searchBarIfActive(state) ) }, 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 7db4deddc..4fc78a366 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 @@ -211,12 +211,12 @@ class KiwixReaderFragment : CoreReaderFragment() { } } - override fun onPause() { - super.onPause() - // ScrollingViewWithBottomNavigationBehavior changes the margin to the size of the nav bar, - // this resets the margin to zero, before fragment navigation. - setBottomMarginToNavHostContainer(0) - } + // override fun onPause() { + // super.onPause() + // // ScrollingViewWithBottomNavigationBehavior changes the margin to the size of the nav bar, + // // this resets the margin to zero, before fragment navigation. + // setBottomMarginToNavHostContainer(ZERO) + // } @Suppress("DEPRECATION") override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) { @@ -233,7 +233,6 @@ class KiwixReaderFragment : CoreReaderFragment() { override fun onResume() { super.onResume() - setFragmentContainerBottomMarginToSizeOfNavBar() if (isFullScreenVideo || isInFullScreenMode()) { hideNavBar() } @@ -306,11 +305,10 @@ class KiwixReaderFragment : CoreReaderFragment() { requireContext(), this, attrs ?: throw IllegalArgumentException("AttributeSet must not be null"), - null, requireNotNull(readerScreenState.value.fullScreenItem.second), CoreWebViewClient(this, requireNotNull(zimReaderContainer)), - // requireNotNull(toolbarContainer), - // requireNotNull(bottomToolbar), + onToolbarOffsetChanged = { offsetY -> toolbarOffsetY.value = offsetY }, + onBottomAppBarOffsetChanged = { bottomOffsetY -> bottomAppBarOffsetY.value = bottomOffsetY }, sharedPreferenceUtil = requireNotNull(sharedPreferenceUtil), parentNavigationBar = requireActivity().findViewById(R.id.bottom_nav_view) ) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/webserver/ZimHostScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/webserver/ZimHostScreen.kt index e5c382bb5..9287b7209 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/webserver/ZimHostScreen.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/webserver/ZimHostScreen.kt @@ -87,11 +87,20 @@ fun ZimHostScreen( ) { KiwixTheme { Scaffold(topBar = { - KiwixAppBar(stringResource(R.string.menu_wifi_hotspot), navigationIcon) + KiwixAppBar( + title = stringResource(R.string.menu_wifi_hotspot), + navigationIcon = navigationIcon + ) }) { contentPadding -> - Column(modifier = Modifier.fillMaxSize().padding(contentPadding)) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(contentPadding) + ) { Row( - modifier = Modifier.fillMaxWidth().padding(horizontal = SIXTEEN_DP), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = SIXTEEN_DP), verticalAlignment = Alignment.CenterVertically ) { ServerIpText(serverIpText, Modifier.weight(1f), LocalContext.current) diff --git a/app/src/main/res/navigation/kiwix_nav_graph.xml b/app/src/main/res/navigation/kiwix_nav_graph.xml index ad2e44327..2cbd53489 100644 --- a/app/src/main/res/navigation/kiwix_nav_graph.xml +++ b/app/src/main/res/navigation/kiwix_nav_graph.xml @@ -23,8 +23,7 @@ + android:label="Reader"> LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean ) LongParameterList:PageTestHelpers.kt$( bookmarkTitle: String = "bookmarkTitle", isSelected: Boolean = false, id: Long = 2, zimId: String = "zimId", zimName: String = "zimName", zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" ) LongParameterList:Repository.kt$Repository$( private val libkiwixBookOnDisk: LibkiwixBookOnDisk, private val libkiwixBookmarks: LibkiwixBookmarks, private val historyRoomDao: HistoryRoomDao, private val webViewHistoryRoomDao: WebViewHistoryRoomDao, private val notesRoomDao: NotesRoomDao, private val languageDao: NewLanguagesDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer ) - LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup?, videoView: ViewGroup?, webViewClient: CoreWebViewClient, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null ) + LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, videoView: ViewGroup?, webViewClient: CoreWebViewClient, private val onToolbarOffsetChanged: ((Float) -> Unit)? = null, private val onBottomAppBarOffsetChanged: ((Float) -> Unit)? = null, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null ) MagicNumber:ArticleCount.kt$ArticleCount$3 MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100 MagicNumber:DownloadItem.kt$DownloadItem$1000L diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/TextViewExtensions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/TextViewExtensions.kt deleted file mode 100644 index 269235cce..000000000 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/TextViewExtensions.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2019 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.extensions - -import android.view.View -import android.widget.TextView - -fun TextView.setTextAndVisibility(nullableText: String?) = - if (nullableText?.isNotEmpty() == true) { - text = nullableText - visibility = View.VISIBLE - } else { - visibility = View.GONE - } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt index e470e6659..ebdeab94d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt @@ -73,7 +73,7 @@ fun HelpScreen( KiwixTheme { Scaffold( topBar = { - KiwixAppBar(stringResource(R.string.menu_help), navigationIcon) + KiwixAppBar(title = stringResource(R.string.menu_help), navigationIcon = navigationIcon) } ) { innerPadding -> Column(modifier = Modifier.padding(innerPadding)) { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialogScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialogScreen.kt index 12ab84867..b04adc7d0 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialogScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialogScreen.kt @@ -72,7 +72,13 @@ fun AddNoteDialogScreen( KiwixDialogTheme { Scaffold( snackbarHost = { KiwixSnackbarHost(snackbarHostState = snackBarHostState) }, - topBar = { KiwixAppBar(stringResource(R.string.note), navigationIcon, actionMenuItems) } + topBar = { + KiwixAppBar( + title = stringResource(R.string.note), + navigationIcon = navigationIcon, + actionMenuItems = actionMenuItems + ) + } ) { paddingValues -> Column( modifier = Modifier diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebChromeClient.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebChromeClient.kt index cc716b3b4..b0d21d16e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebChromeClient.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebChromeClient.kt @@ -23,10 +23,9 @@ import org.kiwix.videowebview.VideoEnabledWebChromeClient class KiwixWebChromeClient( private val callback: WebViewCallback, - nonVideoView: ViewGroup?, videoView: ViewGroup?, webView: KiwixWebView? -) : VideoEnabledWebChromeClient(nonVideoView, videoView, null, webView) { +) : VideoEnabledWebChromeClient(videoView, null, webView) { override fun onProgressChanged(view: WebView, progress: Int) { callback.webViewProgressChanged(progress, view) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt index e2eadce87..d9447bd70 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt @@ -59,7 +59,6 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor( context: Context, private val callback: WebViewCallback, attrs: AttributeSet, - private var nonVideoView: ViewGroup?, videoView: ViewGroup?, private val webViewClient: CoreWebViewClient, val sharedPreferenceUtil: SharedPreferenceUtil @@ -102,7 +101,7 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor( clearCache(true) setWebViewClient(webViewClient) webChromeClient = - KiwixWebChromeClient(callback, nonVideoView, videoView, this).apply { + KiwixWebChromeClient(callback, videoView, this).apply { setOnToggledFullscreen( object : ToggledFullscreenCallback { override fun toggledFullscreen(fullscreen: Boolean) { @@ -154,7 +153,6 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() - nonVideoView = null textZoomJob?.cancel() textZoomJob = null } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/ToolbarScrollingKiwixWebView.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/ToolbarScrollingKiwixWebView.kt index c67f97a0e..9241b146e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/ToolbarScrollingKiwixWebView.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/ToolbarScrollingKiwixWebView.kt @@ -23,8 +23,12 @@ import android.util.AttributeSet import android.view.MotionEvent import android.view.View import android.view.ViewGroup -import org.kiwix.kiwixmobile.core.utils.DimenUtils.getToolbarHeight +import org.kiwix.kiwixmobile.core.utils.ComposeDimens.COMPOSE_BOTTOM_APP_BAR_DEFAULT_HEIGHT +import org.kiwix.kiwixmobile.core.utils.ComposeDimens.COMPOSE_TOOLBAR_DEFAULT_HEIGHT +import org.kiwix.kiwixmobile.core.utils.DimenUtils.dpToPx import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import kotlin.math.max +import kotlin.math.min @SuppressLint("ViewConstructor") @Suppress("UnusedPrivateProperty") @@ -32,28 +36,72 @@ class ToolbarScrollingKiwixWebView @JvmOverloads constructor( context: Context, callback: WebViewCallback, attrs: AttributeSet, - nonVideoView: ViewGroup?, videoView: ViewGroup?, webViewClient: CoreWebViewClient, + private val onToolbarOffsetChanged: ((Float) -> Unit)? = null, + private val onBottomAppBarOffsetChanged: ((Float) -> Unit)? = null, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null ) : KiwixWebView( context, callback, attrs, - nonVideoView, videoView, webViewClient, sharedPreferenceUtil ) { - private val toolbarHeight = context.getToolbarHeight() + private val toolbarHeight = context.dpToPx(COMPOSE_TOOLBAR_DEFAULT_HEIGHT) + private val bottomAppBarHeightPx = context.dpToPx(COMPOSE_BOTTOM_APP_BAR_DEFAULT_HEIGHT) private var startY = 0f + private var currentOffset = 0f init { fixInitalScrollingIssue() } + /** + * Adjusts the internal offset of the WebView based on scroll delta. + * + * Positive scrollDelta = user scrolling down (hide UI) + * Negative scrollDelta = user scrolling up (show UI) + */ + + private fun moveToolbar(scrollDelta: Int): Boolean { + val newOffset = when { + scrollDelta > 0 -> max(-toolbarHeight.toFloat(), currentOffset - scrollDelta) + else -> min(0f, currentOffset - scrollDelta) + } + + if (newOffset != currentOffset) { + currentOffset = newOffset + notifyOffsetChanged(newOffset) + return true + } + + return false + } + + /** + * Notifies Compose UI about toolbar offset. + */ + private fun notifyOffsetChanged(offset: Float) { + onToolbarOffsetChanged?.invoke(offset) + + // Compute offset for bottomAppBar using height ratio + val bottomOffset = offset * -1 * (bottomAppBarHeightPx.toFloat() / toolbarHeight) + onBottomAppBarOffsetChanged?.invoke(bottomOffset) + + // Optional: Animate parent navigation bar (if still using it) + parentNavigationBar?.let { view -> + val offsetFactor = view.height / toolbarHeight.toFloat() + view.translationY = offset * -1 * offsetFactor + } + + // Adjust WebView position to prevent layout jump + this.translationY = offset + } + /** * The webview needs to be scrolled with 0 to not be slightly hidden on startup. * See https://github.com/kiwix/kiwix-android/issues/2304 for issue description. @@ -62,68 +110,27 @@ class ToolbarScrollingKiwixWebView @JvmOverloads constructor( moveToolbar(0) } - @Suppress("FunctionOnlyReturningConstant", "UnusedParameter") - private fun moveToolbar(scrollDelta: Int): Boolean { - // val originalTranslation = toolbarView.translationY - // val newTranslation = - // if (scrollDelta > 0) { - // // scroll down - // max(-toolbarHeight.toFloat(), originalTranslation - scrollDelta) - // } else { - // // scroll up - // min(0f, originalTranslation - scrollDelta) - // } - // - // toolbarView.translationY = newTranslation - // bottomBarView.translationY = - // newTranslation * -1 * (bottomBarView.height / toolbarHeight.toFloat()) - // parentNavigationBar?.let { - // it.translationY = newTranslation * -1 * (it.height / toolbarHeight.toFloat()) - // } - // this.translationY = newTranslation + toolbarHeight - // return toolbarHeight + newTranslation != 0f && newTranslation != 0f - return false - } - @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent): Boolean { - // val transY = toolbarView.translationY.toInt() - // when (event.actionMasked) { - // MotionEvent.ACTION_DOWN -> startY = event.rawY - // MotionEvent.ACTION_MOVE -> { - // // If we are in fullscreen don't scroll bar - // if (sharedPreferenceUtil.prefFullScreen) { - // return super.onTouchEvent(event) - // } - // // Filter out zooms since we don't want to affect the toolbar when zooming - // if (event.pointerCount == 1) { - // val diffY = (event.rawY - startY).toInt() - // startY = event.rawY - // if (moveToolbar(-diffY)) { - // event.offsetLocation(0f, -diffY.toFloat()) - // return super.onTouchEvent(event) - // } - // } - // } - // // If the toolbar is half-visible, - // // either open or close it entirely depending on how far it is visible - // MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> - // if (transY != 0 && transY > -toolbarHeight) { - // if (transY > -toolbarHeight / 2) { - // ensureToolbarDisplayed() - // } else { - // ensureToolbarHidden() - // } - // } - // } + if (sharedPreferenceUtil.prefFullScreen) return super.onTouchEvent(event) + + when (event.actionMasked) { + MotionEvent.ACTION_DOWN -> { + startY = event.rawY + } + + MotionEvent.ACTION_MOVE -> { + if (event.pointerCount == 1) { + val diffY = (event.rawY - startY).toInt() + startY = event.rawY + if (moveToolbar(-diffY)) { + event.offsetLocation(0f, -diffY.toFloat()) + return super.onTouchEvent(event) + } + } + } + } + return super.onTouchEvent(event) } - - private fun ensureToolbarDisplayed() { - moveToolbar(-toolbarHeight) - } - - private fun ensureToolbarHidden() { - moveToolbar(toolbarHeight) - } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt index ef06d8456..5357476d4 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt @@ -62,7 +62,6 @@ import androidx.annotation.AnimRes import androidx.appcompat.app.ActionBar import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.SnackbarDuration @@ -171,7 +170,6 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderSource import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon -import org.kiwix.kiwixmobile.core.ui.components.rememberBottomNavigationVisibility import org.kiwix.kiwixmobile.core.ui.models.IconItem import org.kiwix.kiwixmobile.core.ui.theme.White import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth @@ -320,6 +318,8 @@ abstract class CoreReaderFragment : private var isReadSelection = false private var isReadAloudServiceRunning = false private var libkiwixBook: Book? = null + val toolbarOffsetY = mutableStateOf(0f) + val bottomAppBarOffsetY = mutableStateOf(0f) protected var readerMenuState: ReaderMenuState? = null private var composeView: ComposeView? = null @@ -348,7 +348,7 @@ abstract class CoreReaderFragment : previousPageButtonItem = Triple({ goBack() }, { showBackwardHistory() }, false), onHomeButtonClick = { openMainPage() }, nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false), - onTocClick = { openToc() }, + tocButtonItem = false to { }, onCloseAllTabs = { closeAllTabs() }, bottomNavigationHeight = ZERO, shouldShowBottomAppBar = true, @@ -473,11 +473,6 @@ abstract class CoreReaderFragment : readerMenuState = createMainMenu() composeView?.apply { setContent { - val lazyListState = rememberLazyListState() - val isBottomNavVisible = rememberBottomNavigationVisibility(lazyListState) - LaunchedEffect(isBottomNavVisible) { - (activity as? CoreMainActivity)?.toggleBottomNavigation(isBottomNavVisible) - } LaunchedEffect(Unit) { snapshotFlow { webViewList.size } .distinctUntilChanged() @@ -491,7 +486,8 @@ abstract class CoreReaderFragment : bottomNavigationHeight = getBottomNavigationHeight(), readerScreenTitle = context.getString(R.string.reader), darkModeViewPainter = darkModeViewPainter, - fullScreenItem = fullScreenItem.first to getVideoView() + fullScreenItem = fullScreenItem.first to getVideoView(), + tocButtonItem = getTocButtonStateAndAction() ) } } @@ -514,7 +510,8 @@ abstract class CoreReaderFragment : iconTint = navigationIconTint() ) }, - listState = lazyListState + toolbarOffsetY = toolbarOffsetY, + bottomAppBarOffsetY = bottomAppBarOffsetY ) DialogHost(alertDialogShower as AlertDialogShower) } @@ -629,6 +626,20 @@ abstract class CoreReaderFragment : private fun getBottomNavigationHeight(): Int = getBottomNavigationView()?.measuredHeight ?: ZERO + /** + * Provides the visibility state and click action for the TOC (Table of Contents) button + * shown in the reader's bottom app bar. + * + * @return A [Pair] containing: + * - [Boolean]: Indicates whether the TOC button should be enabled (e.g., can be disabled + * in certain custom app configurations where the sidebar is turned off). + * - [() -> Unit]: The action to perform when the TOC button is clicked. + * + * Note: If modifying this method, ensure it is thoroughly tested in custom app variants + * where sidebar behavior may differ. + */ + open fun getTocButtonStateAndAction(): Pair Unit> = true to { openToc() } + private fun navigationIconContentDescription() = if (readerMenuState?.isInTabSwitcher == true) { R.string.search_open_in_new_tab @@ -949,7 +960,6 @@ abstract class CoreReaderFragment : protected open fun hideTabSwitcher(shouldCloseZimBook: Boolean = true) { setUpDrawerToggle() setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) - selectTab(currentWebViewIndex) readerScreenState.update { copy( shouldShowBottomAppBar = true, @@ -958,6 +968,7 @@ abstract class CoreReaderFragment : } showSearchPlaceHolderInToolbar(false) readerMenuState?.showWebViewOptions(urlIsValid()) + selectTab(currentWebViewIndex) // Reset the top margin of web views to 0 to remove any previously set margin // This ensures that the web views are displayed without any additional top margin for kiwix custom apps. // setTopMarginToWebViews(0) @@ -1077,7 +1088,7 @@ abstract class CoreReaderFragment : } } - private fun openToc() { + protected fun openToc() { drawerLayout?.openDrawer(GravityCompat.END) } @@ -1413,14 +1424,13 @@ abstract class CoreReaderFragment : @Throws(IllegalArgumentException::class) protected open fun createWebView(attrs: AttributeSet?): ToolbarScrollingKiwixWebView? { return ToolbarScrollingKiwixWebView( - requireActivity(), + requireContext(), this, attrs ?: throw IllegalArgumentException("AttributeSet must not be null"), - null, requireNotNull(readerScreenState.value.fullScreenItem.second), CoreWebViewClient(this, requireNotNull(zimReaderContainer)), - // requireNotNull(toolbarContainer), - // requireNotNull(bottomToolbar), + onToolbarOffsetChanged = { offsetY -> toolbarOffsetY.value = offsetY }, + onBottomAppBarOffsetChanged = { bottomOffsetY -> bottomAppBarOffsetY.value = bottomOffsetY }, requireNotNull(sharedPreferenceUtil) ) } @@ -1821,7 +1831,6 @@ abstract class CoreReaderFragment : // Show content if there is `Open Library` button showing // and we are opening the ZIM file hideNoBookOpenViews() - contentFrame?.visibility = VISIBLE openAndSetInContainer(zimReaderSource) updateTitle() } else { @@ -2203,7 +2212,7 @@ abstract class CoreReaderFragment : private fun updateBottomToolbarVisibility() { readerScreenState.update { - copy(shouldShowBottomAppBar = !showTabSwitcher && !isInFullScreenMode()) + copy(shouldShowBottomAppBar = readerMenuState?.isInTabSwitcher == false && !isInFullScreenMode()) } } @@ -2882,6 +2891,7 @@ abstract class CoreReaderFragment : } selectTab(currentTab) onComplete.invoke() + readerMenuState?.showWebViewOptions(urlIsValid()) } catch (ignore: Exception) { Log.w(TAG_KIWIX, "Kiwix shared preferences corrupted", ignore) activity.toast(R.string.could_not_restore_tabs, Toast.LENGTH_LONG) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderMenuState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderMenuState.kt index 6a528d763..91e878ebd 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderMenuState.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderMenuState.kt @@ -61,7 +61,7 @@ const val TAB_MENU_ITEM_TESTING_TAG = "tabMenuItemTestingTag" @Stable class ReaderMenuState( private val menuClickListener: MenuClickListener, - private val isUrlValidInitially: Boolean, + isUrlValidInitially: Boolean, private val disableReadAloud: Boolean = false, private val disableTabs: Boolean = false, private val disableSearch: Boolean = false 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 index 972689c88..916cb0bef 100644 --- 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 @@ -22,6 +22,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout +import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.BorderStroke @@ -36,9 +37,11 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding @@ -94,6 +97,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import kotlinx.coroutines.delay @@ -144,12 +148,12 @@ const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTe @Composable fun ReaderScreen( state: ReaderScreenState, - listState: LazyListState, actionMenuItems: List, + toolbarOffsetY: MutableState, + bottomAppBarOffsetY: MutableState, navigationIcon: @Composable () -> Unit ) { - val (bottomNavHeight, lazyListState) = - rememberScrollBehavior(state.bottomNavigationHeight, listState) + val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() } val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() KiwixDialogTheme { Scaffold( @@ -158,6 +162,7 @@ fun ReaderScreen( ReaderTopBar( state, actionMenuItems, + toolbarOffsetY, scrollBehavior, navigationIcon ) @@ -166,9 +171,13 @@ fun ReaderScreen( modifier = Modifier .systemBarsPadding() .nestedScroll(scrollBehavior.nestedScrollConnection) - .padding(bottom = bottomNavHeight.value) + .padding(bottom = bottomNavHeightInDp) ) { paddingValues -> - ReaderContentLayout(state, Modifier.padding(paddingValues)) + ReaderContentLayout( + state, + Modifier.padding(paddingValues), + bottomAppBarOffsetY + ) } } } @@ -179,23 +188,33 @@ fun ReaderScreen( private fun ReaderTopBar( state: ReaderScreenState, actionMenuItems: List, + toolbarOffsetY: MutableState, scrollBehavior: TopAppBarScrollBehavior, navigationIcon: @Composable () -> Unit, ) { if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) { + val animatedOffsetY by animateDpAsState( + targetValue = with(LocalDensity.current) { toolbarOffsetY.value.toDp() }, + label = "ToolbarScrollOffset" + ) KiwixAppBar( title = if (state.showTabSwitcher) "" else state.readerScreenTitle, navigationIcon = navigationIcon, actionMenuItems = actionMenuItems, topAppBarScrollBehavior = scrollBehavior, searchBar = - searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps) + searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps), + modifier = Modifier.offset { IntOffset(x = ZERO, y = animatedOffsetY.roundToPx()) } ) } } @Composable -private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = Modifier) { +private fun ReaderContentLayout( + state: ReaderScreenState, + modifier: Modifier = Modifier, + bottomAppBarOffsetY: MutableState +) { Box(modifier = modifier.fillMaxSize()) { when { state.showTabSwitcher -> TabSwitcherView( @@ -210,7 +229,7 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M state.fullScreenItem.first -> ShowFullScreenView(state) else -> { - ShowZIMFileContent(state) + ShowZIMFileContent(state, bottomAppBarOffsetY) ShowProgressBarIfZIMFilePageIsLoading(state) Column(Modifier.align(Alignment.BottomCenter)) { TtsControls(state) @@ -219,8 +238,9 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M state.previousPageButtonItem, state.onHomeButtonClick, state.nextPageButtonItem, - state.onTocClick, - state.shouldShowBottomAppBar + state.tocButtonItem, + state.shouldShowBottomAppBar, + bottomAppBarOffsetY ) } CloseFullScreenImageButton( @@ -305,7 +325,15 @@ private fun BoxScope.CloseFullScreenImageButton( } @Composable -private fun ShowZIMFileContent(state: ReaderScreenState) { +private fun ShowZIMFileContent( + state: ReaderScreenState, + bottomAppBarOffsetY: MutableState +) { + val density = LocalDensity.current + + val bottomNavHeightDp = with(density) { state.bottomNavigationHeight.toDp() } + val bottomAppBarOffsetDp = with(density) { -bottomAppBarOffsetY.value.toDp() } + val totalBottomPadding = (bottomNavHeightDp + bottomAppBarOffsetDp).coerceAtLeast(ZERO.dp) state.selectedWebView?.let { selectedWebView -> key(selectedWebView) { AndroidView( @@ -317,7 +345,10 @@ private fun ShowZIMFileContent(state: ReaderScreenState) { addView(selectedWebView) } }, - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .padding(bottom = totalBottomPadding) ) } } @@ -425,14 +456,20 @@ private fun BottomAppBarOfReaderScreen( previousPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>, onHomeButtonClick: () -> Unit, nextPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>, - onTocClick: () -> Unit, - shouldShowBottomAppBar: Boolean + tocButtonItem: Pair Unit>, + shouldShowBottomAppBar: Boolean, + bottomAppBarOffsetY: MutableState ) { if (!shouldShowBottomAppBar) return + val animatedOffsetY by animateDpAsState( + targetValue = with(LocalDensity.current) { bottomAppBarOffsetY.value.toDp() }, + label = "BottomAppBarOffset" + ) BottomAppBar( containerColor = Black, contentColor = White, - scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior() + scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior(), + modifier = Modifier.offset { IntOffset(ZERO, animatedOffsetY.roundToPx()) } ) { Row( modifier = Modifier @@ -472,7 +509,8 @@ private fun BottomAppBarOfReaderScreen( ) // Toggle Icon(to open the table of content in right side bar) BottomAppBarButtonIcon( - onClick = onTocClick, + shouldEnable = tocButtonItem.first, + onClick = tocButtonItem.second, buttonIcon = Drawable(R.drawable.ic_toc_24dp), contentDescription = stringResource(R.string.table_of_contents) ) 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 index ab1b4c901..d6a9438da 100644 --- 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 @@ -132,8 +132,12 @@ data class ReaderScreenState( val nextPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>, /** * Handles the click to open right sidebar button click in reader bottom toolbar. + * + * A [Pair] containing: + * - [Boolean]: Handles the button should enable or not(Specially for custom apps). + * - [Unit]: Handles the click of button. */ - val onTocClick: () -> Unit, + val tocButtonItem: Pair Unit>, val onCloseAllTabs: () -> Unit, /** * Stores the height of the bottom navigation bar in pixels. diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/NavigationHistoryDialogScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/NavigationHistoryDialogScreen.kt index 159437055..4c8156014 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/NavigationHistoryDialogScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/NavigationHistoryDialogScreen.kt @@ -70,7 +70,13 @@ fun NavigationHistoryDialogScreen( ) { KiwixDialogTheme { Scaffold( - topBar = { KiwixAppBar(stringResource(titleId), navigationIcon, actionMenuItems) } + topBar = { + KiwixAppBar( + title = stringResource(titleId), + navigationIcon = navigationIcon, + actionMenuItems = actionMenuItems + ) + } ) { paddingValues -> Box( modifier = Modifier diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/ui/components/KiwixAppBar.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/ui/components/KiwixAppBar.kt index 710c896d3..08c6b357f 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/ui/components/KiwixAppBar.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/ui/components/KiwixAppBar.kt @@ -72,6 +72,7 @@ const val TOOLBAR_TITLE_TESTING_TAG = "toolbarTitle" @OptIn(ExperimentalMaterial3Api::class) @Composable fun KiwixAppBar( + modifier: Modifier = Modifier, title: String, navigationIcon: @Composable () -> Unit, actionMenuItems: List = emptyList(), @@ -92,7 +93,8 @@ fun KiwixAppBar( // Edge-to-Edge mode is already enabled in our application, // so we don't need to apply additional top insets. // This prevents unwanted extra margin at the top. - windowInsets = WindowInsets.statusBars.only(WindowInsetsSides.Horizontal) + windowInsets = WindowInsets.statusBars.only(WindowInsetsSides.Horizontal), + modifier = modifier ) } } 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 1e1cae443..6825b2842 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 @@ -193,4 +193,6 @@ object ComposeDimens { val BACK_TO_TOP_BUTTON_BOTTOM_MARGIN = 80.dp const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp + val COMPOSE_TOOLBAR_DEFAULT_HEIGHT = 64.dp + val COMPOSE_BOTTOM_APP_BAR_DEFAULT_HEIGHT = 80.dp } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DimenUtils.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DimenUtils.kt index 4005bf043..de41a4214 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DimenUtils.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DimenUtils.kt @@ -23,6 +23,7 @@ import android.os.Build import android.util.DisplayMetrics import android.util.TypedValue import androidx.appcompat.R +import androidx.compose.ui.unit.Dp object DimenUtils { @JvmStatic fun Context.getToolbarHeight(): Int { @@ -33,6 +34,14 @@ object DimenUtils { ) } + fun Context.dpToPx(dp: Dp): Int { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + dp.value, + resources.displayMetrics + ).toInt() + } + @JvmStatic fun Activity.getWindowHeight(): Int = computedDisplayMetric.heightPixels diff --git a/core/src/main/java/org/kiwix/videowebview/VideoEnabledWebChromeClient.kt b/core/src/main/java/org/kiwix/videowebview/VideoEnabledWebChromeClient.kt index 648fc4f65..114068d63 100644 --- a/core/src/main/java/org/kiwix/videowebview/VideoEnabledWebChromeClient.kt +++ b/core/src/main/java/org/kiwix/videowebview/VideoEnabledWebChromeClient.kt @@ -55,7 +55,6 @@ open class VideoEnabledWebChromeClient : fun toggledFullscreen(fullscreen: Boolean) } - private var activityNonVideoView: View? = null private var activityVideoView: ViewGroup? = null private var loadingView: View? = null private var webView: VideoEnabledWebView? = null @@ -84,14 +83,11 @@ open class VideoEnabledWebChromeClient : /** * Builds a video enabled WebChromeClient. * - * @param activityNonVideoView A View in the activity's layout that contains every other view that - * should be hidden when the video goes full-screen. * @param activityVideoView A ViewGroup in the activity's layout that will display the video. * Typically you would like this to fill the whole layout. */ @SuppressWarnings("unused") - constructor(activityNonVideoView: View?, activityVideoView: ViewGroup?) { - this.activityNonVideoView = activityNonVideoView + constructor(activityVideoView: ViewGroup?) { this.activityVideoView = activityVideoView loadingView = null webView = null @@ -101,8 +97,6 @@ open class VideoEnabledWebChromeClient : /** * Builds a video enabled WebChromeClient. * - * @param activityNonVideoView A View in the activity's layout that contains every other view that - * should be hidden when the video goes full-screen. * @param activityVideoView A ViewGroup in the activity's layout that will display the video. * Typically you would like this to fill the whole layout. * @param loadingView A View to be shown while the video is loading (typically only used in API @@ -110,11 +104,9 @@ open class VideoEnabledWebChromeClient : */ @SuppressWarnings("unused") constructor( - activityNonVideoView: View?, activityVideoView: ViewGroup?, loadingView: View? ) { - this.activityNonVideoView = activityNonVideoView this.activityVideoView = activityVideoView this.loadingView = loadingView webView = null @@ -124,8 +116,6 @@ open class VideoEnabledWebChromeClient : /** * Builds a video enabled WebChromeClient. * - * @param activityNonVideoView A View in the activity's layout that contains every other view that - * should be hidden when the video goes full-screen. * @param activityVideoView A ViewGroup in the activity's layout that will display the video. * Typically you would like this to fill the whole layout. * @param loadingView A View to be shown while the video is loading (typically only used in API @@ -137,12 +127,10 @@ open class VideoEnabledWebChromeClient : */ @SuppressWarnings("unused") constructor( - activityNonVideoView: View?, activityVideoView: ViewGroup?, loadingView: View?, webView: VideoEnabledWebView? ) { - this.activityNonVideoView = activityNonVideoView this.activityVideoView = activityVideoView this.loadingView = loadingView this.webView = webView @@ -177,9 +165,6 @@ open class VideoEnabledWebChromeClient : isVideoFullscreen = true videoViewContainer = view videoViewCallback = callback - - // Hide the non-video view, add the video view, and show it - activityNonVideoView?.visibility = View.INVISIBLE activityVideoView?.addView( videoViewContainer, ViewGroup.LayoutParams( @@ -248,7 +233,6 @@ open class VideoEnabledWebChromeClient : // Hide the video view, remove it, and show the non-video view activityVideoView?.visibility = View.INVISIBLE activityVideoView?.removeView(videoViewContainer) - activityNonVideoView?.visibility = View.VISIBLE // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes) videoViewCallback?.let { 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 438122de7..6d6dfda38 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 @@ -23,7 +23,6 @@ import android.os.Bundle import android.view.Menu import android.view.MenuInflater import android.view.View -import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu @@ -78,12 +77,6 @@ class CustomReaderFragment : CoreReaderFragment() { if (isAdded) { setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) - if (BuildConfig.DISABLE_SIDEBAR) { - val toolbarToc = - activity?.findViewById(org.kiwix.kiwixmobile.core.R.id.bottom_toolbar_toc) - toolbarToc?.isEnabled = false - // TODO refactor this with compose UI. - } with(activity as AppCompatActivity) { supportActionBar?.setDisplayHomeAsUpEnabled(true) setUpDrawerToggle() @@ -98,6 +91,20 @@ class CustomReaderFragment : CoreReaderFragment() { } } + /** + * Returns the TOC (Table of Contents) button's enabled state and click action. + * + * In this custom app variant, the TOC button is disabled if [BuildConfig.DISABLE_SIDEBAR] is `true`. + * This is typically used when the sidebar functionality is intentionally turned off. + * + * @return A [Pair] containing: + * - [Boolean]: `true` if the TOC button should be enabled (i.e., sidebar is allowed), + * `false` if it should be disabled (i.e., [DISABLE_SIDEBAR] is `true`). + * - [() -> Unit]: Action to execute when the button is clicked. This will only be invoked if enabled. + */ + override fun getTocButtonStateAndAction(): Pair Unit> = + !BuildConfig.DISABLE_SIDEBAR to { openToc() } + /** * Returns the tint color for the navigation icon. *