Fixed: Pressing the back button no longer reopens the Search fragment when you’re on the Reader fragment and navigated there from Search (which was happening before).

* Fixed: Pressing the back button now correctly closes the left drawer when it’s open on the Local Library or Online fragments.
* Introduced: A common mechanism to support back press handling across all fragments and the activity, and added support for "Periodic back navigation".
This commit is contained in:
MohitMaliFtechiz 2025-08-01 17:54:39 +05:30
parent 51bd9e7908
commit 8112632c80
14 changed files with 175 additions and 209 deletions

View File

@ -51,11 +51,9 @@ import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
import org.kiwix.kiwixmobile.core.utils.TestingUtils.RETRY_RULE_ORDER
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.main.topLevel
import org.kiwix.kiwixmobile.nav.destination.library.library
import org.kiwix.kiwixmobile.testutils.RetryRule
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.closeSystemDialogs
import org.kiwix.kiwixmobile.testutils.TestUtils.isSystemUINotRespondingDialogVisible
@ -65,9 +63,9 @@ import java.util.concurrent.TimeUnit
@LargeTest
class DownloadTest : BaseActivityTest() {
@Rule(order = RETRY_RULE_ORDER)
@JvmField
val retryRule = RetryRule()
// @Rule(order = RETRY_RULE_ORDER)
// @JvmField
// val retryRule = RetryRule()
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
val composeTestRule = createComposeRule()

View File

@ -18,6 +18,8 @@
package org.kiwix.kiwixmobile.main
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@ -75,6 +77,7 @@ fun KiwixMainActivityScreen(
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
val shouldShowBottomBar = currentRoute in topLevelDestinationsRoute && shouldShowBottomAppBar
OnUserBackPressed(leftDrawerState, uiCoroutineScope, currentRoute, navController)
KiwixTheme {
ModalNavigationDrawer(
drawerState = leftDrawerState,
@ -119,6 +122,33 @@ fun KiwixMainActivityScreen(
}
}
@Composable
private fun OnUserBackPressed(
leftDrawerState: DrawerState,
uiCoroutineScope: CoroutineScope,
currentRoute: String?,
navController: NavHostController
) {
val activity = LocalActivity.current
BackHandler(enabled = true) {
when {
leftDrawerState.isOpen -> uiCoroutineScope.launch { leftDrawerState.close() }
currentRoute == KiwixDestination.Reader.route &&
navController.previousBackStackEntry?.destination?.route != KiwixDestination.Search.route -> {
activity?.finish()
}
else -> {
val popped = navController.popBackStack()
if (!popped) {
activity?.finish()
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomNavigationBar(

View File

@ -66,6 +66,7 @@ import org.kiwix.kiwixmobile.cachedComponent
import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isManageExternalStoragePermissionGranted
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
@ -200,7 +201,9 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
onMultiSelect = { offerAction(RequestSelect(it)) },
onRefresh = { onSwipeRefresh() },
onDownloadButtonClick = { downloadBookButtonClick() },
bottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour
bottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour,
navHostController = (requireActivity() as CoreMainActivity).navController,
onUserBackPressed = { onUserBackPressed() }
) {
NavigationIcon(
iconItem = IconItem.Vector(Icons.Filled.Menu),
@ -213,12 +216,16 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
}
}
private fun onUserBackPressed(): FragmentActivityExtensions.Super {
val coreMainActivity = (activity as? CoreMainActivity)
if (coreMainActivity?.navigationDrawerIsOpen() == true) {
coreMainActivity.closeNavigationDrawer()
return FragmentActivityExtensions.Super.ShouldNotCall
}
return FragmentActivityExtensions.Super.ShouldCall
}
private fun navigationIconClick() {
// Manually handle the navigation open/close.
// Since currently we are using the view based navigation drawer in other screens.
// Once we fully migrate to jetpack compose we will refactor this code to use the
// compose navigation.
// TODO Replace with compose based navigation when migration is done.
val activity = activity as CoreMainActivity
if (activity.navigationDrawerIsOpen()) {
activity.closeNavigationDrawer()

View File

@ -47,9 +47,12 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.navigation.NavHostController
import org.kiwix.kiwixmobile.R.string
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.main.reader.CONTENT_LOADING_PROGRESSBAR_TESTING_TAG
import org.kiwix.kiwixmobile.core.main.reader.OnBackPressed
import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixButton
@ -73,7 +76,7 @@ const val BOOK_LIST_TESTING_TAG = "bookListTestingTag"
const val SELECT_FILE_BUTTON_TESTING_TAG = "selectFileButtonTestingTag"
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("ComposableLambdaParameterNaming")
@Suppress("ComposableLambdaParameterNaming", "LongParameterList")
@Composable
fun LocalLibraryScreen(
state: LocalLibraryScreenState,
@ -85,6 +88,8 @@ fun LocalLibraryScreen(
onLongClick: ((BookOnDisk) -> Unit)? = null,
onMultiSelect: ((BookOnDisk) -> Unit)? = null,
bottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?,
onUserBackPressed: () -> FragmentActivityExtensions.Super,
navHostController: NavHostController,
navigationIcon: @Composable () -> Unit
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
@ -117,6 +122,7 @@ fun LocalLibraryScreen(
.fillMaxSize()
.padding(contentPadding)
) {
OnBackPressed(onUserBackPressed, navHostController)
if (state.scanningProgressItem.first) {
ContentLoadingProgressBar(
modifier = Modifier.testTag(CONTENT_LOADING_PROGRESSBAR_TESTING_TAG),

View File

@ -29,7 +29,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@ -271,7 +270,9 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
onClick = { navigationIconClick(onlineLibraryScreenState.value.value.isSearchActive) }
)
},
bottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour
bottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour,
navHostController = (requireActivity() as CoreMainActivity).navController,
onUserBackPressed = { onUserBackPressed() }
)
DialogHost(alertDialogShower)
}
@ -460,8 +461,13 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
composeView = null
}
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
if (isKeyboardVisible() || onlineLibraryScreenState.value.value.isSearchActive) {
@Suppress("ReturnCount")
private fun onUserBackPressed(): FragmentActivityExtensions.Super {
val coreMainActivity = (activity as? CoreMainActivity)
if (coreMainActivity?.navigationDrawerIsOpen() == true) {
coreMainActivity.closeNavigationDrawer()
return FragmentActivityExtensions.Super.ShouldNotCall
} else if (isKeyboardVisible() || onlineLibraryScreenState.value.value.isSearchActive) {
closeKeyboard()
closeSearch()
return FragmentActivityExtensions.Super.ShouldNotCall

View File

@ -56,12 +56,15 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.navigation.NavHostController
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.downloader.downloadManager.FIVE
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.hideKeyboardOnLazyColumnScroll
import org.kiwix.kiwixmobile.core.main.reader.OnBackPressed
import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixSearchView
@ -99,6 +102,8 @@ fun OnlineLibraryScreen(
actionMenuItems: List<ActionMenuItem>,
listState: LazyListState,
bottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?,
onUserBackPressed: () -> FragmentActivityExtensions.Super,
navHostController: NavHostController,
navigationIcon: @Composable () -> Unit,
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
@ -135,6 +140,7 @@ fun OnlineLibraryScreen(
end = paddingValues.calculateEndPadding(LocalLayoutDirection.current),
)
) {
OnBackPressed(onUserBackPressed, navHostController)
OnlineLibraryScreenContent(state, listState)
}
}

View File

@ -52,7 +52,7 @@
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="false"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_rules"
android:hardwareAccelerated="true"
android:hasFragileUserData="true"

View File

@ -24,7 +24,6 @@ import android.os.Process
import android.view.ActionMode
import android.view.Menu
import android.view.MenuItem
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.BottomAppBarScrollBehavior
@ -38,7 +37,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavDirections
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import com.google.android.material.navigation.NavigationView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -47,7 +45,6 @@ import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions.Super.ShouldCall
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToRoomMigrator
import org.kiwix.kiwixmobile.core.di.components.CoreActivityComponent
@ -144,6 +141,11 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
*/
val enableLeftDrawer = mutableStateOf(true)
/**
* For managing the back press of fragments.
*/
val customBackHandler = mutableStateOf<(() -> FragmentActivityExtensions.Super)?>(null)
/**
* For managing the the showing/hiding the bottomAppBar when scrolling.
*/
@ -197,7 +199,6 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
lifecycleScope.launch(Dispatchers.IO) {
createApplicationShortcuts()
}
handleBackPressed()
leftDrawerMenu.addAll(leftNavigationDrawerMenuItems)
}
@ -231,11 +232,6 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
}
}
override fun onDestroy() {
onBackPressedCallBack.remove()
super.onDestroy()
}
override fun onStop() {
startMonitoringDownloads()
downloadMonitor.stopListeningDownloads()
@ -253,11 +249,6 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
}
}
@Suppress("UnusedParameter")
private fun NavigationView.setLockMode(lockMode: Int) {
// drawerContainerLayout.setDrawerLockMode(lockMode, this)
}
@Suppress("DEPRECATION")
override fun onRequestPermissionsResult(
requestCode: Int,
@ -339,41 +330,6 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
externalLinkOpener.openExternalUrl(KIWIX_SUPPORT_URL.toUri().browserIntent(), false)
}
private fun handleBackPressed() {
onBackPressedDispatcher.addCallback(this, onBackPressedCallBack)
}
private val onBackPressedCallBack =
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (navigationDrawerIsOpen()) {
closeNavigationDrawer()
return
}
if (activeFragments().filterIsInstance<FragmentActivityExtensions>().isEmpty()) {
isEnabled = false
return onBackPressedDispatcher.onBackPressed().also {
isEnabled = true
}
}
activeFragments().filterIsInstance<FragmentActivityExtensions>().forEach {
if (it.onBackPressed(this@CoreMainActivity) == ShouldCall) {
if (navController.currentDestination?.route?.equals(readerFragmentRoute) == true &&
navController.previousBackStackEntry?.destination
?.route?.equals(searchFragmentRoute) == false
) {
drawerToggle = null
finish()
} else {
isEnabled = false
onBackPressedDispatcher.onBackPressed()
isEnabled = true
}
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
if (activeFragments().filterIsInstance<FragmentActivityExtensions>().isEmpty()) {
return super.onCreateOptionsMenu(menu)

View File

@ -1,125 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.main
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.core.databinding.SectionListBinding
class TableDrawerAdapter constructor(
private val listener: TableClickListener
) : Adapter<ViewHolder>() {
private var title: String = ""
private val sections: MutableList<DocumentSection> = mutableListOf()
fun setSections(sections: List<DocumentSection>) {
this.sections.clear()
this.sections.addAll(sections)
}
fun setTitle(title: String) {
this.title = title
}
override fun getItemViewType(position: Int): Int {
return if (position == 0) {
0
} else {
1
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val binding = SectionListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return if (viewType == 0) {
HeaderTableDrawerViewHolder(binding)
} else {
SectionTableDrawerViewHolder(binding)
}
}
override fun getItemCount(): Int = sections.size + 1
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
when (holder) {
is HeaderTableDrawerViewHolder -> {
holder.bind(title)
holder.itemView.setOnClickListener(listener::onHeaderClick)
}
is SectionTableDrawerViewHolder -> {
val titleAdjustedPosition = position - 1
holder.bind(sections[titleAdjustedPosition])
holder.itemView.setOnClickListener {
listener.onSectionClick(it, titleAdjustedPosition)
}
}
else -> {
throw IllegalStateException("Unknown ViewHolder $holder found")
}
}
}
interface TableClickListener {
fun onHeaderClick(view: View?)
fun onSectionClick(view: View?, position: Int)
}
class HeaderTableDrawerViewHolder(private val sectionListBinding: SectionListBinding) :
BaseViewHolder<String>(sectionListBinding.root) {
override fun bind(item: String) {
val context = itemView.context
sectionListBinding.titleText.typeface = Typeface.DEFAULT_BOLD
sectionListBinding.titleText.text =
when {
item.isNotEmpty() -> item
context is WebViewProvider ->
context.getCurrentWebView()?.title
?: context.getString(R.string.no_section_info)
else -> context.getString(R.string.no_section_info)
}
}
}
class SectionTableDrawerViewHolder(private val sectionListBinding: SectionListBinding) :
BaseViewHolder<TableDrawerAdapter.DocumentSection>(sectionListBinding.root) {
override fun bind(
item: TableDrawerAdapter.DocumentSection
) {
val context = itemView.context
val padding =
((item.level - 1) * context.resources.getDimension(R.dimen.title_text_padding)).toInt()
sectionListBinding.titleText.setPadding(padding, 0, 0, 0)
sectionListBinding.titleText.text = item.title
}
}
data class DocumentSection(var title: String, var id: String, var level: Int)
}

View File

@ -474,7 +474,9 @@ abstract class CoreReaderFragment :
},
mainActivityBottomAppBarScrollBehaviour = (requireActivity() as CoreMainActivity).bottomAppBarScrollBehaviour,
documentSections = documentSections,
showTableOfContentDrawer = shouldTableOfContentDrawer
showTableOfContentDrawer = shouldTableOfContentDrawer,
onUserBackPressed = { onUserBackPressed(requireActivity() as CoreMainActivity) },
navHostController = (requireActivity() as CoreMainActivity).navController
)
DialogHost(alertDialogShower as AlertDialogShower)
}
@ -842,9 +844,16 @@ abstract class CoreReaderFragment :
shouldTableOfContentDrawer.update { true }
}
@Suppress("ReturnCount", "NestedBlockDepth")
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
@Suppress("ReturnCount", "NestedBlockDepth", "LongMethod", "CyclomaticComplexMethod")
private fun onUserBackPressed(coreMainActivity: CoreMainActivity): FragmentActivityExtensions.Super {
when {
coreMainActivity.leftDrawerState.isOpen -> {
coreMainActivity.uiCoroutineScope.launch {
coreMainActivity.leftDrawerState.close()
}
return FragmentActivityExtensions.Super.ShouldNotCall
}
readerScreenState.value.showTabSwitcher -> {
selectTab(
if (currentWebViewIndex < webViewList.size) {
@ -893,7 +902,12 @@ abstract class CoreReaderFragment :
isHomePageOfServiceWorkerZimFiles(url, webViewBackWordHistoryList)
) {
// If it is the last page that is showing to the user, then exit the application.
return@onBackPressed FragmentActivityExtensions.Super.ShouldCall
if (coreMainActivity.navController.previousBackStackEntry?.destination?.route !=
coreMainActivity.searchFragmentRoute
) {
activity?.finish()
}
return FragmentActivityExtensions.Super.ShouldCall
}
}
// Otherwise, go to the previous page.

View File

@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
@ -114,9 +115,11 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.navigation.NavHostController
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.downloader.downloadManager.HUNDERED
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.update
@ -177,13 +180,15 @@ const val READER_BOTTOM_BAR_TABLE_CONTENT_BUTTON_TESTING_TAG =
"readerBottomBarTableContentButtonTestingTag"
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("ComposableLambdaParameterNaming", "LongMethod")
@Suppress("ComposableLambdaParameterNaming", "LongMethod", "LongParameterList")
@Composable
fun ReaderScreen(
state: ReaderScreenState,
actionMenuItems: List<ActionMenuItem>,
showTableOfContentDrawer: MutableState<Boolean>,
documentSections: MutableList<DocumentSection>?,
onUserBackPressed: () -> FragmentActivityExtensions.Super,
navHostController: NavHostController,
mainActivityBottomAppBarScrollBehaviour: BottomAppBarScrollBehavior?,
navigationIcon: @Composable () -> Unit
) {
@ -215,6 +220,7 @@ fun ReaderScreen(
}
.semantics { testTag = READER_SCREEN_TESTING_TAG }
) { paddingValues ->
OnBackPressed(onUserBackPressed, navHostController)
ReaderContentLayout(
state,
Modifier.padding(paddingValues),
@ -251,6 +257,19 @@ fun ReaderScreen(
}
}
@Composable
fun OnBackPressed(
onUserBackPressed: () -> FragmentActivityExtensions.Super,
navHostController: NavHostController
) {
BackHandler(enabled = true) {
val result = onUserBackPressed()
if (result == FragmentActivityExtensions.Super.ShouldCall) {
navHostController.popBackStack()
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("ComposableLambdaParameterNaming")
@Composable
@ -731,7 +750,8 @@ private fun BottomAppBarButtonIcon(
onClick = onClick,
onLongClick = onLongClick,
enabled = shouldEnable
).testTag(testingTag),
)
.testTag(testingTag),
contentAlignment = Alignment.Center
) {
Icon(

View File

@ -22,7 +22,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.Lifecycle
@ -39,6 +39,7 @@ import kotlinx.coroutines.withContext
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.extensions.closeKeyboard
import org.kiwix.kiwixmobile.core.extensions.coreMainActivity
@ -123,6 +124,12 @@ class SearchFragment : BaseFragment() {
super.onViewCreated(view, savedInstanceState)
composeView?.apply {
setContent {
DisposableEffect(Unit) {
(activity as CoreMainActivity).customBackHandler.value = { handleBackPress() }
onDispose {
(activity as CoreMainActivity).customBackHandler.value = null
}
}
SearchScreen(
searchScreenState.value,
actionMenuItems(),
@ -134,7 +141,6 @@ class SearchFragment : BaseFragment() {
searchViewModel.setAlertDialogShower(dialogShower as AlertDialogShower)
observeViewModelData()
handleSearchArgument()
handleBackPress()
}
private fun handleSearchArgument() {
@ -203,15 +209,9 @@ class SearchFragment : BaseFragment() {
}
}
private fun handleBackPress() {
activity?.onBackPressedDispatcher?.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
goBack()
}
}
)
private fun handleBackPress(): FragmentActivityExtensions.Super {
goBack()
return FragmentActivityExtensions.Super.ShouldCall
}
override fun onDestroyView() {

View File

@ -71,7 +71,9 @@ class CustomMainActivity : CoreMainActivity() {
leftDrawerContent = leftDrawerMenu,
topLevelDestinationsRoute = topLevelDestinationsRoute,
leftDrawerState = leftDrawerState,
enableLeftDrawer = enableLeftDrawer.value
enableLeftDrawer = enableLeftDrawer.value,
uiCoroutineScope = uiCoroutineScope,
customBackHandler = customBackHandler
)
DialogHost(alertDialogShower)
}

View File

@ -18,6 +18,8 @@
package org.kiwix.kiwixmobile.custom.main
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@ -27,10 +29,14 @@ import androidx.compose.material3.DrawerState
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.main.DrawerMenuGroup
import org.kiwix.kiwixmobile.core.main.LeftDrawerMenu
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
@ -42,9 +48,18 @@ fun CustomMainActivityScreen(
topLevelDestinationsRoute: Set<String>,
leftDrawerState: DrawerState,
enableLeftDrawer: Boolean,
customBackHandler: MutableState<(() -> FragmentActivityExtensions.Super)?>,
uiCoroutineScope: CoroutineScope
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
OnUserBackPressed(
leftDrawerState,
uiCoroutineScope,
currentRoute,
navController,
customBackHandler
)
KiwixTheme {
ModalNavigationDrawer(
drawerState = leftDrawerState,
@ -76,3 +91,34 @@ fun CustomMainActivityScreen(
}
}
}
@Composable
private fun OnUserBackPressed(
leftDrawerState: DrawerState,
uiCoroutineScope: CoroutineScope,
currentRoute: String?,
navController: NavHostController,
customBackHandler: MutableState<(() -> FragmentActivityExtensions.Super)?>,
) {
val activity = LocalActivity.current
BackHandler(enabled = true) {
when {
leftDrawerState.isOpen -> uiCoroutineScope.launch { leftDrawerState.close() }
customBackHandler.value?.invoke() == FragmentActivityExtensions.Super.ShouldNotCall -> {
// do nothing since fragment handles the back press.
}
currentRoute == CustomDestination.Reader.route &&
navController.previousBackStackEntry?.destination?.route != CustomDestination.Search.route -> {
activity?.finish()
}
else -> {
val popped = navController.popBackStack()
if (!popped) {
activity?.finish()
}
}
}
}
}