diff --git a/app/src/main/java/org/kiwix/kiwixmobile/language/LanguageFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/language/LanguageFragment.kt index ff820a756..7e85e9132 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/language/LanguageFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/language/LanguageFragment.kt @@ -38,6 +38,7 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.base.BaseFragment import org.kiwix.kiwixmobile.core.extensions.viewModel import org.kiwix.kiwixmobile.core.main.CoreMainActivity +import org.kiwix.kiwixmobile.core.page.SEARCH_ICON_TESTING_TAG import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem import org.kiwix.kiwixmobile.core.ui.models.IconItem @@ -46,7 +47,6 @@ import org.kiwix.kiwixmobile.language.viewmodel.Action import org.kiwix.kiwixmobile.language.viewmodel.LanguageViewModel import javax.inject.Inject -const val SEARCH_ICON_TESTING_TAG = "search" const val SAVE_ICON_TESTING_TAG = "saveLanguages" const val SEARCH_FIELD_TESTING_TAG = "searchField" diff --git a/app/src/main/res/navigation/kiwix_nav_graph.xml b/app/src/main/res/navigation/kiwix_nav_graph.xml index 8bf77c014..663efe176 100644 --- a/app/src/main/res/navigation/kiwix_nav_graph.xml +++ b/app/src/main/res/navigation/kiwix_nav_graph.xml @@ -94,13 +94,11 @@ + android:label="BookmarksFragment" /> + android:label="NotesFragment" /> + android:label="HistoryFragment" /> @@ -74,7 +87,26 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv abstract val searchQueryHint: String abstract val pageAdapter: PageAdapter abstract val switchIsChecked: Boolean - abstract val deleteIconTitle: String + abstract val deleteIconTitle: Int + private val pageState: MutableState> = + mutableStateOf( + NotesState( + emptyList(), + true, + "" + ), + policy = referentialEqualityPolicy() + ) + + /** + * Controls the visibility of the "Switch", and its controls. + * + * A [Triple] containing: + * - [String]: The text displayed with switch. + * - [Boolean]: Whether the switch is checked or not. + * - [Boolean]: Whether the switch is enabled or disabled. + */ + private val pageSwitchItem = mutableStateOf(Triple("", true, true)) private var fragmentPageBinding: FragmentPageBinding? = null override val fragmentToolbar: Toolbar? by lazy { fragmentPageBinding?.root?.findViewById(R.id.toolbar) @@ -119,7 +151,7 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv } ) } - menu.findItem(R.id.menu_pages_clear).title = deleteIconTitle // Bug fix #3825 + // menu.findItem(R.id.menu_pages_clear).title = deleteIconTitle // Bug fix #3825 } @Suppress("ReturnCount") @@ -155,16 +187,16 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv } fragmentPageBinding?.noPage?.text = noItemsString - fragmentPageBinding?.pageSwitch?.apply { - text = switchString - isChecked = switchIsChecked - // hide switches for custom apps, see more info here https://github.com/kiwix/kiwix-android/issues/3523 - visibility = if (requireActivity().isCustomApp()) GONE else VISIBLE - } + // fragmentPageBinding?.pageSwitch?.apply { + // text = switchString + // isChecked = switchIsChecked + // // hide switches for custom apps, see more info here https://github.com/kiwix/kiwix-android/issues/3523 + // visibility = if (requireActivity().isCustomApp()) GONE else VISIBLE + // } compositeDisposable.add(pageViewModel.effects.subscribe { it.invokeWith(activity) }) - fragmentPageBinding?.pageSwitch?.setOnCheckedChangeListener { _, isChecked -> - pageViewModel.actions.offer(Action.UserClickedShowAllToggle(isChecked)) - } + // fragmentPageBinding?.pageSwitch?.setOnCheckedChangeListener { _, isChecked -> + // pageViewModel.actions.offer(Action.UserClickedShowAllToggle(isChecked)) + // } pageViewModel.state.observe(viewLifecycleOwner, Observer(::render)) // hides keyboard when scrolled @@ -184,38 +216,85 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv ): View? { return ComposeView(requireContext()).apply { setContent { + val isSearchActive = remember { mutableStateOf(false) } PageScreen( - pageState = pageViewModel.state, - effects = pageViewModel.effects, + pageState = pageState.value, + pageSwitchItem = pageSwitchItem.value, screenTitle = screenTitle, noItemsString = noItemsString, - switchString = switchString, searchQueryHint = searchQueryHint, - switchIsChecked = switchIsChecked, - onSwitchChanged = { isChecked -> - pageViewModel.actions.offer(Action.UserClickedShowAllToggle(isChecked)) + onSwitchChanged = { onSwitchCheckedChanged(it) }, + itemClickListener = this@PageFragment, + navigationIcon = { + NavigationIcon( + iconItem = navigationIconItem(isSearchActive.value), + onClick = navigationIconClick(isSearchActive) + ) }, - onItemClick = { pageViewModel.actions.offer(Action.OnItemClick(it)) }, - onItemLongClick = { pageViewModel.actions.offer(Action.OnItemLongClick(it)) } + actionMenuItems = actionMenuList( + isSearchActive = isSearchActive.value, + onSearchClick = { isSearchActive.value = true }, + onDeleteClick = { pageViewModel.actions.offer(Action.UserClickedDeleteButton) } + ) ) } } } + private fun onSwitchCheckedChanged(isChecked: Boolean): () -> Unit = { + pageSwitchItem.value = pageSwitchItem.value.copy(second = isChecked) + pageViewModel.actions.offer(Action.UserClickedShowAllToggle(isChecked)) + } + + private fun navigationIconItem(isSearchActive: Boolean) = if (isSearchActive) { + IconItem.Drawable(R.drawable.ic_close_white_24dp) + } else { + IconItem.Vector(Icons.AutoMirrored.Filled.ArrowBack) + } + + private fun navigationIconClick(isSearchActive: MutableState): () -> Unit = { + if (isSearchActive.value) { + isSearchActive.value = false + pageViewModel.actions.offer(Action.Exit) + } else { + requireActivity().onBackPressedDispatcher.onBackPressed() + } + } + + private fun actionMenuList( + isSearchActive: Boolean, + onSearchClick: () -> Unit, + onDeleteClick: () -> Unit + ): List { + return listOfNotNull( + when { + !isSearchActive -> ActionMenuItem( + icon = IconItem.Drawable(R.drawable.action_search), + contentDescription = R.string.search_label, + onClick = onSearchClick, + testingTag = SEARCH_ICON_TESTING_TAG + ) + + else -> null + }, + ActionMenuItem( + icon = IconItem.Vector(Icons.Default.Delete), + // Adding content description for #3825. + contentDescription = deleteIconTitle, + onClick = onDeleteClick, + testingTag = DELETE_MENU_ICON_TESTING_TAG + ) + ) + } + override fun onDestroyView() { super.onDestroyView() compositeDisposable.clear() - fragmentPageBinding?.apply { - recyclerView.adapter = null - root.removeAllViews() - } - fragmentPageBinding = null } private fun render(state: PageState<*>) { - pageAdapter.items = state.visiblePageItems - fragmentPageBinding?.pageSwitch?.isEnabled = !state.isInSelectionState - fragmentPageBinding?.noPage?.visibility = if (state.pageItems.isEmpty()) VISIBLE else GONE + pageState.value = state + pageSwitchItem.value = Triple(switchString, switchIsChecked, !state.isInSelectionState) if (state.isInSelectionState) { if (actionMode == null) { actionMode = diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageListItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageListItem.kt index a74c560d2..411aa9b9e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageListItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageListItem.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.text.style.TextOverflow import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.downloader.model.Base64String import org.kiwix.kiwixmobile.core.downloader.model.toPainter +import org.kiwix.kiwixmobile.core.page.adapter.OnItemClickListener import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.PAGE_LIST_ITEM_FAVICON_SIZE @@ -48,13 +49,15 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP @Composable fun PageListItem( page: Page, - onClick: () -> Unit, - onLongClick: () -> Unit + itemClickListener: OnItemClickListener ) { Row( modifier = Modifier .fillMaxWidth() - .combinedClickable(onClick = onClick, onLongClick = onLongClick) + .combinedClickable( + onClick = { itemClickListener.onItemClick(page) }, + onLongClick = { itemClickListener.onItemLongClick(page) } + ) .background(MaterialTheme.colorScheme.surface) .padding( horizontal = SIXTEEN_DP, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageScreen.kt index 8eae0c5f0..b160783b5 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/PageScreen.kt @@ -32,55 +32,51 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.lifecycle.MutableLiveData -import io.reactivex.processors.PublishProcessor -import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp import org.kiwix.kiwixmobile.core.main.CoreMainActivity +import org.kiwix.kiwixmobile.core.page.adapter.OnItemClickListener import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.DateItem import org.kiwix.kiwixmobile.core.page.viewmodel.PageState import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar +import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP -@Suppress("LongParameterList", "IgnoredReturnValue", "UnusedParameter") +@Suppress( + "LongParameterList", + "IgnoredReturnValue", + "UnusedParameter", + "ComposableLambdaParameterNaming" +) @OptIn(ExperimentalMaterial3Api::class) @Composable fun PageScreen( - pageState: MutableLiveData>, - effects: PublishProcessor>, + pageState: PageState, + pageSwitchItem: Triple, screenTitle: Int, noItemsString: String, - switchString: String, searchQueryHint: String, - switchIsChecked: Boolean, onSwitchChanged: (Boolean) -> Unit, - onItemClick: (Page) -> Unit, - onItemLongClick: (Page) -> Unit + itemClickListener: OnItemClickListener, + actionMenuItems: List, + navigationIcon: @Composable () -> Unit, ) { val context = LocalActivity.current as CoreMainActivity - val state by pageState.observeAsState() - - LaunchedEffect(Unit) { - effects.subscribe { it.invokeWith(context) } - } - KiwixTheme { Scaffold( topBar = { Column { KiwixAppBar( titleId = screenTitle, - navigationIcon = {}, + navigationIcon = navigationIcon, + actionMenuItems = actionMenuItems ) + // hide switches for custom apps, see more info here https://github.com/kiwix/kiwix-android/issues/3523 if (!context.isCustomApp()) { Row( modifier = Modifier @@ -88,14 +84,18 @@ fun PageScreen( .padding(horizontal = SIXTEEN_DP, vertical = EIGHT_DP), verticalAlignment = Alignment.CenterVertically ) { - Text(switchString, modifier = Modifier.weight(1f)) - Switch(checked = switchIsChecked, onCheckedChange = onSwitchChanged) + Text(pageSwitchItem.first, modifier = Modifier.weight(1f)) + Switch( + checked = pageSwitchItem.second, + onCheckedChange = onSwitchChanged, + enabled = pageSwitchItem.third + ) } } } } ) { padding -> - val items = state?.visiblePageItems.orEmpty() + val items = pageState.pageItems Box(modifier = Modifier.padding(padding)) { if (items.isEmpty()) { Text( @@ -105,13 +105,12 @@ fun PageScreen( ) } else { LazyColumn { - items(items) { item -> + items(pageState.visiblePageItems) { item -> when (item) { is Page -> { PageListItem( page = item, - onClick = { onItemClick(item) }, - onLongClick = { onItemLongClick(item) } + itemClickListener = itemClickListener ) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/BookmarksFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/BookmarksFragment.kt index 2ba944d47..9e57f0f01 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/BookmarksFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/BookmarksFragment.kt @@ -19,8 +19,8 @@ class BookmarksFragment : PageFragment() { override val screenTitle: Int by lazy { R.string.bookmarks } override val noItemsString: String by lazy { getString(R.string.no_bookmarks) } override val switchString: String by lazy { getString(R.string.bookmarks_from_current_book) } - override val deleteIconTitle: String by lazy { - getString(R.string.pref_clear_all_bookmarks_title) + override val deleteIconTitle: Int by lazy { + R.string.pref_clear_all_bookmarks_title } override val switchIsChecked: Boolean by lazy { sharedPreferenceUtil.showBookmarksAllBooks } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/HistoryFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/HistoryFragment.kt index 85d2d822e..891c60758 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/HistoryFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/HistoryFragment.kt @@ -22,8 +22,8 @@ class HistoryFragment : PageFragment() { override val noItemsString: String by lazy { getString(R.string.no_history) } override val switchString: String by lazy { getString(R.string.history_from_current_book) } override val screenTitle: Int by lazy { R.string.history } - override val deleteIconTitle: String by lazy { - getString(R.string.pref_clear_all_history_title) + override val deleteIconTitle: Int by lazy { + R.string.pref_clear_all_history_title } override val switchIsChecked: Boolean by lazy { sharedPreferenceUtil.showHistoryAllBooks } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/NotesFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/NotesFragment.kt index 4c6a4ae44..aaa610ff3 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/NotesFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/NotesFragment.kt @@ -38,8 +38,8 @@ class NotesFragment : PageFragment() { override val noItemsString: String by lazy { getString(R.string.no_notes) } override val switchString: String by lazy { getString(R.string.notes_from_all_books) } - override val deleteIconTitle: String by lazy { - getString(R.string.pref_clear_notes) + override val deleteIconTitle: Int by lazy { + R.string.pref_clear_notes } override val switchIsChecked: Boolean by lazy { sharedPreferenceUtil.showNotesAllBooks }