Fixed: Tabs were being automatically restored after closing them, navigating away from the reader screen, and then returning.

* Removed unused code from the reader screen.
* Refactored fullscreen mode functionality to align with Compose UI.
* Improved the `CloseAllTabs` button styling to match the app theme.
* Refactored the "Back to Top" button functionality using Compose, and enhanced its UI.
* Fixed: `Long-press` on bottom app bar buttons was not working due to `IconButton` consuming the touch event.
* Removed the unused `AnimationUtils` file.
* Fixed: Navigation history (forward and backward) was not being displayed.
This commit is contained in:
MohitMaliFtechiz 2025-06-19 23:58:38 +05:30
parent b694ae3170
commit 234e9169f6
6 changed files with 115 additions and 327 deletions

View File

@ -29,15 +29,12 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.cachedComponent
import org.kiwix.kiwixmobile.core.R.anim
import org.kiwix.kiwixmobile.core.R.drawable
import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions.Super
@ -47,7 +44,6 @@ import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.setupDrawerToggl
import org.kiwix.kiwixmobile.core.extensions.coreMainActivity
import org.kiwix.kiwixmobile.core.extensions.isFileExist
import org.kiwix.kiwixmobile.core.extensions.setBottomMarginToFragmentContainerView
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.extensions.update
@ -184,32 +180,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
* @see closeAllTabs
*/
override fun hideTabSwitcher(shouldCloseZimBook: Boolean) {
actionBar?.let { actionBar ->
actionBar.setDisplayShowTitleEnabled(true)
toolbar?.let { activity?.setupDrawerToggle(it, true) }
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
closeAllTabsButton?.setImageDrawableCompat(drawable.ic_close_black_24dp)
if (tabSwitcherRoot?.isVisible == true) {
tabSwitcherRoot?.visibility = GONE
startAnimation(tabSwitcherRoot, anim.slide_up)
progressBar?.visibility = View.GONE
progressBar?.progress = 0
contentFrame?.visibility = View.VISIBLE
}
readerMenuState?.showWebViewOptions(true)
if (webViewList.isEmpty()) {
exitBook(shouldCloseZimBook)
} else {
// 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 main app.
setTopMarginToWebViews(0)
selectTab(currentWebViewIndex)
}
}
actionBar?.setDisplayShowTitleEnabled(true)
toolbar?.let { activity?.setupDrawerToggle(it, true) }
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
if (webViewList.isEmpty()) {

View File

@ -52,10 +52,7 @@ import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.webkit.WebBackForwardList
import android.webkit.WebView
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
@ -79,16 +76,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.platform.ComposeView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.Group
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.core.view.GravityCompat
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
@ -96,10 +89,8 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@ -134,8 +125,6 @@ import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigatio
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificationPermission
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.findFirstTextView
import org.kiwix.kiwixmobile.core.extensions.closeFullScreenMode
import org.kiwix.kiwixmobile.core.extensions.getToolbarNavigationIcon
import org.kiwix.kiwixmobile.core.extensions.setToolTipWithContentDescription
import org.kiwix.kiwixmobile.core.extensions.showFullScreenMode
import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.extensions.toast
@ -185,7 +174,6 @@ 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.utils.AnimationUtils.rotate
import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth
import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler
import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler.ShowDonationDialogCallback
@ -237,24 +225,16 @@ abstract class CoreReaderFragment :
private var fragmentReaderBinding: FragmentReaderBinding? = null
var toolbar: Toolbar? = null
var toolbarContainer: AppBarLayout? = null
var progressBar: ContentLoadingProgressBar? = null
var drawerLayout: DrawerLayout? = null
protected var tableDrawerRightContainer: NavigationView? = null
var contentFrame: FrameLayout? = null
var bottomToolbar: BottomAppBar? = null
var tabSwitcherRoot: View? = null
var closeAllTabsButton: FloatingActionButton? = null
var videoView: ViewGroup? = null
var noOpenBookButton: Button? = null
var activityMainRoot: View? = null
@JvmField
@ -273,10 +253,6 @@ abstract class CoreReaderFragment :
@Inject
var darkModeConfig: DarkModeConfig? = null
@JvmField
@Inject
var menuFactory: MainMenu.Factory? = null
@JvmField
@Inject
var libkiwixBookmarks: LibkiwixBookmarks? = null
@ -299,29 +275,7 @@ abstract class CoreReaderFragment :
var toolbarWithSearchPlaceholder: ConstraintLayout? = null
var backToTopButton: FloatingActionButton? = null
private var stopTTSButton: Button? = null
var pauseTTSButton: Button? = null
var ttsControls: Group? = null
private var exitFullscreenButton: ImageButton? = null
private var bottomToolbarBookmark: ImageView? = null
private var bottomToolbarArrowBack: ImageView? = null
private var bottomToolbarArrowForward: ImageView? = null
private var bottomToolbarHome: ImageView? = null
private var tabRecyclerView: RecyclerView? = null
private var noOpenBookText: TextView? = null
private var bottomToolbarToc: ImageView? = null
private var isFirstTimeMainPageLoaded = true
private var isFromManageExternalLaunch = false
private val savingTabsMutex = Mutex()
@ -384,7 +338,6 @@ abstract class CoreReaderFragment :
fullScreenItem = false to null,
showBackToTopButton = false,
backToTopButtonClick = { backToTop() },
showFullscreenButton = false,
onExitFullscreenClick = { closeFullScreen() },
showTtsControls = false,
onPauseTtsClick = { pauseTts() },
@ -420,7 +373,8 @@ abstract class CoreReaderFragment :
override fun onCloseTab(position: Int) {
closeTab(position)
}
}
},
shouldShowFullScreenMode = false
)
)
private var readerLifeCycleScope: CoroutineScope? = null
@ -699,29 +653,13 @@ abstract class CoreReaderFragment :
private fun prepareViews() {
fragmentReaderBinding?.let { readerBinding ->
videoView = readerBinding.fullscreenVideoContainer
noOpenBookButton = readerBinding.goToLibraryButtonNoOpenBook
noOpenBookText = readerBinding.noOpenBookText
with(readerBinding.root) {
activityMainRoot = findViewById(R.id.activity_main_root)
contentFrame = findViewById(R.id.activity_main_content_frame)
toolbar = findViewById(R.id.toolbar)
toolbarContainer = findViewById(R.id.fragment_main_app_bar)
progressBar = findViewById(R.id.main_fragment_progress_view)
bottomToolbar = findViewById(R.id.bottom_toolbar)
tabSwitcherRoot = findViewById(R.id.activity_main_tab_switcher)
closeAllTabsButton = findViewById(R.id.tab_switcher_close_all_tabs)
toolbarWithSearchPlaceholder = findViewById(R.id.toolbarWithSearchPlaceholder)
backToTopButton = findViewById(R.id.activity_main_back_to_top_fab)
stopTTSButton = findViewById(R.id.activity_main_button_stop_tts)
pauseTTSButton = findViewById(R.id.activity_main_button_pause_tts)
ttsControls = findViewById(R.id.activity_main_tts_controls)
exitFullscreenButton = findViewById(R.id.activity_main_fullscreen_button)
bottomToolbarBookmark = findViewById(R.id.bottom_toolbar_bookmark)
bottomToolbarArrowBack = findViewById(R.id.bottom_toolbar_arrow_back)
bottomToolbarArrowForward = findViewById(R.id.bottom_toolbar_arrow_forward)
bottomToolbarHome = findViewById(R.id.bottom_toolbar_home)
tabRecyclerView = findViewById(R.id.tab_switcher_recycler_view)
bottomToolbarToc = findViewById(R.id.bottom_toolbar_toc)
donationLayout = findViewById(R.id.donation_layout)
}
}
@ -731,53 +669,6 @@ abstract class CoreReaderFragment :
toolbarWithSearchPlaceholder?.setOnClickListener {
openSearch(searchString = "", isOpenedFromTabView = false, false)
}
backToTopButton?.setOnClickListener {
backToTop()
}
stopTTSButton?.setOnClickListener {
stopTts()
}
pauseTTSButton?.setOnClickListener {
pauseTts()
}
exitFullscreenButton?.setOnClickListener {
closeFullScreen()
}
bottomToolbarBookmark?.apply {
setOnClickListener {
toggleBookmark()
}
setOnLongClickListener {
goToBookmarks()
}
}
bottomToolbarArrowBack?.apply {
setOnClickListener {
goBack()
}
setOnLongClickListener {
showBackwardHistory()
true
}
}
bottomToolbarArrowForward?.apply {
setOnClickListener {
goForward()
}
setOnLongClickListener {
showForwardHistory()
true
}
}
bottomToolbarToc?.setOnClickListener {
openToc()
}
closeAllTabsButton?.setOnClickListener {
closeAllTabs()
}
bottomToolbarHome?.setOnClickListener {
openMainPage()
}
}
private fun initTabCallback() {
@ -820,7 +711,7 @@ abstract class CoreReaderFragment :
}
override fun onFinish() {
backToTopButton?.hide()
hideBackToTopButton()
}
}
}
@ -956,21 +847,6 @@ abstract class CoreReaderFragment :
private fun showTabSwitcher() {
(requireActivity() as CoreMainActivity).disableDrawer()
actionBar?.apply {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(
ContextCompat.getDrawable(requireActivity(), R.drawable.ic_round_add_white_36dp)
)
// set the contentDescription to UpIndicator icon.
toolbar?.getToolbarNavigationIcon()?.setToolTipWithContentDescription(
getString(R.string.search_open_in_new_tab)
)
setDisplayShowTitleEnabled(false)
}
closeAllTabsButton?.setToolTipWithContentDescription(
resources.getString(R.string.close_all_tabs)
)
setIsCloseAllTabButtonClickable(true)
// Set a negative top margin to the web views to remove
// the unwanted blank space caused by the toolbar.
// setTopMarginToWebViews(-requireActivity().getToolbarHeight())
@ -979,13 +855,10 @@ abstract class CoreReaderFragment :
copy(
shouldShowBottomAppBar = false,
pageLoadingItem = false to ZERO,
readerScreenTitle = ""
readerScreenTitle = "",
showBackToTopButton = false
)
}
contentFrame?.visibility = GONE
progressBar?.visibility = GONE
backToTopButton?.hide()
setTabSwitcherVisibility(VISIBLE)
startAnimation(tabSwitcherRoot, R.anim.slide_down)
tabsAdapter?.let { tabsAdapter ->
tabRecyclerView?.let { recyclerView ->
@ -1053,23 +926,8 @@ abstract class CoreReaderFragment :
* as closing the ZIM book would require reloading the ZIM file, which can be a resource-intensive operation.
*/
protected open fun hideTabSwitcher(shouldCloseZimBook: Boolean = true) {
actionBar?.apply {
setDisplayShowTitleEnabled(true)
}
toolbar?.let(::setUpDrawerToggle)
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
closeAllTabsButton?.setImageDrawable(
ContextCompat.getDrawable(requireActivity(), R.drawable.ic_close_black_24dp)
)
tabSwitcherRoot?.let {
if (it.isVisible) {
setTabSwitcherVisibility(GONE)
startAnimation(it, R.anim.slide_up)
progressBar?.visibility = VISIBLE
contentFrame?.visibility = VISIBLE
}
}
progressBar?.hide()
selectTab(currentWebViewIndex)
readerScreenState.update {
copy(
@ -1477,32 +1335,16 @@ abstract class CoreReaderFragment :
@SuppressLint("ClickableViewAccessibility")
private fun unBindViewsAndBinding() {
activityMainRoot = null
noOpenBookButton = null
toolbarWithSearchPlaceholder = null
backToTopButton = null
stopTTSButton = null
pauseTTSButton = null
ttsControls = null
exitFullscreenButton = null
bottomToolbarBookmark = null
bottomToolbarArrowBack = null
bottomToolbarArrowForward = null
bottomToolbarHome = null
tabRecyclerView = null
noOpenBookText = null
bottomToolbarToc = null
bottomToolbar = null
tabSwitcherRoot = null
videoView = null
contentFrame = null
toolbarContainer = null
compatCallback?.finish()
compatCallback = null
toolbar?.setOnTouchListener(null)
toolbar = null
progressBar = null
drawerLayout = null
closeAllTabsButton = null
tableDrawerRightContainer = null
fragmentReaderBinding?.root?.removeAllViews()
fragmentReaderBinding = null
@ -1627,18 +1469,17 @@ abstract class CoreReaderFragment :
if (index <= currentWebViewIndex && currentWebViewIndex > 0) {
currentWebViewIndex--
}
tabsAdapter?.apply {
notifyItemRemoved(index)
notifyDataSetChanged()
}
readerScreenState.value.snackBarHostState.snack(
requireActivity().getString(R.string.tab_closed),
actionLabel = requireActivity().getString(R.string.undo),
actionClick = { restoreDeletedTab(index) },
lifecycleScope = lifecycleScope,
snackBarResult = { result ->
if (result == SnackbarResult.Dismissed && webViewList.isEmpty() && isAdded) {
closeZimBook()
if (result == SnackbarResult.Dismissed && isAdded) {
saveTabStates()
if (webViewList.isEmpty()) {
closeZimBook()
}
}
}
)
@ -1774,14 +1615,13 @@ abstract class CoreReaderFragment :
if (readerScreenState.value.showTtsControls) {
// currently TTS is running
if (isBackToTopEnabled) {
readerScreenState.update { copy(showBackToTopButton = true) }
backToTopButton?.show()
showBackToTopButton()
}
tts?.stop()
} else {
// TTS is not running.
if (isBackToTopEnabled) {
readerScreenState.update { copy(showBackToTopButton = false) }
hideBackToTopButton()
}
readerScreenState.update {
copy(pauseTtsButtonText = context?.getString(R.string.tts_pause).orEmpty())
@ -1911,12 +1751,12 @@ abstract class CoreReaderFragment :
@Suppress("MagicNumber")
protected open fun openFullScreen() {
(requireActivity() as CoreMainActivity).disableDrawer(false)
toolbarContainer?.visibility = GONE
readerScreenState.update {
copy(shouldShowBottomAppBar = false)
copy(
shouldShowBottomAppBar = false,
shouldShowFullScreenMode = true
)
}
exitFullscreenButton?.visibility = VISIBLE
exitFullscreenButton?.background?.alpha = 153
val window = requireActivity().window
window.decorView.showFullScreenMode(window)
getCurrentWebView()?.apply {
@ -1931,10 +1771,13 @@ abstract class CoreReaderFragment :
toolbar?.let(::setUpDrawerToggle)
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
sharedPreferenceUtil?.putPrefFullScreen(false)
toolbarContainer?.visibility = VISIBLE
readerScreenState.update {
copy(
shouldShowBottomAppBar = true,
shouldShowFullScreenMode = false
)
}
updateBottomToolbarVisibility()
exitFullscreenButton?.visibility = GONE
exitFullscreenButton?.background?.alpha = 255
val window = requireActivity().window
window.decorView.closeFullScreenMode(window)
getCurrentWebView()?.requestLayout()
@ -2132,17 +1975,12 @@ abstract class CoreReaderFragment :
private fun closeAllTabs() {
onReadAloudStop()
closeAllTabsButton?.apply {
rotate()
setIsCloseAllTabButtonClickable(false)
}
tempZimSourceForUndo = zimReaderContainer?.zimReaderSource
tempWebViewListForUndo.apply {
clear()
addAll(webViewList)
}
webViewList.clear()
tabsAdapter?.notifyDataSetChanged()
openHomeScreen()
readerScreenState.value.snackBarHostState.snack(
context?.getString(R.string.tabs_closed).orEmpty(),
@ -2150,17 +1988,16 @@ abstract class CoreReaderFragment :
actionClick = { restoreDeletedTabs() },
lifecycleScope = lifecycleScope,
snackBarResult = { result ->
if (result == SnackbarResult.Dismissed && webViewList.isEmpty() && isAdded) {
closeZimBook()
if (result == SnackbarResult.Dismissed && isAdded) {
saveTabStates()
if (webViewList.isEmpty()) {
closeZimBook()
}
}
}
)
}
private fun setIsCloseAllTabButtonClickable(isClickable: Boolean) {
closeAllTabsButton?.isClickable = isClickable
}
private fun restoreDeletedTabs() {
if (tempWebViewListForUndo.isNotEmpty()) {
webViewList.addAll(tempWebViewListForUndo)
@ -2652,12 +2489,20 @@ abstract class CoreReaderFragment :
isBackToTopEnabled = sharedPreferenceUtil?.prefBackToTop == true
isOpenNewTabInBackground = sharedPreferenceUtil?.prefNewTabBackground == true
if (!isBackToTopEnabled) {
backToTopButton?.hide()
hideBackToTopButton()
}
openFullScreenIfEnabled()
updateNightMode()
}
private fun showBackToTopButton() {
readerScreenState.update { copy(showBackToTopButton = true) }
}
private fun hideBackToTopButton() {
readerScreenState.update { copy(showBackToTopButton = false) }
}
/**
* Saves the current state of tabs and web view history to persistent storage.
*
@ -2869,28 +2714,18 @@ abstract class CoreReaderFragment :
tabsAdapter?.notifyDataSetChanged()
}
@Suppress("NestedBlockDepth", "MagicNumber")
@Suppress("MagicNumber")
override fun webViewPageChanged(page: Int, maxPages: Int) {
if (isBackToTopEnabled) {
hideBackToTopTimer?.apply {
cancel()
start()
}
getCurrentWebView()?.scrollY?.let {
if (it > 200) {
if (
(backToTopButton?.isGone == true || backToTopButton?.isInvisible == true) &&
ttsControls?.visibility == GONE
) {
backToTopButton?.show()
}
} else {
backToTopButton?.isVisible
if (backToTopButton?.visibility == VISIBLE) {
backToTopButton?.hide()
}
}
}
if (!isBackToTopEnabled) return
hideBackToTopTimer?.apply {
cancel()
start()
}
val scrollY = getCurrentWebView()?.scrollY ?: return
if (scrollY > 200 && !readerScreenState.value.showTtsControls) {
showBackToTopButton()
} else {
hideBackToTopButton()
}
}

View File

@ -25,6 +25,7 @@ import android.widget.FrameLayout
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@ -46,6 +47,7 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.BottomAppBarDefaults
@ -54,14 +56,16 @@ import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@ -73,6 +77,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -106,8 +111,10 @@ import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.models.IconItem.Drawable
import org.kiwix.kiwixmobile.core.ui.models.toPainter
import org.kiwix.kiwixmobile.core.ui.theme.Black
import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue800
import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme
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.CLOSE_ALL_TAB_BUTTON_BOTTOM_PADDING
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_TAB_ICON_ANIMATION_TIMEOUT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_TAB_ICON_SIZE
@ -115,8 +122,11 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.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_DISABLE_BUTTON_ALPHA
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_LAYOUT_HEIGHT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SEVEN_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TEN_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TTS_BUTTONS_CONTROL_ALPHA
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TWO_DP
import org.kiwix.kiwixmobile.core.utils.StyleUtils.fromHtml
@ -166,7 +176,7 @@ private fun ReaderTopBar(
scrollBehavior: TopAppBarScrollBehavior,
navigationIcon: @Composable () -> Unit,
) {
if (!state.fullScreenItem.first) {
if (!state.shouldShowFullScreenMode) {
KiwixAppBar(
title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
navigationIcon = navigationIcon,
@ -205,6 +215,10 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
)
}
ShowFullScreenView(state)
CloseFullScreenImageButton(
state.shouldShowFullScreenMode,
state.onExitFullscreenClick
)
}
}
@ -212,6 +226,29 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
}
}
@Composable
private fun BoxScope.CloseFullScreenImageButton(
shouldShowFullScreenMode: Boolean,
onExitFullScreen: () -> Unit
) {
if (shouldShowFullScreenMode) {
IconButton(
onClick = onExitFullScreen,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(SEVEN_DP)
.minimumInteractiveComponentSize()
.background(MaterialTheme.colorScheme.onSurface)
) {
Icon(
painter = IconItem.Drawable(R.drawable.fullscreen_exit).toPainter(),
contentDescription = stringResource(id = R.string.menu_exit_full_screen),
tint = MaterialTheme.colorScheme.surface
)
}
}
}
@Composable
private fun ShowZIMFileContent(state: ReaderScreenState) {
state.selectedWebView?.let { selectedWebView ->
@ -303,12 +340,12 @@ private fun TtsControls(state: ReaderScreenState) {
@Composable
private fun BackToTopFab(state: ReaderScreenState) {
if (state.showBackToTopButton) {
FloatingActionButton(
SmallFloatingActionButton(
onClick = state.backToTopButtonClick,
modifier = Modifier,
modifier = Modifier.padding(bottom = BACK_TO_TOP_BUTTON_BOTTOM_MARGIN),
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary,
shape = FloatingActionButtonDefaults.smallShape
shape = CircleShape
) {
Icon(
painter = Drawable(R.drawable.action_find_previous).toPainter(),
@ -389,15 +426,26 @@ private fun BottomAppBarButtonIcon(
shouldEnable: Boolean = true,
contentDescription: String
) {
IconButton(
onClick = onClick,
modifier = Modifier.combinedClickable(onClick = onClick, onLongClick = onLongClick),
enabled = shouldEnable
Box(
modifier = Modifier
.size(READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE + TEN_DP)
.clip(CircleShape)
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
enabled = shouldEnable
),
contentAlignment = Alignment.Center
) {
Icon(
buttonIcon.toPainter(),
contentDescription,
modifier = Modifier.size(READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE)
modifier = Modifier.size(READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE),
tint = if (shouldEnable) {
LocalContentColor.current
} else {
LocalContentColor.current.copy(alpha = READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA)
}
)
}
}
@ -504,6 +552,8 @@ private fun BoxScope.CloseAllTabButton(onCloseAllTabs: () -> Unit) {
onCloseAllTabs()
}
),
containerColor = DenimBlue800,
contentColor = White
) {
Icon(
painter = painterResource(

View File

@ -56,13 +56,17 @@ data class ReaderScreenState(
*/
val shouldShowDonationPopup: Boolean,
/**
* Manages the showing of "Full screen view".
* Manages the showing of "Full screen view" of webView's video.
*
* A [Pair] containing:
* - [Boolean]: Whether to show/hide full screen mode.
* - [ComposeView]: full screen view.
*/
val fullScreenItem: Pair<Boolean, ComposeView?>,
/**
* Manages the showing of "Full screen mode".
*/
val shouldShowFullScreenMode: Boolean,
/**
* Manages the showing of "BackToTop" fab button.
*/
@ -71,7 +75,6 @@ data class ReaderScreenState(
* Handles the click of "BackToTop" fab button.
*/
val backToTopButtonClick: () -> Unit,
val showFullscreenButton: Boolean = false,
val onExitFullscreenClick: () -> Unit = {},
val showTtsControls: Boolean = false,
val onPauseTtsClick: () -> Unit = {},

View File

@ -1,73 +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.utils
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.animation.LinearInterpolator
import androidx.core.animation.addListener
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
private const val HEIGHT_ANIM_DURATION = 100L
private const val ROTATE_ANIM_DURATION = 200L
private const val FULL_ROTATION = 360.0f
object AnimationUtils {
@JvmStatic fun View.expand() {
measure(MATCH_PARENT, WRAP_CONTENT)
// Older versions of android (pre API 21) cancel animations for views with a height of 0.
layoutParams.height = 1
visibility = View.VISIBLE
animateHeight(1, measuredHeight) {
layoutParams.height = WRAP_CONTENT
}
}
@JvmStatic fun View.collapse() {
animateHeight(measuredHeight, 0) { visibility = View.GONE }
}
@SuppressLint("Recycle")
private fun View.animateHeight(start: Int, end: Int, onEndAction: () -> Unit) {
ValueAnimator.ofInt(start, end).apply {
addUpdateListener {
layoutParams.height = it.animatedValue as Int
requestLayout()
}
addListener(onEnd = { onEndAction.invoke() })
duration = HEIGHT_ANIM_DURATION
interpolator = LinearInterpolator()
}.start()
}
@JvmStatic fun FloatingActionButton.rotate() {
animate().apply {
withStartAction { setImageDrawableCompat(R.drawable.ic_close_black_24dp) }
withEndAction { setImageDrawableCompat(R.drawable.ic_done_white_24dp) }
rotationBy(FULL_ROTATION)
duration = ROTATE_ANIM_DURATION
}.start()
}
}

View File

@ -48,6 +48,7 @@ object ComposeDimens {
val TWELVE_DP = 12.dp
val TEN_DP = 10.dp
val EIGHT_DP = 8.dp
val SEVEN_DP = 7.dp
val SIX_DP = 6.dp
val FIVE_DP = 5.dp
val FOUR_DP = 4.dp
@ -189,4 +190,6 @@ object ComposeDimens {
const val TAB_SWITCHER_ICON_CORNER_RADIUS = 10
val CLOSE_TAB_ICON_SIZE = 20.dp
const val CLOSE_TAB_ICON_ANIMATION_TIMEOUT = 1200L
val BACK_TO_TOP_BUTTON_BOTTOM_MARGIN = 80.dp
const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f
}