Skip to content
Open
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
3 changes: 2 additions & 1 deletion app/src/main/java/otus/homework/reactivecats/CatsService.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package otus.homework.reactivecats

import io.reactivex.Single
import retrofit2.Call
import retrofit2.http.GET

interface CatsService {

//@GET("random?animal_type=cat")
@GET("fact")
fun getCatFact(): Call<Fact>
fun getCatFact(): Single<Fact>
}
127 changes: 107 additions & 20 deletions app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,127 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import io.reactivex.Flowable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import retrofit2.HttpException
import java.io.IOException
import java.util.concurrent.TimeUnit

class CatsViewModel(
catsService: CatsService,
localCatFactsGenerator: LocalCatFactsGenerator,
context: Context
private val catsService: CatsService,
private val localCatFactsGenerator: LocalCatFactsGenerator,
private val context: Context
) : ViewModel() {

private val _catsLiveData = MutableLiveData<Result>()
val catsLiveData: LiveData<Result> = _catsLiveData

private val compositeDisposable = CompositeDisposable()

private var isPeriodicUpdatesActive = false

init {
catsService.getCatFact().enqueue(object : Callback<Fact> {
override fun onResponse(call: Call<Fact>, response: Response<Fact>) {
if (response.isSuccessful && response.body() != null) {
_catsLiveData.value = Success(response.body()!!)
} else {
_catsLiveData.value = Error(
response.errorBody()?.string() ?: context.getString(
R.string.default_error_text
)
)
getSingleFact()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

По заданию нужно убрать колбеки из init блока

}

fun getFacts() {
if (isPeriodicUpdatesActive) {
stopPeriodicUpdates()
return
}

isPeriodicUpdatesActive = true

val disposable = Flowable.interval(0, 2, TimeUnit.SECONDS)
.flatMapSingle { _ ->
catsService.getCatFact()
.subscribeOn(Schedulers.io())
.onErrorResumeNext { throwable: Throwable ->
if (throwable is IOException || throwable is HttpException) {
localCatFactsGenerator.generateCatFact()
.subscribeOn(Schedulers.io())
} else {
Single.error(throwable)
}
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ fact ->
_catsLiveData.value = Success(fact)
},
{ throwable ->
handleError(throwable)
isPeriodicUpdatesActive = false
}
)

compositeDisposable.add(disposable)
}

fun getSingleFact() {
val disposable = catsService.getCatFact()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ fact ->
_catsLiveData.value = Success(fact)
},
{ throwable ->
handleError(throwable)
}
)

compositeDisposable.add(disposable)
}

fun stopPeriodicUpdates() {
isPeriodicUpdatesActive = false
compositeDisposable.clear()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отметим на будущее, что clear() очищает все подписки. Если в будущем появятся другие подписки, они тоже будут отменены

}

private fun handleError(throwable: Throwable) {
when (throwable) {
is HttpException -> {
val errorBody = throwable.response()?.errorBody()?.string()
?: context.getString(R.string.default_error_text)
_catsLiveData.value = Error(errorBody)
}

is IOException -> {
getLocalFactAsFallback()
}

override fun onFailure(call: Call<Fact>, t: Throwable) {
_catsLiveData.value = ServerError
else -> {
_catsLiveData.value = Error(context.getString(R.string.default_error_text))
}
})
}
}

private fun getLocalFactAsFallback() {
val disposable = localCatFactsGenerator.generateCatFact()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ fact ->
_catsLiveData.value = Success(fact)
},
{ error ->
_catsLiveData.value = ServerError
}
)

compositeDisposable.add(disposable)
}

fun getFacts() {}
override fun onCleared() {
super.onCleared()
compositeDisposable.clear()
}
}

class CatsViewModelFactory(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package otus.homework.reactivecats
import android.content.Context
import io.reactivex.Flowable
import io.reactivex.Single
import java.util.concurrent.TimeUnit
import kotlin.random.Random

class LocalCatFactsGenerator(
Expand All @@ -15,7 +16,12 @@ class LocalCatFactsGenerator(
* обернутую в подходящий стрим(Flowable/Single/Observable и т.п)
*/
fun generateCatFact(): Single<Fact> {
return Single.never()
return Single.fromCallable {
val factsArray = context.resources.getStringArray(R.array.local_cat_facts)
val randomIndex = Random.nextInt(factsArray.size)
val randomFactText = factsArray[randomIndex]
Fact(randomFactText)
}
}

/**
Expand All @@ -24,7 +30,15 @@ class LocalCatFactsGenerator(
* Если вновь заэмиченный Fact совпадает с предыдущим - пропускаем элемент.
*/
fun generateCatFactPeriodically(): Flowable<Fact> {
val success = Fact(context.resources.getStringArray(R.array.local_cat_facts)[Random.nextInt(5)])
return Flowable.empty()
val factsArray = context.resources.getStringArray(R.array.local_cat_facts)

return Flowable.interval(2_000, TimeUnit.MILLISECONDS)
.map {
val randomIndex = Random.nextInt(factsArray.size)
val randomFactText = factsArray[randomIndex]
Fact(randomFactText)
}
.distinctUntilChanged()
.onBackpressureLatest()
}
}