mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 18:56:44 -04:00
Fixed: Clicking on a table of content item wasn’t scrolling the WebView to the selected section because the Compose ScrollState caused recomposition back to the previous scroll position — now replaced with a custom approach to smoothly scroll to the target section. * Fixed: The whole screen was scrolling when scrolling the table of contents.
* Fixed: App was crashing and not opening the intro screen on fresh install. * Fixed: Design and appearance of the table of contents in dark mode.
This commit is contained in:
parent
f041ee9cf0
commit
8afaa8be52
@ -33,6 +33,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.rememberDrawerState
|
import androidx.compose.material3.rememberDrawerState
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.core.content.pm.ShortcutInfoCompat
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
@ -40,7 +41,6 @@ import androidx.core.graphics.drawable.IconCompat
|
|||||||
import androidx.core.os.ConfigurationCompat
|
import androidx.core.os.ConfigurationCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavOptions
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.mhutti1.utils.storage.StorageDevice
|
import eu.mhutti1.utils.storage.StorageDevice
|
||||||
import eu.mhutti1.utils.storage.StorageDeviceUtils
|
import eu.mhutti1.utils.storage.StorageDeviceUtils
|
||||||
@ -128,9 +128,17 @@ class KiwixMainActivity : CoreMainActivity() {
|
|||||||
leftDrawerState = rememberDrawerState(DrawerValue.Closed)
|
leftDrawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
uiCoroutineScope = rememberCoroutineScope()
|
uiCoroutineScope = rememberCoroutineScope()
|
||||||
bottomAppBarScrollBehaviour = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
bottomAppBarScrollBehaviour = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
||||||
|
val startDestination = remember {
|
||||||
|
if (sharedPreferenceUtil.showIntro() && !isIntroScreenNotVisible()) {
|
||||||
|
KiwixDestination.Intro.route
|
||||||
|
} else {
|
||||||
|
KiwixDestination.Reader.route
|
||||||
|
}
|
||||||
|
}
|
||||||
KiwixMainActivityScreen(
|
KiwixMainActivityScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
leftDrawerContent = leftDrawerMenu,
|
leftDrawerContent = leftDrawerMenu,
|
||||||
|
startDestination = startDestination,
|
||||||
topLevelDestinationsRoute = topLevelDestinationsRoute,
|
topLevelDestinationsRoute = topLevelDestinationsRoute,
|
||||||
leftDrawerState = leftDrawerState,
|
leftDrawerState = leftDrawerState,
|
||||||
uiCoroutineScope = uiCoroutineScope,
|
uiCoroutineScope = uiCoroutineScope,
|
||||||
@ -140,12 +148,6 @@ class KiwixMainActivity : CoreMainActivity() {
|
|||||||
)
|
)
|
||||||
LaunchedEffect(navController) {
|
LaunchedEffect(navController) {
|
||||||
navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange)
|
navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange)
|
||||||
if (sharedPreferenceUtil.showIntro() && !isIntroScreenNotVisible()) {
|
|
||||||
val navOptions = NavOptions.Builder()
|
|
||||||
.setPopUpTo(KiwixDestination.Reader.route, inclusive = true)
|
|
||||||
.build()
|
|
||||||
navigate(KiwixDestination.Intro.route, navOptions)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DialogHost(alertDialogShower)
|
DialogHost(alertDialogShower)
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ import org.kiwix.kiwixmobile.ui.KiwixNavGraph
|
|||||||
fun KiwixMainActivityScreen(
|
fun KiwixMainActivityScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
leftDrawerContent: List<DrawerMenuGroup>,
|
leftDrawerContent: List<DrawerMenuGroup>,
|
||||||
|
startDestination: String,
|
||||||
topLevelDestinationsRoute: Set<String>,
|
topLevelDestinationsRoute: Set<String>,
|
||||||
leftDrawerState: DrawerState,
|
leftDrawerState: DrawerState,
|
||||||
uiCoroutineScope: CoroutineScope,
|
uiCoroutineScope: CoroutineScope,
|
||||||
@ -100,6 +101,7 @@ fun KiwixMainActivityScreen(
|
|||||||
Box(modifier = Modifier.padding(paddingValues)) {
|
Box(modifier = Modifier.padding(paddingValues)) {
|
||||||
KiwixNavGraph(
|
KiwixNavGraph(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
startDestination = startDestination,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -221,9 +221,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
|||||||
exitBook()
|
exitBook()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBottomNavigationView(): BottomNavigationView? = null
|
|
||||||
// requireActivity().findViewById(R.id.bottom_nav_view)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the view state based on the provided webViewHistoryItemList data and restore origin.
|
* Restores the view state based on the provided webViewHistoryItemList data and restore origin.
|
||||||
*
|
*
|
||||||
|
@ -77,11 +77,12 @@ import org.kiwix.kiwixmobile.webserver.ZimHostFragment
|
|||||||
@Composable
|
@Composable
|
||||||
fun KiwixNavGraph(
|
fun KiwixNavGraph(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
|
startDestination: String,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = KiwixDestination.Reader.route,
|
startDestination = startDestination,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
composable(
|
composable(
|
||||||
|
@ -73,7 +73,6 @@ import androidx.drawerlayout.widget.DrawerLayout
|
|||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -303,7 +302,6 @@ abstract class CoreReaderFragment :
|
|||||||
nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false),
|
nextPageButtonItem = Triple({ goForward() }, { showForwardHistory() }, false),
|
||||||
tocButtonItem = false to { },
|
tocButtonItem = false to { },
|
||||||
onCloseAllTabs = { closeAllTabs() },
|
onCloseAllTabs = { closeAllTabs() },
|
||||||
bottomNavigationHeight = ZERO,
|
|
||||||
shouldShowBottomAppBar = true,
|
shouldShowBottomAppBar = true,
|
||||||
selectedWebView = null,
|
selectedWebView = null,
|
||||||
readerScreenTitle = "",
|
readerScreenTitle = "",
|
||||||
@ -330,9 +328,7 @@ abstract class CoreReaderFragment :
|
|||||||
appName = "",
|
appName = "",
|
||||||
donateButtonClick = {},
|
donateButtonClick = {},
|
||||||
laterButtonClick = {},
|
laterButtonClick = {},
|
||||||
tableOfContentTitle = "",
|
tableOfContentTitle = ""
|
||||||
tableContentHeaderClick = { tableOfContentHeaderClick() },
|
|
||||||
tableOfContentSectionClick = { tableOfContentSectionClick(it) },
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private var readerLifeCycleScope: CoroutineScope? = null
|
private var readerLifeCycleScope: CoroutineScope? = null
|
||||||
@ -443,7 +439,6 @@ abstract class CoreReaderFragment :
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
readerScreenState.update {
|
readerScreenState.update {
|
||||||
copy(
|
copy(
|
||||||
bottomNavigationHeight = getBottomNavigationHeight(),
|
|
||||||
readerScreenTitle = context.getString(string.reader),
|
readerScreenTitle = context.getString(string.reader),
|
||||||
darkModeViewPainter = darkModeViewPainter,
|
darkModeViewPainter = darkModeViewPainter,
|
||||||
fullScreenItem = fullScreenItem.first to getVideoView(),
|
fullScreenItem = fullScreenItem.first to getVideoView(),
|
||||||
@ -482,7 +477,7 @@ abstract class CoreReaderFragment :
|
|||||||
},
|
},
|
||||||
mainActivityBottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour,
|
mainActivityBottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour,
|
||||||
documentSections = documentSections,
|
documentSections = documentSections,
|
||||||
showTableOfContentDrawer = shouldTableOfContentDrawer,
|
showTableOfContentDrawer = shouldTableOfContentDrawer
|
||||||
)
|
)
|
||||||
DialogHost(alertDialogShower as AlertDialogShower)
|
DialogHost(alertDialogShower as AlertDialogShower)
|
||||||
}
|
}
|
||||||
@ -545,8 +540,6 @@ abstract class CoreReaderFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBottomNavigationHeight(): Int = getBottomNavigationView()?.measuredHeight ?: ZERO
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the visibility state and click action for the TOC (Table of Contents) button
|
* Provides the visibility state and click action for the TOC (Table of Contents) button
|
||||||
* shown in the reader's bottom app bar.
|
* shown in the reader's bottom app bar.
|
||||||
@ -677,31 +670,6 @@ abstract class CoreReaderFragment :
|
|||||||
documentSections?.clear()
|
documentSections?.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tableOfContentHeaderClick() {
|
|
||||||
getCurrentWebView()?.scrollY = 0
|
|
||||||
shouldTableOfContentDrawer.update { false }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun tableOfContentSectionClick(position: Int) {
|
|
||||||
if (hasItemForPositionInDocumentSectionsList(position)) { // Bug Fix #3796
|
|
||||||
loadUrlWithCurrentWebview(
|
|
||||||
"javascript:document.getElementById('" +
|
|
||||||
documentSections?.get(position)?.id?.replace("'", "\\'") +
|
|
||||||
"').scrollIntoView();"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
shouldTableOfContentDrawer.update { false }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasItemForPositionInDocumentSectionsList(position: Int): Boolean {
|
|
||||||
val documentListSize = documentSections?.size ?: return false
|
|
||||||
return when {
|
|
||||||
position < 0 -> false
|
|
||||||
position >= documentListSize -> false
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showTabSwitcher() {
|
private fun showTabSwitcher() {
|
||||||
(requireActivity() as CoreMainActivity).apply {
|
(requireActivity() as CoreMainActivity).apply {
|
||||||
disableDrawer()
|
disableDrawer()
|
||||||
@ -2699,8 +2667,6 @@ abstract class CoreReaderFragment :
|
|||||||
* when handling invalid JSON scenarios.
|
* when handling invalid JSON scenarios.
|
||||||
*/
|
*/
|
||||||
abstract fun restoreViewStateOnInvalidWebViewHistory()
|
abstract fun restoreViewStateOnInvalidWebViewHistory()
|
||||||
|
|
||||||
abstract fun getBottomNavigationView(): BottomNavigationView?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RestoreOrigin {
|
enum class RestoreOrigin {
|
||||||
|
@ -33,10 +33,12 @@ import androidx.compose.animation.slideOutHorizontally
|
|||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
@ -86,12 +88,14 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@ -111,8 +115,10 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.HUNDERED
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.HUNDERED
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.extensions.update
|
import org.kiwix.kiwixmobile.core.extensions.update
|
||||||
import org.kiwix.kiwixmobile.core.main.DarkModeViewPainter
|
import org.kiwix.kiwixmobile.core.main.DarkModeViewPainter
|
||||||
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
||||||
@ -130,6 +136,7 @@ import org.kiwix.kiwixmobile.core.ui.models.toPainter
|
|||||||
import org.kiwix.kiwixmobile.core.ui.theme.Black
|
import org.kiwix.kiwixmobile.core.ui.theme.Black
|
||||||
import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue800
|
import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue800
|
||||||
import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme
|
import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray700
|
||||||
import org.kiwix.kiwixmobile.core.ui.theme.White
|
import org.kiwix.kiwixmobile.core.ui.theme.White
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.BACK_TO_TOP_BUTTON_BOTTOM_MARGIN
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.BACK_TO_TOP_BUTTON_BOTTOM_MARGIN
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_ALL_TAB_BUTTON_BOTTOM_PADDING
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_ALL_TAB_BUTTON_BOTTOM_PADDING
|
||||||
@ -139,6 +146,7 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
|
|||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FIVE_DP
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FIVE_DP
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.KIWIX_TOOLBAR_HEIGHT
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.KIWIX_TOOLBAR_HEIGHT
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.LARGE_BODY_TEXT_SIZE
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.ONE_DP
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.ONE_DP
|
||||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE
|
||||||
@ -171,60 +179,65 @@ fun ReaderScreen(
|
|||||||
mainActivityBottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?,
|
mainActivityBottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?,
|
||||||
navigationIcon: @Composable () -> Unit
|
navigationIcon: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() }
|
val localWebViewScrollState: MutableState<ScrollState?> =
|
||||||
|
remember { mutableStateOf(ScrollState(0)) }
|
||||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
||||||
KiwixDialogTheme {
|
KiwixDialogTheme {
|
||||||
Scaffold(
|
Box(Modifier.fillMaxSize()) {
|
||||||
snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) },
|
Scaffold(
|
||||||
topBar = {
|
snackbarHost = { KiwixSnackbarHost(snackbarHostState = state.snackBarHostState) },
|
||||||
ReaderTopBar(
|
topBar = {
|
||||||
state,
|
ReaderTopBar(
|
||||||
actionMenuItems,
|
state,
|
||||||
topAppBarScrollBehavior,
|
actionMenuItems,
|
||||||
navigationIcon
|
topAppBarScrollBehavior,
|
||||||
)
|
navigationIcon
|
||||||
},
|
)
|
||||||
floatingActionButton = { BackToTopFab(state) },
|
},
|
||||||
modifier = Modifier
|
floatingActionButton = { BackToTopFab(state) },
|
||||||
.systemBarsPadding()
|
modifier = Modifier
|
||||||
.padding(bottom = bottomNavHeightInDp)
|
.systemBarsPadding()
|
||||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
|
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
|
||||||
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection)
|
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection)
|
||||||
.let { baseModifier ->
|
.let { baseModifier ->
|
||||||
mainActivityBottomAppBarScrollBehaviour?.let {
|
mainActivityBottomAppBarScrollBehaviour?.let {
|
||||||
baseModifier.nestedScroll(it.nestedScrollConnection)
|
baseModifier.nestedScroll(it.nestedScrollConnection)
|
||||||
} ?: baseModifier
|
} ?: baseModifier
|
||||||
}
|
}
|
||||||
.semantics { testTag = READER_SCREEN_TESTING_TAG }
|
.semantics { testTag = READER_SCREEN_TESTING_TAG }
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Box(Modifier.fillMaxSize()) {
|
|
||||||
ReaderContentLayout(
|
ReaderContentLayout(
|
||||||
state,
|
state,
|
||||||
Modifier.padding(paddingValues),
|
Modifier.padding(paddingValues),
|
||||||
bottomAppBarScrollBehavior
|
bottomAppBarScrollBehavior,
|
||||||
|
localWebViewScrollState.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showTableOfContentDrawer.value) {
|
||||||
|
// Showing the background color on screen so that it look same as navigation drawer.
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Black.copy(alpha = 0.3f))
|
||||||
|
.clickable { showTableOfContentDrawer.update { false } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = showTableOfContentDrawer.value,
|
||||||
|
enter = slideInHorizontally(initialOffsetX = { it }) + fadeIn(),
|
||||||
|
exit = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
|
||||||
|
modifier = Modifier
|
||||||
|
.systemBarsPadding()
|
||||||
|
.align(Alignment.CenterEnd)
|
||||||
|
) {
|
||||||
|
TableDrawerSheet(
|
||||||
|
title = state.tableOfContentTitle,
|
||||||
|
sections = documentSections.orEmpty(),
|
||||||
|
localWebViewScrollState,
|
||||||
|
state.selectedWebView,
|
||||||
|
showTableOfContentDrawer
|
||||||
)
|
)
|
||||||
AnimatedVisibility(
|
|
||||||
visible = showTableOfContentDrawer.value,
|
|
||||||
enter = slideInHorizontally(initialOffsetX = { it }) + fadeIn(),
|
|
||||||
exit = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
|
|
||||||
modifier = Modifier.align(Alignment.CenterEnd)
|
|
||||||
) {
|
|
||||||
TableDrawerSheet(
|
|
||||||
title = state.tableOfContentTitle,
|
|
||||||
sections = documentSections.orEmpty(),
|
|
||||||
onHeaderClick = state.tableContentHeaderClick,
|
|
||||||
onSectionClick = state.tableOfContentSectionClick
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (showTableOfContentDrawer.value) {
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.background(Color.Black.copy(alpha = 0.3f))
|
|
||||||
.clickable { showTableOfContentDrawer.update { false } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,7 +269,8 @@ private fun ReaderTopBar(
|
|||||||
private fun ReaderContentLayout(
|
private fun ReaderContentLayout(
|
||||||
state: ReaderScreenState,
|
state: ReaderScreenState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior
|
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
|
||||||
|
webViewScrollState: ScrollState?
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier.fillMaxSize()) {
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
TabSwitcherAnimated(state)
|
TabSwitcherAnimated(state)
|
||||||
@ -266,7 +280,7 @@ private fun ReaderContentLayout(
|
|||||||
state.fullScreenItem.first -> ShowFullScreenView(state)
|
state.fullScreenItem.first -> ShowFullScreenView(state)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
ShowZIMFileContent(state)
|
ShowZIMFileContent(state, webViewScrollState)
|
||||||
ShowProgressBarIfZIMFilePageIsLoading(state)
|
ShowProgressBarIfZIMFilePageIsLoading(state)
|
||||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||||
TtsControls(state)
|
TtsControls(state)
|
||||||
@ -295,23 +309,40 @@ private fun ReaderContentLayout(
|
|||||||
fun TableDrawerSheet(
|
fun TableDrawerSheet(
|
||||||
title: String,
|
title: String,
|
||||||
sections: List<DocumentSection>,
|
sections: List<DocumentSection>,
|
||||||
onHeaderClick: () -> Unit,
|
webViewScrollState: MutableState<ScrollState?>,
|
||||||
onSectionClick: (Int) -> Unit
|
selectedWebView: KiwixWebView?,
|
||||||
|
showTableOfContentDrawer: MutableState<Boolean>
|
||||||
) {
|
) {
|
||||||
|
val drawerBackgroundColor = if (isSystemInDarkTheme()) {
|
||||||
|
MineShaftGray700
|
||||||
|
} else {
|
||||||
|
White
|
||||||
|
}
|
||||||
|
var scrollToSectionIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
LaunchedEffect(scrollToSectionIndex) {
|
||||||
|
scrollToSectionIndex?.let {
|
||||||
|
webViewScrollState.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
ModalDrawerSheet(
|
ModalDrawerSheet(
|
||||||
modifier = Modifier.width(NAVIGATION_DRAWER_WIDTH)
|
modifier = Modifier.width(NAVIGATION_DRAWER_WIDTH),
|
||||||
|
drawerShape = RectangleShape,
|
||||||
|
drawerContainerColor = drawerBackgroundColor
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(modifier = Modifier.fillMaxHeight()) {
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxHeight()
|
|
||||||
) {
|
|
||||||
item {
|
item {
|
||||||
Text(
|
Text(
|
||||||
text = title.ifEmpty { stringResource(id = R.string.no_section_info) },
|
text = title.ifEmpty { stringResource(id = R.string.no_section_info) },
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
|
style = MaterialTheme.typography.titleMedium,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { onHeaderClick() }
|
.clickable {
|
||||||
|
coroutineScope.launch {
|
||||||
|
webViewScrollState.value?.animateScrollTo(ZERO)
|
||||||
|
}
|
||||||
|
showTableOfContentDrawer.update { false }
|
||||||
|
}
|
||||||
.padding(horizontal = SIXTEEN_DP, vertical = TWELVE_DP)
|
.padding(horizontal = SIXTEEN_DP, vertical = TWELVE_DP)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -319,15 +350,46 @@ fun TableDrawerSheet(
|
|||||||
val paddingStart = (section.level - ONE) * TWELVE
|
val paddingStart = (section.level - ONE) * TWELVE
|
||||||
Text(
|
Text(
|
||||||
text = section.title,
|
text = section.title,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
fontSize = LARGE_BODY_TEXT_SIZE
|
||||||
|
),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { onSectionClick(index) }
|
.clickable { scrollToSectionIndex = index }
|
||||||
.padding(start = paddingStart.dp, top = EIGHT_DP, bottom = EIGHT_DP, end = SIXTEEN_DP)
|
.padding(start = paddingStart.dp, top = EIGHT_DP, bottom = EIGHT_DP, end = SIXTEEN_DP)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LaunchedEffect(webViewScrollState.value) {
|
||||||
|
if (webViewScrollState.value == null &&
|
||||||
|
scrollToSectionIndex != null &&
|
||||||
|
hasItemForPositionInDocumentSectionsList(scrollToSectionIndex!!, sections)
|
||||||
|
) {
|
||||||
|
val targetId = sections[scrollToSectionIndex!!].id.replace("'", "\\'")
|
||||||
|
selectedWebView?.evaluateJavascript(
|
||||||
|
"document.getElementById('$targetId')?.scrollIntoView();",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
delay(100)
|
||||||
|
webViewScrollState.value = ScrollState(selectedWebView?.scrollY ?: ZERO)
|
||||||
|
scrollToSectionIndex = null
|
||||||
|
showTableOfContentDrawer.update { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasItemForPositionInDocumentSectionsList(
|
||||||
|
position: Int,
|
||||||
|
sections: List<DocumentSection>
|
||||||
|
): Boolean {
|
||||||
|
val documentListSize = sections.size
|
||||||
|
return when {
|
||||||
|
position < 0 -> false
|
||||||
|
position >= documentListSize -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -433,12 +495,13 @@ private fun BoxScope.CloseFullScreenImageButton(
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ShowZIMFileContent(state: ReaderScreenState) {
|
private fun ShowZIMFileContent(state: ReaderScreenState, webViewScrollState: ScrollState?) {
|
||||||
state.selectedWebView?.let { selectedWebView ->
|
state.selectedWebView?.let { selectedWebView ->
|
||||||
key(selectedWebView) {
|
key(selectedWebView) {
|
||||||
ScrollableWebViewWithNestedScroll(
|
ScrollableWebViewWithNestedScroll(
|
||||||
selectedWebView = selectedWebView,
|
selectedWebView = selectedWebView,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
webViewScrollState = webViewScrollState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,12 +511,19 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
|
|||||||
@Composable
|
@Composable
|
||||||
fun ScrollableWebViewWithNestedScroll(
|
fun ScrollableWebViewWithNestedScroll(
|
||||||
selectedWebView: KiwixWebView,
|
selectedWebView: KiwixWebView,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
|
webViewScrollState: ScrollState?
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
.let { baseModifier ->
|
||||||
|
webViewScrollState?.let {
|
||||||
|
baseModifier.verticalScroll(it)
|
||||||
|
} ?: run {
|
||||||
|
baseModifier
|
||||||
|
}
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { context ->
|
factory = { context ->
|
||||||
|
@ -139,10 +139,6 @@ data class ReaderScreenState(
|
|||||||
*/
|
*/
|
||||||
val tocButtonItem: Pair<Boolean, () -> Unit>,
|
val tocButtonItem: Pair<Boolean, () -> Unit>,
|
||||||
val onCloseAllTabs: () -> Unit,
|
val onCloseAllTabs: () -> Unit,
|
||||||
/**
|
|
||||||
* Stores the height of the bottom navigation bar in pixels.
|
|
||||||
*/
|
|
||||||
val bottomNavigationHeight: Int,
|
|
||||||
/**
|
/**
|
||||||
* Manages the showing of Reader's [BottomAppBarOfReaderScreen].
|
* Manages the showing of Reader's [BottomAppBarOfReaderScreen].
|
||||||
*/
|
*/
|
||||||
@ -172,13 +168,5 @@ data class ReaderScreenState(
|
|||||||
/**
|
/**
|
||||||
* Manages the showing of header title of "table of content".
|
* Manages the showing of header title of "table of content".
|
||||||
*/
|
*/
|
||||||
val tableOfContentTitle: String,
|
val tableOfContentTitle: String
|
||||||
/**
|
|
||||||
* Handles the click when user clicks on the "Header" of "table of content".
|
|
||||||
*/
|
|
||||||
val tableContentHeaderClick: () -> Unit,
|
|
||||||
/**
|
|
||||||
* Handles the click when user clicks on the "section" of "table of content".
|
|
||||||
*/
|
|
||||||
val tableOfContentSectionClick: (Int) -> Unit
|
|
||||||
)
|
)
|
||||||
|
@ -173,9 +173,6 @@ class CustomReaderFragment : CoreReaderFragment() {
|
|||||||
openHomeScreen()
|
openHomeScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since custom apps do not have the bottomNavigationView, so returning null.
|
|
||||||
override fun getBottomNavigationView(): BottomNavigationView? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the view state when the webViewHistory data is valid.
|
* Restores the view state when the webViewHistory data is valid.
|
||||||
* This method restores the tabs with webView pages history.
|
* This method restores the tabs with webView pages history.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user