mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 18:56:44 -04:00
Fixed: The system bar was not correctly showing the icons.
* Fixed: When scrolling, the BottomAppBar was appearing behind the navigation buttons. * Fixed: The hint in the table of contents was not showing on first-time app install. * Fixed: Pressing the back button was immediately exiting the app when the table of contents drawer was open. * Created the `CustomNavGraph` for custom app navigation and refactored related code. * Refactored the logic to dynamically disable the left drawer when a custom app is configured not to show it. * Simplified the code for enabling/disabling the sidebar. * Resolved all lint and Detekt errors.
This commit is contained in:
parent
8afaa8be52
commit
4d97eeebc8
@ -23,7 +23,6 @@ import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
@ -50,7 +49,6 @@ import org.kiwix.kiwixmobile.BuildConfig
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.core.CoreApp
|
||||
import org.kiwix.kiwixmobile.core.R.drawable
|
||||
import org.kiwix.kiwixmobile.core.R.id
|
||||
import org.kiwix.kiwixmobile.core.R.mipmap
|
||||
import org.kiwix.kiwixmobile.core.R.string
|
||||
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
|
||||
@ -63,7 +61,6 @@ import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||
import org.kiwix.kiwixmobile.core.main.DrawerMenuItem
|
||||
import org.kiwix.kiwixmobile.core.main.NEW_TAB_SHORTCUT_ID
|
||||
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.DialogHost
|
||||
import org.kiwix.kiwixmobile.kiwixActivityComponent
|
||||
import org.kiwix.kiwixmobile.ui.KiwixDestination
|
||||
@ -72,25 +69,12 @@ import javax.inject.Inject
|
||||
const val ACTION_GET_CONTENT = "GET_CONTENT"
|
||||
const val OPENING_ZIM_FILE_DELAY = 300L
|
||||
const val GET_CONTENT_SHORTCUT_ID = "get_content_shortcut"
|
||||
const val KIWIX_BOTTOM_BAR_ANIMATION_DURATION = 250L
|
||||
|
||||
class KiwixMainActivity : CoreMainActivity() {
|
||||
private var actionMode: ActionMode? = null
|
||||
override val cachedComponent by lazy { kiwixActivityComponent }
|
||||
override val searchFragmentRoute: String = KiwixDestination.Search.route
|
||||
|
||||
// override val drawerContainerLayout: DrawerLayout by lazy {
|
||||
// // activityKiwixMainBinding.navigationContainer
|
||||
// }
|
||||
|
||||
// override val drawerNavView: NavigationView by lazy {
|
||||
// activityKiwixMainBinding.drawerNavView
|
||||
// }
|
||||
|
||||
// override val readerTableOfContentsDrawer: NavigationView by lazy {
|
||||
// activityKiwixMainBinding.readerDrawerNavView
|
||||
// }
|
||||
|
||||
@Inject lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk
|
||||
|
||||
override val mainActivity: AppCompatActivity by lazy { this }
|
||||
@ -288,14 +272,6 @@ class KiwixMainActivity : CoreMainActivity() {
|
||||
navigate(KiwixDestination.Reader.createRoute(zimFileUri = path))
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
id.menu_host_books -> openZimHostFragment()
|
||||
else -> return super.onNavigationItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override val zimHostDrawerMenuItem: DrawerMenuItem? = DrawerMenuItem(
|
||||
title = CoreApp.instance.getString(string.menu_wifi_hotspot),
|
||||
iconRes = drawable.ic_mobile_screen_share_24px,
|
||||
@ -323,7 +299,7 @@ class KiwixMainActivity : CoreMainActivity() {
|
||||
override val aboutAppDrawerMenuItem: DrawerMenuItem? = null
|
||||
|
||||
private fun openZimHostFragment() {
|
||||
disableDrawer()
|
||||
disableLeftDrawer()
|
||||
handleDrawerOnNavigation()
|
||||
navigate(KiwixDestination.ZimHost.route)
|
||||
}
|
||||
@ -336,10 +312,6 @@ class KiwixMainActivity : CoreMainActivity() {
|
||||
ShortcutManagerCompat.addDynamicShortcuts(this, dynamicShortcutList())
|
||||
}
|
||||
|
||||
override fun setDialogHostToActivity(alertDialogShower: AlertDialogShower) {
|
||||
// activityKiwixMainBinding.root.addView(getDialogHostComposeView(alertDialogShower), 0)
|
||||
}
|
||||
|
||||
override fun openSearch(searchString: String, isOpenedFromTabView: Boolean, isVoice: Boolean) {
|
||||
navigate(
|
||||
KiwixDestination.Search.createRoute(
|
||||
|
@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.BottomAppBarScrollBehavior
|
||||
import androidx.compose.material3.DrawerState
|
||||
@ -51,6 +52,7 @@ import org.kiwix.kiwixmobile.core.ui.theme.White
|
||||
import org.kiwix.kiwixmobile.ui.KiwixDestination
|
||||
import org.kiwix.kiwixmobile.ui.KiwixNavGraph
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun KiwixMainActivityScreen(
|
||||
@ -83,7 +85,6 @@ fun KiwixMainActivityScreen(
|
||||
// on the hamburger button.
|
||||
(currentRoute != KiwixDestination.Reader.route || leftDrawerState.isOpen)
|
||||
) {
|
||||
Box {
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
if (shouldShowBottomBar) {
|
||||
@ -96,7 +97,9 @@ fun KiwixMainActivityScreen(
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
) { paddingValues ->
|
||||
Box(modifier = Modifier.padding(paddingValues)) {
|
||||
KiwixNavGraph(
|
||||
@ -108,7 +111,6 @@ fun KiwixMainActivityScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
@ -113,7 +113,6 @@ import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val WAS_IN_ACTION_MODE = "WAS_IN_ACTION_MODE"
|
||||
private const val MATERIAL_BOTTOM_VIEW_ENTER_ANIMATION_DURATION = 225L
|
||||
const val LOCAL_FILE_TRANSFER_MENU_BUTTON_TESTING_TAG = "localFileTransferMenuButtonTestingTag"
|
||||
|
||||
@Suppress("LargeClass")
|
||||
|
@ -26,10 +26,8 @@ import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.net.toUri
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavOptions
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kiwix.kiwixmobile.cachedComponent
|
||||
import org.kiwix.kiwixmobile.core.R.string
|
||||
@ -37,7 +35,6 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions.Super
|
||||
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions.Super.ShouldCall
|
||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.setupDrawerToggle
|
||||
import org.kiwix.kiwixmobile.core.extensions.isFileExist
|
||||
import org.kiwix.kiwixmobile.core.extensions.snack
|
||||
import org.kiwix.kiwixmobile.core.extensions.toast
|
||||
@ -79,7 +76,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
})
|
||||
}
|
||||
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
activity.setupDrawerToggle(true)
|
||||
activity.enableLeftDrawer()
|
||||
openPageInBookFromNavigationArguments()
|
||||
}
|
||||
|
||||
@ -147,11 +144,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
openZimFile(zimReaderSource)
|
||||
}
|
||||
|
||||
override fun loadDrawerViews() {
|
||||
// drawerLayout = requireActivity().findViewById(R.id.navigation_container)
|
||||
// tableDrawerRightContainer = requireActivity().findViewById(R.id.reader_drawer_nav_view)
|
||||
}
|
||||
|
||||
override fun openHomeScreen() {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
if (webViewList.isEmpty()) {
|
||||
@ -174,9 +166,8 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
* @see closeAllTabs
|
||||
*/
|
||||
override fun hideTabSwitcher(shouldCloseZimBook: Boolean) {
|
||||
activity?.setupDrawerToggle(true)
|
||||
enableLeftDrawer()
|
||||
(requireActivity() as CoreMainActivity).showBottomAppBar()
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
if (webViewList.isEmpty()) {
|
||||
readerMenuState?.hideTabSwitcher()
|
||||
exitBook(shouldCloseZimBook)
|
||||
|
@ -74,6 +74,7 @@ import org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragment
|
||||
import org.kiwix.kiwixmobile.settings.KiwixSettingsFragment
|
||||
import org.kiwix.kiwixmobile.webserver.ZimHostFragment
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun KiwixNavGraph(
|
||||
navController: NavHostController,
|
||||
|
@ -95,9 +95,6 @@ object ActivityExtensions {
|
||||
val Activity.cachedComponent: CoreActivityComponent
|
||||
get() = coreMainActivity.cachedComponent
|
||||
|
||||
fun Activity.setupDrawerToggle(shouldEnableRightDrawer: Boolean = false) =
|
||||
coreMainActivity.setupDrawerToggle(shouldEnableRightDrawer)
|
||||
|
||||
fun Activity.navigate(route: String, navOptions: NavOptions? = null) {
|
||||
coreMainActivity.navigate(route, navOptions)
|
||||
}
|
||||
|
@ -21,16 +21,13 @@ package org.kiwix.kiwixmobile.core.extensions
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
@ -130,31 +127,3 @@ fun View.closeFullScreenMode(window: Window) {
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies edge-to-edge insets to the current view by adjusting its margins
|
||||
* to account for system bars and display cutouts (e.g., status bar, navigation bar, and notches).
|
||||
*
|
||||
* This method ensures that the view avoids overlapping with system UI components by dynamically
|
||||
* setting margins based on the insets provided by the system.
|
||||
*
|
||||
* Usage: Call this method on any view to apply edge-to-edge handling.
|
||||
*/
|
||||
fun View?.applyEdgeToEdgeInsets() {
|
||||
this?.let {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(it) { view, windowInsets ->
|
||||
val systemBarsInsets =
|
||||
windowInsets.getInsets(
|
||||
WindowInsetsCompat.Type.displayCutout() or
|
||||
WindowInsetsCompat.Type.systemBars()
|
||||
)
|
||||
view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = systemBarsInsets.top
|
||||
leftMargin = systemBarsInsets.left
|
||||
bottomMargin = systemBarsInsets.bottom
|
||||
rightMargin = systemBarsInsets.right
|
||||
}
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,17 +127,13 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
/**
|
||||
* Manages the enabling/disabling the left drawer
|
||||
*/
|
||||
protected val enableLeftDrawer = mutableStateOf(true)
|
||||
val enableLeftDrawer = mutableStateOf(true)
|
||||
|
||||
/**
|
||||
* For managing the the showing/hiding the bottomAppBar when scrolling.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
var bottomAppBarScrollBehaviour: BottomAppBarScrollBehavior? = null
|
||||
|
||||
// abstract val drawerContainerLayout: DrawerLayout
|
||||
// abstract val drawerNavView: NavigationView
|
||||
// abstract val readerTableOfContentsDrawer: NavigationView
|
||||
abstract val bookmarksFragmentRoute: String
|
||||
abstract val settingsFragmentRoute: String
|
||||
abstract val historyFragmentRoute: String
|
||||
@ -145,8 +141,6 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
abstract val helpFragmentRoute: String
|
||||
abstract val cachedComponent: CoreActivityComponent
|
||||
abstract val topLevelDestinationsRoute: Set<String>
|
||||
|
||||
// abstract val navHostContainer: FragmentContainerView
|
||||
abstract val mainActivity: AppCompatActivity
|
||||
abstract val appName: String
|
||||
|
||||
@ -200,15 +194,11 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setDialogHostToActivity(alertDialogShower)
|
||||
externalLinkOpener.setAlertDialogShower(alertDialogShower)
|
||||
rateDialogHandler.setAlertDialogShower(alertDialogShower)
|
||||
downloadMonitor.startMonitoringDownload()
|
||||
stopDownloadServiceIfRunning()
|
||||
rateDialogHandler.checkForRateDialog(getIconResId())
|
||||
// navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
// configureActivityBasedOn(destination)
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,59 +292,12 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
override fun onSupportNavigateUp(): Boolean =
|
||||
navController.navigateUp() || super.onSupportNavigateUp()
|
||||
|
||||
open fun setupDrawerToggle(shouldEnableRightDrawer: Boolean = false) {
|
||||
fun enableLeftDrawer() {
|
||||
enableLeftDrawer.value = true
|
||||
// Set the initial contentDescription to the hamburger icon.
|
||||
// This method is called from various locations after modifying the navigationIcon.
|
||||
// For example, we previously changed this icon/contentDescription to the "+" button
|
||||
// when opening the tabSwitcher. After closing the tabSwitcher, we reset the
|
||||
// contentDescription to the default hamburger icon.
|
||||
// Todo we will refactore this when migrating the CoreMainActivity.
|
||||
// toolbar.getToolbarNavigationIcon()?.setToolTipWithContentDescription(
|
||||
// getString(R.string.open_drawer)
|
||||
// )
|
||||
// drawerToggle =
|
||||
// ActionBarDrawerToggle(
|
||||
// this,
|
||||
// drawerContainerLayout,
|
||||
// R.string.open_drawer,
|
||||
// R.string.close_drawer
|
||||
// )
|
||||
// drawerToggle?.let {
|
||||
// drawerContainerLayout.addDrawerListener(it)
|
||||
// it.syncState()
|
||||
// }
|
||||
// drawerContainerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
// if (shouldEnableRightDrawer) {
|
||||
// // Enable the right drawer
|
||||
// drawerContainerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END)
|
||||
// }
|
||||
}
|
||||
|
||||
open fun disableDrawer(disableRightDrawer: Boolean = true) {
|
||||
open fun disableLeftDrawer() {
|
||||
enableLeftDrawer.value = false
|
||||
// drawerToggle?.isDrawerIndicatorEnabled = false
|
||||
// drawerContainerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
// if (disableRightDrawer) {
|
||||
// // Disable the right drawer
|
||||
// drawerContainerLayout.setDrawerLockMode(
|
||||
// DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
|
||||
// GravityCompat.END
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
open fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.menu_support_kiwix -> openSupportKiwixExternalLink()
|
||||
R.id.menu_settings -> openSettings()
|
||||
R.id.menu_help -> openHelpFragment()
|
||||
R.id.menu_notes -> openNotes()
|
||||
R.id.menu_history -> openHistory()
|
||||
R.id.menu_bookmarks_list -> openBookmarks()
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun openHelpFragment() {
|
||||
@ -492,7 +435,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
|
||||
protected fun handleDrawerOnNavigation() {
|
||||
closeNavigationDrawer()
|
||||
disableDrawer()
|
||||
disableLeftDrawer()
|
||||
}
|
||||
|
||||
private fun setMainActivityToCoreApp() {
|
||||
@ -592,7 +535,6 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
protected abstract fun getIconResId(): Int
|
||||
abstract val readerFragmentRoute: String
|
||||
abstract fun createApplicationShortcuts()
|
||||
abstract fun setDialogHostToActivity(alertDialogShower: AlertDialogShower)
|
||||
abstract fun hideBottomAppBar()
|
||||
abstract fun showBottomAppBar()
|
||||
}
|
||||
|
@ -68,8 +68,6 @@ 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.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -193,8 +191,6 @@ abstract class CoreReaderFragment :
|
||||
protected val webViewList = mutableStateListOf<KiwixWebView>()
|
||||
private val webUrlsFlow = MutableStateFlow("")
|
||||
|
||||
var drawerLayout: DrawerLayout? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var sharedPreferenceUtil: SharedPreferenceUtil? = null
|
||||
@ -490,7 +486,6 @@ abstract class CoreReaderFragment :
|
||||
}
|
||||
handleLocaleCheck()
|
||||
initHideBackToTopTimer()
|
||||
loadDrawerViews()
|
||||
addFileReader()
|
||||
activity?.let {
|
||||
compatCallback = CompatFindActionModeCallback(it)
|
||||
@ -561,10 +556,23 @@ abstract class CoreReaderFragment :
|
||||
string.open_drawer
|
||||
}
|
||||
|
||||
private fun navigationIconClick() {
|
||||
/**
|
||||
* Handles clicks on the navigation icon.
|
||||
* - If the tab switcher is active, triggers the home menu action.
|
||||
* - Otherwise, toggles the navigation drawer: opens it if closed, closes it if open.
|
||||
*
|
||||
* Subclasses like CustomReaderFragment can override this method to provide custom behavior,
|
||||
* such as disabling the hamburger icon click when the sidebar is configured to be hidden.
|
||||
*
|
||||
* WARNING: If modifying this method, ensure thorough testing with custom apps
|
||||
* to verify proper functionality.
|
||||
*/
|
||||
open fun navigationIconClick() {
|
||||
if (readerMenuState?.isInTabSwitcher == true) {
|
||||
onHomeMenuClicked()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
val activity = activity as CoreMainActivity
|
||||
if (activity.navigationDrawerIsOpen()) {
|
||||
activity.closeNavigationDrawer()
|
||||
@ -572,7 +580,6 @@ abstract class CoreReaderFragment :
|
||||
activity.openNavigationDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tint color to be applied to the navigation icon.
|
||||
@ -616,13 +623,6 @@ abstract class CoreReaderFragment :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method to be implemented by subclasses for loading drawer-related views.
|
||||
* Subclasses like CustomReaderFragment and KiwixReaderFragment should override this method
|
||||
* to set up specific views for both the left and right drawers, such as custom containers
|
||||
* or navigation views.
|
||||
*/
|
||||
protected abstract fun loadDrawerViews()
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@ -672,10 +672,9 @@ abstract class CoreReaderFragment :
|
||||
|
||||
private fun showTabSwitcher() {
|
||||
(requireActivity() as CoreMainActivity).apply {
|
||||
disableDrawer()
|
||||
disableLeftDrawer()
|
||||
hideBottomAppBar()
|
||||
}
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
readerScreenState.update {
|
||||
copy(
|
||||
shouldShowBottomAppBar = false,
|
||||
@ -716,9 +715,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) {
|
||||
setUpDrawerToggle()
|
||||
enableLeftDrawer()
|
||||
(requireActivity() as CoreMainActivity).showBottomAppBar()
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
readerScreenState.update {
|
||||
copy(
|
||||
shouldShowBottomAppBar = true,
|
||||
@ -730,20 +728,15 @@ abstract class CoreReaderFragment :
|
||||
selectTab(currentWebViewIndex)
|
||||
}
|
||||
|
||||
open fun setUpDrawerToggle() {
|
||||
(requireActivity() as CoreMainActivity).setupDrawerToggle(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lock mode for the drawer, controlling whether the drawer can be opened or closed.
|
||||
* Subclasses like CustomReaderFragment override this method to provide custom
|
||||
* behavior, such as disabling the sidebar when configured not to show it.
|
||||
* Enables the activity's left drawer, allowing it to be opened by clicking the hamburger icon.
|
||||
* Subclasses like CustomReaderFragment can override this method to customize the behavior,
|
||||
* for example, to disable the sidebar when it shouldn't be shown.
|
||||
*
|
||||
* WARNING: If modifying this method, ensure thorough testing with custom apps
|
||||
* to verify proper functionality.
|
||||
* WARNING: If you modify this method, thoroughly test it in custom apps to ensure it works correctly.
|
||||
*/
|
||||
protected open fun setDrawerLockMode(lockMode: Int) {
|
||||
drawerLayout?.setDrawerLockMode(lockMode)
|
||||
open fun enableLeftDrawer() {
|
||||
(requireActivity() as CoreMainActivity).enableLeftDrawer()
|
||||
}
|
||||
|
||||
private fun goBack() {
|
||||
@ -873,8 +866,8 @@ abstract class CoreReaderFragment :
|
||||
return FragmentActivityExtensions.Super.ShouldNotCall
|
||||
}
|
||||
|
||||
drawerLayout?.isDrawerOpen(GravityCompat.END) == true -> {
|
||||
drawerLayout?.closeDrawers()
|
||||
shouldTableOfContentDrawer.value -> {
|
||||
shouldTableOfContentDrawer.update { false }
|
||||
return FragmentActivityExtensions.Super.ShouldNotCall
|
||||
}
|
||||
|
||||
@ -1108,7 +1101,6 @@ abstract class CoreReaderFragment :
|
||||
private fun unBindViewsAndBinding() {
|
||||
compatCallback?.finish()
|
||||
compatCallback = null
|
||||
drawerLayout = null
|
||||
}
|
||||
|
||||
private fun updateTableOfContents() {
|
||||
@ -1484,13 +1476,12 @@ abstract class CoreReaderFragment :
|
||||
shouldShowBottomAppBar = false
|
||||
)
|
||||
}
|
||||
(requireActivity() as CoreMainActivity).disableDrawer(false)
|
||||
(requireActivity() as CoreMainActivity).disableLeftDrawer()
|
||||
} else {
|
||||
readerScreenState.update { copy(fullScreenItem = fullScreenItem.copy(first = false)) }
|
||||
if (!isInFullScreenMode()) {
|
||||
readerScreenState.update { copy(shouldShowBottomAppBar = true) }
|
||||
setUpDrawerToggle()
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
enableLeftDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1498,7 +1489,7 @@ abstract class CoreReaderFragment :
|
||||
@Suppress("MagicNumber")
|
||||
protected open fun openFullScreen() {
|
||||
(requireActivity() as CoreMainActivity).apply {
|
||||
disableDrawer(false)
|
||||
disableLeftDrawer()
|
||||
hideBottomAppBar()
|
||||
}
|
||||
readerScreenState.update {
|
||||
@ -1518,9 +1509,8 @@ abstract class CoreReaderFragment :
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
open fun closeFullScreen() {
|
||||
setUpDrawerToggle()
|
||||
enableLeftDrawer()
|
||||
(requireActivity() as CoreMainActivity).showBottomAppBar()
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
sharedPreferenceUtil?.putPrefFullScreen(false)
|
||||
updateBottomToolbarVisibility()
|
||||
val window = requireActivity().window
|
||||
@ -1982,7 +1972,9 @@ abstract class CoreReaderFragment :
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun contentsDrawerHint() {
|
||||
drawerLayout?.postDelayed({ drawerLayout?.openDrawer(GravityCompat.END) }, 500)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
shouldTableOfContentDrawer.update { true }
|
||||
}, 500)
|
||||
alertDialogShower?.show(KiwixDialog.ContentsDrawerHint)
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ const val CLOSE_ALL_TABS_BUTTON_TESTING_TAG = "closeAllTabsButtonTestingTag"
|
||||
const val TAB_TITLE_TESTING_TAG = "tabTitleTestingTag"
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("ComposableLambdaParameterNaming")
|
||||
@Suppress("ComposableLambdaParameterNaming", "LongMethod")
|
||||
@Composable
|
||||
fun ReaderScreen(
|
||||
state: ReaderScreenState,
|
||||
@ -305,6 +305,7 @@ private fun ReaderContentLayout(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "UnsafeCallOnNullableType")
|
||||
@Composable
|
||||
fun TableDrawerSheet(
|
||||
title: String,
|
||||
@ -372,7 +373,7 @@ fun TableDrawerSheet(
|
||||
"document.getElementById('$targetId')?.scrollIntoView();",
|
||||
null
|
||||
)
|
||||
delay(100)
|
||||
delay(HUNDERED.toLong())
|
||||
webViewScrollState.value = ScrollState(selectedWebView?.scrollY ?: ZERO)
|
||||
scrollToSectionIndex = null
|
||||
showTableOfContentDrawer.update { false }
|
||||
|
@ -20,178 +20,64 @@ package org.kiwix.kiwixmobile.custom.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import org.kiwix.kiwixmobile.core.CoreApp
|
||||
import org.kiwix.kiwixmobile.core.R.drawable
|
||||
import org.kiwix.kiwixmobile.core.R.string
|
||||
import org.kiwix.kiwixmobile.core.extensions.applyEdgeToEdgeInsets
|
||||
import org.kiwix.kiwixmobile.core.extensions.browserIntent
|
||||
import org.kiwix.kiwixmobile.core.extensions.getDialogHostComposeView
|
||||
import org.kiwix.kiwixmobile.core.main.ACTION_NEW_TAB
|
||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||
import org.kiwix.kiwixmobile.core.main.DrawerMenuItem
|
||||
import org.kiwix.kiwixmobile.core.main.NEW_TAB_SHORTCUT_ID
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.DialogHost
|
||||
import org.kiwix.kiwixmobile.custom.BuildConfig
|
||||
import org.kiwix.kiwixmobile.custom.R
|
||||
import org.kiwix.kiwixmobile.custom.customActivityComponent
|
||||
import org.kiwix.kiwixmobile.custom.databinding.ActivityCustomMainBinding
|
||||
|
||||
class CustomMainActivity : CoreMainActivity() {
|
||||
override val navController: NavController by lazy {
|
||||
(
|
||||
supportFragmentManager.findFragmentById(
|
||||
R.id.custom_nav_controller
|
||||
) as NavHostFragment
|
||||
)
|
||||
.navController
|
||||
}
|
||||
override val drawerContainerLayout: DrawerLayout by lazy {
|
||||
activityCustomMainBinding.customDrawerContainer
|
||||
}
|
||||
override val drawerNavView: NavigationView by lazy { activityCustomMainBinding.drawerNavView }
|
||||
override val readerTableOfContentsDrawer: NavigationView by lazy {
|
||||
activityCustomMainBinding.activityMainNavView
|
||||
}
|
||||
|
||||
override val navHostContainer by lazy {
|
||||
activityCustomMainBinding.customNavController
|
||||
}
|
||||
|
||||
override val mainActivity: AppCompatActivity by lazy { this }
|
||||
override val appName: String by lazy { getString(R.string.app_name) }
|
||||
|
||||
override val searchFragmentResId: Int = R.id.searchFragment
|
||||
override val bookmarksFragmentResId: Int = R.id.bookmarksFragment
|
||||
override val settingsFragmentResId: Int = R.id.customSettingsFragment
|
||||
override val readerFragmentResId: Int = R.id.customReaderFragment
|
||||
override val historyFragmentResId: Int = R.id.historyFragment
|
||||
override val notesFragmentResId: Int = R.id.notesFragment
|
||||
override val helpFragmentResId: Int = R.id.helpFragment
|
||||
override val searchFragmentRoute: String = CustomDestination.Search.route
|
||||
override val bookmarksFragmentRoute: String = CustomDestination.Bookmarks.route
|
||||
override val settingsFragmentRoute: String = CustomDestination.Settings.route
|
||||
override val readerFragmentRoute: String = CustomDestination.Reader.route
|
||||
override val historyFragmentRoute: String = CustomDestination.History.route
|
||||
override val notesFragmentRoute: String = CustomDestination.Notes.route
|
||||
override val helpFragmentRoute: String = CustomDestination.Help.route
|
||||
override val cachedComponent by lazy { customActivityComponent }
|
||||
override val topLevelDestinations =
|
||||
setOf(R.id.customReaderFragment)
|
||||
|
||||
lateinit var activityCustomMainBinding: ActivityCustomMainBinding
|
||||
override val topLevelDestinationsRoute = setOf(CustomDestination.Reader.route)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
customActivityComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
activityCustomMainBinding = ActivityCustomMainBinding.inflate(layoutInflater)
|
||||
setContentView(activityCustomMainBinding.root)
|
||||
activityCustomMainBinding.root.applyEdgeToEdgeInsets()
|
||||
setContent {
|
||||
navController = rememberNavController()
|
||||
leftDrawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
uiCoroutineScope = rememberCoroutineScope()
|
||||
CustomMainActivityScreen(
|
||||
navController = navController,
|
||||
leftDrawerContent = leftDrawerMenu,
|
||||
topLevelDestinationsRoute = topLevelDestinationsRoute,
|
||||
leftDrawerState = leftDrawerState,
|
||||
enableLeftDrawer = enableLeftDrawer.value
|
||||
)
|
||||
DialogHost(alertDialogShower)
|
||||
}
|
||||
if (savedInstanceState != null) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
if (destination.id !in topLevelDestinations) {
|
||||
handleDrawerOnNavigation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupDrawerToggle(shouldEnableRightDrawer: Boolean) {
|
||||
super.setupDrawerToggle(shouldEnableRightDrawer)
|
||||
activityCustomMainBinding.drawerNavView.apply {
|
||||
/**
|
||||
* Hide the 'ZimHostFragment' option from the navigation menu
|
||||
* because we are now using fd (FileDescriptor)
|
||||
* to read the zim file from the asset folder. Currently,
|
||||
* 'KiwixServer' is unable to host zim files via fd.
|
||||
* This feature is temporarily removed for custom apps.
|
||||
* We will re-enable it for custom apps once the issue is resolved.
|
||||
* For more info see https://github.com/kiwix/kiwix-android/pull/3516,
|
||||
* https://github.com/kiwix/kiwix-android/issues/4026
|
||||
*/
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_host_books)?.isVisible = false
|
||||
/**
|
||||
* Hide the `HelpFragment` from custom apps.
|
||||
* We have not removed the relevant code for `HelpFragment` from custom apps.
|
||||
* If, in the future, we need to display this for all/some custom apps,
|
||||
* we can either remove the line below or configure it according to the requirements.
|
||||
* For more information, see https://github.com/kiwix/kiwix-android/issues/3584
|
||||
*/
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_help)?.isVisible = false
|
||||
|
||||
/**
|
||||
* If custom app is configured to show the "About app_name app" in navigation
|
||||
* then show it navigation. "app_name" will be replaced with custom app name.
|
||||
*/
|
||||
if (BuildConfig.ABOUT_APP_URL.isNotEmpty()) {
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_about_app)?.apply {
|
||||
title = getString(
|
||||
org.kiwix.kiwixmobile.core.R.string.menu_about_app,
|
||||
getString(R.string.app_name)
|
||||
)
|
||||
isVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If custom app is configured to show the "Support app_name" in navigation
|
||||
* then show it navigation. "app_name" will be replaced with custom app name.
|
||||
*/
|
||||
if (BuildConfig.SUPPORT_URL.isNotEmpty()) {
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_support_kiwix)?.apply {
|
||||
title =
|
||||
getString(
|
||||
org.kiwix.kiwixmobile.core.R.string.menu_support_kiwix_for_custom_apps,
|
||||
getString(R.string.app_name)
|
||||
)
|
||||
isVisible = true
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* If custom app is not configured to show the "Support app_name" in navigation
|
||||
* then hide it from navigation.
|
||||
*/
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_support_kiwix)?.isVisible = false
|
||||
}
|
||||
setNavigationItemSelectedListener { item ->
|
||||
closeNavigationDrawer()
|
||||
onNavigationItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the method to configure the click behavior of the "About the app"
|
||||
* and "Support URL" features. When the "About the app" and "Support URL"
|
||||
* are enabled in a custom app, this function handles those clicks.
|
||||
*/
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
org.kiwix.kiwixmobile.core.R.id.menu_about_app -> {
|
||||
if (BuildConfig.ABOUT_APP_URL.isNotEmpty()) {
|
||||
externalLinkOpener.openExternalUrl(BuildConfig.ABOUT_APP_URL.toUri().browserIntent())
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
org.kiwix.kiwixmobile.core.R.id.menu_support_kiwix -> {
|
||||
if (BuildConfig.SUPPORT_URL.isNotEmpty()) {
|
||||
externalLinkOpener.openExternalUrl(BuildConfig.SUPPORT_URL.toUri().browserIntent(), false)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onNavigationItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconResId() = R.mipmap.ic_launcher
|
||||
|
||||
/**
|
||||
@ -256,7 +142,10 @@ class CustomMainActivity : CoreMainActivity() {
|
||||
true,
|
||||
onClick = {
|
||||
closeNavigationDrawer()
|
||||
externalLinkOpener.openExternalUrl(BuildConfig.ABOUT_APP_URL.toUri().browserIntent(), false)
|
||||
externalLinkOpener.openExternalUrl(
|
||||
BuildConfig.ABOUT_APP_URL.toUri().browserIntent(),
|
||||
false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
@ -281,12 +170,14 @@ class CustomMainActivity : CoreMainActivity() {
|
||||
ShortcutManagerCompat.addDynamicShortcuts(this, listOf(newTabShortcut))
|
||||
}
|
||||
|
||||
override fun setDialogHostToActivity(alertDialogShower: AlertDialogShower) {
|
||||
activityCustomMainBinding.root.addView(getDialogHostComposeView(alertDialogShower), 0)
|
||||
}
|
||||
|
||||
override fun openSearch(searchString: String, isOpenedFromTabView: Boolean, isVoice: Boolean) {
|
||||
// TODO implement when refactoring the custom app UI.
|
||||
navigate(
|
||||
CustomDestination.Search.createRoute(
|
||||
searchString = searchString,
|
||||
isOpenedFromTabView = isOpenedFromTabView,
|
||||
isVoice = isVoice
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun hideBottomAppBar() {
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2025 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.custom.main
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.material3.DrawerState
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import org.kiwix.kiwixmobile.core.main.DrawerMenuGroup
|
||||
import org.kiwix.kiwixmobile.core.main.LeftDrawerMenu
|
||||
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
|
||||
|
||||
@Composable
|
||||
fun CustomMainActivityScreen(
|
||||
navController: NavHostController,
|
||||
leftDrawerContent: List<DrawerMenuGroup>,
|
||||
topLevelDestinationsRoute: Set<String>,
|
||||
leftDrawerState: DrawerState,
|
||||
enableLeftDrawer: Boolean,
|
||||
) {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
KiwixTheme {
|
||||
ModalNavigationDrawer(
|
||||
drawerState = leftDrawerState,
|
||||
drawerContent = {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
LeftDrawerMenu(leftDrawerContent)
|
||||
}
|
||||
},
|
||||
gesturesEnabled = enableLeftDrawer &&
|
||||
currentRoute in topLevelDestinationsRoute &&
|
||||
// Fixing the webView scrolling is lagging when navigation gesture is enabled,
|
||||
// since navigation consumes the swipes event makes webView lagging.
|
||||
// However, on reader screen navigation drawer can be opened by clicking
|
||||
// on the hamburger button.
|
||||
(currentRoute != CustomDestination.Reader.route || leftDrawerState.isOpen)
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
) { paddingValues ->
|
||||
Box(modifier = Modifier.padding(paddingValues)) {
|
||||
CustomNavGraph(
|
||||
navController = navController,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2025 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.custom.main
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.doOnAttach
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentContainerView
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import org.kiwix.kiwixmobile.core.main.BOOKMARK_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.DOWNLOAD_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.FIND_IN_PAGE_SEARCH_STRING
|
||||
import org.kiwix.kiwixmobile.core.main.HELP_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.HISTORY_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.NOTES_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.PAGE_URL_KEY
|
||||
import org.kiwix.kiwixmobile.core.main.READER_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.SEARCH_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.SETTINGS_FRAGMENT
|
||||
import org.kiwix.kiwixmobile.core.main.SHOULD_OPEN_IN_NEW_TAB
|
||||
import org.kiwix.kiwixmobile.core.main.ZIM_FILE_URI_KEY
|
||||
import org.kiwix.kiwixmobile.core.main.reader.SEARCH_ITEM_TITLE_KEY
|
||||
import org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment
|
||||
import org.kiwix.kiwixmobile.core.page.history.HistoryFragment
|
||||
import org.kiwix.kiwixmobile.core.page.notes.NotesFragment
|
||||
import org.kiwix.kiwixmobile.core.search.NAV_ARG_SEARCH_STRING
|
||||
import org.kiwix.kiwixmobile.core.search.SearchFragment
|
||||
import org.kiwix.kiwixmobile.core.utils.EXTRA_IS_WIDGET_VOICE
|
||||
import org.kiwix.kiwixmobile.core.utils.TAG_FROM_TAB_SWITCHER
|
||||
import org.kiwix.kiwixmobile.custom.download.CustomDownloadFragment
|
||||
import org.kiwix.kiwixmobile.custom.help.CustomHelpFragment
|
||||
import org.kiwix.kiwixmobile.custom.settings.CustomSettingsFragment
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun CustomNavGraph(
|
||||
navController: NavHostController,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = CustomDestination.Reader.route,
|
||||
modifier = modifier
|
||||
) {
|
||||
composable(
|
||||
route = CustomDestination.Reader.route,
|
||||
arguments = listOf(
|
||||
navArgument(FIND_IN_PAGE_SEARCH_STRING) {
|
||||
type = NavType.StringType
|
||||
defaultValue = ""
|
||||
},
|
||||
navArgument(PAGE_URL_KEY) {
|
||||
type = NavType.StringType
|
||||
defaultValue = ""
|
||||
},
|
||||
navArgument(SHOULD_OPEN_IN_NEW_TAB) {
|
||||
type = NavType.BoolType
|
||||
defaultValue = false
|
||||
}
|
||||
)
|
||||
) { backStackEntry ->
|
||||
val findInPageSearchString =
|
||||
backStackEntry.arguments?.getString(FIND_IN_PAGE_SEARCH_STRING).orEmpty()
|
||||
val pageUrl = backStackEntry.arguments?.getString(PAGE_URL_KEY).orEmpty()
|
||||
val shouldOpenInNewTab = backStackEntry.arguments?.getBoolean(SHOULD_OPEN_IN_NEW_TAB) ?: false
|
||||
|
||||
FragmentContainer {
|
||||
CustomReaderFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(FIND_IN_PAGE_SEARCH_STRING, findInPageSearchString)
|
||||
putString(PAGE_URL_KEY, pageUrl)
|
||||
putBoolean(SHOULD_OPEN_IN_NEW_TAB, shouldOpenInNewTab)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
composable(CustomDestination.History.route) {
|
||||
FragmentContainer {
|
||||
HistoryFragment()
|
||||
}
|
||||
}
|
||||
composable(CustomDestination.Notes.route) {
|
||||
FragmentContainer {
|
||||
NotesFragment()
|
||||
}
|
||||
}
|
||||
composable(CustomDestination.Bookmarks.route) {
|
||||
FragmentContainer {
|
||||
BookmarksFragment()
|
||||
}
|
||||
}
|
||||
composable(CustomDestination.Help.route) {
|
||||
FragmentContainer {
|
||||
CustomHelpFragment()
|
||||
}
|
||||
}
|
||||
composable(CustomDestination.Settings.route) {
|
||||
FragmentContainer {
|
||||
CustomSettingsFragment()
|
||||
}
|
||||
}
|
||||
composable(CustomDestination.Downloads.route) {
|
||||
FragmentContainer {
|
||||
CustomDownloadFragment()
|
||||
}
|
||||
}
|
||||
composable(
|
||||
route = CustomDestination.Search.route,
|
||||
arguments = listOf(
|
||||
navArgument(NAV_ARG_SEARCH_STRING) {
|
||||
type = NavType.StringType
|
||||
defaultValue = ""
|
||||
},
|
||||
navArgument(TAG_FROM_TAB_SWITCHER) {
|
||||
type = NavType.BoolType
|
||||
defaultValue = false
|
||||
},
|
||||
navArgument(EXTRA_IS_WIDGET_VOICE) {
|
||||
type = NavType.BoolType
|
||||
defaultValue = false
|
||||
}
|
||||
)
|
||||
) { backStackEntry ->
|
||||
val searchString = backStackEntry.arguments?.getString(NAV_ARG_SEARCH_STRING).orEmpty()
|
||||
val isOpenedFromTabSwitcher =
|
||||
backStackEntry.arguments?.getBoolean(TAG_FROM_TAB_SWITCHER) ?: false
|
||||
val isVoice = backStackEntry.arguments?.getBoolean(EXTRA_IS_WIDGET_VOICE) ?: false
|
||||
FragmentContainer {
|
||||
SearchFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(NAV_ARG_SEARCH_STRING, searchString)
|
||||
putBoolean(TAG_FROM_TAB_SWITCHER, isOpenedFromTabSwitcher)
|
||||
putBoolean(EXTRA_IS_WIDGET_VOICE, isVoice)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun FragmentContainer(
|
||||
fragmentProvider: () -> Fragment
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val fragmentManager = remember {
|
||||
(context as AppCompatActivity).supportFragmentManager
|
||||
}
|
||||
val viewId = remember { View.generateViewId() }
|
||||
|
||||
AndroidView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
factory = { ctx ->
|
||||
FragmentContainerView(ctx).apply {
|
||||
id = viewId
|
||||
doOnAttach {
|
||||
fragmentManager.commit {
|
||||
if (fragmentManager.findFragmentById(viewId) == null) {
|
||||
add(viewId, fragmentProvider())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
sealed class CustomDestination(val route: String) {
|
||||
object Reader : CustomDestination(
|
||||
READER_FRAGMENT +
|
||||
"?$ZIM_FILE_URI_KEY={$ZIM_FILE_URI_KEY}" +
|
||||
"&$FIND_IN_PAGE_SEARCH_STRING={$FIND_IN_PAGE_SEARCH_STRING}" +
|
||||
"&$PAGE_URL_KEY={$PAGE_URL_KEY}" +
|
||||
"&$SHOULD_OPEN_IN_NEW_TAB={$SHOULD_OPEN_IN_NEW_TAB}" +
|
||||
"&$SEARCH_ITEM_TITLE_KEY={$SEARCH_ITEM_TITLE_KEY}"
|
||||
) {
|
||||
fun createRoute(
|
||||
zimFileUri: String = "",
|
||||
findInPageSearchString: String = "",
|
||||
pageUrl: String = "",
|
||||
shouldOpenInNewTab: Boolean = false,
|
||||
searchItemTitle: String = ""
|
||||
): String {
|
||||
return READER_FRAGMENT +
|
||||
"?$ZIM_FILE_URI_KEY=${Uri.encode(zimFileUri)}" +
|
||||
"&$FIND_IN_PAGE_SEARCH_STRING=${Uri.encode(findInPageSearchString)}" +
|
||||
"&$PAGE_URL_KEY=${Uri.encode(pageUrl)}" +
|
||||
"&$SHOULD_OPEN_IN_NEW_TAB=$shouldOpenInNewTab" +
|
||||
"&$SEARCH_ITEM_TITLE_KEY=${Uri.encode(searchItemTitle)}"
|
||||
}
|
||||
}
|
||||
|
||||
object History : CustomDestination(HISTORY_FRAGMENT)
|
||||
object Notes : CustomDestination(NOTES_FRAGMENT)
|
||||
object Bookmarks : CustomDestination(BOOKMARK_FRAGMENT)
|
||||
object Help : CustomDestination(HELP_FRAGMENT)
|
||||
object Settings : CustomDestination(SETTINGS_FRAGMENT)
|
||||
object Downloads : CustomDestination(DOWNLOAD_FRAGMENT)
|
||||
object Search : CustomDestination(
|
||||
SEARCH_FRAGMENT +
|
||||
"?$NAV_ARG_SEARCH_STRING={$NAV_ARG_SEARCH_STRING}" +
|
||||
"&$TAG_FROM_TAB_SWITCHER={$TAG_FROM_TAB_SWITCHER}" +
|
||||
"&$EXTRA_IS_WIDGET_VOICE={$EXTRA_IS_WIDGET_VOICE}"
|
||||
) {
|
||||
fun createRoute(
|
||||
searchString: String = "",
|
||||
isOpenedFromTabView: Boolean = false,
|
||||
isVoice: Boolean = false
|
||||
): String {
|
||||
return SEARCH_FRAGMENT +
|
||||
"?$NAV_ARG_SEARCH_STRING=$searchString" +
|
||||
"&$TAG_FROM_TAB_SWITCHER=$isOpenedFromTabView" +
|
||||
"&$EXTRA_IS_WIDGET_VOICE=$isVoice"
|
||||
}
|
||||
}
|
||||
}
|
@ -21,20 +21,19 @@ package org.kiwix.kiwixmobile.custom.main
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.core.net.toUri
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import androidx.navigation.NavOptions
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.core.extensions.browserIntent
|
||||
import org.kiwix.kiwixmobile.core.extensions.isFileExist
|
||||
import org.kiwix.kiwixmobile.core.extensions.update
|
||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||
import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
|
||||
import org.kiwix.kiwixmobile.core.main.reader.ReaderMenuState
|
||||
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin
|
||||
@ -75,10 +74,10 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
}
|
||||
|
||||
if (isAdded) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
enableLeftDrawer()
|
||||
with(activity as AppCompatActivity) {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
setUpDrawerToggle()
|
||||
enableLeftDrawer()
|
||||
}
|
||||
loadPageFromNavigationArguments()
|
||||
if (BuildConfig.DISABLE_EXTERNAL_LINK) {
|
||||
@ -151,6 +150,28 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on the navigation icon in custom apps.
|
||||
* - If the tab switcher is active, triggers the home menu action.
|
||||
* - Otherwise, toggles the navigation drawer: closes it if open; opens it only if the sidebar is enabled.
|
||||
*
|
||||
* This override customizes the default behavior by preventing the drawer from opening
|
||||
* when the sidebar is disabled in the app configuration.
|
||||
*/
|
||||
override fun navigationIconClick() {
|
||||
if (readerMenuState?.isInTabSwitcher == true) {
|
||||
onHomeMenuClicked()
|
||||
return
|
||||
}
|
||||
|
||||
val activity = activity as CoreMainActivity
|
||||
if (activity.navigationDrawerIsOpen()) {
|
||||
activity.closeNavigationDrawer()
|
||||
} else if (!BuildConfig.DISABLE_SIDEBAR) {
|
||||
activity.openNavigationDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadPageFromNavigationArguments() {
|
||||
val args = CustomReaderFragmentArgs.fromBundle(requireArguments())
|
||||
if (args.pageUrl.isNotEmpty()) {
|
||||
@ -188,18 +209,16 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the locking mode for the sidebar in a custom app. If the app is configured not to show the sidebar,
|
||||
* this function disables the sidebar by locking it in the closed position through the parent class.
|
||||
* https://developer.android.com/reference/kotlin/androidx/drawerlayout/widget/DrawerLayout#LOCK_MODE_LOCKED_CLOSED()
|
||||
* Enables or disables the sidebar in a custom app based on the configuration.
|
||||
* If the app is configured to disable the sidebar, this method disables it;
|
||||
* otherwise, it enables the sidebar.
|
||||
*/
|
||||
override fun setDrawerLockMode(lockMode: Int) {
|
||||
super.setDrawerLockMode(
|
||||
override fun enableLeftDrawer() {
|
||||
if (BuildConfig.DISABLE_SIDEBAR) {
|
||||
DrawerLayout.LOCK_MODE_LOCKED_CLOSED
|
||||
(requireActivity() as CoreMainActivity).disableLeftDrawer()
|
||||
} else {
|
||||
lockMode
|
||||
super.enableLeftDrawer()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,8 +287,13 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
},
|
||||
onNoFilesFound = {
|
||||
if (sharedPreferenceUtil?.prefIsTest == false) {
|
||||
// TODO refactor this when migrating the custom app in compose
|
||||
// (requireActivity() as CoreMainActivity).navigate(R.id.customDownloadFragment)
|
||||
val navOptions = NavOptions.Builder()
|
||||
.setPopUpTo(CustomDestination.Reader.route, true)
|
||||
.build()
|
||||
(requireActivity() as CoreMainActivity).navigate(
|
||||
CustomDestination.Downloads.route,
|
||||
navOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -280,13 +304,6 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
if (!it.isFileExist()) it.createNewFile()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_help)?.isVisible = false
|
||||
menu.findItem(org.kiwix.kiwixmobile.core.R.id.menu_host_books)?.isVisible = false
|
||||
}
|
||||
|
||||
private fun enforcedLanguage(): Boolean {
|
||||
val currentLocaleCode = Locale.getDefault().toString()
|
||||
if (BuildConfig.ENFORCED_LANG.isNotEmpty() && BuildConfig.ENFORCED_LANG != currentLocaleCode) {
|
||||
@ -304,17 +321,6 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is overridden to set the IDs of the `drawerLayout` and `tableDrawerRightContainer`
|
||||
* specific to the custom module in the `CoreReaderFragment`. Since we have an app and a custom module,
|
||||
* and `CoreReaderFragment` is a common class for both modules, we set the IDs of the custom module
|
||||
* in the parent class to ensure proper integration.
|
||||
*/
|
||||
override fun loadDrawerViews() {
|
||||
drawerLayout = requireActivity().findViewById(R.id.custom_drawer_container)
|
||||
tableDrawerRightContainer = requireActivity().findViewById(R.id.activity_main_nav_view)
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the method to create the main menu for the app. The custom app can be configured to disable
|
||||
* features like "read aloud" and "tabs," and this method dynamically generates the menu based on the
|
||||
|
Loading…
x
Reference in New Issue
Block a user