diff --git a/README.md b/README.md index 0e62fd9e..12095b5a 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,6 @@ *Создать sealed класс `Result`. Унаследовать от него классы `Success`, `Error`. Использовать эти классы как стейт необходимый для рендеринга/отображени ошибки -### Реализовать функции с использование flow операторов +### Реализовать функции с использованием flow операторов 1. В классе `SampleInteractor` реализуйте функции `task1`-`task4` в соответствии с условиями. Для проверки функций используйте тесты в `SampleInteractorTest` diff --git a/flowcats/build.gradle b/flowcats/build.gradle index 0ea35e68..209f14cf 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 36 buildToolsVersion "30.0.3" namespace = "otus.homework.flowcats" @@ -12,7 +12,7 @@ android { defaultConfig { applicationId "otus.homework.flowcats" minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 1 versionName "1.0" @@ -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' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.4' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' } \ No newline at end of file diff --git a/flowcats/src/main/AndroidManifest.xml b/flowcats/src/main/AndroidManifest.xml index 2deb6454..feb3f274 100644 --- a/flowcats/src/main/AndroidManifest.xml +++ b/flowcats/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt index 10fcb77d..2b29fbc0 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 { + data class Success(val data: T) : Result() + data class Error(val throwable: Throwable) : Result() +} + class CatsRepository( private val catsService: CatsService, private val refreshIntervalMs: Long = 5000 @@ -10,8 +15,12 @@ class CatsRepository( fun listenForCatFacts() = flow { while (true) { - val latestNews = catsService.getCatFact() - emit(latestNews) + try { + val fact = catsService.getCatFact() + emit(Result.Success(fact)) + } catch (e: Exception) { + emit(Result.Error(e)) + } delay(refreshIntervalMs) } } diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt index 25192882..aefe965f 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt @@ -4,6 +4,7 @@ import retrofit2.http.GET interface CatsService { - @GET("random?animal_type=cat") +// @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/CatsView.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt index 6a195f3a..eba22638 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt @@ -12,7 +12,7 @@ class CatsView @JvmOverloads constructor( ) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView { override fun populate(fact: Fact) { - findViewById(R.id.fact_textView).text = fact.text + findViewById(R.id.fact_textView).text = fact.fact } } diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt index 0d8ba8a7..5146f8d2 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -1,8 +1,12 @@ package otus.homework.flowcats -import androidx.lifecycle.* +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -10,22 +14,22 @@ class CatsViewModel( private val catsRepository: CatsRepository ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + private val _catsStateFlow = MutableStateFlow(null) + val catsStateFlow: StateFlow = _catsStateFlow.asStateFlow() init { viewModelScope.launch { withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it - } + catsRepository.listenForCatFacts() + .collect { result -> + _catsStateFlow.value = result + } } } } } -class CatsViewModelFactory(private val catsRepository: CatsRepository) : - ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = +class CatsViewModelFactory(private val catsRepository: CatsRepository) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T = CatsViewModel(catsRepository) as T } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt b/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt index 485152e2..0acce752 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt @@ -7,7 +7,8 @@ class DiContainer { private val retrofit by lazy { Retrofit.Builder() - .baseUrl("https://cat-fact.herokuapp.com/facts/") +// .baseUrl("https://cat-fact.herokuapp.com/facts/") + .baseUrl("https://catfact.ninja/") .addConverterFactory(GsonConverterFactory.create()) .build() } diff --git a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt index 602303eb..ced6adcc 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt @@ -3,22 +3,6 @@ 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") - 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 + @field:SerializedName("fact") + val fact: String ) \ 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..7ea01547 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -1,8 +1,13 @@ package otus.homework.flowcats -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import android.widget.Toast import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { @@ -14,8 +19,19 @@ class MainActivity : AppCompatActivity() { val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - catsViewModel.catsLiveData.observe(this){ - view.populate(it) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + catsViewModel.catsStateFlow.collect { result -> + result?.let { + when (it) { + is Result.Success<*> -> view.populate(it.data as Fact) + is Result.Error -> { + Toast.makeText(this@MainActivity, it.throwable.message, Toast.LENGTH_SHORT).show() + } + } + } + } + } } } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 98bed167..1aec05a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,5 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +org.gradle.configuration-cache=true \ No newline at end of file diff --git a/operators/src/main/AndroidManifest.xml b/operators/src/main/AndroidManifest.xml index b8ab777d..b1ee20bc 100644 --- a/operators/src/main/AndroidManifest.xml +++ b/operators/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:android="http://schemas.android.com/apk/res/android"> { - return flowOf() + return sampleRepository.produceNumbers() + .map { number -> number * 5 } + .filter { number -> number > 20 } + .filter { number -> number % 2 != 0 } + .map { number -> "$number won" } + .take(3) } /** @@ -29,7 +34,16 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .transform { number -> + emit(number.toString()) + + when { + number % 15 == 0 -> emit("FizzBuzz") + number % 3 == 0 -> emit("Fizz") + number % 5 == 0 -> emit("Buzz") + } + } } /** @@ -38,7 +52,12 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + val flow1 = sampleRepository.produceColors() + val flow2 = sampleRepository.produceForms() + + return flow1.zip(flow2) { color, form -> + Pair(color, form) + } } /** @@ -48,6 +67,15 @@ class SampleInteractor( * При любом исходе, будь то выброс исключения или успешная отработка функции вызовите метод dotsRepository.completed() */ fun task4(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .catch { exception -> + if (exception is IllegalArgumentException) { + emit(-1) } else { + throw exception + } + } + .onCompletion { + sampleRepository.completed() + } } } \ No newline at end of file