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:
MohitMaliFtechiz 2025-07-30 13:38:58 +05:30
parent 8afaa8be52
commit 4d97eeebc8
14 changed files with 474 additions and 385 deletions

View File

@ -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(

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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,

View File

@ -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)
}

View File

@ -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
}
}
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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 }

View File

@ -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() {

View File

@ -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()
)
}
}
}
}
}

View File

@ -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"
}
}
}

View File

@ -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