Skip to content

Instantly share code, notes, and snippets.

@ryohji
Created July 15, 2019 15:22
Show Gist options
  • Save ryohji/e06fc27029793aaa9e0426f51f3c7ea4 to your computer and use it in GitHub Desktop.
Save ryohji/e06fc27029793aaa9e0426f51f3c7ea4 to your computer and use it in GitHub Desktop.
The diff of google code lab's android-paging.
diff --git a/app/src/main/java/com/example/android/codelabs/paging/data/GithubRepository.kt b/app/src/main/java/com/example/android/codelabs/paging/data/GithubRepository.kt
index b1b9b6c..c23688f 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/data/GithubRepository.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/data/GithubRepository.kt
@@ -17,9 +17,8 @@
package com.example.android.codelabs.paging.data
import android.util.Log
-import androidx.lifecycle.MutableLiveData
+import androidx.paging.LivePagedListBuilder
import com.example.android.codelabs.paging.api.GithubService
-import com.example.android.codelabs.paging.api.searchRepos
import com.example.android.codelabs.paging.db.GithubLocalCache
import com.example.android.codelabs.paging.model.RepoSearchResult
@@ -31,49 +30,28 @@ class GithubRepository(
private val cache: GithubLocalCache
) {
- // keep the last requested page. When the request is successful, increment the page number.
- private var lastRequestedPage = 1
-
- // LiveData of network errors.
- private val networkErrors = MutableLiveData<String>()
-
- // avoid triggering multiple requests in the same time
- private var isRequestInProgress = false
-
/**
* Search repositories whose names match the query.
*/
fun search(query: String): RepoSearchResult {
Log.d("GithubRepository", "New query: $query")
- lastRequestedPage = 1
- requestAndSaveData(query)
- // Get data from the local cache
- val data = cache.reposByName(query)
+ // Get data source factory from the local cache
+ val dataSourceFactory = cache.reposByName(query)
- return RepoSearchResult(data, networkErrors)
- }
-
- fun requestMore(query: String) {
- requestAndSaveData(query)
- }
+ // Construct the boundary callback
+ val callback = RepoBoundaryCallback(query, service, cache)
- private fun requestAndSaveData(query: String) {
- if (isRequestInProgress) return
+ // Get the paged list
+ val data = LivePagedListBuilder(dataSourceFactory, DATABASE_PAGE_SIZE)
+ .setBoundaryCallback(callback)
+ .build()
- isRequestInProgress = true
- searchRepos(service, query, lastRequestedPage, NETWORK_PAGE_SIZE, { repos ->
- cache.insert(repos) {
- lastRequestedPage++
- isRequestInProgress = false
- }
- }, { error ->
- networkErrors.postValue(error)
- isRequestInProgress = false
- })
+ // Get the network errors exposed by the boundary callback
+ return RepoSearchResult(data, callback.networkErrors)
}
companion object {
- private const val NETWORK_PAGE_SIZE = 50
+ private const val DATABASE_PAGE_SIZE = 20
}
}
diff --git a/app/src/main/java/com/example/android/codelabs/paging/data/RepoBoundaryCallback.kt b/app/src/main/java/com/example/android/codelabs/paging/data/RepoBoundaryCallback.kt
index 354bb82..dd1caa2 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/data/RepoBoundaryCallback.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/data/RepoBoundaryCallback.kt
@@ -1,4 +1,56 @@
package com.example.android.codelabs.paging.data
-class RepoBoundaryCallback {
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.paging.PagedList
+import com.example.android.codelabs.paging.api.GithubService
+import com.example.android.codelabs.paging.api.searchRepos
+import com.example.android.codelabs.paging.db.GithubLocalCache
+import com.example.android.codelabs.paging.model.Repo
+
+class RepoBoundaryCallback(
+ private val query: String,
+ private val service: GithubService,
+ private val cache: GithubLocalCache
+) : PagedList.BoundaryCallback<Repo>() {
+
+ // keep the last requested page.
+ // When the request is successful, increment the page number.
+ private var lastRequestedPage = 1
+
+ private val _networkErrors = MutableLiveData<String>()
+
+ // LiveData of network errors.
+ val networkErrors: LiveData<String>
+ get() = _networkErrors
+
+ // avoid triggering multiple requests in the same time
+ private var isRequestInProgress = false
+
+ override fun onZeroItemsLoaded() {
+ requestAndSaveData(query)
+ }
+
+ override fun onItemAtEndLoaded(itemAtEnd: Repo) {
+ requestAndSaveData(query)
+ }
+
+ private fun requestAndSaveData(query: String) {
+ if (isRequestInProgress) return
+
+ isRequestInProgress = true
+ searchRepos(service, query, lastRequestedPage, NETWORK_PAGE_SIZE, { repos ->
+ cache.insert(repos) {
+ lastRequestedPage++
+ isRequestInProgress = false
+ }
+ }, { error ->
+ _networkErrors.postValue(error)
+ isRequestInProgress = false
+ })
+ }
+
+ companion object {
+ private const val NETWORK_PAGE_SIZE = 50
+ }
}
diff --git a/app/src/main/java/com/example/android/codelabs/paging/db/GithubLocalCache.kt b/app/src/main/java/com/example/android/codelabs/paging/db/GithubLocalCache.kt
index e427134..eca23ce 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/db/GithubLocalCache.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/db/GithubLocalCache.kt
@@ -17,7 +17,7 @@
package com.example.android.codelabs.paging.db
import android.util.Log
-import androidx.lifecycle.LiveData
+import androidx.paging.DataSource
import com.example.android.codelabs.paging.model.Repo
import java.util.concurrent.Executor
@@ -47,7 +47,7 @@ class GithubLocalCache(
* any characters between the words.
* @param name repository name
*/
- fun reposByName(name: String): LiveData<List<Repo>> {
+ fun reposByName(name: String): DataSource.Factory<Int, Repo> {
// appending '%' so we can allow other characters to be before and after the query string
val query = "%${name.replace(' ', '%')}%"
return repoDao.reposByName(query)
diff --git a/app/src/main/java/com/example/android/codelabs/paging/db/RepoDao.kt b/app/src/main/java/com/example/android/codelabs/paging/db/RepoDao.kt
index 60665fa..f8b3b2f 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/db/RepoDao.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/db/RepoDao.kt
@@ -17,6 +17,7 @@
package com.example.android.codelabs.paging.db
import androidx.lifecycle.LiveData
+import androidx.paging.DataSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
@@ -37,5 +38,5 @@ interface RepoDao {
// and order those results descending, by the number of stars and then by name
@Query("SELECT * FROM repos WHERE (name LIKE :queryString) OR (description LIKE " +
":queryString) ORDER BY stars DESC, name ASC")
- fun reposByName(queryString: String): LiveData<List<Repo>>
+ fun reposByName(queryString: String): DataSource.Factory<Int, Repo>
}
diff --git a/app/src/main/java/com/example/android/codelabs/paging/model/RepoSearchResult.kt b/app/src/main/java/com/example/android/codelabs/paging/model/RepoSearchResult.kt
index 75688b3..55ddbe8 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/model/RepoSearchResult.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/model/RepoSearchResult.kt
@@ -17,12 +17,13 @@
package com.example.android.codelabs.paging.model
import androidx.lifecycle.LiveData
+import androidx.paging.PagedList
/**
* RepoSearchResult from a search, which contains LiveData<List<Repo>> holding query data,
* and a LiveData<String> of network error state.
*/
data class RepoSearchResult(
- val data: LiveData<List<Repo>>,
- val networkErrors: LiveData<String>
+ val data: LiveData<PagedList<Repo>>,
+ val networkErrors: LiveData<String>
)
diff --git a/app/src/main/java/com/example/android/codelabs/paging/ui/ReposAdapter.kt b/app/src/main/java/com/example/android/codelabs/paging/ui/ReposAdapter.kt
index d959d26..3e028b9 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/ui/ReposAdapter.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/ui/ReposAdapter.kt
@@ -17,14 +17,16 @@
package com.example.android.codelabs.paging.ui
import android.view.ViewGroup
+import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
import com.example.android.codelabs.paging.model.Repo
/**
* Adapter for the list of repositories.
*/
-class ReposAdapter : ListAdapter<Repo, androidx.recyclerview.widget.RecyclerView.ViewHolder>(REPO_COMPARATOR) {
+class ReposAdapter : PagedListAdapter<Repo, RecyclerView.ViewHolder>(REPO_COMPARATOR) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
return RepoViewHolder.create(parent)
diff --git a/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesActivity.kt b/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesActivity.kt
index 53029c1..cf1a3fe 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesActivity.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesActivity.kt
@@ -25,6 +25,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
+import androidx.paging.PagedList
import androidx.recyclerview.widget.DividerItemDecoration
import com.example.android.codelabs.paging.Injection
import com.example.android.codelabs.paging.R
@@ -47,7 +48,6 @@ class SearchRepositoriesActivity : AppCompatActivity() {
// add dividers between RecyclerView's row items
val decoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
list.addItemDecoration(decoration)
- setupScrollListener()
initAdapter()
val query = savedInstanceState?.getString(LAST_SEARCH_QUERY) ?: DEFAULT_QUERY
@@ -62,7 +62,7 @@ class SearchRepositoriesActivity : AppCompatActivity() {
private fun initAdapter() {
list.adapter = adapter
- viewModel.repos.observe(this, Observer<List<Repo>> {
+ viewModel.repos.observe(this, Observer<PagedList<Repo>> {
Log.d("Activity", "list: ${it?.size}")
showEmptyList(it?.size == 0)
adapter.submitList(it)
@@ -113,20 +113,6 @@ class SearchRepositoriesActivity : AppCompatActivity() {
}
}
- private fun setupScrollListener() {
- val layoutManager = list.layoutManager as androidx.recyclerview.widget.LinearLayoutManager
- list.addOnScrollListener(object : androidx.recyclerview.widget.RecyclerView.OnScrollListener() {
- override fun onScrolled(recyclerView: androidx.recyclerview.widget.RecyclerView, dx: Int, dy: Int) {
- super.onScrolled(recyclerView, dx, dy)
- val totalItemCount = layoutManager.itemCount
- val visibleItemCount = layoutManager.childCount
- val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
-
- viewModel.listScrolled(visibleItemCount, lastVisibleItem, totalItemCount)
- }
- })
- }
-
companion object {
private const val LAST_SEARCH_QUERY: String = "last_search_query"
private const val DEFAULT_QUERY = "Android"
diff --git a/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesViewModel.kt b/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesViewModel.kt
index 4034f91..3b6d43c 100644
--- a/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesViewModel.kt
+++ b/app/src/main/java/com/example/android/codelabs/paging/ui/SearchRepositoriesViewModel.kt
@@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
+import androidx.paging.PagedList
import com.example.android.codelabs.paging.data.GithubRepository
import com.example.android.codelabs.paging.model.Repo
import com.example.android.codelabs.paging.model.RepoSearchResult
@@ -30,16 +31,12 @@ import com.example.android.codelabs.paging.model.RepoSearchResult
*/
class SearchRepositoriesViewModel(private val repository: GithubRepository) : ViewModel() {
- companion object {
- private const val VISIBLE_THRESHOLD = 5
- }
-
private val queryLiveData = MutableLiveData<String>()
private val repoResult: LiveData<RepoSearchResult> = Transformations.map(queryLiveData) {
repository.search(it)
}
- val repos: LiveData<List<Repo>> = Transformations.switchMap(repoResult) { it -> it.data }
+ val repos: LiveData<PagedList<Repo>> = Transformations.switchMap(repoResult) { it -> it.data }
val networkErrors: LiveData<String> = Transformations.switchMap(repoResult) { it ->
it.networkErrors
}
@@ -51,15 +48,6 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi
queryLiveData.postValue(queryString)
}
- fun listScrolled(visibleItemCount: Int, lastVisibleItemPosition: Int, totalItemCount: Int) {
- if (visibleItemCount + lastVisibleItemPosition + VISIBLE_THRESHOLD >= totalItemCount) {
- val immutableQuery = lastQueryValue()
- if (immutableQuery != null) {
- repository.requestMore(immutableQuery)
- }
- }
- }
-
/**
* Get the last query value.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment