Closes #24276: updating pagination library, changing history related classes

upstream-sync
mike a 2 years ago committed by mergify[bot]
parent ffbcaa82de
commit 7b5d3181df

@ -76,9 +76,9 @@ interface PagedHistoryProvider {
*
* @param offset How much to offset the list by
* @param numberOfItems How many items to fetch
* @param onComplete A callback that returns the list of [HistoryDB]
* @return list of [HistoryDB]
*/
fun getHistory(offset: Int, numberOfItems: Int, onComplete: (List<HistoryDB>) -> Unit)
fun getHistory(offset: Int, numberOfItems: Int): List<HistoryDB>
}
/**
@ -112,15 +112,11 @@ class DefaultPagedHistoryProvider(
@Volatile private var historyGroups: List<HistoryDB.Group>? = null
@Suppress("LongMethod")
override fun getHistory(
offset: Int,
numberOfItems: Int,
onComplete: (List<HistoryDB>) -> Unit,
) {
// A PagedList DataSource runs on a background thread automatically.
// If we run this in our own coroutineScope it breaks the PagedList
runBlockingIncrement {
numberOfItems: Int
): List<HistoryDB> {
return runBlockingIncrement {
// We need to re-fetch all the history metadata if the offset resets back at 0
// in the case of a pull to refresh.
if (historyGroups == null || offset == 0) {
@ -146,7 +142,7 @@ class DefaultPagedHistoryProvider(
.toList()
}
onComplete(getHistoryAndSearchGroups(offset, numberOfItems))
getHistoryAndSearchGroups(offset, numberOfItems)
}
}

@ -6,14 +6,14 @@ package org.mozilla.fenix.library.history
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import org.mozilla.fenix.selection.SelectionHolder
import org.mozilla.fenix.library.history.viewholders.HistoryListItemViewHolder
class HistoryAdapter(
private val historyInteractor: HistoryInteractor,
) : PagedListAdapter<History, HistoryListItemViewHolder>(historyDiffCallback),
) : PagingDataAdapter<History, HistoryListItemViewHolder>(historyDiffCallback),
SelectionHolder<History> {
private var mode: HistoryFragmentState.Mode = HistoryFragmentState.Mode.Normal

@ -5,38 +5,40 @@
package org.mozilla.fenix.library.history
import androidx.annotation.VisibleForTesting
import androidx.paging.ItemKeyedDataSource
import androidx.paging.PagingSource
import androidx.paging.PagingState
import org.mozilla.fenix.components.history.HistoryDB
import org.mozilla.fenix.components.history.PagedHistoryProvider
class HistoryDataSource(
private val historyProvider: PagedHistoryProvider
) : ItemKeyedDataSource<Int, History>() {
private val historyProvider: PagedHistoryProvider,
private val onZeroItemsLoaded: () -> Unit
) : PagingSource<Int, History>() {
// Because the pagination is not based off of the key
// we want to start at 1, not 0 to be able to send the correct offset
// to the `historyProvider.getHistory` call.
override fun getKey(item: History): Int = item.position + 1
// having any value but null creates visual glitches in case or swipe to refresh and immediate
// scroll down
override fun getRefreshKey(state: PagingState<Int, History>): Int? = null
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<History>
) {
historyProvider.getHistory(INITIAL_OFFSET, params.requestedLoadSize) { history ->
callback.onResult(history.positionWithOffset(INITIAL_OFFSET))
// params.key is expected to be null for the initial load or a refresh
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, History> {
val offset = params.key ?: 0
val historyItems = historyProvider.getHistory(offset, params.loadSize).run {
positionWithOffset(offset)
}
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<History>) {
historyProvider.getHistory(params.key, params.requestedLoadSize) { history ->
callback.onResult(history.positionWithOffset(params.key))
val nextOffset = if (historyItems.isEmpty()) {
if (params.key == null) {
onZeroItemsLoaded.invoke()
}
null
} else {
(offset + historyItems.size) + 1
}
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<History>) { /* noop */ }
companion object {
internal const val INITIAL_OFFSET = 0
// prevKey is needed in case load would work upwards, so passing null is fine
return LoadResult.Page(
data = historyItems,
prevKey = null,
nextKey = nextOffset
)
}
}

@ -1,21 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.library.history
import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource
import org.mozilla.fenix.components.history.PagedHistoryProvider
class HistoryDataSourceFactory(
private val historyProvider: PagedHistoryProvider
) : DataSource.Factory<Int, History>() {
val datasource = MutableLiveData<HistoryDataSource>()
override fun create(): DataSource<Int, History> {
val datasource = HistoryDataSource(historyProvider)
this.datasource.postValue(datasource)
return datasource
}
}

@ -15,11 +15,13 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavDirections
import androidx.navigation.fragment.findNavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import mozilla.components.browser.state.action.EngineAction
import mozilla.components.browser.state.action.HistoryMetadataAction
@ -158,10 +160,11 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
historyView::updateEmptyState
)
model.history.observe(
viewLifecycleOwner,
historyView.historyAdapter::submitList
)
model.viewModelScope.launch {
model.history.collect {
historyView.historyAdapter.submitData(it)
}
}
}
}
@ -322,7 +325,7 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
requireComponents.core.historyStorage.deleteEverything()
deleteOpenTabsEngineHistory(requireComponents.core.store)
launch(Main) {
viewModel.invalidate()
historyView.historyAdapter.refresh()
historyStore.dispatch(HistoryFragmentAction.ExitDeletionMode)
showSnackBar(
requireView(),
@ -411,6 +414,6 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler {
private suspend fun syncHistory() {
val accountManager = requireComponents.backgroundServices.accountManager
accountManager.syncNow(SyncReason.User)
viewModel.invalidate()
historyView.historyAdapter.refresh()
}
}

@ -61,7 +61,7 @@ class HistoryView(
historyAdapter.updatePendingDeletionIds(state.pendingDeletionIds)
updateEmptyState(state.pendingDeletionIds.size != historyAdapter.currentList?.size)
updateEmptyState(state.pendingDeletionIds.size != historyAdapter.itemCount)
historyAdapter.updateMode(state.mode)
val first = layoutManager.findFirstVisibleItemPosition()

@ -4,33 +4,28 @@
package org.mozilla.fenix.library.history
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import org.mozilla.fenix.components.history.PagedHistoryProvider
class HistoryViewModel(historyProvider: PagedHistoryProvider) : ViewModel() {
var history: LiveData<PagedList<History>>
var history: Flow<PagingData<History>>
var userHasHistory = MutableLiveData(true)
private val datasource: LiveData<HistoryDataSource>
init {
val historyDataSourceFactory = HistoryDataSourceFactory(historyProvider)
datasource = historyDataSourceFactory.datasource
history = LivePagedListBuilder(historyDataSourceFactory, PAGE_SIZE)
.setBoundaryCallback(object : PagedList.BoundaryCallback<History>() {
override fun onZeroItemsLoaded() {
userHasHistory.value = false
}
})
.build()
}
fun invalidate() {
datasource.value?.invalidate()
history = Pager(
PagingConfig(PAGE_SIZE),
null
) {
HistoryDataSource(
historyProvider = historyProvider,
onZeroItemsLoaded = { userHasHistory.value = false }
)
}.flow
}
companion object {

@ -17,8 +17,6 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -36,6 +34,7 @@ import org.mozilla.fenix.ext.nav
private const val MAX_ITEMS_PER_PAGE = 50
@Suppress("DEPRECATION")
class SitePermissionsExceptionsFragment :
Fragment(R.layout.fragment_site_permissions_exceptions), View.OnClickListener {
private lateinit var emptyContainerMessage: View
@ -63,7 +62,7 @@ class SitePermissionsExceptionsFragment :
withContext(Main) {
val adapter = ExceptionsAdapter(this@SitePermissionsExceptionsFragment)
val liveData = LivePagedListBuilder(sitePermissionsPaged, MAX_ITEMS_PER_PAGE).build()
val liveData = androidx.paging.LivePagedListBuilder(sitePermissionsPaged, MAX_ITEMS_PER_PAGE).build()
liveData.observe(
viewLifecycleOwner,
@ -135,8 +134,9 @@ class SitePermissionsExceptionsFragment :
class SitePermissionsViewHolder(val view: View, val iconView: ImageView, val siteTextView: TextView) :
RecyclerView.ViewHolder(view)
@Suppress("DEPRECATION")
class ExceptionsAdapter(private val clickListener: View.OnClickListener) :
PagedListAdapter<SitePermissions, SitePermissionsViewHolder>(diffCallback) {
androidx.paging.PagedListAdapter<SitePermissions, SitePermissionsViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SitePermissionsViewHolder {
val context = parent.context

@ -35,7 +35,7 @@ object Versions {
const val androidx_navigation = "2.3.3"
const val androidx_recyclerview = "1.2.1"
const val androidx_core = "1.3.2"
const val androidx_paging = "2.1.2"
const val androidx_paging = "3.1.0"
const val androidx_transition = "1.4.0"
const val androidx_work = "2.7.1"
const val androidx_datastore = "1.0.0"

Loading…
Cancel
Save