From a1352b8871eee9c5ea2cacce6770c9974f584263 Mon Sep 17 00:00:00 2001 From: "adil.ziganshin" Date: Sat, 1 Feb 2025 13:02:00 +0300 Subject: [PATCH] hw --- flowcats/build.gradle | 3 +- .../otus/homework/flowcats/CatsRepository.kt | 15 +++++-- .../otus/homework/flowcats/CatsService.kt | 2 +- .../otus/homework/flowcats/CatsViewModel.kt | 40 +++++++++++++------ .../otus/homework/flowcats/DiContainer.kt | 4 +- .../main/java/otus/homework/flowcats/Fact.kt | 19 +-------- .../otus/homework/flowcats/MainActivity.kt | 34 +++++++++++++--- flowcats/src/main/res/values/strings.xml | 1 + .../otus/homework/flow/SampleInteractor.kt | 40 ++++++++++++++++--- 9 files changed, 111 insertions(+), 47 deletions(-) diff --git a/flowcats/build.gradle b/flowcats/build.gradle index 0ea35e68..cd8c0319 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 30 + compileSdk 34 buildToolsVersion "30.0.3" namespace = "otus.homework.flowcats" @@ -46,6 +46,7 @@ dependencies { implementation 'com.squareup.picasso:picasso:2.71828' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3' implementation 'androidx.activity:activity-ktx:1.2.3' + implementation 'androidx.lifecycle:lifecycle-viewmodel-android:2.8.7' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt index 10fcb77d..6d548ec9 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -3,6 +3,11 @@ package otus.homework.flowcats import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow +sealed class Result { + class Success(val fact: Fact): Result() + class Error(val exception: Throwable): Result() +} + class CatsRepository( private val catsService: CatsService, private val refreshIntervalMs: Long = 5000 @@ -10,9 +15,13 @@ class CatsRepository( fun listenForCatFacts() = flow { while (true) { - val latestNews = catsService.getCatFact() - emit(latestNews) + try { + val latestFact = catsService.getCatFact() + emit(Result.Success(latestFact)) + } catch (exception: Throwable) { + emit(Result.Error(exception)) + } delay(refreshIntervalMs) } } -} \ No newline at end of file +} diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt index 25192882..787829a1 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt @@ -4,6 +4,6 @@ import retrofit2.http.GET interface CatsService { - @GET("random?animal_type=cat") + @GET("fact") suspend fun getCatFact(): Fact } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt index 0d8ba8a7..07093c31 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -1,8 +1,14 @@ package otus.homework.flowcats +import android.util.Log import androidx.lifecycle.* +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -10,22 +16,32 @@ class CatsViewModel( private val catsRepository: CatsRepository ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + private val _catsFlow: MutableStateFlow = MutableStateFlow(null) + val catsFlow: StateFlow = _catsFlow init { - viewModelScope.launch { + val handler = CoroutineExceptionHandler { _, exception -> + Log.d("init", "Exception = '$exception'") + } + viewModelScope.launch(handler) { withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it + catsRepository.listenForCatFacts().collect { result -> + when (result) { + is Result.Success -> _catsFlow.value = result.fact + is Result.Error -> _catsFlow.value = null + } } } } } -} -class CatsViewModelFactory(private val catsRepository: CatsRepository) : - ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = - CatsViewModel(catsRepository) as T -} \ No newline at end of file + companion object { + val REPOSITORY_KEY = object : CreationExtras.Key { } + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val repository = this[REPOSITORY_KEY] as CatsRepository + CatsViewModel(repository) + } + } + } +} diff --git a/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt b/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt index 485152e2..915e8920 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt @@ -7,7 +7,7 @@ class DiContainer { private val retrofit by lazy { Retrofit.Builder() - .baseUrl("https://cat-fact.herokuapp.com/facts/") + .baseUrl("https://catfact.ninja/") .addConverterFactory(GsonConverterFactory.create()) .build() } @@ -15,4 +15,4 @@ class DiContainer { val service by lazy { retrofit.create(CatsService::class.java) } val repository by lazy { CatsRepository(service) } -} \ No newline at end of file +} diff --git a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt index 602303eb..f47803eb 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt @@ -3,22 +3,7 @@ package otus.homework.flowcats import com.google.gson.annotations.SerializedName data class Fact( - @field:SerializedName("createdAt") - val createdAt: String, - @field:SerializedName("deleted") - val deleted: Boolean, - @field:SerializedName("_id") - val id: String, - @field:SerializedName("text") + @field:SerializedName("fact") val text: String, - @field:SerializedName("source") - val source: String, - @field:SerializedName("used") - val used: Boolean, - @field:SerializedName("type") - val type: String, - @field:SerializedName("user") - val user: String, - @field:SerializedName("updatedAt") - val updatedAt: String + val length: Int ) \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt index edea434b..2c5a20d9 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -2,20 +2,42 @@ package otus.homework.flowcats import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import androidx.activity.viewModels +import android.widget.Toast +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.viewmodel.MutableCreationExtras +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { private val diContainer = DiContainer() - private val catsViewModel by viewModels { CatsViewModelFactory(diContainer.repository) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - - catsViewModel.catsLiveData.observe(this){ - view.populate(it) + val catsViewModel: CatsViewModel = ViewModelProvider.create(this, + factory = CatsViewModel.Factory, + extras = MutableCreationExtras().apply { + set(CatsViewModel.REPOSITORY_KEY, diContainer.repository) + } + ) [CatsViewModel::class] + lifecycleScope.launch { + catsViewModel.catsFlow.collect { + if (it != null) { + view.populate(it) + } else { + showError() + } + } } } -} \ No newline at end of file + + private fun showError() { + Toast.makeText( + this@MainActivity, + getString(R.string.could_not_get_response_from_server), + Toast.LENGTH_SHORT) + .show() + } +} diff --git a/flowcats/src/main/res/values/strings.xml b/flowcats/src/main/res/values/strings.xml index ba737b82..1904c5ab 100644 --- a/flowcats/src/main/res/values/strings.xml +++ b/flowcats/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ Flow cats + Не удалось получить ответ от сервера \ No newline at end of file diff --git a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt index 1993c064..04e42491 100644 --- a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt +++ b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt @@ -1,7 +1,15 @@ package otus.homework.flow import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapConcat +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.zip @ExperimentalCoroutinesApi class SampleInteractor( @@ -18,7 +26,12 @@ class SampleInteractor( * 6) возвращает результат */ fun task1(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .map { it * 5 } + .filter { it > 20 } + .filter { it.rem(2) == 1 } + .map { "$it won" } + .take(3) } /** @@ -29,7 +42,14 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + return sampleRepository.produceNumbers().flatMapConcat { + when { + it.rem(15) == 0 -> flowOf(it.toString(), "FizzBuzz") + it.rem(3) == 0 -> flowOf(it.toString(), "Fizz") + it.rem(5) == 0 -> flowOf(it.toString(), "Buzz") + else -> flowOf(it.toString()) + } + } } /** @@ -38,7 +58,11 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + return with (sampleRepository) { + produceColors().zip(produceForms()) { color, form -> + Pair(color, form) + } + } } /** @@ -48,6 +72,12 @@ class SampleInteractor( * При любом исходе, будь то выброс исключения или успешная отработка функции вызовите метод dotsRepository.completed() */ fun task4(): Flow { - return flowOf() + return sampleRepository.produceNumbers().catch { + if (it is IllegalArgumentException) { + emit(-1) + } else { + throw it + } + }.onCompletion { sampleRepository.completed() } } } \ No newline at end of file