Created
July 15, 2019 15:22
-
-
Save ryohji/e06fc27029793aaa9e0426f51f3c7ea4 to your computer and use it in GitHub Desktop.
The diff of google code lab's android-paging.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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