Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ class GithubPagingSource(
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repo> {
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
)
} else {
LoadResult.Error(IOException(apiResponse.message()))
try {
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
)
} else {
LoadResult.Error(IOException(apiResponse.message()))
}
} catch (exception: Exception) {
return LoadResult.Error(exception)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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.ViewGroup
import androidx.paging.LoadState
import androidx.paging.LoadStateAdapter

class ReposLoadStateAdapter(private val retry: () -> Unit) : LoadStateAdapter<ReposLoadStateViewHolder>() {
override fun onBindViewHolder(holder: ReposLoadStateViewHolder, loadState: LoadState) {
holder.bind(loadState)
}

override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): ReposLoadStateViewHolder {
return ReposLoadStateViewHolder.create(retry, parent)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.paging.LoadState
import androidx.recyclerview.widget.RecyclerView
import com.example.android.codelabs.paging.R

class ReposLoadStateViewHolder(
retry: () -> Unit,
view: View
) : RecyclerView.ViewHolder(view) {
private val progressBar: ProgressBar = itemView.findViewById(R.id.progress_bar)
private val errorMsg: TextView = itemView.findViewById(R.id.error_msg)
private val retry: Button = itemView.findViewById<Button>(R.id.retry_button).also {
it.setOnClickListener { retry.invoke() }
}

fun bind(loadState: LoadState) {
if (loadState is LoadState.Error) {
errorMsg.text = loadState.error.localizedMessage
}
progressBar.visibility = toVisibility(loadState == LoadState.Loading)
retry.visibility = toVisibility(loadState != LoadState.Loading)
errorMsg.visibility = toVisibility(loadState != LoadState.Loading)
}

private fun toVisibility(constraint: Boolean): Int = if (constraint) {
View.VISIBLE
} else {
View.GONE
}

companion object {
fun create(retry: () -> Unit, parent: ViewGroup): ReposLoadStateViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.repos_load_state_header_view_item, parent, false)
return ReposLoadStateViewHolder(retry, view)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.paging.LoadState
import androidx.paging.LoadType
import androidx.recyclerview.widget.DividerItemDecoration
import com.example.android.codelabs.paging.Injection
import com.example.android.codelabs.paging.databinding.ActivitySearchRepositoriesBinding
Expand Down Expand Up @@ -58,9 +59,21 @@ class SearchRepositoriesActivity : AppCompatActivity() {
val decoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
binding.list.addItemDecoration(decoration)

binding.list.adapter = adapter
binding.list.adapter = adapter.withLoadStateHeaderAndFooter(
header = ReposLoadStateAdapter { adapter.retry() },
footer = ReposLoadStateAdapter { adapter.retry() }
)
adapter.addLoadStateListener { loadType, loadState ->
Log.d("SearchRepositoriesActivity", "adapter load: type = $loadType state = $loadState")
if (loadType == LoadType.REFRESH) {
binding.list.visibility = View.GONE
binding.progressBar.visibility = toVisibility(loadState == LoadState.Loading)
binding.retryButton.visibility = toVisibility(loadState is LoadState.Error)
} else {
binding.list.visibility = View.VISIBLE
binding.progressBar.visibility = View.GONE
binding.retryButton.visibility = View.GONE
}
if (loadState is LoadState.Error) {
Toast.makeText(
this,
Expand All @@ -72,6 +85,7 @@ class SearchRepositoriesActivity : AppCompatActivity() {
val query = savedInstanceState?.getString(LAST_SEARCH_QUERY) ?: DEFAULT_QUERY
search(query)
initSearch(query)
binding.retryButton.setOnClickListener { adapter.retry() }
}

override fun onSaveInstanceState(outState: Bundle) {
Expand Down Expand Up @@ -105,9 +119,6 @@ class SearchRepositoriesActivity : AppCompatActivity() {
if (it.isNotEmpty()) {
binding.list.scrollToPosition(0)
search(it.toString())
// TODO how to clear the list
// might not need it because of how Paging works
// adapter.collectFrom(PagingData.empty())
}
}
}
Expand All @@ -123,14 +134,10 @@ class SearchRepositoriesActivity : AppCompatActivity() {
}
}

private fun showEmptyList(show: Boolean) {
if (show) {
binding.emptyList.visibility = View.VISIBLE
binding.list.visibility = View.GONE
} else {
binding.emptyList.visibility = View.GONE
binding.list.visibility = View.VISIBLE
}
private fun toVisibility(constraint: Boolean): Int = if (constraint) {
View.VISIBLE
} else {
View.GONE
}

companion object {
Expand Down
31 changes: 20 additions & 11 deletions app/src/main/res/layout/activity_search_repositories.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,25 @@
app:layout_constraintTop_toBottomOf="@+id/input_layout"
tools:ignore="UnusedAttribute"/>

<TextView android:id="@+id/emptyList"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_results"
android:textSize="@dimen/repo_name_size"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>

<Button
android:id="@+id/retry_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/retry"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
42 changes: 42 additions & 0 deletions app/src/main/res/layout/repos_load_state_header_view_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/error_msg"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/error_text_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
tools:text="Timeout"/>
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<Button
android:id="@+id/retry_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/retry"/>
</LinearLayout>
1 change: 1 addition & 0 deletions app/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@
<dimen name="repo_icon_size">24dp</dimen>
<dimen name="loading_progress_bar_size">36dp</dimen>
<dimen name="placeholder_item_height">32dp</dimen>
<dimen name="error_text_size">24sp</dimen>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
<string name="search_hint">GitHub Repository</string>
<string name="no_results">No results 😓</string>
<string name="unknown">\?</string>
<string name="retry">Retry</string>
</resources>