mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
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:
parent
234e9169f6
commit
2608694442
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -167,6 +167,7 @@ class ReaderMenuState(
|
||||
|
||||
fun hideTabSwitcher() {
|
||||
isInTabSwitcher = false
|
||||
updateMenuItems()
|
||||
}
|
||||
|
||||
private fun updateMenuItems() {
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user