We are trying to implement paging in Leanback VerticalGridSupportFragment with Architecture Components Paging Library. Leanback on it's own doesn't have any sort of out-of-box compatibility with Paging Library so we extended it's ObjectAdapter class and managed to implement append and clear operations quite easily but we are having a hard time trying to make modify operation work. During content modification operation, Paging Library's PagedList class computes the diff using AsyncPagedListDiffer which internally uses PagedStorageDiffHelper which is a package-private class and it internally uses package-private PagedStorage field of PagedList to get access to actual underlying data. Thus we can't implement the same logic as Paging Library uses internally because of visibility restrictions. We are looking for a clean and clever way to make Leanback work together with Paging without extracting and modifying internals of any of the two. This is our implementation of ObjectAdapter which supports appending and clearing data but does not support content modification.
Has anybody ever managed to implement paging in Leanback through Paging Library?
class LeanbackVerticalGridPagedListAdapter<T>(
presenter: Presenter,
private val stubItem: T
) : ObjectAdapter(presenter) {
private val mUpdateCallback = object : ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
notifyItemRangeInserted(position, count)
}
override fun onRemoved(position: Int, count: Int) {
notifyItemRangeRemoved(position, count)
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
notifyItemMoved(fromPosition, toPosition)
}
override fun onChanged(position: Int, count: Int, payload: Any?) {
notifyItemRangeChanged(position, count, payload)
}
}
private var mPagedList: PagedList<T>? = null
private var mSnapshot: PagedList<T>? = null
private val mPagedListCallback = object : PagedList.Callback() {
override fun onInserted(position: Int, count: Int) {
mUpdateCallback.onInserted(position, count)
}
override fun onRemoved(position: Int, count: Int) {
mUpdateCallback.onRemoved(position, count)
}
override fun onChanged(position: Int, count: Int) {
mUpdateCallback.onChanged(position, count, null)
}
}
override fun size(): Int =
mPagedList?.size
?: mSnapshot?.size
?: 0
override fun get(index: Int): T? =
mPagedList?.let {
it.loadAround(index)
it[index] ?: stubItem
} ?: mSnapshot?.let {
it[index]
} ?: throw IndexOutOfBoundsException("Item count is zero, getItem() call is invalid")
fun submitList(pagedList: PagedList<T>?) {
if (pagedList == null) {
val removedCount = size()
if (mPagedList != null) {
mPagedList!!.removeWeakCallback(mPagedListCallback)
mPagedList = null
} else if (mSnapshot != null) {
mSnapshot = null
}
// dispatch update callback after updating mPagedList/mSnapshot
mUpdateCallback.onRemoved(0, removedCount)
return
}
if (mPagedList == null && mSnapshot == null) {
// fast simple first insert
mPagedList = pagedList
pagedList.addWeakCallback(null, mPagedListCallback)
// dispatch update callback after updating mPagedList/mSnapshot
mUpdateCallback.onInserted(0, pagedList.size)
return
}
if (mPagedList != null) {
// first update scheduled on this list, so capture mPages as a snapshot, removing
// callbacks so we don't have resolve to updates against a moving target
mPagedList!!.removeWeakCallback(mPagedListCallback)
mSnapshot = mPagedList!!.snapshot() as PagedList<T>
mPagedList = null
}
if (mSnapshot == null || mPagedList != null) {
DevUtil.crashDuringDevelopment(IllegalStateException("must be in snapshot state to diff"))
}
}
}