Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3 paging loadstate #195

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
implementation 'androidx.paging:paging-runtime-ktx:3.0.0-beta03'

kapt "androidx.databinding:databinding-compiler:$agp_version"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package me.tatarka.bindingcollectionadapter.sample

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.get
import me.tatarka.bindingcollectionadapter.sample.databinding.PagedV3RecyclerViewBinding

class FragmentPagedV3RecyclerView : Fragment() {
private lateinit var viewModel: ImmutableViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get()
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return PagedV3RecyclerViewBinding.inflate(inflater, container, false).also {
it.setLifecycleOwner(this)
it.viewModel = viewModel
it.listeners = viewModel
it.executePendingBindings()
}.root
}
}
Original file line number Diff line number Diff line change
@@ -1,76 +1,123 @@
package me.tatarka.bindingcollectionadapter.sample

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.paging.PositionalDataSource
import androidx.lifecycle.*
import androidx.paging.*
import androidx.recyclerview.widget.DiffUtil
import kotlinx.coroutines.delay
import me.tatarka.bindingcollectionadapter2.ItemBinding
import me.tatarka.bindingcollectionadapter2.itembindings.OnItemBindLoadState
import me.tatarka.bindingcollectionadapter2.itemBindingOf
import me.tatarka.bindingcollectionadapter2.itembindings.OnItemBindClass
import me.tatarka.bindingcollectionadapter2.map
import me.tatarka.bindingcollectionadapter2.toItemBinding
import java.util.*
import kotlin.random.Random

class ImmutableViewModel : ViewModel(), ImmutableListeners {

private val mutList = MutableLiveData<List<ImmutableItem>>().apply {
value = (0 until 3).map { i -> ImmutableItem(index = i, checked = false) }
}
private val headerFooterList =
Transformations.map<List<ImmutableItem>, List<Any>>(mutList) { input ->
val list = ArrayList<Any>(input.size + 2)
list.add("Header")
list.addAll(input)
list.add("Footer")
list
}
Transformations.map<List<ImmutableItem>, List<Any>>(mutList) { input ->
val list = ArrayList<Any>(input.size + 2)
list.add("Header")
list.addAll(input)
list.add("Footer")
list
}
val list: LiveData<List<Any>> = headerFooterList

val pagedList: LiveData<PagedList<Any>> =
LivePagedListBuilder(object : DataSource.Factory<Int, Any>() {
override fun create(): DataSource<Int, Any> =
object : PositionalDataSource<Any>() {

override fun loadInitial(
params: LoadInitialParams,
callback: LoadInitialCallback<Any>
) {
val list =
(0 until params.requestedLoadSize).map {
ImmutableItem(
index = it + params.requestedStartPosition,
checked = false
)
LivePagedListBuilder(object : DataSource.Factory<Int, Any>() {
override fun create(): DataSource<Int, Any> =
object : PositionalDataSource<Any>() {

override fun loadInitial(
params: LoadInitialParams,
callback: LoadInitialCallback<Any>
) {
val list =
(0 until params.requestedLoadSize).map {
ImmutableItem(
index = it + params.requestedStartPosition,
checked = false
)
}
// Pretend we are slow
Thread.sleep(1000)
callback.onResult(list, params.requestedStartPosition, 200)
}
// Pretend we are slow
Thread.sleep(1000)
callback.onResult(list, params.requestedStartPosition, 200)
}

override fun loadRange(
params: LoadRangeParams,
callback: LoadRangeCallback<Any>
) {
val list =
(0 until params.loadSize).map {
ImmutableItem(
index = it + params.startPosition,
checked = false
)

override fun loadRange(
params: LoadRangeParams,
callback: LoadRangeCallback<Any>
) {
val list =
(0 until params.loadSize).map {
ImmutableItem(
index = it + params.startPosition,
checked = false
)
}
// Pretend we are slow
Thread.sleep(1000)
callback.onResult(list)
}
// Pretend we are slow
Thread.sleep(1000)
callback.onResult(list)
}
}
}, PAGE_SIZE).build()

val pagedListV3: LiveData<PagingData<Any>> = Pager<Int, Any>(PagingConfig(
pageSize = PAGE_SIZE,
maxSize = 100,
prefetchDistance = PAGE_SIZE * 2,
enablePlaceholders = false)) {
object : PagingSource<Int, Any>() {
private val random = Random(TOTAL_COUNT)

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Any> {
val safeKey = params.key ?: 0
val list =
(0 until params.loadSize).map {
ImmutableItem(
index = it + safeKey,
checked = false
)
}
// Pretend we are slow
delay(1000)

if (random.nextBoolean()) {
return LoadResult.Error(IllegalAccessError("Error plz try again"))
}
}, 20).build()

return LoadResult.Page(
data = list,
prevKey = if (safeKey == 0) null else (safeKey - params.loadSize),
nextKey = if (safeKey >= TOTAL_COUNT - params.loadSize) null else (safeKey + params.loadSize),
itemsBefore = safeKey,
itemsAfter = TOTAL_COUNT - params.loadSize - safeKey
)
}

override fun getRefreshKey(state: PagingState<Int, Any>): Int = 0
}
}.flow.asLiveData()

val items = itemBindingOf<Any>(BR.item, R.layout.item_immutable)

val loadStateItems = OnItemBindLoadState.Builder<Any>()
.item(BR.item, R.layout.item_immutable)
.headerLoadState(OnItemBindClass<LoadState>().apply {
map<LoadState.Loading>(ItemBinding.VAR_NONE, R.layout.network_state_item_progress)
map<LoadState.Error>(BR.item, R.layout.network_state_item_error)
})
.footerLoadState(OnItemBindClass<LoadState>().apply {
map<LoadState.Loading>(ItemBinding.VAR_NONE, R.layout.network_state_item_progress)
map<LoadState.Error>(BR.item, R.layout.network_state_item_error)
})
.pagingCallbackVariableId(BR.callback)
.build()

val multipleItems = OnItemBindClass<Any>().apply {
map<String>(BR.item, R.layout.item_header_footer)
map<ImmutableItem>(BR.item, R.layout.item_immutable)
Expand Down Expand Up @@ -108,4 +155,9 @@ class ImmutableViewModel : ViewModel(), ImmutableListeners {
}
}
}

companion object {
private val TOTAL_COUNT = 200
private val PAGE_SIZE = 20
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class MainActivity : AppCompatActivity() {
R.id.action_recyclerview -> FragmentRecyclerView()
R.id.action_diff_recyclerview -> FragmentDiffRecyclerView()
R.id.action_paged_recyclerview -> FragmentPagedRecyclerView()
R.id.action_paged_v3_recyclerview -> FragmentPagedV3RecyclerView()
R.id.action_viewpager -> FragmentViewPagerView()
R.id.action_viewpager2 -> FragmentViewPager2View()
R.id.action_spinner -> FragmentSpinnerView()
Expand Down
54 changes: 54 additions & 0 deletions app/src/main/res/layout/network_state_item_error.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2017 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable
name="item"
type="me.tatarka.bindingcollectionadapter2.itembindings.Load" />

<variable
name="callback"
type="me.tatarka.bindingcollectionadapter2.PagedDataCallback" />

</data>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">

<TextView
android:id="@+id/error_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@{item.error.message}"/>

<Button
android:id="@+id/retry_button"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/retry"
android:onClick="@{() -> callback.retry()}"/>
</LinearLayout>

</layout>
34 changes: 34 additions & 0 deletions app/src/main/res/layout/network_state_item_progress.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2017 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">

<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />

</LinearLayout>

</layout>
33 changes: 33 additions & 0 deletions app/src/main/res/layout/paged_v3_recycler_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>

<import type="me.tatarka.bindingcollectionadapter.sample.ImmutableItem" />

<variable
name="viewModel"
type="me.tatarka.bindingcollectionadapter.sample.ImmutableViewModel" />

<variable
name="listeners"
type="me.tatarka.bindingcollectionadapter.sample.Listeners" />
</data>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:diffConfig="@{viewModel.diff}"
app:itemBinding="@{viewModel.loadStateItems}"
app:items="@{viewModel.pagedListV3}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
</layout>
5 changes: 5 additions & 0 deletions app/src/main/res/menu/menu_drawer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
android:id="@+id/action_paged_recyclerview"
android:title="Paged RecyclerView" />

<item
android:id="@+id/action_paged_v3_recyclerview"
android:title="Paged V3 RecyclerView" />


<item
android:id="@+id/action_viewpager"
android:title="ViewPager" />
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
<string name="action_settings">Settings</string>
<string name="open_drawer">Open Drawer</string>
<string name="close_drawer">Close Drawer</string>

<string name="retry">Retry</string>
</resources>
2 changes: 1 addition & 1 deletion bindingcollectionadapter-paging/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ android {

dependencies {
implementation project(':bindingcollectionadapter-recyclerview')
implementation 'androidx.paging:paging-runtime:2.0.0'
implementation 'androidx.paging:paging-runtime:3.0.0-beta03'

testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.6.2'
Expand Down
Loading