Migrated the NavigationHistoryDialog to Jetpack Compose.

* Created the `NavigationHistoryDialogScreen` using our app theme.
* Refactored the code according to comose UI.
* Refactored the UI test cases according to compose UI.
* Removed the unused code from project.
This commit is contained in:
MohitMaliFtechiz 2025-04-29 18:42:42 +05:30
parent 5eabf29ed9
commit 9de216beec
18 changed files with 235 additions and 447 deletions

View File

@ -18,6 +18,10 @@
package org.kiwix.kiwixmobile.page.history
import android.util.Log
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.longClick
@ -30,9 +34,11 @@ import applyWithViewHierarchyPrinting
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import junit.framework.AssertionFailedError
import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.Findable.StringId.TextId
import org.kiwix.kiwixmobile.Findable.ViewId
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.page.DELETE_MENU_ICON_TESTING_TAG
import org.kiwix.kiwixmobile.core.ui.components.TOOLBAR_TITLE_TESTING_TAG
import org.kiwix.kiwixmobile.core.utils.dialog.ALERT_DIALOG_TITLE_TEXT_TESTING_TAG
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
@ -54,7 +60,7 @@ class NavigationHistoryRobot : BaseRobot() {
pauseForBetterTestPerformance()
isVisible(ViewId(R.id.tab_switcher_close_all_tabs))
pressBack()
} catch (ignore: Exception) {
} catch (_: Exception) {
Log.i(
"NAVIGATION_HISTORY_TEST",
"Couldn't found tab switcher, probably it is not visible"
@ -95,14 +101,18 @@ class NavigationHistoryRobot : BaseRobot() {
longClickOn(ViewId(R.id.bottom_toolbar_arrow_forward))
}
fun assertBackwardNavigationHistoryDialogDisplayed() {
fun assertBackwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
try {
isVisible(TextId(R.string.backward_history))
} catch (ignore: AssertionFailedError) {
composeTestRule.apply {
waitForIdle()
onNodeWithTag(TOOLBAR_TITLE_TESTING_TAG)
.assertTextEquals(context.getString(R.string.backward_history))
}
} catch (_: AssertionError) {
pauseForBetterTestPerformance()
if (retryCountForBackwardNavigationHistory > 0) {
retryCountForBackwardNavigationHistory--
assertBackwardNavigationHistoryDialogDisplayed()
assertBackwardNavigationHistoryDialogDisplayed(composeTestRule)
}
}
}
@ -112,31 +122,46 @@ class NavigationHistoryRobot : BaseRobot() {
clickOn(ViewId(R.id.bottom_toolbar_arrow_back))
}
fun assertForwardNavigationHistoryDialogDisplayed() {
fun assertForwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
try {
isVisible(TextId(R.string.forward_history))
} catch (ignore: AssertionFailedError) {
composeTestRule.apply {
waitForIdle()
onNodeWithTag(TOOLBAR_TITLE_TESTING_TAG)
.assertTextEquals(context.getString(R.string.forward_history))
}
} catch (_: AssertionError) {
pauseForBetterTestPerformance()
if (retryCountForForwardNavigationHistory > 0) {
retryCountForForwardNavigationHistory--
assertForwardNavigationHistoryDialogDisplayed()
assertForwardNavigationHistoryDialogDisplayed(composeTestRule)
}
}
}
fun clickOnDeleteHistory() {
fun clickOnDeleteHistory(composeTestRule: ComposeContentTestRule) {
pauseForBetterTestPerformance()
clickOn(ViewId(R.id.menu_pages_clear))
testFlakyView({
composeTestRule.apply {
waitForIdle()
onNodeWithTag(DELETE_MENU_ICON_TESTING_TAG).performClick()
}
})
}
fun assertDeleteDialogDisplayed() {
fun assertDeleteDialogDisplayed(composeTestRule: ComposeContentTestRule) {
try {
isVisible(TextId(R.string.clear_all_history_dialog_title))
composeTestRule.apply {
waitForIdle()
onNodeWithTag(ALERT_DIALOG_TITLE_TEXT_TESTING_TAG)
.assertTextEquals(context.getString(R.string.clear_all_history_dialog_title))
}
} catch (ignore: AssertionFailedError) {
pauseForBetterTestPerformance()
if (retryCountForClearNavigationHistory > 0) {
retryCountForClearNavigationHistory--
assertDeleteDialogDisplayed()
assertDeleteDialogDisplayed(composeTestRule)
} else {
throw RuntimeException("Could not found the NavigationHistoryDeleteDialog. Original exception = $ignore")
}
}
}

View File

@ -19,6 +19,7 @@
package org.kiwix.kiwixmobile.page.history
import android.os.Build
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
@ -44,6 +45,7 @@ import org.kiwix.kiwixmobile.BaseActivityTest
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
import org.kiwix.kiwixmobile.core.utils.TestingUtils.RETRY_RULE_ORDER
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections
@ -61,6 +63,9 @@ class NavigationHistoryTest : BaseActivityTest() {
@JvmField
val retryRule = RetryRule()
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
val composeTestRule = createComposeRule()
private lateinit var kiwixMainActivity: KiwixMainActivity
@Before
@ -146,13 +151,13 @@ class NavigationHistoryTest : BaseActivityTest() {
checkZimFileLoadedSuccessful(R.id.readerFragment)
clickOnAndroidArticle()
longClickOnBackwardButton()
assertBackwardNavigationHistoryDialogDisplayed()
assertBackwardNavigationHistoryDialogDisplayed(composeTestRule)
pressBack()
clickOnBackwardButton()
longClickOnForwardButton()
assertForwardNavigationHistoryDialogDisplayed()
clickOnDeleteHistory()
assertDeleteDialogDisplayed()
assertForwardNavigationHistoryDialogDisplayed(composeTestRule)
clickOnDeleteHistory(composeTestRule)
assertDeleteDialogDisplayed(composeTestRule)
pressBack()
pressBack()
}

View File

@ -1005,9 +1005,9 @@ abstract class CoreReaderFragment :
**/
val dialogFragment = NavigationHistoryDialog(
if (isForwardHistory) {
getString(R.string.forward_history)
R.string.forward_history
} else {
getString(R.string.backward_history)
R.string.backward_history
},
navigationHistoryList,
this

View File

@ -42,7 +42,6 @@ import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
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.adapter.PageAdapter
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.NotesState
import org.kiwix.kiwixmobile.core.page.viewmodel.Action
import org.kiwix.kiwixmobile.core.page.viewmodel.PageState
@ -72,7 +71,6 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
abstract val noItemsString: String
abstract val switchString: String
abstract val searchQueryHint: String
abstract val pageAdapter: PageAdapter
abstract val switchIsChecked: Boolean
abstract val deleteIconTitle: Int
private val pageState: MutableState<PageState<*>> =

View File

@ -43,6 +43,7 @@ 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.ui.components.ONE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.PAGE_LIST_ITEM_FAVICON_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
@ -87,7 +88,7 @@ fun PageListItem(
text = page.title,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f),
maxLines = 1,
maxLines = ONE,
overflow = TextOverflow.Ellipsis
)
}

View File

@ -1,52 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.core.page.adapter
import android.view.ViewGroup
import org.kiwix.kiwixmobile.core.base.adapter.AbsDelegateAdapter
import org.kiwix.kiwixmobile.core.databinding.HeaderDateBinding
import org.kiwix.kiwixmobile.core.databinding.ItemBookmarkHistoryBinding
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.viewBinding
import org.kiwix.kiwixmobile.core.page.adapter.PageRelatedListItemViewHolder.DateItemViewHolder
import org.kiwix.kiwixmobile.core.page.adapter.PageRelatedListItemViewHolder.PageListItemViewHolder
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.DateItem
sealed class PageDelegate<I : PageRelated, out VH : PageRelatedListItemViewHolder<I>> :
AbsDelegateAdapter<I, PageRelated, VH> {
class PageItemDelegate(
private val itemClickListener: OnItemClickListener
) : PageDelegate<Page, PageListItemViewHolder>() {
override val itemClass = Page::class.java
override fun createViewHolder(parent: ViewGroup) =
PageListItemViewHolder(
parent.viewBinding(ItemBookmarkHistoryBinding::inflate, false),
itemClickListener
)
}
class HistoryDateDelegate : PageDelegate<DateItem, DateItemViewHolder>() {
override val itemClass = DateItem::class.java
override fun createViewHolder(parent: ViewGroup): DateItemViewHolder =
DateItemViewHolder(
parent.viewBinding(HeaderDateBinding::inflate, false)
)
}
}

View File

@ -1,73 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2020 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.core.page.adapter
import android.view.View
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.core.databinding.HeaderDateBinding
import org.kiwix.kiwixmobile.core.databinding.ItemBookmarkHistoryBinding
import org.kiwix.kiwixmobile.core.downloader.model.Base64String
import org.kiwix.kiwixmobile.core.extensions.setBitmap
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.DateItem
import org.threeten.bp.LocalDate
import org.threeten.bp.format.DateTimeFormatter
import org.threeten.bp.format.DateTimeParseException
sealed class PageRelatedListItemViewHolder<in T : PageRelated>(containerView: View) :
BaseViewHolder<T>(containerView) {
class PageListItemViewHolder(
private val itemBookmarkHistoryBinding: ItemBookmarkHistoryBinding,
private val itemClickListener: OnItemClickListener
) : PageRelatedListItemViewHolder<Page>(itemBookmarkHistoryBinding.root) {
override fun bind(item: Page) {
itemBookmarkHistoryBinding.title.text = item.title
if (item.isSelected) {
itemBookmarkHistoryBinding.favicon.setImageDrawableCompat(
R.drawable.ic_check_circle_blue_24dp
)
} else {
itemBookmarkHistoryBinding.favicon.setBitmap(Base64String(item.favicon))
}
itemView.setOnClickListener { itemClickListener.onItemClick(item) }
itemView.setOnLongClickListener { itemClickListener.onItemLongClick(item) }
}
}
class DateItemViewHolder(private val headerDateBinding: HeaderDateBinding) :
PageRelatedListItemViewHolder<DateItem>(headerDateBinding.root) {
override fun bind(item: DateItem) {
val todaysDate = LocalDate.now()
val yesterdayDate = todaysDate.minusDays(1)
val givenDate =
try {
LocalDate.parse(item.dateString, DateTimeFormatter.ofPattern("d MMM yyyy"))
} catch (ignore: DateTimeParseException) {
null
}
when (givenDate) {
todaysDate -> headerDateBinding.headerDate.setText(R.string.time_today)
yesterdayDate -> headerDateBinding.headerDate.setText(R.string.time_yesterday)
else -> headerDateBinding.headerDate.text = item.dateString
}
}
}
}

View File

@ -5,17 +5,11 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.page.PageFragment
import org.kiwix.kiwixmobile.core.page.adapter.PageAdapter
import org.kiwix.kiwixmobile.core.page.adapter.PageDelegate.PageItemDelegate
import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.BookmarkViewModel
class BookmarksFragment : PageFragment() {
override val pageViewModel by lazy { viewModel<BookmarkViewModel>(viewModelFactory) }
override val pageAdapter: PageAdapter by lazy {
PageAdapter(PageItemDelegate(this))
}
override val screenTitle: Int = 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) }

View File

@ -5,20 +5,11 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.page.PageFragment
import org.kiwix.kiwixmobile.core.page.adapter.PageAdapter
import org.kiwix.kiwixmobile.core.page.adapter.PageDelegate.HistoryDateDelegate
import org.kiwix.kiwixmobile.core.page.adapter.PageDelegate.PageItemDelegate
import org.kiwix.kiwixmobile.core.page.history.viewmodel.HistoryViewModel
const val USER_CLEARED_HISTORY: String = "user_cleared_history"
class HistoryFragment : PageFragment() {
override val pageViewModel by lazy { viewModel<HistoryViewModel>(viewModelFactory) }
override val pageAdapter by lazy {
PageAdapter(PageItemDelegate(this), HistoryDateDelegate())
}
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 = R.string.history

View File

@ -24,39 +24,31 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.Toolbar
import androidx.annotation.StringRes
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.databinding.DialogNavigationHistoryBinding
import org.kiwix.kiwixmobile.core.extensions.getDialogHostComposeView
import org.kiwix.kiwixmobile.core.main.DISABLE_ICON_ITEM_ALPHA
import org.kiwix.kiwixmobile.core.main.ENABLE_ICON_ITEM_ALPHA
import org.kiwix.kiwixmobile.core.page.history.adapter.NavigationHistoryAdapter
import org.kiwix.kiwixmobile.core.page.history.adapter.NavigationHistoryDelegate.NavigationDelegate
import org.kiwix.kiwixmobile.core.page.DELETE_MENU_ICON_TESTING_TAG
import org.kiwix.kiwixmobile.core.page.history.adapter.NavigationHistoryListItem
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.DialogHost
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import javax.inject.Inject
class NavigationHistoryDialog(
private val toolbarTitle: String,
@StringRes private val titleId: Int,
private val navigationHistoryList: MutableList<NavigationHistoryListItem>,
private val navigationHistoryClickListener: NavigationHistoryClickListener
) : DialogFragment() {
private var dialogNavigationHistoryBinding: DialogNavigationHistoryBinding? = null
private var navigationHistoryAdapter: NavigationHistoryAdapter? = null
private var composeView: ComposeView? = null
@Inject
lateinit var alertDialogShower: AlertDialogShower
private val toolbar by lazy {
dialogNavigationHistoryBinding?.root?.findViewById<Toolbar>(R.id.toolbar)
}
private val deleteItem by lazy { toolbar?.menu?.findItem(R.id.menu_pages_clear) }
override fun onStart() {
super.onStart()
dialog?.let {
@ -79,46 +71,34 @@ class NavigationHistoryDialog(
savedInstanceState: Bundle?
): View? {
super.onCreateView(inflater, container, savedInstanceState)
dialogNavigationHistoryBinding =
DialogNavigationHistoryBinding.inflate(inflater, container, false)
return dialogNavigationHistoryBinding?.root
return ComposeView(requireContext()).apply {
setContent {
NavigationHistoryDialogScreen(
titleId,
navigationHistoryList,
listOf(
ActionMenuItem(
IconItem.Drawable(R.drawable.ic_delete_white_24dp),
R.string.pref_clear_all_history_title,
{ showConfirmClearHistoryDialog() },
isEnabled = navigationHistoryList.isNotEmpty(),
testingTag = DELETE_MENU_ICON_TESTING_TAG
)
),
{ onItemClick(it) },
{
NavigationIcon(
iconItem = IconItem.Drawable(R.drawable.ic_close_white_24dp),
onClick = {
dismissNavigationHistoryDialog()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navigationHistoryAdapter =
NavigationHistoryAdapter(NavigationDelegate(::onItemClick)).apply {
items = navigationHistoryList
)
}
toolbar?.apply {
title = toolbarTitle
setNavigationIcon(R.drawable.ic_close_white_24dp)
setNavigationOnClickListener { dismissNavigationHistoryDialog() }
inflateMenu(R.menu.menu_page)
this.menu?.findItem(R.id.menu_page_search)?.isVisible = false
)
DialogHost(alertDialogShower)
}
dialogNavigationHistoryBinding?.apply {
root.addView(requireContext().getDialogHostComposeView(alertDialogShower))
if (navigationHistoryList.isEmpty()) {
deleteItem?.isEnabled = false
deleteItem?.icon?.alpha = DISABLE_ICON_ITEM_ALPHA
searchNoResults.visibility = View.VISIBLE
navigationHistoryRecyclerView.visibility = View.GONE
} else {
deleteItem?.isEnabled = true
deleteItem?.icon?.alpha = ENABLE_ICON_ITEM_ALPHA
searchNoResults.visibility = View.GONE
navigationHistoryRecyclerView.visibility = View.VISIBLE
}
navigationHistoryRecyclerView.run {
adapter = navigationHistoryAdapter
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
setHasFixedSize(true)
}
}
deleteItem?.setOnMenuItemClickListener {
showConfirmClearHistoryDialog()
true
}.also {
composeView = it
}
}
@ -151,8 +131,8 @@ class NavigationHistoryDialog(
override fun onDestroyView() {
super.onDestroyView()
navigationHistoryAdapter = null
dialogNavigationHistoryBinding = null
composeView?.disposeComposition()
composeView = null
onBackPressedCallBack.remove()
}

View File

@ -0,0 +1,144 @@
/*
* 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.core.page.history
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.text.style.TextOverflow
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.page.NO_ITEMS_TEXT_TESTING_TAG
import org.kiwix.kiwixmobile.core.page.history.adapter.NavigationHistoryListItem
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
import org.kiwix.kiwixmobile.core.ui.components.ONE
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.models.toPainter
import org.kiwix.kiwixmobile.core.ui.theme.KiwixDialogTheme
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.PAGE_LIST_ITEM_FAVICON_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
@Suppress("ComposableLambdaParameterNaming")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NavigationHistoryDialogScreen(
@StringRes titleId: Int,
navigationHistoryList: MutableList<NavigationHistoryListItem>,
actionMenuItems: List<ActionMenuItem>,
onNavigationItemClick: ((NavigationHistoryListItem) -> Unit),
navigationIcon: @Composable () -> Unit
) {
KiwixDialogTheme {
Scaffold(
topBar = { KiwixAppBar(titleId, navigationIcon, actionMenuItems) }
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
.imePadding()
.padding(paddingValues),
) {
if (navigationHistoryList.isEmpty()) {
Text(
text = stringResource(R.string.no_history),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier
.align(Alignment.Center)
.semantics { testTag = NO_ITEMS_TEXT_TESTING_TAG }
)
} else {
NavigationHistoryList(navigationHistoryList, onNavigationItemClick)
}
}
}
}
}
@Composable
fun NavigationHistoryList(
navigationHistoryList: MutableList<NavigationHistoryListItem>,
onNavigationItemClick: (NavigationHistoryListItem) -> Unit
) {
LazyColumn {
items(navigationHistoryList) {
NavigationHistoryItem(it, onNavigationItemClick)
}
}
}
@Composable
private fun NavigationHistoryItem(
item: NavigationHistoryListItem,
onNavigationItemClick: (NavigationHistoryListItem) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(
onClick = { onNavigationItemClick(item) },
)
.padding(
horizontal = SIXTEEN_DP,
vertical = EIGHT_DP
),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = IconItem.MipmapImage(R.mipmap.ic_launcher_round).toPainter(),
contentDescription = stringResource(R.string.fav_icon),
modifier = Modifier
.size(PAGE_LIST_ITEM_FAVICON_SIZE)
)
Spacer(modifier = Modifier.width(SIXTEEN_DP))
Text(
text = item.title,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f),
maxLines = ONE,
overflow = TextOverflow.Ellipsis
)
}
}

View File

@ -1,28 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2023 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.core.page.history.adapter
import org.kiwix.kiwixmobile.core.base.adapter.AdapterDelegate
import org.kiwix.kiwixmobile.core.base.adapter.BaseDelegateAdapter
class NavigationHistoryAdapter(
vararg delegates: AdapterDelegate<NavigationHistoryListItem>
) : BaseDelegateAdapter<NavigationHistoryListItem>(*delegates) {
override fun getIdFor(item: NavigationHistoryListItem) = item.title.hashCode().toLong()
}

View File

@ -1,43 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2023 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.core.page.history.adapter
import android.view.ViewGroup
import org.kiwix.kiwixmobile.core.base.adapter.AbsDelegateAdapter
import org.kiwix.kiwixmobile.core.databinding.ItemBookmarkHistoryBinding
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.viewBinding
sealed class NavigationHistoryDelegate<
I : NavigationHistoryListItem,
out VH : NavigationHistoryViewHolder<I>
> : AbsDelegateAdapter<I, NavigationHistoryListItem, VH> {
class NavigationDelegate(private val onClickListener: ((NavigationHistoryListItem) -> Unit)) :
NavigationHistoryDelegate<
NavigationHistoryListItem,
NavigationHistoryViewHolder.HistoryViewHolder
>() {
override val itemClass = NavigationHistoryListItem::class.java
override fun createViewHolder(parent: ViewGroup) =
NavigationHistoryViewHolder.HistoryViewHolder(
parent.viewBinding(ItemBookmarkHistoryBinding::inflate, false),
onClickListener
)
}
}

View File

@ -1,41 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2023 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.core.page.history.adapter
import android.view.View
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.core.databinding.ItemBookmarkHistoryBinding
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
sealed class NavigationHistoryViewHolder<in T : NavigationHistoryListItem>(containerView: View) :
BaseViewHolder<T>(containerView) {
class HistoryViewHolder(
private val itemBookmarkHistoryBinding: ItemBookmarkHistoryBinding,
private val onClickListener: ((NavigationHistoryListItem) -> Unit)
) : NavigationHistoryViewHolder<NavigationHistoryListItem>(itemBookmarkHistoryBinding.root) {
override fun bind(item: NavigationHistoryListItem) {
containerView.setOnClickListener { onClickListener(item) }
itemBookmarkHistoryBinding.apply {
title.text = item.title
favicon.setImageDrawableCompat(R.mipmap.ic_launcher_round)
}
}
}
}

View File

@ -23,8 +23,6 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.page.PageFragment
import org.kiwix.kiwixmobile.core.page.adapter.PageAdapter
import org.kiwix.kiwixmobile.core.page.adapter.PageDelegate
import org.kiwix.kiwixmobile.core.page.notes.viewmodel.NotesViewModel
class NotesFragment : PageFragment() {
@ -32,10 +30,6 @@ class NotesFragment : PageFragment() {
override val screenTitle: Int = R.string.pref_notes
override val pageAdapter: PageAdapter by lazy {
PageAdapter(PageDelegate.PageItemDelegate(this))
}
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: Int by lazy {

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Kiwix Android
~ Copyright (c) 2023 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/>.
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<include layout="@layout/layout_standard_app_bar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/navigationHistoryRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/app_bar"
tools:listitem="@layout/item_bookmark_history" />
<TextView
android:id="@+id/searchNoResults"
style="@style/no_content"
android:text="@string/no_history"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/header_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin"
android:alpha="0.54"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="English" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
tools:ignore="Overdraw">
<ImageView
android:id="@+id/favicon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginBottom="8dp"
android:contentDescription="@string/fav_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/title"
style="@style/list_item_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:alpha="0.87"
android:paddingTop="8dp"
android:paddingBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/favicon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Wikipedia - Main Page" />
</androidx.constraintlayout.widget.ConstraintLayout>