Fixed: Clicking on "Plus button" in tab switcher crashes the application.

* Refactored the "FullScreen Video" functionality according to compose UI.
* Refactored the functionality where custom app is configured to show search placeholder in toolbar, and created the search placeholder in compose.
* Refactored the functionality where custom app is configured to show app icon in place of humbuger icon.
This commit is contained in:
MohitMaliFtechiz 2025-06-21 00:46:15 +05:30
parent 234e9169f6
commit 2608694442
10 changed files with 208 additions and 126 deletions

View File

@ -85,7 +85,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
})
}
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar?.let { activity.setupDrawerToggle(it, true) }
activity.setupDrawerToggle(true)
openPageInBookFromNavigationArguments()
}
@ -180,7 +180,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
* @see closeAllTabs
*/
override fun hideTabSwitcher(shouldCloseZimBook: Boolean) {
toolbar?.let { activity?.setupDrawerToggle(it, true) }
activity?.setupDrawerToggle(true)
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
if (webViewList.isEmpty()) {
readerMenuState?.hideTabSwitcher()
@ -302,14 +302,12 @@ class KiwixReaderFragment : CoreReaderFragment() {
@Throws(IllegalArgumentException::class)
override fun createWebView(attrs: AttributeSet?): ToolbarScrollingKiwixWebView? {
// requireNotNull(activityMainRoot)
return ToolbarScrollingKiwixWebView(
requireContext(),
this,
attrs ?: throw IllegalArgumentException("AttributeSet must not be null"),
null,
// requireNotNull(videoView),
null,
requireNotNull(readerScreenState.value.fullScreenItem.second),
CoreWebViewClient(this, requireNotNull(zimReaderContainer)),
// requireNotNull(toolbarContainer),
// requireNotNull(bottomToolbar),

View File

@ -33,7 +33,6 @@ import android.view.MenuItem
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
@ -101,8 +100,8 @@ object ActivityExtensions {
val Activity.cachedComponent: CoreActivityComponent
get() = coreMainActivity.cachedComponent
fun Activity.setupDrawerToggle(toolbar: Toolbar, shouldEnableRightDrawer: Boolean = false) =
coreMainActivity.setupDrawerToggle(toolbar, shouldEnableRightDrawer)
fun Activity.setupDrawerToggle(shouldEnableRightDrawer: Boolean = false) =
coreMainActivity.setupDrawerToggle(shouldEnableRightDrawer)
fun Activity.navigate(fragmentId: Int) {
coreMainActivity.navigate(fragmentId)

View File

@ -27,7 +27,6 @@ import android.view.MenuItem
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.core.view.GravityCompat
@ -59,9 +58,7 @@ import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadMonitorServ
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadMonitorService.Companion.STOP_DOWNLOAD_SERVICE
import org.kiwix.kiwixmobile.core.error.ErrorActivity
import org.kiwix.kiwixmobile.core.extensions.browserIntent
import org.kiwix.kiwixmobile.core.extensions.getToolbarNavigationIcon
import org.kiwix.kiwixmobile.core.extensions.isServiceRunning
import org.kiwix.kiwixmobile.core.extensions.setToolTipWithContentDescription
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.search.NAV_ARG_SEARCH_STRING
@ -271,15 +268,16 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
override fun onSupportNavigateUp(): Boolean =
navController.navigateUp() || super.onSupportNavigateUp()
open fun setupDrawerToggle(toolbar: Toolbar, shouldEnableRightDrawer: Boolean = false) {
open fun setupDrawerToggle(shouldEnableRightDrawer: Boolean = false) {
// 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.
toolbar.getToolbarNavigationIcon()?.setToolTipWithContentDescription(
getString(R.string.open_drawer)
)
// Todo we will refactore this when migrating the CoreMainActivity.
// toolbar.getToolbarNavigationIcon()?.setToolTipWithContentDescription(
// getString(R.string.open_drawer)
// )
drawerToggle =
ActionBarDrawerToggle(
this,

View File

@ -75,7 +75,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.platform.ComposeView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
@ -174,6 +173,7 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
import org.kiwix.kiwixmobile.core.ui.components.rememberBottomNavigationVisibility
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.theme.White
import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth
import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler
import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler.ShowDonationDialogCallback
@ -233,8 +233,6 @@ abstract class CoreReaderFragment :
var tabSwitcherRoot: View? = null
var videoView: ViewGroup? = null
var activityMainRoot: View? = null
@JvmField
@ -273,8 +271,6 @@ abstract class CoreReaderFragment :
protected var actionBar: ActionBar? = null
protected var mainMenu: MainMenu? = null
var toolbarWithSearchPlaceholder: ConstraintLayout? = null
private var tabRecyclerView: RecyclerView? = null
private var isFirstTimeMainPageLoaded = true
private var isFromManageExternalLaunch = false
@ -374,7 +370,10 @@ abstract class CoreReaderFragment :
closeTab(position)
}
},
shouldShowFullScreenMode = false
shouldShowFullScreenMode = false,
searchPlaceHolderItemForCustomApps = false to {
openSearch(searchString = "", isOpenedFromTabView = false, false)
}
)
)
private var readerLifeCycleScope: CoroutineScope? = null
@ -486,12 +485,19 @@ abstract class CoreReaderFragment :
updateTabIcon(size)
}
}
LaunchedEffect(currentWebViewIndex, readerMenuState?.isInTabSwitcher) {
LaunchedEffect(Unit) {
readerScreenState.update {
copy(
bottomNavigationHeight = getBottomNavigationHeight(),
readerScreenTitle = context.getString(R.string.reader),
darkModeViewPainter = darkModeViewPainter,
fullScreenItem = fullScreenItem.first to getVideoView()
)
}
}
LaunchedEffect(currentWebViewIndex, readerMenuState?.isInTabSwitcher) {
readerScreenState.update {
copy(
currentWebViewPosition = currentWebViewIndex,
showTabSwitcher = readerMenuState?.isInTabSwitcher == true
)
@ -504,7 +510,8 @@ abstract class CoreReaderFragment :
NavigationIcon(
iconItem = navigationIcon(),
contentDescription = navigationIconContentDescription(),
onClick = { navigationIconClick() }
onClick = { navigationIconClick() },
iconTint = navigationIconTint()
)
},
listState = lazyListState
@ -609,7 +616,15 @@ abstract class CoreReaderFragment :
viewLifecycleOwner,
Observer(::storeSearchItem)
)
handleClicks()
}
private fun getVideoView() = context?.let {
FrameLayout(it).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}
private fun getBottomNavigationHeight(): Int = getBottomNavigationView()?.measuredHeight ?: ZERO
@ -639,7 +654,25 @@ abstract class CoreReaderFragment :
}
}
private fun navigationIcon() = if (readerMenuState?.isInTabSwitcher == true) {
/**
* Returns the tint color to be applied to the navigation icon.
*
* Subclasses (e.g., CustomReaderFragment) can override this method to provide custom behavior,
* such as setting a colored app icon in place of the default hamburger icon when configured.
*
* By default, this returns [White], which is appropriate for vector icons that rely on tinting.
*/
open fun navigationIconTint() = White
/**
* Provides the navigationIcon based on condition.
* Subclasses like CustomReaderFragment override this method to provide custom
* behavior, such as set the app icon on hamburger when configure to not show the title.
*
* WARNING: If modifying this method, ensure thorough testing with custom apps
* to verify proper functionality.
*/
open fun navigationIcon() = if (readerMenuState?.isInTabSwitcher == true) {
IconItem.Drawable(R.drawable.ic_round_add_white_36dp)
} else {
IconItem.Vector(Icons.Filled.Menu)
@ -652,25 +685,17 @@ abstract class CoreReaderFragment :
private fun prepareViews() {
fragmentReaderBinding?.let { readerBinding ->
videoView = readerBinding.fullscreenVideoContainer
with(readerBinding.root) {
activityMainRoot = findViewById(R.id.activity_main_root)
contentFrame = findViewById(R.id.activity_main_content_frame)
toolbar = findViewById(R.id.toolbar)
tabSwitcherRoot = findViewById(R.id.activity_main_tab_switcher)
toolbarWithSearchPlaceholder = findViewById(R.id.toolbarWithSearchPlaceholder)
tabRecyclerView = findViewById(R.id.tab_switcher_recycler_view)
donationLayout = findViewById(R.id.donation_layout)
}
}
}
private fun handleClicks() {
toolbarWithSearchPlaceholder?.setOnClickListener {
openSearch(searchString = "", isOpenedFromTabView = false, false)
}
}
private fun initTabCallback() {
tabCallback = object : ItemTouchHelper.Callback() {
override fun getMovementFlags(
@ -859,34 +884,30 @@ abstract class CoreReaderFragment :
showBackToTopButton = false
)
}
showSearchPlaceHolderInToolbar(true)
startAnimation(tabSwitcherRoot, R.anim.slide_down)
tabsAdapter?.let { tabsAdapter ->
tabRecyclerView?.let { recyclerView ->
if (tabsAdapter.selected < webViewList.size &&
recyclerView.layoutManager != null
) {
recyclerView.layoutManager?.scrollToPosition(tabsAdapter.selected)
}
}
// Notify the tabs adapter to update the UI when the tab switcher is shown
// This ensures that any changes made to the adapter's data or views are
// reflected correctly.
tabsAdapter.notifyDataSetChanged()
}
readerMenuState?.showTabSwitcherOptions()
}
/**
* Sets the tabs switcher visibility, controlling the visibility of the tab.
* Subclasses, like CustomReaderFragment, override this method to provide custom
* behavior, such as hiding the placeholder in the toolbar when a custom app is configured
* not to show the title. This is necessary because the same toolbar is used for displaying tabs.
* Controls the visibility of the search placeholder in the toolbar.
*
* WARNING: If modifying this method, ensure thorough testing with custom apps
* to verify proper functionality.
* Subclasses (e.g., CustomReaderFragment) can override this method to customize behavior,
* such as showing a search placeholder instead of the title when the app is configured to
* hide the title. This is important because the same toolbar is shared with the tab display.
*
* NOTE: This method sets `showSearchPlaceHolderForCustomApps` to `false` by default.
* Subclasses must explicitly handle the `true` case if needed.
*
* When modifying this method, thoroughly test with custom app configurations to
* ensure correct toolbar behavior.
*/
open fun setTabSwitcherVisibility(visibility: Int) {
tabSwitcherRoot?.visibility = visibility
open fun showSearchPlaceHolderInToolbar(isTabSwitcherShowing: Boolean) {
readerScreenState.update {
copy(
searchPlaceHolderItemForCustomApps = searchPlaceHolderItemForCustomApps.copy(first = false)
)
}
}
/**
@ -926,7 +947,7 @@ 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) {
toolbar?.let(::setUpDrawerToggle)
setUpDrawerToggle()
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
selectTab(currentWebViewIndex)
readerScreenState.update {
@ -935,24 +956,15 @@ abstract class CoreReaderFragment :
pageLoadingItem = false to ZERO,
)
}
showSearchPlaceHolderInToolbar(false)
readerMenuState?.showWebViewOptions(urlIsValid())
// Reset the top margin of web views to 0 to remove any previously set margin
// This ensures that the web views are displayed without any additional top margin for kiwix custom apps.
// setTopMarginToWebViews(0)
}
/**
* Sets the drawer toggle, controlling the toolbar.
* Subclasses like CustomReaderFragment override this method to provide custom
* behavior, such as set the app icon on hamburger when configure to not show the title.
*
* WARNING: If modifying this method, ensure thorough testing with custom apps
* to verify proper functionality.
*/
open fun setUpDrawerToggle(toolbar: Toolbar) {
toolbar.let {
(requireActivity() as CoreMainActivity).setupDrawerToggle(it, true)
}
open fun setUpDrawerToggle() {
(requireActivity() as CoreMainActivity).setupDrawerToggle(true)
}
/**
@ -1335,10 +1347,8 @@ abstract class CoreReaderFragment :
@SuppressLint("ClickableViewAccessibility")
private fun unBindViewsAndBinding() {
activityMainRoot = null
toolbarWithSearchPlaceholder = null
tabRecyclerView = null
tabSwitcherRoot = null
videoView = null
contentFrame = null
compatCallback?.finish()
compatCallback = null
@ -1402,14 +1412,12 @@ abstract class CoreReaderFragment :
@Throws(IllegalArgumentException::class)
protected open fun createWebView(attrs: AttributeSet?): ToolbarScrollingKiwixWebView? {
// requireNotNull(activityMainRoot)
return ToolbarScrollingKiwixWebView(
requireActivity(),
this,
attrs ?: throw IllegalArgumentException("AttributeSet must not be null"),
null,
// requireNotNull(readerScreenState.value.fullScreenItem.second),
null,
requireNotNull(readerScreenState.value.fullScreenItem.second),
CoreWebViewClient(this, requireNotNull(zimReaderContainer)),
// requireNotNull(toolbarContainer),
// requireNotNull(bottomToolbar),
@ -1446,7 +1454,6 @@ abstract class CoreReaderFragment :
if (selectTab) {
selectTab(webViewList.size - 1)
}
tabsAdapter?.notifyDataSetChanged()
}
return webView
}
@ -1739,10 +1746,18 @@ abstract class CoreReaderFragment :
*/
override fun onFullscreenVideoToggled(isFullScreen: Boolean) {
if (isFullScreen) {
readerScreenState.update {
copy(
fullScreenItem = fullScreenItem.copy(first = true),
shouldShowBottomAppBar = false
)
}
(requireActivity() as CoreMainActivity).disableDrawer(false)
} else {
readerScreenState.update { copy(fullScreenItem = fullScreenItem.copy(first = false)) }
if (!isInFullScreenMode()) {
toolbar?.let(::setUpDrawerToggle)
readerScreenState.update { copy(shouldShowBottomAppBar = true) }
setUpDrawerToggle()
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
}
}
@ -1768,7 +1783,7 @@ abstract class CoreReaderFragment :
@Suppress("MagicNumber")
open fun closeFullScreen() {
toolbar?.let(::setUpDrawerToggle)
setUpDrawerToggle()
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
sharedPreferenceUtil?.putPrefFullScreen(false)
readerScreenState.update {
@ -2187,10 +2202,8 @@ abstract class CoreReaderFragment :
protected fun isInFullScreenMode(): Boolean = sharedPreferenceUtil?.prefFullScreen == true
private fun updateBottomToolbarVisibility() {
// TODO refactroe this code once we integrate the tabSwitcher
// tabSwitcherRoot?.visibility != VISIBLE && !isInFullScreenMode()
readerScreenState.update {
copy(shouldShowBottomAppBar = !isInFullScreenMode())
copy(shouldShowBottomAppBar = !showTabSwitcher && !isInFullScreenMode())
}
}
@ -2861,11 +2874,7 @@ abstract class CoreReaderFragment :
try {
isFromManageExternalLaunch = true
currentWebViewIndex = 0
tabsAdapter?.apply {
webViewList.removeAt(0)
notifyItemRemoved(0)
notifyDataSetChanged()
}
webViewList.removeFirstOrNull()
webViewHistoryItemList.forEach { webViewHistoryItem ->
newTab("", shouldLoadUrl = false)?.let {
restoreTabState(it, webViewHistoryItem)

View File

@ -167,6 +167,7 @@ class ReaderMenuState(
fun hideTabSwitcher() {
isInTabSwitcher = false
updateMenuItems()
}
private fun updateMenuItems() {

View File

@ -26,6 +26,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@ -48,6 +49,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.BottomAppBarDefaults
@ -85,6 +87,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@ -119,14 +122,17 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_ALL_TAB_BUTTON_BOTTO
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_TAB_ICON_ANIMATION_TIMEOUT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_TAB_ICON_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FIVE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.ONE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_LAYOUT_HEIGHT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SEARCH_PLACEHOLDER_TEXT_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SEVEN_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TEN_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.THREE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TTS_BUTTONS_CONTROL_ALPHA
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TWO_DP
import org.kiwix.kiwixmobile.core.utils.StyleUtils.fromHtml
@ -176,12 +182,14 @@ private fun ReaderTopBar(
scrollBehavior: TopAppBarScrollBehavior,
navigationIcon: @Composable () -> Unit,
) {
if (!state.shouldShowFullScreenMode) {
if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) {
KiwixAppBar(
title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
navigationIcon = navigationIcon,
actionMenuItems = actionMenuItems,
topAppBarScrollBehavior = scrollBehavior
topAppBarScrollBehavior = scrollBehavior,
searchBar =
searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps)
)
}
}
@ -199,6 +207,7 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
)
state.isNoBookOpenInReader -> NoBookOpenView(state.onOpenLibraryButtonClicked)
state.fullScreenItem.first -> ShowFullScreenView(state)
else -> {
ShowZIMFileContent(state)
@ -214,7 +223,6 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
state.shouldShowBottomAppBar
)
}
ShowFullScreenView(state)
CloseFullScreenImageButton(
state.shouldShowFullScreenMode,
state.onExitFullscreenClick
@ -226,6 +234,53 @@ private fun ReaderContentLayout(state: ReaderScreenState, modifier: Modifier = M
}
}
@Composable
private fun searchPlaceHolderIfActive(
searchPlaceHolderItemForCustomApps: Pair<Boolean, () -> Unit>
): (@Composable () -> Unit)? = if (searchPlaceHolderItemForCustomApps.first) {
{
SearchPlaceholder(
stringResource(R.string.search_label),
searchPlaceHolderItemForCustomApps.second
)
}
} else {
null
}
@Composable
fun SearchPlaceholder(hint: String, searchPlaceHolderClick: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(
color = Color.Transparent,
shape = RoundedCornerShape(THREE_DP)
)
.border(
width = 1.5.dp,
color = colorResource(id = R.color.alabaster_white),
shape = RoundedCornerShape(THREE_DP)
)
.padding(horizontal = FIVE_DP, vertical = FIVE_DP)
.clickable(onClick = searchPlaceHolderClick),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = hint,
color = Color.Gray,
modifier = Modifier.weight(1f),
fontSize = SEARCH_PLACEHOLDER_TEXT_SIZE
)
Spacer(modifier = Modifier.width(TEN_DP))
Icon(
painter = IconItem.Drawable(R.drawable.action_search).toPainter(),
contentDescription = null,
tint = White
)
}
}
@Composable
private fun BoxScope.CloseFullScreenImageButton(
shouldShowFullScreenMode: Boolean,
@ -254,7 +309,14 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
state.selectedWebView?.let { selectedWebView ->
key(selectedWebView) {
AndroidView(
factory = { selectedWebView },
factory = { context ->
// Create a new container and add the WebView to it
FrameLayout(context).apply {
// Ensure the WebView has no parent before adding
(selectedWebView.parent as? ViewGroup)?.removeView(selectedWebView)
addView(selectedWebView)
}
},
modifier = Modifier.fillMaxSize()
)
}
@ -263,8 +325,8 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
@Composable
private fun ShowFullScreenView(state: ReaderScreenState) {
if (state.fullScreenItem.first) {
state.fullScreenItem.second
state.fullScreenItem.second?.let { videoView ->
AndroidView(factory = { videoView })
}
}

View File

@ -18,8 +18,8 @@
package org.kiwix.kiwixmobile.core.main.reader
import android.widget.FrameLayout
import androidx.compose.material3.SnackbarHostState
import androidx.compose.ui.platform.ComposeView
import org.kiwix.kiwixmobile.core.main.DarkModeViewPainter
import org.kiwix.kiwixmobile.core.main.KiwixWebView
import org.kiwix.kiwixmobile.core.ui.models.IconItem.Drawable
@ -60,9 +60,9 @@ data class ReaderScreenState(
*
* A [Pair] containing:
* - [Boolean]: Whether to show/hide full screen mode.
* - [ComposeView]: full screen view.
* - [FrameLayout]: full screen view.
*/
val fullScreenItem: Pair<Boolean, ComposeView?>,
val fullScreenItem: Pair<Boolean, FrameLayout?>,
/**
* Manages the showing of "Full screen mode".
*/
@ -124,7 +124,7 @@ data class ReaderScreenState(
/**
* Handles the clicks of next page button in reader bottom toolbar.
*
* A [Pair] containing:
* A [Triple] containing:
* - [Unit]: Handles the normal click of button(For going to next page).
* - [Unit]: Handles the long click of button(For showing the next pages history).
* - [Boolean]: Handles the button should enable or not.
@ -149,4 +149,8 @@ data class ReaderScreenState(
* Manages the click event on tabs.
*/
val onTabClickListener: TabClickListener,
/**
* Manages the showing/hiding of search placeholder in toolbar for custom apps.
*/
val searchPlaceHolderItemForCustomApps: Pair<Boolean, () -> Unit>
)

View File

@ -192,4 +192,5 @@ object ComposeDimens {
const val CLOSE_TAB_ICON_ANIMATION_TIMEOUT = 1200L
val BACK_TO_TOP_BUTTON_BOTTOM_MARGIN = 80.dp
const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f
val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp
}

View File

@ -22,7 +22,6 @@ import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
@ -102,8 +101,8 @@ class CustomMainActivity : CoreMainActivity() {
}
}
override fun setupDrawerToggle(toolbar: Toolbar, shouldEnableRightDrawer: Boolean) {
super.setupDrawerToggle(toolbar, shouldEnableRightDrawer)
override fun setupDrawerToggle(shouldEnableRightDrawer: Boolean) {
super.setupDrawerToggle(shouldEnableRightDrawer)
activityCustomMainBinding.drawerNavView.apply {
/**
* Hide the 'ZimHostFragment' option from the navigation menu

View File

@ -23,26 +23,27 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
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 androidx.navigation.fragment.findNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.R.dimen
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.browserIntent
import org.kiwix.kiwixmobile.core.extensions.getResizedDrawable
import org.kiwix.kiwixmobile.core.extensions.isFileExist
import org.kiwix.kiwixmobile.core.extensions.update
import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
import org.kiwix.kiwixmobile.core.main.reader.ReaderMenuState
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin
import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.theme.White
import org.kiwix.kiwixmobile.core.utils.LanguageUtils
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getDemoFilePathForCustomApp
@ -81,10 +82,11 @@ class CustomReaderFragment : CoreReaderFragment() {
val toolbarToc =
activity?.findViewById<ImageView>(org.kiwix.kiwixmobile.core.R.id.bottom_toolbar_toc)
toolbarToc?.isEnabled = false
// TODO refactor this with compose UI.
}
with(activity as AppCompatActivity) {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar?.let(::setUpDrawerToggle)
setUpDrawerToggle()
}
loadPageFromNavigationArguments()
if (BuildConfig.DISABLE_EXTERNAL_LINK) {
@ -97,25 +99,33 @@ class CustomReaderFragment : CoreReaderFragment() {
}
/**
* Overrides the method to configure the hamburger icon. When the "setting title" is disabled
* in a custom app, this function set the app logo on hamburger.
* Returns the tint color for the navigation icon.
*
* If the custom app is configured to show the app icon in place of the hamburger icon
* (i.e., [BuildConfig.DISABLE_TITLE] is true), the tint is set to [Color.Unspecified] to preserve
* the original colors of the image.
*
* Otherwise, [White] is used as the default tint, which is suitable for vector icons.
*/
override fun setUpDrawerToggle(toolbar: Toolbar) {
super.setUpDrawerToggle(toolbar)
override fun navigationIconTint(): Color =
if (BuildConfig.DISABLE_TITLE) {
Color.Unspecified
} else {
White
}
override fun navigationIcon(): IconItem = when {
readerMenuState?.isInTabSwitcher == true -> {
IconItem.Drawable(org.kiwix.kiwixmobile.core.R.drawable.ic_round_add_white_36dp)
}
BuildConfig.DISABLE_TITLE -> {
// if the title is disable then set the app logo to hamburger icon,
// see https://github.com/kiwix/kiwix-android/issues/3528#issuecomment-1814905330
val iconSize =
resources.getDimensionPixelSize(dimen.hamburger_icon_size)
requireActivity().getResizedDrawable(R.mipmap.ic_launcher, iconSize, iconSize)
?.let { drawable ->
super.toolbar?.apply {
navigationIcon = drawable
// remove the default margin between hamburger and placeholder
contentInsetStartWithNavigation = 0
}
}
IconItem.MipmapImage(R.mipmap.ic_launcher)
}
else -> IconItem.Vector(Icons.Filled.Menu)
}
/**
@ -123,17 +133,16 @@ class CustomReaderFragment : CoreReaderFragment() {
* When the "setting title" is disabled/enabled in a custom app,
* this function set the visibility of placeholder in toolbar when showing the tabs.
*/
override fun setTabSwitcherVisibility(visibility: Int) {
override fun showSearchPlaceHolderInToolbar(isTabSwitcherShowing: Boolean) {
if (BuildConfig.DISABLE_TITLE) {
// If custom apps are configured to show the placeholder,
// and if tabs are visible, hide the placeholder.
// If tabs are hidden, show the placeholder.
updateToolbarSearchPlaceholderVisibility(if (visibility == VISIBLE) GONE else VISIBLE)
updateToolbarSearchPlaceholderVisibility(!isTabSwitcherShowing)
} else {
// Permanently hide the placeholder if the custom app is not configured to show it.
updateToolbarSearchPlaceholderVisibility(GONE)
updateToolbarSearchPlaceholderVisibility(false)
}
super.setTabSwitcherVisibility(visibility)
}
private fun loadPageFromNavigationArguments() {
@ -346,21 +355,23 @@ class CustomReaderFragment : CoreReaderFragment() {
*/
override fun updateTitle() {
if (BuildConfig.DISABLE_TITLE) {
// Set an empty title for the toolbar because we are handling the toolbar click on behalf of this title.
// Since we have increased the zone for triggering search suggestions (see https://github.com/kiwix/kiwix-android/pull/3566),
// we need to set this title for handling the toolbar click,
// even if it is empty. If we do not set up this title,
// the search screen will open if the user clicks on the toolbar from the tabs screen.
actionBar?.title = " "
updateToolbarSearchPlaceholderVisibility(VISIBLE)
updateToolbarSearchPlaceholderVisibility(true)
} else {
updateToolbarSearchPlaceholderVisibility(GONE)
updateToolbarSearchPlaceholderVisibility(false)
super.updateTitle()
}
}
private fun updateToolbarSearchPlaceholderVisibility(visibility: Int) {
toolbarWithSearchPlaceholder?.visibility = visibility
private fun updateToolbarSearchPlaceholderVisibility(show: Boolean) {
readerScreenState.update {
copy(
searchPlaceHolderItemForCustomApps = searchPlaceHolderItemForCustomApps.copy(first = show)
)
}
}
override fun createNewTab() {