From 24d16131df674f10d6fd565a8d414260ced38b25 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Sat, 1 Feb 2020 22:31:10 +0000 Subject: [PATCH 1/8] Migrating to Paging 3.0 --- .../paging/data/GithubPagingSource.kt | 29 ++++++++++++++++ .../codelabs/paging/data/GithubRepository.kt | 15 ++++++--- .../codelabs/paging/ui/ReposAdapter.kt | 9 +++-- .../paging/ui/SearchRepositoriesActivity.kt | 33 ++++++++++--------- .../paging/ui/SearchRepositoriesViewModel.kt | 18 +++++++--- build.gradle | 1 + 6 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt diff --git a/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt b/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt new file mode 100644 index 00000000..59585cb6 --- /dev/null +++ b/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt @@ -0,0 +1,29 @@ +package com.example.android.codelabs.paging.data + +import androidx.paging.PagingSource +import com.example.android.codelabs.paging.api.GithubService +import com.example.android.codelabs.paging.api.searchRepos +import com.example.android.codelabs.paging.model.Repo +import com.example.android.codelabs.paging.model.RepoSearchResult +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview + +@ExperimentalCoroutinesApi +@FlowPreview +class GithubPagingSource( + private val service: GithubService, + private val query: String +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + val position = params.key ?: 0 + val apiResponse = searchRepos(service, query, position, GithubRepository.NETWORK_PAGE_SIZE) + return when(apiResponse){ + is RepoSearchResult.Success -> LoadResult.Page( + data = apiResponse.data, + prevKey = position, + nextKey = position + 1 + ) + is RepoSearchResult.Error -> LoadResult.Error(apiResponse.error) + } + } +} \ No newline at end of file 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 25e6d478..51dee1f4 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,6 +17,9 @@ package com.example.android.codelabs.paging.data import android.util.Log +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingDataFlow import com.example.android.codelabs.paging.api.GithubService import com.example.android.codelabs.paging.api.IN_QUALIFIER import com.example.android.codelabs.paging.model.Repo @@ -25,7 +28,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow import java.io.IOException // GitHub page API is 1 based: https://developer.github.com/v3/#pagination @@ -55,12 +57,17 @@ class GithubRepository(private val service: GithubService) { * Search repositories whose names match the query, exposed as a stream of data that will emit * every time we get more data from the network. */ - suspend fun getSearchResultStream(query: String): Flow { + suspend fun getSearchResultStream(query: String): Flow> { Log.d("GithubRepository", "New query: $query") lastRequestedPage = 1 requestAndSaveData(query) - return searchResults.asFlow() + val pagedList = PagingDataFlow( + config = PagingConfig(pageSize = NETWORK_PAGE_SIZE), + pagingSourceFactory = { GithubPagingSource(service, query) } + ) + + return pagedList } suspend fun requestMore(query: String) { @@ -105,6 +112,6 @@ class GithubRepository(private val service: GithubService) { } companion object { - private const val NETWORK_PAGE_SIZE = 50 + const val NETWORK_PAGE_SIZE = 50 } } 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 d959d265..6a3440bf 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,20 +17,23 @@ package com.example.android.codelabs.paging.ui import android.view.ViewGroup +import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.example.android.codelabs.paging.model.Repo /** * Adapter for the list of repositories. */ -class ReposAdapter : ListAdapter(REPO_COMPARATOR) { +class ReposAdapter : PagingDataAdapter(REPO_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return RepoViewHolder.create(parent) } - override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) { + override fun onBindViewHolder(holder: ViewHolder, position: Int) { val repoItem = getItem(position) if (repoItem != null) { (holder as RepoViewHolder).bind(repoItem) 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 bef1f07b..d6b22958 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 @@ -30,7 +30,6 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.OnScrollListener import com.example.android.codelabs.paging.Injection import com.example.android.codelabs.paging.databinding.ActivitySearchRepositoriesBinding -import com.example.android.codelabs.paging.model.RepoSearchResult import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview @@ -71,19 +70,22 @@ class SearchRepositoriesActivity : AppCompatActivity() { private fun initAdapter() { binding.list.adapter = adapter viewModel.repoResult.observe(this) { result -> - when (result) { - is RepoSearchResult.Success -> { - showEmptyList(result.data.isEmpty()) - adapter.submitList(result.data) - } - is RepoSearchResult.Error -> { - Toast.makeText( - this, - "\uD83D\uDE28 Wooops $result.message}", - Toast.LENGTH_LONG - ).show() - } - } + //adapter.collectFrom(result) + // TODO how to handle empty list? + // TODO how to handle error? +// when (result) { +// is RepoSearchResult.Success -> { +// showEmptyList(result.data.isEmpty()) +// +// } +// is RepoSearchResult.Error -> { +// Toast.makeText( +// this, +// "\uD83D\uDE28 Wooops $result.message}", +// Toast.LENGTH_LONG +// ).show() +// } +// } } } @@ -113,7 +115,8 @@ class SearchRepositoriesActivity : AppCompatActivity() { if (it.isNotEmpty()) { binding.list.scrollToPosition(0) viewModel.searchRepo(it.toString()) - adapter.submitList(null) + // TODO how to clear the list +// adapter.submitList(null) } } } 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 e7f893a3..0cdb7178 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 @@ -16,9 +16,17 @@ package com.example.android.codelabs.paging.ui -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.liveData +import androidx.lifecycle.switchMap +import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn import com.example.android.codelabs.paging.data.GithubRepository -import com.example.android.codelabs.paging.model.RepoSearchResult +import com.example.android.codelabs.paging.model.Repo import kotlinx.coroutines.* /** @@ -34,9 +42,11 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi } private val queryLiveData = MutableLiveData() - val repoResult: LiveData = queryLiveData.switchMap { + val repoResult: LiveData> = queryLiveData.switchMap { liveData { - val repos = repository.getSearchResultStream(it).asLiveData(Dispatchers.Main) + val repos = repository.getSearchResultStream(it) + .cachedIn(viewModelScope) + .asLiveData(Dispatchers.Main) emitSource(repos) } } diff --git a/build.gradle b/build.gradle index 3a48c031..6c6a20b6 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ allprojects { repositories { google() jcenter() + maven { url 'https://ci.android.com/builds/submitted/6229151/androidx_snapshot/latest/repository' } } } From 5b49e4c1614c064be5906f5fec1c0540820f1759 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Tue, 25 Feb 2020 17:34:23 +0000 Subject: [PATCH 2/8] Removing LiveData usages --- .../paging/ui/SearchRepositoriesActivity.kt | 61 +++++++++++-------- .../paging/ui/SearchRepositoriesViewModel.kt | 29 ++++----- 2 files changed, 50 insertions(+), 40 deletions(-) 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 d6b22958..5bb569a2 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 @@ -17,13 +17,18 @@ package com.example.android.codelabs.paging.ui import android.os.Bundle +import android.util.Log import android.view.KeyEvent import android.view.View import android.view.inputmethod.EditorInfo import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.lifecycle.observe +import androidx.paging.LoadState +import androidx.paging.LoadType +import androidx.paging.PagingData import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -32,6 +37,9 @@ import com.example.android.codelabs.paging.Injection import com.example.android.codelabs.paging.databinding.ActivitySearchRepositoriesBinding import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.collect @FlowPreview @ExperimentalCoroutinesApi @@ -40,6 +48,7 @@ class SearchRepositoriesActivity : AppCompatActivity() { private lateinit var binding: ActivitySearchRepositoriesBinding private lateinit var viewModel: SearchRepositoriesViewModel private val adapter = ReposAdapter() + private var searchJob: Job? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -56,9 +65,19 @@ class SearchRepositoriesActivity : AppCompatActivity() { binding.list.addItemDecoration(decoration) setupScrollListener() - initAdapter() + binding.list.adapter = adapter + adapter.addLoadStateListener { loadType, loadState -> + Log.d("SearchRepositoriesActivity", "adapter load: type = $loadType state = $loadState") + if(loadState is LoadState.Error){ + Toast.makeText( + this, + "\uD83D\uDE28 Wooops $loadState.message}", + Toast.LENGTH_LONG + ).show() + } + } val query = savedInstanceState?.getString(LAST_SEARCH_QUERY) ?: DEFAULT_QUERY - viewModel.searchRepo(query) + search(query) initSearch(query) } @@ -67,28 +86,6 @@ class SearchRepositoriesActivity : AppCompatActivity() { outState.putString(LAST_SEARCH_QUERY, binding.searchRepo.text.trim().toString()) } - private fun initAdapter() { - binding.list.adapter = adapter - viewModel.repoResult.observe(this) { result -> - //adapter.collectFrom(result) - // TODO how to handle empty list? - // TODO how to handle error? -// when (result) { -// is RepoSearchResult.Success -> { -// showEmptyList(result.data.isEmpty()) -// -// } -// is RepoSearchResult.Error -> { -// Toast.makeText( -// this, -// "\uD83D\uDE28 Wooops $result.message}", -// Toast.LENGTH_LONG -// ).show() -// } -// } - } - } - private fun initSearch(query: String) { binding.searchRepo.setText(query) @@ -114,9 +111,21 @@ class SearchRepositoriesActivity : AppCompatActivity() { binding.searchRepo.text.trim().let { if (it.isNotEmpty()) { binding.list.scrollToPosition(0) - viewModel.searchRepo(it.toString()) + search(it.toString()) // TODO how to clear the list -// adapter.submitList(null) + // might not need it because of how Paging works +// adapter.collectFrom(PagingData.empty()) + } + } + } + + private fun search(query: String) { + searchJob?.cancel() + searchJob = lifecycleScope.launch { + val result = viewModel.searchRepo(query) + result.collect { + Log.d("SearchRepositoriesActivity", "query: $query, collecting $it") + adapter.collectFrom(it) } } } 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 0cdb7178..3729fad7 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 @@ -27,7 +27,10 @@ import androidx.paging.PagingData import androidx.paging.cachedIn import com.example.android.codelabs.paging.data.GithubRepository import com.example.android.codelabs.paging.model.Repo -import kotlinx.coroutines.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch /** * ViewModel for the [SearchRepositoriesActivity] screen. @@ -41,26 +44,19 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi private const val VISIBLE_THRESHOLD = 5 } - private val queryLiveData = MutableLiveData() - val repoResult: LiveData> = queryLiveData.switchMap { - liveData { - val repos = repository.getSearchResultStream(it) - .cachedIn(viewModelScope) - .asLiveData(Dispatchers.Main) - emitSource(repos) - } - } - + private var lastQueryValue: String? = null /** * Search a repository based on a query string. */ - fun searchRepo(queryString: String) { - queryLiveData.postValue(queryString) + suspend fun searchRepo(queryString: String): Flow> { + lastQueryValue = queryString + return repository.getSearchResultStream(queryString) + .cachedIn(viewModelScope) } fun listScrolled(visibleItemCount: Int, lastVisibleItemPosition: Int, totalItemCount: Int) { if (visibleItemCount + lastVisibleItemPosition + VISIBLE_THRESHOLD >= totalItemCount) { - val immutableQuery = queryLiveData.value + val immutableQuery = lastQueryValue() if (immutableQuery != null) { viewModelScope.launch { repository.requestMore(immutableQuery) @@ -68,4 +64,9 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi } } } + + /** + * Get the last query value. + */ + fun lastQueryValue(): String? = lastQueryValue } \ No newline at end of file From 97c078b66e987823904368146482d6cd1c06f5ac Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Thu, 27 Feb 2020 15:13:49 +0000 Subject: [PATCH 3/8] Remove code no longer needed due to paging --- .../paging/data/GithubPagingSource.kt | 27 +++++--- .../codelabs/paging/data/GithubRepository.kt | 69 +------------------ .../codelabs/paging/model/RepoSearchResult.kt | 28 -------- .../paging/ui/SearchRepositoriesActivity.kt | 29 ++------ .../paging/ui/SearchRepositoriesViewModel.kt | 35 ++++------ build.gradle | 2 +- 6 files changed, 36 insertions(+), 154 deletions(-) delete mode 100644 app/src/main/java/com/example/android/codelabs/paging/model/RepoSearchResult.kt diff --git a/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt b/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt index 59585cb6..77f6cb68 100644 --- a/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt +++ b/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt @@ -2,11 +2,14 @@ package com.example.android.codelabs.paging.data import androidx.paging.PagingSource import com.example.android.codelabs.paging.api.GithubService -import com.example.android.codelabs.paging.api.searchRepos +import com.example.android.codelabs.paging.api.IN_QUALIFIER import com.example.android.codelabs.paging.model.Repo -import com.example.android.codelabs.paging.model.RepoSearchResult import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import java.io.IOException + +// GitHub page API is 1 based: https://developer.github.com/v3/#pagination +private const val GITHUB_STARTING_PAGE_INDEX = 1 @ExperimentalCoroutinesApi @FlowPreview @@ -15,15 +18,19 @@ class GithubPagingSource( private val query: String ) : PagingSource() { override suspend fun load(params: LoadParams): LoadResult { - val position = params.key ?: 0 - val apiResponse = searchRepos(service, query, position, GithubRepository.NETWORK_PAGE_SIZE) - return when(apiResponse){ - is RepoSearchResult.Success -> LoadResult.Page( - data = apiResponse.data, - prevKey = position, - nextKey = position + 1 + val position = params.key ?: GITHUB_STARTING_PAGE_INDEX + val apiQuery = query + IN_QUALIFIER + val apiResponse = service.searchRepos(apiQuery, position, GithubRepository.NETWORK_PAGE_SIZE) + return if (apiResponse.isSuccessful) { + val repos = apiResponse.body()?.items ?: emptyList() + LoadResult.Page( + data = repos, + prevKey = if (position == GITHUB_STARTING_PAGE_INDEX) null else -1, + // if we don't get any results, we consider that we're at the last page + nextKey = if (repos.isEmpty()) null else position + 1 ) - is RepoSearchResult.Error -> LoadResult.Error(apiResponse.error) + } else { + LoadResult.Error(IOException(apiResponse.message())) } } } \ No newline at end of file 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 51dee1f4..8d8183dd 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 @@ -21,17 +21,10 @@ import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.PagingDataFlow import com.example.android.codelabs.paging.api.GithubService -import com.example.android.codelabs.paging.api.IN_QUALIFIER import com.example.android.codelabs.paging.model.Repo -import com.example.android.codelabs.paging.model.RepoSearchResult import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.flow.Flow -import java.io.IOException - -// GitHub page API is 1 based: https://developer.github.com/v3/#pagination -private const val GITHUB_STARTING_PAGE_INDEX = 1 /** * Repository class that works with local and remote data sources. @@ -40,75 +33,17 @@ private const val GITHUB_STARTING_PAGE_INDEX = 1 @ExperimentalCoroutinesApi class GithubRepository(private val service: GithubService) { - // keep the list of all results received - private val inMemoryCache = mutableListOf() - - // keep channel of results. The channel allows us to broadcast updates so - // the subscriber will have the latest data - private val searchResults = ConflatedBroadcastChannel() - - // keep the last requested page. When the request is successful, increment the page number. - private var lastRequestedPage = GITHUB_STARTING_PAGE_INDEX - - // avoid triggering multiple requests in the same time - private var isRequestInProgress = false - /** * Search repositories whose names match the query, exposed as a stream of data that will emit * every time we get more data from the network. */ - suspend fun getSearchResultStream(query: String): Flow> { + fun getSearchResultStream(query: String): Flow> { Log.d("GithubRepository", "New query: $query") - lastRequestedPage = 1 - requestAndSaveData(query) - val pagedList = PagingDataFlow( + return PagingDataFlow( config = PagingConfig(pageSize = NETWORK_PAGE_SIZE), pagingSourceFactory = { GithubPagingSource(service, query) } ) - - return pagedList - } - - suspend fun requestMore(query: String) { - requestAndSaveData(query) - } - - private suspend fun requestAndSaveData(query: String) { - if (isRequestInProgress) return - - isRequestInProgress = true - - val apiQuery = query + IN_QUALIFIER - val response = service.searchRepos(apiQuery, lastRequestedPage, NETWORK_PAGE_SIZE) - Log.d("GithubRepository", "response $response") - if (response.isSuccessful) { - if (response.isSuccessful) { - val repos = response.body()?.items ?: emptyList() - inMemoryCache.addAll(repos) - val reposByName = reposByName(query) - searchResults.offer(RepoSearchResult.Success(reposByName)) - } else { - Log.d("GithubRepository", "fail to get data") - searchResults.offer(RepoSearchResult.Error(IOException(response.message() - ?: "Unknown error"))) - } - } else { - Log.d("GithubRepository", "fail to get data") - searchResults.offer(RepoSearchResult.Error(IOException(response.message() - ?: "Unknown error"))) - } - lastRequestedPage++ - isRequestInProgress = false - } - - private fun reposByName(query: String): List { - // from the in memory cache select only the repos whose name or description matches - // the query. Then order the results. - return inMemoryCache.filter { - it.name.contains(query, true) || - (it.description != null && it.description.contains(query, true)) - }.sortedWith(compareByDescending { it.stars }.thenBy { it.name }) } companion object { 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 deleted file mode 100644 index d00c1f07..00000000 --- a/app/src/main/java/com/example/android/codelabs/paging/model/RepoSearchResult.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -package com.example.android.codelabs.paging.model - -import java.lang.Exception - -/** - * RepoSearchResult from a search, which contains List holding query data, - * and a String of network error state. - */ -sealed class RepoSearchResult { - data class Success(val data: List) : RepoSearchResult() - data class Error(val error: Exception) : RepoSearchResult() -} 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 5bb569a2..0602dabf 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,21 +25,15 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.observe import androidx.paging.LoadState -import androidx.paging.LoadType -import androidx.paging.PagingData import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.OnScrollListener import com.example.android.codelabs.paging.Injection import com.example.android.codelabs.paging.databinding.ActivitySearchRepositoriesBinding import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch @FlowPreview @ExperimentalCoroutinesApi @@ -63,12 +57,11 @@ class SearchRepositoriesActivity : AppCompatActivity() { // add dividers between RecyclerView's row items val decoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) binding.list.addItemDecoration(decoration) - setupScrollListener() binding.list.adapter = adapter adapter.addLoadStateListener { loadType, loadState -> Log.d("SearchRepositoriesActivity", "adapter load: type = $loadType state = $loadState") - if(loadState is LoadState.Error){ + if (loadState is LoadState.Error) { Toast.makeText( this, "\uD83D\uDE28 Wooops $loadState.message}", @@ -120,10 +113,10 @@ class SearchRepositoriesActivity : AppCompatActivity() { } private fun search(query: String) { + // Make sure we cancel the previous job before creating a new one searchJob?.cancel() searchJob = lifecycleScope.launch { - val result = viewModel.searchRepo(query) - result.collect { + viewModel.searchRepo(query).collect { Log.d("SearchRepositoriesActivity", "query: $query, collecting $it") adapter.collectFrom(it) } @@ -140,20 +133,6 @@ class SearchRepositoriesActivity : AppCompatActivity() { } } - private fun setupScrollListener() { - val layoutManager = binding.list.layoutManager as LinearLayoutManager - binding.list.addOnScrollListener(object : OnScrollListener() { - override fun onScrolled(recyclerView: 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 3729fad7..de77320c 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 @@ -30,7 +30,6 @@ import com.example.android.codelabs.paging.model.Repo import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch /** * ViewModel for the [SearchRepositoriesActivity] screen. @@ -40,33 +39,23 @@ import kotlinx.coroutines.launch @FlowPreview class SearchRepositoriesViewModel(private val repository: GithubRepository) : ViewModel() { - companion object { - private const val VISIBLE_THRESHOLD = 5 - } - + @Volatile private var lastQueryValue: String? = null + @Volatile + private var lastSearchResult: Flow>? = null + /** * Search a repository based on a query string. */ - suspend fun searchRepo(queryString: String): Flow> { + fun searchRepo(queryString: String): Flow> { + val result = lastSearchResult + if(queryString == lastQueryValue && result != null){ + return result + } lastQueryValue = queryString - return repository.getSearchResultStream(queryString) + val newResult = repository.getSearchResultStream(queryString) .cachedIn(viewModelScope) + lastSearchResult = newResult + return newResult } - - fun listScrolled(visibleItemCount: Int, lastVisibleItemPosition: Int, totalItemCount: Int) { - if (visibleItemCount + lastVisibleItemPosition + VISIBLE_THRESHOLD >= totalItemCount) { - val immutableQuery = lastQueryValue() - if (immutableQuery != null) { - viewModelScope.launch { - repository.requestMore(immutableQuery) - } - } - } - } - - /** - * Get the last query value. - */ - fun lastQueryValue(): String? = lastQueryValue } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6c6a20b6..db02f375 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,7 @@ ext { materialVersion = '1.1.0' archComponentsVersion = '2.2.0' roomVersion = '2.2.4' - pagingVersion = '2.1.0-alpha01' + pagingVersion = '3.0.0-SNAPSHOT' retrofitVersion = '2.6.0' okhttpLoggingInterceptorVersion = '4.0.0' coroutines = '1.3.0' From 781b2a9b12355c36b1fbe633fa024c14dfd7f106 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Wed, 4 Mar 2020 16:47:15 +0000 Subject: [PATCH 4/8] Removing RepoSearchResult and updating paging to latest version --- .../codelabs/paging/data/GithubPagingSource.kt | 18 +++++++++++++++++- .../paging/ui/SearchRepositoriesActivity.kt | 2 +- build.gradle | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt b/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt index 77f6cb68..3d941400 100644 --- a/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt +++ b/app/src/main/java/com/example/android/codelabs/paging/data/GithubPagingSource.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2018 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. + */ + package com.example.android.codelabs.paging.data import androidx.paging.PagingSource @@ -33,4 +49,4 @@ class GithubPagingSource( LoadResult.Error(IOException(apiResponse.message())) } } -} \ No newline at end of file +} 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 0602dabf..8265b9c3 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 @@ -118,7 +118,7 @@ class SearchRepositoriesActivity : AppCompatActivity() { searchJob = lifecycleScope.launch { viewModel.searchRepo(query).collect { Log.d("SearchRepositoriesActivity", "query: $query, collecting $it") - adapter.collectFrom(it) + adapter.presentData(it) } } } diff --git a/build.gradle b/build.gradle index db02f375..52f37cd2 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ allprojects { repositories { google() jcenter() - maven { url 'https://ci.android.com/builds/submitted/6229151/androidx_snapshot/latest/repository' } + maven { url 'https://ci.android.com/builds/submitted/6261180/androidx_snapshot/latest/repository' } } } From b8eae012ee016a4f8b17969dd1b876b2e59585a9 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Thu, 5 Mar 2020 15:24:23 +0000 Subject: [PATCH 5/8] Improving the ReposAdapter based on review comments --- .../codelabs/paging/ui/ReposAdapter.kt | 13 ++++++------- .../paging/ui/SearchRepositoriesViewModel.kt | 19 +++++++------------ 2 files changed, 13 insertions(+), 19 deletions(-) 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 6a3440bf..778678d5 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 @@ -27,17 +27,16 @@ import com.example.android.codelabs.paging.model.Repo /** * Adapter for the list of repositories. */ -class ReposAdapter : PagingDataAdapter(REPO_COMPARATOR) { +class ReposAdapter : PagingDataAdapter(REPO_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepoViewHolder { return RepoViewHolder.create(parent) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val repoItem = getItem(position) - if (repoItem != null) { - (holder as RepoViewHolder).bind(repoItem) - } + override fun onBindViewHolder(holder: RepoViewHolder, position: Int) { + // placeholders are not enabled, so items will never be null + val repoItem = getItem(position)!! + holder.bind(repoItem) } companion object { 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 de77320c..63c10015 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 @@ -16,12 +16,7 @@ package com.example.android.codelabs.paging.ui -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.asLiveData -import androidx.lifecycle.liveData -import androidx.lifecycle.switchMap import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn @@ -40,22 +35,22 @@ import kotlinx.coroutines.flow.Flow class SearchRepositoriesViewModel(private val repository: GithubRepository) : ViewModel() { @Volatile - private var lastQueryValue: String? = null + private var currentQueryValue: String? = null @Volatile - private var lastSearchResult: Flow>? = null + private var currentSearchResult: Flow>? = null /** * Search a repository based on a query string. */ fun searchRepo(queryString: String): Flow> { - val result = lastSearchResult - if(queryString == lastQueryValue && result != null){ - return result + val lastResult = currentSearchResult + if(queryString == currentQueryValue && lastResult != null){ + return lastResult } - lastQueryValue = queryString + currentQueryValue = queryString val newResult = repository.getSearchResultStream(queryString) .cachedIn(viewModelScope) - lastSearchResult = newResult + currentSearchResult = newResult return newResult } } \ No newline at end of file From 091918a5b4b50a7ac9ee53aa573f67ab7bb26390 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Wed, 4 Mar 2020 16:47:15 +0000 Subject: [PATCH 6/8] Removing RepoSearchResult and updating paging to latest version --- .../codelabs/paging/api/RepoSearchResponse.kt | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt diff --git a/app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt b/app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt deleted file mode 100644 index b48f15ab..00000000 --- a/app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -package com.example.android.codelabs.paging.api - -import com.example.android.codelabs.paging.model.Repo -import com.google.gson.annotations.SerializedName - -/** - * Data class to hold repo responses from searchRepo API calls. - */ -data class RepoSearchResponse( - @SerializedName("total_count") val total: Int = 0, - @SerializedName("items") val items: List = emptyList(), - val nextPage: Int? = null -) From 850fad0e346604a2d22abca5da85a02c3d27e65a Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Fri, 28 Feb 2020 09:02:57 +0000 Subject: [PATCH 7/8] Adding separators to list of repositories --- .../codelabs/paging/api/RepoSearchResponse.kt | 29 ++++++++++++ .../codelabs/paging/ui/ReposAdapter.kt | 47 +++++++++++++------ .../paging/ui/SearchRepositoriesViewModel.kt | 35 ++++++++++++-- .../codelabs/paging/ui/SeparatorViewHolder.kt | 40 ++++++++++++++++ .../main/res/layout/separator_view_item.xml | 34 ++++++++++++++ 5 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt create mode 100644 app/src/main/java/com/example/android/codelabs/paging/ui/SeparatorViewHolder.kt create mode 100644 app/src/main/res/layout/separator_view_item.xml diff --git a/app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt b/app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt new file mode 100644 index 00000000..b48f15ab --- /dev/null +++ b/app/src/main/java/com/example/android/codelabs/paging/api/RepoSearchResponse.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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. + */ + +package com.example.android.codelabs.paging.api + +import com.example.android.codelabs.paging.model.Repo +import com.google.gson.annotations.SerializedName + +/** + * Data class to hold repo responses from searchRepo API calls. + */ +data class RepoSearchResponse( + @SerializedName("total_count") val total: Int = 0, + @SerializedName("items") val items: List = emptyList(), + val nextPage: Int? = null +) 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 778678d5..3b7c9bbe 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 @@ -19,32 +19,51 @@ package com.example.android.codelabs.paging.ui import android.view.ViewGroup import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder -import com.example.android.codelabs.paging.model.Repo +import com.example.android.codelabs.paging.R /** * Adapter for the list of repositories. */ -class ReposAdapter : PagingDataAdapter(REPO_COMPARATOR) { +class ReposAdapter : PagingDataAdapter(UIMODEL_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepoViewHolder { - return RepoViewHolder.create(parent) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return if(viewType == R.layout.repo_view_item) { + RepoViewHolder.create(parent) + } else { + SeparatorViewHolder.create(parent) + } } - override fun onBindViewHolder(holder: RepoViewHolder, position: Int) { - // placeholders are not enabled, so items will never be null - val repoItem = getItem(position)!! - holder.bind(repoItem) + override fun getItemViewType(position: Int): Int { + return when(getItem(position)){ + is UiModel.RepoItem -> R.layout.repo_view_item + is UiModel.SeparatorItem -> R.layout.separator_view_item + null -> 0 + } + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val uiModel = getItem(position) + uiModel.let { + when(uiModel){ + is UiModel.RepoItem -> (holder as RepoViewHolder).bind(uiModel.repo) + is UiModel.SeparatorItem -> (holder as SeparatorViewHolder).bind(uiModel.description) + } + } } companion object { - private val REPO_COMPARATOR = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Repo, newItem: Repo): Boolean = - oldItem.fullName == newItem.fullName + private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: UiModel, newItem: UiModel): Boolean { + return if (oldItem is UiModel.RepoItem && newItem is UiModel.RepoItem) { + oldItem.repo.fullName == newItem.repo.fullName + } else { + false + } + } - override fun areContentsTheSame(oldItem: Repo, newItem: Repo): Boolean = + override fun areContentsTheSame(oldItem: UiModel, newItem: UiModel): Boolean = oldItem == newItem } } 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 63c10015..8ff325f5 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 @@ -16,6 +16,7 @@ package com.example.android.codelabs.paging.ui +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData @@ -25,6 +26,7 @@ import com.example.android.codelabs.paging.model.Repo import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * ViewModel for the [SearchRepositoriesActivity] screen. @@ -37,20 +39,45 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi @Volatile private var currentQueryValue: String? = null @Volatile - private var currentSearchResult: Flow>? = null + private var currentSearchResult: Flow>? = null /** * Search a repository based on a query string. */ - fun searchRepo(queryString: String): Flow> { + fun searchRepo(queryString: String): Flow> { val lastResult = currentSearchResult if(queryString == currentQueryValue && lastResult != null){ return lastResult } currentQueryValue = queryString - val newResult = repository.getSearchResultStream(queryString) + val newResult: Flow> = repository.getSearchResultStream(queryString) + .map { pagingData -> pagingData.map { UiModel.RepoItem(it) as UiModel } } + .map { + it.insertSeparators { before, after -> + if (before == null && after is UiModel.RepoItem) { + Log.d("ViewModel", "-> adding null separator") + UiModel.SeparatorItem("${after.repo.stars / 10_000}0.000+ stars") + } + if (before is UiModel.RepoItem && after is UiModel.RepoItem + && before.repo.stars / 10_000 > after.repo.stars / 10_000) { + if (after.repo.stars >= 10_000) { + UiModel.SeparatorItem("${after.repo.stars / 10_000}0.000+ stars") + } else { + UiModel.SeparatorItem("< 10.000+ stars") + } + } else { + // no separator + null + } + } + } .cachedIn(viewModelScope) currentSearchResult = newResult return newResult } -} \ No newline at end of file +} + +sealed class UiModel { + data class RepoItem(val repo: Repo) : UiModel() + data class SeparatorItem(val description: String) : UiModel() +} diff --git a/app/src/main/java/com/example/android/codelabs/paging/ui/SeparatorViewHolder.kt b/app/src/main/java/com/example/android/codelabs/paging/ui/SeparatorViewHolder.kt new file mode 100644 index 00000000..07a490cb --- /dev/null +++ b/app/src/main/java/com/example/android/codelabs/paging/ui/SeparatorViewHolder.kt @@ -0,0 +1,40 @@ +package com.example.android.codelabs.paging.ui + +/* + * Copyright (C) 2018 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. + */ + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.android.codelabs.paging.R + +class SeparatorViewHolder(view: View) : RecyclerView.ViewHolder(view) { + private val description: TextView = view.findViewById(R.id.separator_description) + + fun bind(separatorText: String) { + description.text = separatorText + } + + companion object { + fun create(parent: ViewGroup): SeparatorViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.separator_view_item, parent, false) + return SeparatorViewHolder(view) + } + } +} diff --git a/app/src/main/res/layout/separator_view_item.xml b/app/src/main/res/layout/separator_view_item.xml new file mode 100644 index 00000000..fdae76f4 --- /dev/null +++ b/app/src/main/res/layout/separator_view_item.xml @@ -0,0 +1,34 @@ + + + + + \ No newline at end of file From 8e1f3746c4a0c25f97f317a6bd87f202370cefb1 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Wed, 11 Mar 2020 17:34:35 +0000 Subject: [PATCH 8/8] Fixing first separator not showing and making the project AS 3.6 compatible --- app/build.gradle | 4 ++-- .../codelabs/paging/ui/SearchRepositoriesViewModel.kt | 11 ++++++----- build.gradle | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 03bb3552..bf719fb6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,8 +40,8 @@ android { jvmTarget = "1.8" } - buildFeatures { - viewBinding = true + viewBinding { + enabled = true } } 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 8ff325f5..8b1f7b94 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,7 +20,9 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData +import androidx.paging.PagingData.Companion.insertSeparators import androidx.paging.cachedIn +import androidx.paging.insertSeparators import com.example.android.codelabs.paging.data.GithubRepository import com.example.android.codelabs.paging.model.Repo import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -46,19 +48,18 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi */ fun searchRepo(queryString: String): Flow> { val lastResult = currentSearchResult - if(queryString == currentQueryValue && lastResult != null){ + if (queryString == currentQueryValue && lastResult != null) { return lastResult } currentQueryValue = queryString val newResult: Flow> = repository.getSearchResultStream(queryString) - .map { pagingData -> pagingData.map { UiModel.RepoItem(it) as UiModel } } + .map { pagingData -> pagingData.map { UiModel.RepoItem(it) } } .map { - it.insertSeparators { before, after -> + it.insertSeparators { before, after -> if (before == null && after is UiModel.RepoItem) { Log.d("ViewModel", "-> adding null separator") UiModel.SeparatorItem("${after.repo.stars / 10_000}0.000+ stars") - } - if (before is UiModel.RepoItem && after is UiModel.RepoItem + } else if (before is UiModel.RepoItem && after is UiModel.RepoItem && before.repo.stars / 10_000 > after.repo.stars / 10_000) { if (after.repo.stars >= 10_000) { UiModel.SeparatorItem("${after.repo.stars / 10_000}0.000+ stars") diff --git a/build.gradle b/build.gradle index 52f37cd2..132d3a12 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0-alpha09' + classpath 'com.android.tools.build:gradle:3.6.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong @@ -52,7 +52,7 @@ ext { materialVersion = '1.1.0' archComponentsVersion = '2.2.0' roomVersion = '2.2.4' - pagingVersion = '3.0.0-SNAPSHOT' + pagingVersion = '3.0.0-alpha01' retrofitVersion = '2.6.0' okhttpLoggingInterceptorVersion = '4.0.0' coroutines = '1.3.0'