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 index b48f15ab..efd7cee2 100644 --- 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 @@ -23,7 +23,7 @@ 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 -) + @SerializedName("total_count") val total: Int = 0, + @SerializedName("items") val items: List = emptyList(), + val nextPage: Int? = null +) \ No newline at end of file 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 e7aecf2f..4b354a93 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 @@ -21,28 +21,49 @@ import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.example.android.codelabs.paging.R -import com.example.android.codelabs.paging.model.Repo +import java.lang.UnsupportedOperationException /** * Adapter for the list of repositories. */ -class ReposAdapter : PagingDataAdapter(REPO_COMPARATOR) { +class ReposAdapter : PagingDataAdapter(UIMODEL_COMPARATOR) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return RepoViewHolder.create(parent) + return if (viewType == R.layout.repo_view_item) { + RepoViewHolder.create(parent) + } else { + SeparatorViewHolder.create(parent) + } + } + + 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 -> throw UnsupportedOperationException("Unknown view") + } } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val repo = getItem(position) - (holder as RepoViewHolder).bind(repo) + 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 (oldItem is UiModel.RepoItem && newItem is UiModel.RepoItem && + oldItem.repo.fullName == newItem.repo.fullName) || + (oldItem is UiModel.SeparatorItem && newItem is UiModel.SeparatorItem && + oldItem.description == newItem.description) + } - 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/ReposLoadStateViewHolder.kt b/app/src/main/java/com/example/android/codelabs/paging/ui/ReposLoadStateViewHolder.kt index 19d806f6..dd09a41d 100644 --- a/app/src/main/java/com/example/android/codelabs/paging/ui/ReposLoadStateViewHolder.kt +++ b/app/src/main/java/com/example/android/codelabs/paging/ui/ReposLoadStateViewHolder.kt @@ -62,3 +62,4 @@ class ReposLoadStateViewHolder( } } } + 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 121fdbb9..8ab04500 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 @@ -77,7 +77,7 @@ class SearchRepositoriesActivity : AppCompatActivity() { adapter.addLoadStateListener { loadType, loadState -> Log.d("SearchRepositoriesActivity", "adapter load: type = $loadType state = $loadState") if (loadType == LoadType.REFRESH) { - binding.list.visibility = View.GONE + binding.list.visibility = toVisibility(loadState == LoadState.Idle) binding.progressBar.visibility = toVisibility(loadState == LoadState.Loading) binding.retryButton.visibility = toVisibility(loadState is LoadState.Error) } else { 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 8197f43b..85706c75 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,14 +16,17 @@ package com.example.android.codelabs.paging.ui +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData 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 import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * ViewModel for the [SearchRepositoriesActivity] screen. @@ -36,20 +39,46 @@ class SearchRepositoriesViewModel(private val repository: GithubRepository) : Vi 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: Flow> = repository.getSearchResultStream(queryString) + val newResult: Flow> = repository.getSearchResultStream(queryString) + .map { pagingData -> pagingData.map { UiModel.RepoItem(it) } } + .map { + it.insertSeparators { before, after -> + if (before == null && after != null) { + UiModel.SeparatorItem("${after.roundedStarCount}0.000+ stars") + } else if (before != null && after != null + && before.roundedStarCount > after.roundedStarCount) { + if (after.roundedStarCount >= 1) { + UiModel.SeparatorItem("${after.roundedStarCount}0.000+ stars") + } else { + UiModel.SeparatorItem("< 10.000+ stars") + } + } else { + // no separator + null + } + } + } .cachedIn(viewModelScope) currentSearchResult = newResult return newResult } } + +sealed class UiModel { + data class RepoItem(val repo: Repo) : UiModel() + data class SeparatorItem(val description: String) : UiModel() +} + +private val UiModel.RepoItem.roundedStarCount: Int + get() = this.repo.stars / 10_000 \ No newline at end of file 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..22bd5222 --- /dev/null +++ b/app/src/main/java/com/example/android/codelabs/paging/ui/SeparatorViewHolder.kt @@ -0,0 +1,40 @@ +/* + * 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.ui + +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) + } + } +} \ No newline at end of file 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..ad3a5e39 --- /dev/null +++ b/app/src/main/res/layout/separator_view_item.xml @@ -0,0 +1,34 @@ + + + + +