diff --git a/app/src/main/java/com/example/android/codelabs/paging/Injection.kt b/app/src/main/java/com/example/android/codelabs/paging/Injection.kt index c67a1950..619d00c9 100644 --- a/app/src/main/java/com/example/android/codelabs/paging/Injection.kt +++ b/app/src/main/java/com/example/android/codelabs/paging/Injection.kt @@ -20,10 +20,7 @@ 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 -import com.example.android.codelabs.paging.db.RepoDatabase import com.example.android.codelabs.paging.ui.ViewModelFactory -import java.util.concurrent.Executors /** * Class that handles object creation. @@ -32,27 +29,19 @@ import java.util.concurrent.Executors */ object Injection { - /** - * Creates an instance of [GithubLocalCache] based on the database DAO. - */ - private fun provideCache(context: Context): GithubLocalCache { - val database = RepoDatabase.getInstance(context) - return GithubLocalCache(database.reposDao(), Executors.newSingleThreadExecutor()) - } - /** * Creates an instance of [GithubRepository] based on the [GithubService] and a * [GithubLocalCache] */ - private fun provideGithubRepository(context: Context): GithubRepository { - return GithubRepository(GithubService.create(), provideCache(context)) + private fun provideGithubRepository(): GithubRepository { + return GithubRepository(GithubService.create()) } /** * Provides the [ViewModelProvider.Factory] that is then used to get a reference to * [ViewModel] objects. */ - fun provideViewModelFactory(context: Context): ViewModelProvider.Factory { - return ViewModelFactory(provideGithubRepository(context)) + fun provideViewModelFactory(): ViewModelProvider.Factory { + return ViewModelFactory(provideGithubRepository()) } } 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 b1b9b6c4..c6c59e64 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,19 +17,21 @@ package com.example.android.codelabs.paging.data import android.util.Log +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations 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 import com.example.android.codelabs.paging.model.RepoSearchResult /** * Repository class that works with local and remote data sources. */ -class GithubRepository( - private val service: GithubService, - private val cache: GithubLocalCache -) { +class GithubRepository(private val service: GithubService) { + + // keep the list of responses + private val inMemoryCache = MutableLiveData>() // keep the last requested page. When the request is successful, increment the page number. private var lastRequestedPage = 1 @@ -48,8 +50,8 @@ class GithubRepository( lastRequestedPage = 1 requestAndSaveData(query) - // Get data from the local cache - val data = cache.reposByName(query) + // Get data from the in memory cache + val data = reposByName(query) return RepoSearchResult(data, networkErrors) } @@ -63,16 +65,33 @@ class GithubRepository( isRequestInProgress = true searchRepos(service, query, lastRequestedPage, NETWORK_PAGE_SIZE, { repos -> - cache.insert(repos) { - lastRequestedPage++ - isRequestInProgress = false - } + // add the new result list to the existing list + val allResults = mutableListOf() + inMemoryCache.value?.let { allResults.addAll(it) } + allResults.addAll(repos) + + inMemoryCache.postValue(allResults) + lastRequestedPage++ + isRequestInProgress = false }, { error -> networkErrors.postValue(error) isRequestInProgress = false }) } + private fun reposByName(query: String): LiveData> { + return Transformations.switchMap(inMemoryCache) { repos -> + // from the in memory cache select only the repos whose name or description matches + // the query. Then order the results. + val filteredList = repos.filter { + it.name.contains(query, true) || + (it.description != null && it.description.contains(query, true)) + }.sortedWith(compareByDescending { it.stars }.thenBy { it.name }) + + MutableLiveData(filteredList) + } + } + 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 deleted file mode 100644 index e427134f..00000000 --- a/app/src/main/java/com/example/android/codelabs/paging/db/GithubLocalCache.kt +++ /dev/null @@ -1,55 +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.db - -import android.util.Log -import androidx.lifecycle.LiveData -import com.example.android.codelabs.paging.model.Repo -import java.util.concurrent.Executor - -/** - * Class that handles the DAO local data source. This ensures that methods are triggered on the - * correct executor. - */ -class GithubLocalCache( - private val repoDao: RepoDao, - private val ioExecutor: Executor -) { - - /** - * Insert a list of repos in the database, on a background thread. - */ - fun insert(repos: List, insertFinished: () -> Unit) { - ioExecutor.execute { - Log.d("GithubLocalCache", "inserting ${repos.size} repos") - repoDao.insert(repos) - insertFinished() - } - } - - /** - * Request a LiveData> from the Dao, based on a repo name. If the name contains - * multiple words separated by spaces, then we're emulating the GitHub API behavior and allow - * any characters between the words. - * @param name repository name - */ - fun reposByName(name: String): LiveData> { - // 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 deleted file mode 100644 index 60665fad..00000000 --- a/app/src/main/java/com/example/android/codelabs/paging/db/RepoDao.kt +++ /dev/null @@ -1,41 +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.db - -import androidx.lifecycle.LiveData -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.example.android.codelabs.paging.model.Repo - -/** - * Room data access object for accessing the [Repo] table. - */ -@Dao -interface RepoDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insert(posts: List) - - // Do a similar query as the search API: - // Look for repos that contain the query string in the name or in the description - // 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> -} diff --git a/app/src/main/java/com/example/android/codelabs/paging/db/RepoDatabase.kt b/app/src/main/java/com/example/android/codelabs/paging/db/RepoDatabase.kt deleted file mode 100644 index 2c5db0c0..00000000 --- a/app/src/main/java/com/example/android/codelabs/paging/db/RepoDatabase.kt +++ /dev/null @@ -1,53 +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.db - -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import android.content.Context -import com.example.android.codelabs.paging.model.Repo - -/** - * Database schema that holds the list of repos. - */ -@Database( - entities = [Repo::class], - version = 1, - exportSchema = false -) -abstract class RepoDatabase : RoomDatabase() { - - abstract fun reposDao(): RepoDao - - companion object { - - @Volatile - private var INSTANCE: RepoDatabase? = null - - fun getInstance(context: Context): RepoDatabase = - INSTANCE ?: synchronized(this) { - INSTANCE - ?: buildDatabase(context).also { INSTANCE = it } - } - - private fun buildDatabase(context: Context) = - Room.databaseBuilder(context.applicationContext, - RepoDatabase::class.java, "Github.db") - .build() - } -} diff --git a/app/src/main/java/com/example/android/codelabs/paging/model/Repo.kt b/app/src/main/java/com/example/android/codelabs/paging/model/Repo.kt index 87fbc41a..36f0fa59 100644 --- a/app/src/main/java/com/example/android/codelabs/paging/model/Repo.kt +++ b/app/src/main/java/com/example/android/codelabs/paging/model/Repo.kt @@ -36,4 +36,9 @@ data class Repo( @field:SerializedName("stargazers_count") val stars: Int, @field:SerializedName("forks_count") val forks: Int, @field:SerializedName("language") val language: String? -) +){ + + override fun toString(): String { + return "Repo(name='$name', stars=$stars)" + } +} 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 53029c1c..acb85977 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 @@ -24,7 +24,7 @@ import android.view.inputmethod.EditorInfo import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import com.example.android.codelabs.paging.Injection import com.example.android.codelabs.paging.R @@ -41,7 +41,7 @@ class SearchRepositoriesActivity : AppCompatActivity() { setContentView(R.layout.activity_search_repositories) // get the view model - viewModel = ViewModelProviders.of(this, Injection.provideViewModelFactory(this)) + viewModel = ViewModelProvider(this, Injection.provideViewModelFactory()) .get(SearchRepositoriesViewModel::class.java) // add dividers between RecyclerView's row items