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.
This commit is contained in:
MohitMaliFtechiz 2025-06-24 01:23:18 +05:30
parent 2608694442
commit 3ebb37d5cc
22 changed files with 235 additions and 187 deletions

View File

@ -95,10 +95,10 @@ fun LocalLibraryScreen(
snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) }, snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) },
topBar = { topBar = {
KiwixAppBar( KiwixAppBar(
stringResource(R.string.library), title = stringResource(R.string.library),
navigationIcon, navigationIcon = navigationIcon,
state.actionMenuItems, actionMenuItems = state.actionMenuItems,
scrollBehavior topAppBarScrollBehavior = scrollBehavior
) )
}, },
floatingActionButton = { SelectFileButton(fabButtonClick) }, floatingActionButton = { SelectFileButton(fabButtonClick) },

View File

@ -100,10 +100,10 @@ fun OnlineLibraryScreen(
snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) }, snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) },
topBar = { topBar = {
KiwixAppBar( KiwixAppBar(
stringResource(string.download), title = stringResource(string.download),
navigationIcon, navigationIcon = navigationIcon,
actionMenuItems, actionMenuItems = actionMenuItems,
scrollBehavior, topAppBarScrollBehavior = scrollBehavior,
searchBar = searchBarIfActive(state) searchBar = searchBarIfActive(state)
) )
}, },

View File

@ -211,12 +211,12 @@ class KiwixReaderFragment : CoreReaderFragment() {
} }
} }
override fun onPause() { // override fun onPause() {
super.onPause() // super.onPause()
// ScrollingViewWithBottomNavigationBehavior changes the margin to the size of the nav bar, // // ScrollingViewWithBottomNavigationBehavior changes the margin to the size of the nav bar,
// this resets the margin to zero, before fragment navigation. // // this resets the margin to zero, before fragment navigation.
setBottomMarginToNavHostContainer(0) // setBottomMarginToNavHostContainer(ZERO)
} // }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
@ -233,7 +233,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setFragmentContainerBottomMarginToSizeOfNavBar()
if (isFullScreenVideo || isInFullScreenMode()) { if (isFullScreenVideo || isInFullScreenMode()) {
hideNavBar() hideNavBar()
} }
@ -306,11 +305,10 @@ class KiwixReaderFragment : CoreReaderFragment() {
requireContext(), requireContext(),
this, this,
attrs ?: throw IllegalArgumentException("AttributeSet must not be null"), attrs ?: throw IllegalArgumentException("AttributeSet must not be null"),
null,
requireNotNull(readerScreenState.value.fullScreenItem.second), requireNotNull(readerScreenState.value.fullScreenItem.second),
CoreWebViewClient(this, requireNotNull(zimReaderContainer)), CoreWebViewClient(this, requireNotNull(zimReaderContainer)),
// requireNotNull(toolbarContainer), onToolbarOffsetChanged = { offsetY -> toolbarOffsetY.value = offsetY },
// requireNotNull(bottomToolbar), onBottomAppBarOffsetChanged = { bottomOffsetY -> bottomAppBarOffsetY.value = bottomOffsetY },
sharedPreferenceUtil = requireNotNull(sharedPreferenceUtil), sharedPreferenceUtil = requireNotNull(sharedPreferenceUtil),
parentNavigationBar = requireActivity().findViewById(R.id.bottom_nav_view) parentNavigationBar = requireActivity().findViewById(R.id.bottom_nav_view)
) )

View File

@ -87,11 +87,20 @@ fun ZimHostScreen(
) { ) {
KiwixTheme { KiwixTheme {
Scaffold(topBar = { Scaffold(topBar = {
KiwixAppBar(stringResource(R.string.menu_wifi_hotspot), navigationIcon) KiwixAppBar(
title = stringResource(R.string.menu_wifi_hotspot),
navigationIcon = navigationIcon
)
}) { contentPadding -> }) { contentPadding ->
Column(modifier = Modifier.fillMaxSize().padding(contentPadding)) { Column(
modifier = Modifier
.fillMaxSize()
.padding(contentPadding)
) {
Row( Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = SIXTEEN_DP), modifier = Modifier
.fillMaxWidth()
.padding(horizontal = SIXTEEN_DP),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
ServerIpText(serverIpText, Modifier.weight(1f), LocalContext.current) ServerIpText(serverIpText, Modifier.weight(1f), LocalContext.current)

View File

@ -23,8 +23,7 @@
<fragment <fragment
android:id="@+id/readerFragment" android:id="@+id/readerFragment"
android:name="org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragment" android:name="org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragment"
android:label="Reader" android:label="Reader">
tools:layout="@layout/fragment_reader">
<argument <argument
android:name="zimFileUri" android:name="zimFileUri"
android:defaultValue="" android:defaultValue=""

View File

@ -13,7 +13,7 @@
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID> <ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
<ID>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" )</ID> <ID>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" )</ID>
<ID>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 )</ID> <ID>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 )</ID>
<ID>LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup?, videoView: ViewGroup?, webViewClient: CoreWebViewClient, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null )</ID> <ID>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 )</ID>
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID> <ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
<ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID> <ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID>
<ID>MagicNumber:DownloadItem.kt$DownloadItem$1000L</ID> <ID>MagicNumber:DownloadItem.kt$DownloadItem$1000L</ID>

View File

@ -1,30 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2019 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.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
}

View File

@ -73,7 +73,7 @@ fun HelpScreen(
KiwixTheme { KiwixTheme {
Scaffold( Scaffold(
topBar = { topBar = {
KiwixAppBar(stringResource(R.string.menu_help), navigationIcon) KiwixAppBar(title = stringResource(R.string.menu_help), navigationIcon = navigationIcon)
} }
) { innerPadding -> ) { innerPadding ->
Column(modifier = Modifier.padding(innerPadding)) { Column(modifier = Modifier.padding(innerPadding)) {

View File

@ -72,7 +72,13 @@ fun AddNoteDialogScreen(
KiwixDialogTheme { KiwixDialogTheme {
Scaffold( Scaffold(
snackbarHost = { KiwixSnackbarHost(snackbarHostState = snackBarHostState) }, snackbarHost = { KiwixSnackbarHost(snackbarHostState = snackBarHostState) },
topBar = { KiwixAppBar(stringResource(R.string.note), navigationIcon, actionMenuItems) } topBar = {
KiwixAppBar(
title = stringResource(R.string.note),
navigationIcon = navigationIcon,
actionMenuItems = actionMenuItems
)
}
) { paddingValues -> ) { paddingValues ->
Column( Column(
modifier = Modifier modifier = Modifier

View File

@ -23,10 +23,9 @@ import org.kiwix.videowebview.VideoEnabledWebChromeClient
class KiwixWebChromeClient( class KiwixWebChromeClient(
private val callback: WebViewCallback, private val callback: WebViewCallback,
nonVideoView: ViewGroup?,
videoView: ViewGroup?, videoView: ViewGroup?,
webView: KiwixWebView? webView: KiwixWebView?
) : VideoEnabledWebChromeClient(nonVideoView, videoView, null, webView) { ) : VideoEnabledWebChromeClient(videoView, null, webView) {
override fun onProgressChanged(view: WebView, progress: Int) { override fun onProgressChanged(view: WebView, progress: Int) {
callback.webViewProgressChanged(progress, view) callback.webViewProgressChanged(progress, view)
} }

View File

@ -59,7 +59,6 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor(
context: Context, context: Context,
private val callback: WebViewCallback, private val callback: WebViewCallback,
attrs: AttributeSet, attrs: AttributeSet,
private var nonVideoView: ViewGroup?,
videoView: ViewGroup?, videoView: ViewGroup?,
private val webViewClient: CoreWebViewClient, private val webViewClient: CoreWebViewClient,
val sharedPreferenceUtil: SharedPreferenceUtil val sharedPreferenceUtil: SharedPreferenceUtil
@ -102,7 +101,7 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor(
clearCache(true) clearCache(true)
setWebViewClient(webViewClient) setWebViewClient(webViewClient)
webChromeClient = webChromeClient =
KiwixWebChromeClient(callback, nonVideoView, videoView, this).apply { KiwixWebChromeClient(callback, videoView, this).apply {
setOnToggledFullscreen( setOnToggledFullscreen(
object : ToggledFullscreenCallback { object : ToggledFullscreenCallback {
override fun toggledFullscreen(fullscreen: Boolean) { override fun toggledFullscreen(fullscreen: Boolean) {
@ -154,7 +153,6 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor(
override fun onDetachedFromWindow() { override fun onDetachedFromWindow() {
super.onDetachedFromWindow() super.onDetachedFromWindow()
nonVideoView = null
textZoomJob?.cancel() textZoomJob?.cancel()
textZoomJob = null textZoomJob = null
} }

View File

@ -23,8 +23,12 @@ import android.util.AttributeSet
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup 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 org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import kotlin.math.max
import kotlin.math.min
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
@Suppress("UnusedPrivateProperty") @Suppress("UnusedPrivateProperty")
@ -32,28 +36,72 @@ class ToolbarScrollingKiwixWebView @JvmOverloads constructor(
context: Context, context: Context,
callback: WebViewCallback, callback: WebViewCallback,
attrs: AttributeSet, attrs: AttributeSet,
nonVideoView: ViewGroup?,
videoView: ViewGroup?, videoView: ViewGroup?,
webViewClient: CoreWebViewClient, webViewClient: CoreWebViewClient,
private val onToolbarOffsetChanged: ((Float) -> Unit)? = null,
private val onBottomAppBarOffsetChanged: ((Float) -> Unit)? = null,
sharedPreferenceUtil: SharedPreferenceUtil, sharedPreferenceUtil: SharedPreferenceUtil,
private val parentNavigationBar: View? = null private val parentNavigationBar: View? = null
) : KiwixWebView( ) : KiwixWebView(
context, context,
callback, callback,
attrs, attrs,
nonVideoView,
videoView, videoView,
webViewClient, webViewClient,
sharedPreferenceUtil 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 startY = 0f
private var currentOffset = 0f
init { init {
fixInitalScrollingIssue() 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. * 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. * See https://github.com/kiwix/kiwix-android/issues/2304 for issue description.
@ -62,68 +110,27 @@ class ToolbarScrollingKiwixWebView @JvmOverloads constructor(
moveToolbar(0) 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") @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean { override fun onTouchEvent(event: MotionEvent): Boolean {
// val transY = toolbarView.translationY.toInt() if (sharedPreferenceUtil.prefFullScreen) return super.onTouchEvent(event)
// when (event.actionMasked) {
// MotionEvent.ACTION_DOWN -> startY = event.rawY when (event.actionMasked) {
// MotionEvent.ACTION_MOVE -> { MotionEvent.ACTION_DOWN -> {
// // If we are in fullscreen don't scroll bar startY = event.rawY
// if (sharedPreferenceUtil.prefFullScreen) { }
// return super.onTouchEvent(event)
// } MotionEvent.ACTION_MOVE -> {
// // Filter out zooms since we don't want to affect the toolbar when zooming if (event.pointerCount == 1) {
// if (event.pointerCount == 1) { val diffY = (event.rawY - startY).toInt()
// val diffY = (event.rawY - startY).toInt() startY = event.rawY
// startY = event.rawY if (moveToolbar(-diffY)) {
// if (moveToolbar(-diffY)) { event.offsetLocation(0f, -diffY.toFloat())
// event.offsetLocation(0f, -diffY.toFloat()) return super.onTouchEvent(event)
// 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()
// }
// }
// }
return super.onTouchEvent(event) return super.onTouchEvent(event)
} }
private fun ensureToolbarDisplayed() {
moveToolbar(-toolbarHeight)
}
private fun ensureToolbarHidden() {
moveToolbar(toolbarHeight)
}
} }

View File

@ -62,7 +62,6 @@ import androidx.annotation.AnimRes
import androidx.appcompat.app.ActionBar import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.SnackbarDuration 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.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon 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.models.IconItem
import org.kiwix.kiwixmobile.core.ui.theme.White import org.kiwix.kiwixmobile.core.ui.theme.White
import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth
@ -320,6 +318,8 @@ abstract class CoreReaderFragment :
private var isReadSelection = false private var isReadSelection = false
private var isReadAloudServiceRunning = false private var isReadAloudServiceRunning = false
private var libkiwixBook: Book? = null private var libkiwixBook: Book? = null
val toolbarOffsetY = mutableStateOf(0f)
val bottomAppBarOffsetY = mutableStateOf(0f)
protected var readerMenuState: ReaderMenuState? = null protected var readerMenuState: ReaderMenuState? = null
private var composeView: ComposeView? = null private var composeView: ComposeView? = null
@ -348,7 +348,7 @@ abstract class CoreReaderFragment :
previousPageButtonItem = Triple({ goBack() }, { showBackwardHistory() }, false), previousPageButtonItem = Triple({ goBack() }, { showBackwardHistory() }, false),
onHomeButtonClick = { openMainPage() }, onHomeButtonClick = { openMainPage() },
nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false), nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false),
onTocClick = { openToc() }, tocButtonItem = false to { },
onCloseAllTabs = { closeAllTabs() }, onCloseAllTabs = { closeAllTabs() },
bottomNavigationHeight = ZERO, bottomNavigationHeight = ZERO,
shouldShowBottomAppBar = true, shouldShowBottomAppBar = true,
@ -473,11 +473,6 @@ abstract class CoreReaderFragment :
readerMenuState = createMainMenu() readerMenuState = createMainMenu()
composeView?.apply { composeView?.apply {
setContent { setContent {
val lazyListState = rememberLazyListState()
val isBottomNavVisible = rememberBottomNavigationVisibility(lazyListState)
LaunchedEffect(isBottomNavVisible) {
(activity as? CoreMainActivity)?.toggleBottomNavigation(isBottomNavVisible)
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
snapshotFlow { webViewList.size } snapshotFlow { webViewList.size }
.distinctUntilChanged() .distinctUntilChanged()
@ -491,7 +486,8 @@ abstract class CoreReaderFragment :
bottomNavigationHeight = getBottomNavigationHeight(), bottomNavigationHeight = getBottomNavigationHeight(),
readerScreenTitle = context.getString(R.string.reader), readerScreenTitle = context.getString(R.string.reader),
darkModeViewPainter = darkModeViewPainter, darkModeViewPainter = darkModeViewPainter,
fullScreenItem = fullScreenItem.first to getVideoView() fullScreenItem = fullScreenItem.first to getVideoView(),
tocButtonItem = getTocButtonStateAndAction()
) )
} }
} }
@ -514,7 +510,8 @@ abstract class CoreReaderFragment :
iconTint = navigationIconTint() iconTint = navigationIconTint()
) )
}, },
listState = lazyListState toolbarOffsetY = toolbarOffsetY,
bottomAppBarOffsetY = bottomAppBarOffsetY
) )
DialogHost(alertDialogShower as AlertDialogShower) DialogHost(alertDialogShower as AlertDialogShower)
} }
@ -629,6 +626,20 @@ abstract class CoreReaderFragment :
private fun getBottomNavigationHeight(): Int = getBottomNavigationView()?.measuredHeight ?: ZERO 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<Boolean, () -> Unit> = true to { openToc() }
private fun navigationIconContentDescription() = private fun navigationIconContentDescription() =
if (readerMenuState?.isInTabSwitcher == true) { if (readerMenuState?.isInTabSwitcher == true) {
R.string.search_open_in_new_tab R.string.search_open_in_new_tab
@ -949,7 +960,6 @@ abstract class CoreReaderFragment :
protected open fun hideTabSwitcher(shouldCloseZimBook: Boolean = true) { protected open fun hideTabSwitcher(shouldCloseZimBook: Boolean = true) {
setUpDrawerToggle() setUpDrawerToggle()
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
selectTab(currentWebViewIndex)
readerScreenState.update { readerScreenState.update {
copy( copy(
shouldShowBottomAppBar = true, shouldShowBottomAppBar = true,
@ -958,6 +968,7 @@ abstract class CoreReaderFragment :
} }
showSearchPlaceHolderInToolbar(false) showSearchPlaceHolderInToolbar(false)
readerMenuState?.showWebViewOptions(urlIsValid()) readerMenuState?.showWebViewOptions(urlIsValid())
selectTab(currentWebViewIndex)
// Reset the top margin of web views to 0 to remove any previously set margin // 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. // This ensures that the web views are displayed without any additional top margin for kiwix custom apps.
// setTopMarginToWebViews(0) // setTopMarginToWebViews(0)
@ -1077,7 +1088,7 @@ abstract class CoreReaderFragment :
} }
} }
private fun openToc() { protected fun openToc() {
drawerLayout?.openDrawer(GravityCompat.END) drawerLayout?.openDrawer(GravityCompat.END)
} }
@ -1413,14 +1424,13 @@ abstract class CoreReaderFragment :
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
protected open fun createWebView(attrs: AttributeSet?): ToolbarScrollingKiwixWebView? { protected open fun createWebView(attrs: AttributeSet?): ToolbarScrollingKiwixWebView? {
return ToolbarScrollingKiwixWebView( return ToolbarScrollingKiwixWebView(
requireActivity(), requireContext(),
this, this,
attrs ?: throw IllegalArgumentException("AttributeSet must not be null"), attrs ?: throw IllegalArgumentException("AttributeSet must not be null"),
null,
requireNotNull(readerScreenState.value.fullScreenItem.second), requireNotNull(readerScreenState.value.fullScreenItem.second),
CoreWebViewClient(this, requireNotNull(zimReaderContainer)), CoreWebViewClient(this, requireNotNull(zimReaderContainer)),
// requireNotNull(toolbarContainer), onToolbarOffsetChanged = { offsetY -> toolbarOffsetY.value = offsetY },
// requireNotNull(bottomToolbar), onBottomAppBarOffsetChanged = { bottomOffsetY -> bottomAppBarOffsetY.value = bottomOffsetY },
requireNotNull(sharedPreferenceUtil) requireNotNull(sharedPreferenceUtil)
) )
} }
@ -1821,7 +1831,6 @@ abstract class CoreReaderFragment :
// Show content if there is `Open Library` button showing // Show content if there is `Open Library` button showing
// and we are opening the ZIM file // and we are opening the ZIM file
hideNoBookOpenViews() hideNoBookOpenViews()
contentFrame?.visibility = VISIBLE
openAndSetInContainer(zimReaderSource) openAndSetInContainer(zimReaderSource)
updateTitle() updateTitle()
} else { } else {
@ -2203,7 +2212,7 @@ abstract class CoreReaderFragment :
private fun updateBottomToolbarVisibility() { private fun updateBottomToolbarVisibility() {
readerScreenState.update { readerScreenState.update {
copy(shouldShowBottomAppBar = !showTabSwitcher && !isInFullScreenMode()) copy(shouldShowBottomAppBar = readerMenuState?.isInTabSwitcher == false && !isInFullScreenMode())
} }
} }
@ -2882,6 +2891,7 @@ abstract class CoreReaderFragment :
} }
selectTab(currentTab) selectTab(currentTab)
onComplete.invoke() onComplete.invoke()
readerMenuState?.showWebViewOptions(urlIsValid())
} catch (ignore: Exception) { } catch (ignore: Exception) {
Log.w(TAG_KIWIX, "Kiwix shared preferences corrupted", ignore) Log.w(TAG_KIWIX, "Kiwix shared preferences corrupted", ignore)
activity.toast(R.string.could_not_restore_tabs, Toast.LENGTH_LONG) activity.toast(R.string.could_not_restore_tabs, Toast.LENGTH_LONG)

View File

@ -61,7 +61,7 @@ const val TAB_MENU_ITEM_TESTING_TAG = "tabMenuItemTestingTag"
@Stable @Stable
class ReaderMenuState( class ReaderMenuState(
private val menuClickListener: MenuClickListener, private val menuClickListener: MenuClickListener,
private val isUrlValidInitially: Boolean, isUrlValidInitially: Boolean,
private val disableReadAloud: Boolean = false, private val disableReadAloud: Boolean = false,
private val disableTabs: Boolean = false, private val disableTabs: Boolean = false,
private val disableSearch: Boolean = false private val disableSearch: Boolean = false

View File

@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke 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.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding 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.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -144,12 +148,12 @@ const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTe
@Composable @Composable
fun ReaderScreen( fun ReaderScreen(
state: ReaderScreenState, state: ReaderScreenState,
listState: LazyListState,
actionMenuItems: List<ActionMenuItem>, actionMenuItems: List<ActionMenuItem>,
toolbarOffsetY: MutableState<Float>,
bottomAppBarOffsetY: MutableState<Float>,
navigationIcon: @Composable () -> Unit navigationIcon: @Composable () -> Unit
) { ) {
val (bottomNavHeight, lazyListState) = val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() }
rememberScrollBehavior(state.bottomNavigationHeight, listState)
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
KiwixDialogTheme { KiwixDialogTheme {
Scaffold( Scaffold(
@ -158,6 +162,7 @@ fun ReaderScreen(
ReaderTopBar( ReaderTopBar(
state, state,
actionMenuItems, actionMenuItems,
toolbarOffsetY,
scrollBehavior, scrollBehavior,
navigationIcon navigationIcon
) )
@ -166,9 +171,13 @@ fun ReaderScreen(
modifier = Modifier modifier = Modifier
.systemBarsPadding() .systemBarsPadding()
.nestedScroll(scrollBehavior.nestedScrollConnection) .nestedScroll(scrollBehavior.nestedScrollConnection)
.padding(bottom = bottomNavHeight.value) .padding(bottom = bottomNavHeightInDp)
) { paddingValues -> ) { paddingValues ->
ReaderContentLayout(state, Modifier.padding(paddingValues)) ReaderContentLayout(
state,
Modifier.padding(paddingValues),
bottomAppBarOffsetY
)
} }
} }
} }
@ -179,23 +188,33 @@ fun ReaderScreen(
private fun ReaderTopBar( private fun ReaderTopBar(
state: ReaderScreenState, state: ReaderScreenState,
actionMenuItems: List<ActionMenuItem>, actionMenuItems: List<ActionMenuItem>,
toolbarOffsetY: MutableState<Float>,
scrollBehavior: TopAppBarScrollBehavior, scrollBehavior: TopAppBarScrollBehavior,
navigationIcon: @Composable () -> Unit, navigationIcon: @Composable () -> Unit,
) { ) {
if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) { if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) {
val animatedOffsetY by animateDpAsState(
targetValue = with(LocalDensity.current) { toolbarOffsetY.value.toDp() },
label = "ToolbarScrollOffset"
)
KiwixAppBar( KiwixAppBar(
title = if (state.showTabSwitcher) "" else state.readerScreenTitle, title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actionMenuItems = actionMenuItems, actionMenuItems = actionMenuItems,
topAppBarScrollBehavior = scrollBehavior, topAppBarScrollBehavior = scrollBehavior,
searchBar = searchBar =
searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps) searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps),
modifier = Modifier.offset { IntOffset(x = ZERO, y = animatedOffsetY.roundToPx()) }
) )
} }
} }
@Composable @Composable
private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = Modifier) { private fun ReaderContentLayout(
state: ReaderScreenState,
modifier: Modifier = Modifier,
bottomAppBarOffsetY: MutableState<Float>
) {
Box(modifier = modifier.fillMaxSize()) { Box(modifier = modifier.fillMaxSize()) {
when { when {
state.showTabSwitcher -> TabSwitcherView( state.showTabSwitcher -> TabSwitcherView(
@ -210,7 +229,7 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
state.fullScreenItem.first -> ShowFullScreenView(state) state.fullScreenItem.first -> ShowFullScreenView(state)
else -> { else -> {
ShowZIMFileContent(state) ShowZIMFileContent(state, bottomAppBarOffsetY)
ShowProgressBarIfZIMFilePageIsLoading(state) ShowProgressBarIfZIMFilePageIsLoading(state)
Column(Modifier.align(Alignment.BottomCenter)) { Column(Modifier.align(Alignment.BottomCenter)) {
TtsControls(state) TtsControls(state)
@ -219,8 +238,9 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
state.previousPageButtonItem, state.previousPageButtonItem,
state.onHomeButtonClick, state.onHomeButtonClick,
state.nextPageButtonItem, state.nextPageButtonItem,
state.onTocClick, state.tocButtonItem,
state.shouldShowBottomAppBar state.shouldShowBottomAppBar,
bottomAppBarOffsetY
) )
} }
CloseFullScreenImageButton( CloseFullScreenImageButton(
@ -305,7 +325,15 @@ private fun BoxScope.CloseFullScreenImageButton(
} }
@Composable @Composable
private fun ShowZIMFileContent(state: ReaderScreenState) { private fun ShowZIMFileContent(
state: ReaderScreenState,
bottomAppBarOffsetY: MutableState<Float>
) {
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 -> state.selectedWebView?.let { selectedWebView ->
key(selectedWebView) { key(selectedWebView) {
AndroidView( AndroidView(
@ -317,7 +345,10 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
addView(selectedWebView) addView(selectedWebView)
} }
}, },
modifier = Modifier.fillMaxSize() modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(bottom = totalBottomPadding)
) )
} }
} }
@ -425,14 +456,20 @@ private fun BottomAppBarOfReaderScreen(
previousPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>, previousPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>,
onHomeButtonClick: () -> Unit, onHomeButtonClick: () -> Unit,
nextPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>, nextPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>,
onTocClick: () -> Unit, tocButtonItem: Pair<Boolean, () -> Unit>,
shouldShowBottomAppBar: Boolean shouldShowBottomAppBar: Boolean,
bottomAppBarOffsetY: MutableState<Float>
) { ) {
if (!shouldShowBottomAppBar) return if (!shouldShowBottomAppBar) return
val animatedOffsetY by animateDpAsState(
targetValue = with(LocalDensity.current) { bottomAppBarOffsetY.value.toDp() },
label = "BottomAppBarOffset"
)
BottomAppBar( BottomAppBar(
containerColor = Black, containerColor = Black,
contentColor = White, contentColor = White,
scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior() scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior(),
modifier = Modifier.offset { IntOffset(ZERO, animatedOffsetY.roundToPx()) }
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -472,7 +509,8 @@ private fun BottomAppBarOfReaderScreen(
) )
// Toggle Icon(to open the table of content in right side bar) // Toggle Icon(to open the table of content in right side bar)
BottomAppBarButtonIcon( BottomAppBarButtonIcon(
onClick = onTocClick, shouldEnable = tocButtonItem.first,
onClick = tocButtonItem.second,
buttonIcon = Drawable(R.drawable.ic_toc_24dp), buttonIcon = Drawable(R.drawable.ic_toc_24dp),
contentDescription = stringResource(R.string.table_of_contents) contentDescription = stringResource(R.string.table_of_contents)
) )

View File

@ -132,8 +132,12 @@ data class ReaderScreenState(
val nextPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>, val nextPageButtonItem: Triple<() -> Unit, () -> Unit, Boolean>,
/** /**
* Handles the click to open right sidebar button click in reader bottom toolbar. * 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<Boolean, () -> Unit>,
val onCloseAllTabs: () -> Unit, val onCloseAllTabs: () -> Unit,
/** /**
* Stores the height of the bottom navigation bar in pixels. * Stores the height of the bottom navigation bar in pixels.

View File

@ -70,7 +70,13 @@ fun NavigationHistoryDialogScreen(
) { ) {
KiwixDialogTheme { KiwixDialogTheme {
Scaffold( Scaffold(
topBar = { KiwixAppBar(stringResource(titleId), navigationIcon, actionMenuItems) } topBar = {
KiwixAppBar(
title = stringResource(titleId),
navigationIcon = navigationIcon,
actionMenuItems = actionMenuItems
)
}
) { paddingValues -> ) { paddingValues ->
Box( Box(
modifier = Modifier modifier = Modifier

View File

@ -72,6 +72,7 @@ const val TOOLBAR_TITLE_TESTING_TAG = "toolbarTitle"
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun KiwixAppBar( fun KiwixAppBar(
modifier: Modifier = Modifier,
title: String, title: String,
navigationIcon: @Composable () -> Unit, navigationIcon: @Composable () -> Unit,
actionMenuItems: List<ActionMenuItem> = emptyList(), actionMenuItems: List<ActionMenuItem> = emptyList(),
@ -92,7 +93,8 @@ fun KiwixAppBar(
// Edge-to-Edge mode is already enabled in our application, // Edge-to-Edge mode is already enabled in our application,
// so we don't need to apply additional top insets. // so we don't need to apply additional top insets.
// This prevents unwanted extra margin at the top. // This prevents unwanted extra margin at the top.
windowInsets = WindowInsets.statusBars.only(WindowInsetsSides.Horizontal) windowInsets = WindowInsets.statusBars.only(WindowInsetsSides.Horizontal),
modifier = modifier
) )
} }
} }

View File

@ -193,4 +193,6 @@ object ComposeDimens {
val BACK_TO_TOP_BUTTON_BOTTOM_MARGIN = 80.dp val BACK_TO_TOP_BUTTON_BOTTOM_MARGIN = 80.dp
const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f
val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp
val COMPOSE_TOOLBAR_DEFAULT_HEIGHT = 64.dp
val COMPOSE_BOTTOM_APP_BAR_DEFAULT_HEIGHT = 80.dp
} }

View File

@ -23,6 +23,7 @@ import android.os.Build
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.TypedValue import android.util.TypedValue
import androidx.appcompat.R import androidx.appcompat.R
import androidx.compose.ui.unit.Dp
object DimenUtils { object DimenUtils {
@JvmStatic fun Context.getToolbarHeight(): Int { @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 = @JvmStatic fun Activity.getWindowHeight(): Int =
computedDisplayMetric.heightPixels computedDisplayMetric.heightPixels

View File

@ -55,7 +55,6 @@ open class VideoEnabledWebChromeClient :
fun toggledFullscreen(fullscreen: Boolean) fun toggledFullscreen(fullscreen: Boolean)
} }
private var activityNonVideoView: View? = null
private var activityVideoView: ViewGroup? = null private var activityVideoView: ViewGroup? = null
private var loadingView: View? = null private var loadingView: View? = null
private var webView: VideoEnabledWebView? = null private var webView: VideoEnabledWebView? = null
@ -84,14 +83,11 @@ open class VideoEnabledWebChromeClient :
/** /**
* Builds a video enabled WebChromeClient. * 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. * @param activityVideoView A ViewGroup in the activity's layout that will display the video.
* Typically you would like this to fill the whole layout. * Typically you would like this to fill the whole layout.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
constructor(activityNonVideoView: View?, activityVideoView: ViewGroup?) { constructor(activityVideoView: ViewGroup?) {
this.activityNonVideoView = activityNonVideoView
this.activityVideoView = activityVideoView this.activityVideoView = activityVideoView
loadingView = null loadingView = null
webView = null webView = null
@ -101,8 +97,6 @@ open class VideoEnabledWebChromeClient :
/** /**
* Builds a video enabled WebChromeClient. * 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. * @param activityVideoView A ViewGroup in the activity's layout that will display the video.
* Typically you would like this to fill the whole layout. * 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 * @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") @SuppressWarnings("unused")
constructor( constructor(
activityNonVideoView: View?,
activityVideoView: ViewGroup?, activityVideoView: ViewGroup?,
loadingView: View? loadingView: View?
) { ) {
this.activityNonVideoView = activityNonVideoView
this.activityVideoView = activityVideoView this.activityVideoView = activityVideoView
this.loadingView = loadingView this.loadingView = loadingView
webView = null webView = null
@ -124,8 +116,6 @@ open class VideoEnabledWebChromeClient :
/** /**
* Builds a video enabled WebChromeClient. * 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. * @param activityVideoView A ViewGroup in the activity's layout that will display the video.
* Typically you would like this to fill the whole layout. * 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 * @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") @SuppressWarnings("unused")
constructor( constructor(
activityNonVideoView: View?,
activityVideoView: ViewGroup?, activityVideoView: ViewGroup?,
loadingView: View?, loadingView: View?,
webView: VideoEnabledWebView? webView: VideoEnabledWebView?
) { ) {
this.activityNonVideoView = activityNonVideoView
this.activityVideoView = activityVideoView this.activityVideoView = activityVideoView
this.loadingView = loadingView this.loadingView = loadingView
this.webView = webView this.webView = webView
@ -177,9 +165,6 @@ open class VideoEnabledWebChromeClient :
isVideoFullscreen = true isVideoFullscreen = true
videoViewContainer = view videoViewContainer = view
videoViewCallback = callback videoViewCallback = callback
// Hide the non-video view, add the video view, and show it
activityNonVideoView?.visibility = View.INVISIBLE
activityVideoView?.addView( activityVideoView?.addView(
videoViewContainer, videoViewContainer,
ViewGroup.LayoutParams( ViewGroup.LayoutParams(
@ -248,7 +233,6 @@ open class VideoEnabledWebChromeClient :
// Hide the video view, remove it, and show the non-video view // Hide the video view, remove it, and show the non-video view
activityVideoView?.visibility = View.INVISIBLE activityVideoView?.visibility = View.INVISIBLE
activityVideoView?.removeView(videoViewContainer) activityVideoView?.removeView(videoViewContainer)
activityNonVideoView?.visibility = View.VISIBLE
// Call back (only in API level <19, because in API level 19+ with chromium webview it crashes) // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
videoViewCallback?.let { videoViewCallback?.let {

View File

@ -23,7 +23,6 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.View import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Menu
@ -78,12 +77,6 @@ class CustomReaderFragment : CoreReaderFragment() {
if (isAdded) { if (isAdded) {
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
if (BuildConfig.DISABLE_SIDEBAR) {
val toolbarToc =
activity?.findViewById<ImageView>(org.kiwix.kiwixmobile.core.R.id.bottom_toolbar_toc)
toolbarToc?.isEnabled = false
// TODO refactor this with compose UI.
}
with(activity as AppCompatActivity) { with(activity as AppCompatActivity) {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
setUpDrawerToggle() 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<Boolean, () -> Unit> =
!BuildConfig.DISABLE_SIDEBAR to { openToc() }
/** /**
* Returns the tint color for the navigation icon. * Returns the tint color for the navigation icon.
* *