diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt index 49edea82d..ae6b38c65 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt @@ -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() diff --git a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt index 373e04531..3a9a1c90b 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt @@ -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( diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt index 397741cb7..0b5f3e0c7 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt @@ -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() diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt index 88547b68b..907aea0d3 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryScreen.kt @@ -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), diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt index 14efb7084..8ef5e6374 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt @@ -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 diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt index c31c3d9a1..404f9dd18 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryScreen.kt @@ -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, 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) } } diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index adf34d1cb..e55d06111 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -52,7 +52,7 @@ 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().isEmpty()) { - isEnabled = false - return onBackPressedDispatcher.onBackPressed().also { - isEnabled = true - } - } - activeFragments().filterIsInstance().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().isEmpty()) { return super.onCreateOptionsMenu(menu) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/TableDrawerAdapter.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/TableDrawerAdapter.kt deleted file mode 100644 index 80ef14dfd..000000000 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/TableDrawerAdapter.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2020 Kiwix - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package org.kiwix.kiwixmobile.core.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() { - private var title: String = "" - private val sections: MutableList = mutableListOf() - - fun setSections(sections: List) { - 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(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(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) -} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt index afaa6de2f..5013a09cc 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt @@ -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. diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt index d01fc6753..8b6af2fc1 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt @@ -22,6 +22,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout +import androidx.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, showTableOfContentDrawer: MutableState, documentSections: MutableList?, + 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( diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt index 99420137c..2b7bbfd30 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt @@ -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() { diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt index a29e61fc2..f4287235b 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt @@ -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) } diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivityScreen.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivityScreen.kt index 2d625bb45..98f2acb71 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivityScreen.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivityScreen.kt @@ -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, 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() + } + } + } + } +}