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
27 changes: 15 additions & 12 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'

android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion rootProject.compileSdkVersion
defaultConfig {
applicationId "com.example.android.codelabs.paging"
Expand All @@ -39,18 +43,17 @@ android {

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation "com.android.support:appcompat-v7:$supportLibVersion"
implementation "com.android.support.constraint:constraint-layout:$constraintLayoutVersion"
implementation "com.android.support:design:$supportLibVersion"
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "androidx.appcompat:appcompat:$appCompat"
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
implementation "com.google.android.material:material:$design"

// architecture components
implementation "android.arch.lifecycle:extensions:$archComponentsVersion"
implementation "android.arch.lifecycle:runtime:$archComponentsVersion"
implementation "android.arch.persistence.room:runtime:$roomVersion"
implementation "android.arch.paging:runtime:$pagingVersion"
kapt "android.arch.lifecycle:compiler:$archComponentsVersion"
kapt "android.arch.persistence.room:compiler:$roomVersion"
implementation "androidx.paging:paging-runtime:$pagingVersion"
kapt "androidx.room:room-compiler:$roomVersion"
implementation "androidx.room:room-runtime:$roomVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$archComponentsVersion"
implementation "androidx.lifecycle:lifecycle-common-java8:$archComponentsVersion"

// retrofit
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
Expand All @@ -60,6 +63,6 @@ dependencies {

// testing
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "com.android.support.test:runner:$runnerVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "androidx.test:runner:$runnerVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package com.example.android.codelabs.paging

import android.arch.lifecycle.ViewModelProvider
import android.content.Context
import androidx.lifecycle.ViewModelProvider
import com.example.android.codelabs.paging.api.GithubService
import com.example.android.codelabs.paging.data.GithubRepository
import com.example.android.codelabs.paging.db.GithubLocalCache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

package com.example.android.codelabs.paging.data

import android.arch.lifecycle.MutableLiveData
import android.util.Log
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

Expand All @@ -31,49 +30,31 @@ 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)

return RepoSearchResult(data, networkErrors)
}
// Get data source factory from the local cache
val dataSourceFactory = cache.reposByName(query)

fun requestMore(query: String) {
requestAndSaveData(query)
}
// every new query creates a new BoundaryCallback
// The BoundaryCallback will observe when the user reaches to the edges of
// the list and update the database with extra data
val boundaryCallback = RepoBoundaryCallback(query, service, cache)
val networkErrors = boundaryCallback.networkErrors

private fun requestAndSaveData(query: String) {
if (isRequestInProgress) return
// Get the paged list
val data = LivePagedListBuilder(dataSourceFactory, DATABASE_PAGE_SIZE)
.setBoundaryCallback(boundaryCallback)
.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, networkErrors)
}

companion object {
private const val NETWORK_PAGE_SIZE = 50
private const val DATABASE_PAGE_SIZE = 20
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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 android.util.Log
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

/**
* This boundary callback gets notified when user reaches to the edges of the list for example when
* the database cannot provide any more data.
**/
class RepoBoundaryCallback(
private val query: String,
private val service: GithubService,
private val cache: GithubLocalCache
) : PagedList.BoundaryCallback<Repo>() {

companion object {
private const val NETWORK_PAGE_SIZE = 50
}

// 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

/**
* Database returned 0 items. We should query the backend for more items.
*/
override fun onZeroItemsLoaded() {
Log.d("RepoBoundaryCallback", "onZeroItemsLoaded")
requestAndSaveData(query)
}

/**
* When all items in the database were loaded, we need to query the backend for more items.
*/
override fun onItemAtEndLoaded(itemAtEnd: Repo) {
Log.d("RepoBoundaryCallback", "onItemAtEndLoaded")
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
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@

package com.example.android.codelabs.paging.db

import android.arch.lifecycle.LiveData
import android.arch.paging.DataSource
import android.util.Log
import androidx.paging.DataSource
import com.example.android.codelabs.paging.model.Repo
import java.util.concurrent.Executor

Expand All @@ -34,7 +33,7 @@ class GithubLocalCache(
/**
* Insert a list of repos in the database, on a background thread.
*/
fun insert(repos: List<Repo>, insertFinished: ()-> Unit) {
fun insert(repos: List<Repo>, insertFinished: () -> Unit) {
ioExecutor.execute {
Log.d("GithubLocalCache", "inserting ${repos.size} repos")
repoDao.insert(repos)
Expand All @@ -48,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

package com.example.android.codelabs.paging.db

import android.arch.lifecycle.LiveData
import android.arch.persistence.room.Dao
import android.arch.persistence.room.Insert
import android.arch.persistence.room.OnConflictStrategy
import android.arch.persistence.room.Query
import androidx.paging.DataSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.android.codelabs.paging.model.Repo

/**
Expand All @@ -37,6 +37,6 @@ 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>

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

package com.example.android.codelabs.paging.db

import android.arch.persistence.room.Database
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.android.codelabs.paging.model.Repo

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package com.example.android.codelabs.paging.model

import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@

package com.example.android.codelabs.paging.model

import android.arch.lifecycle.LiveData
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 data: LiveData<PagedList<Repo>>,
val networkErrors: LiveData<String>
)
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ package com.example.android.codelabs.paging.ui

import android.content.Intent
import android.net.Uri
import android.support.v7.widget.RecyclerView
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
import com.example.android.codelabs.paging.model.Repo

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@

package com.example.android.codelabs.paging.ui

import android.support.v7.recyclerview.extensions.ListAdapter
import android.support.v7.util.DiffUtil
import android.support.v7.widget.RecyclerView
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.android.codelabs.paging.model.Repo

/**
* Adapter for the list of repositories.
*/
class ReposAdapter : ListAdapter<Repo, RecyclerView.ViewHolder>(REPO_COMPARATOR) {
class ReposAdapter : PagedListAdapter<Repo, RecyclerView.ViewHolder>(REPO_COMPARATOR) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return RepoViewHolder.create(parent)
Expand Down
Loading