From f9ad7e3a53c0deb2700f4147fb7d6dac71a4024d Mon Sep 17 00:00:00 2001 From: denis Date: Thu, 6 Feb 2025 14:14:12 +0300 Subject: [PATCH] hw 2 --- flowcats/build.gradle | 5 ++ .../otus/homework/flowcats/CatsRepository.kt | 6 ++- .../otus/homework/flowcats/CatsService.kt | 2 +- .../java/otus/homework/flowcats/CatsView.kt | 10 +++- .../otus/homework/flowcats/CatsViewModel.kt | 21 ++++++-- .../otus/homework/flowcats/DiContainer.kt | 20 ++++++-- .../main/java/otus/homework/flowcats/Fact.kt | 22 ++------- .../otus/homework/flowcats/MainActivity.kt | 13 +++-- .../java/otus/homework/flowcats/Result.kt | 6 +++ .../otus/homework/flow/SampleInteractor.kt | 49 +++++++++++++++++-- 10 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 flowcats/src/main/java/otus/homework/flowcats/Result.kt diff --git a/flowcats/build.gradle b/flowcats/build.gradle index 0ea35e68..3b1ade45 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -48,4 +48,9 @@ dependencies { 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' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.0-beta02' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' } \ 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..4ae64112 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -10,7 +10,11 @@ class CatsRepository( fun listenForCatFacts() = flow { while (true) { - val latestNews = catsService.getCatFact() + val latestNews = try { + Result.Success(catsService.getCatFact()) + } catch (ex: Throwable) { + Result.Error(ex.message, ex) + } emit(latestNews) 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..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/CatsView.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt index 6a195f3a..a82140e4 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt @@ -3,6 +3,7 @@ package otus.homework.flowcats import android.content.Context import android.util.AttributeSet import android.widget.TextView +import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout class CatsView @JvmOverloads constructor( @@ -12,11 +13,18 @@ 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 } + + override fun onError(text: String) { + Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + } + } interface ICatsView { fun populate(fact: Fact) + + fun onError(text: String) } \ 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..527e5d5a 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -2,6 +2,9 @@ package otus.homework.flowcats import androidx.lifecycle.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -10,14 +13,15 @@ class CatsViewModel( private val catsRepository: CatsRepository ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + private val _catsState: MutableStateFlow = MutableStateFlow(Result.Success(Fact("", 0))) + + val catsState: StateFlow = _catsState init { viewModelScope.launch { withContext(Dispatchers.IO) { catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it + _catsState.value = it } } } @@ -26,6 +30,13 @@ class CatsViewModel( class CatsViewModelFactory(private val catsRepository: CatsRepository) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = - CatsViewModel(catsRepository) as T + + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(CatsViewModel::class.java)) { + return CatsViewModel(catsRepository) as T + } + throw IllegalArgumentException("Unknown ViewModel class: " + modelClass.name) + + } } \ 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..1be8e79e 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt @@ -1,18 +1,32 @@ package otus.homework.flowcats +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import okhttp3.logging.HttpLoggingInterceptor.Level import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory class DiContainer { - private val retrofit by lazy { + private fun getClient(): OkHttpClient { + val interceptor = HttpLoggingInterceptor() + interceptor.setLevel(Level.BODY); + val client = OkHttpClient.Builder() + .addInterceptor(interceptor) + .build(); + return client + } + + private val catsFactRetrofit by lazy { Retrofit.Builder() - .baseUrl("https://cat-fact.herokuapp.com/facts/") + .baseUrl("https://catfact.ninja/") .addConverterFactory(GsonConverterFactory.create()) + .client(getClient()) .build() } - val service by lazy { retrofit.create(CatsService::class.java) } + + val service by lazy { catsFactRetrofit.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..81998454 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt @@ -3,22 +3,8 @@ 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, + @field:SerializedName("length") + 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..609849b1 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -3,6 +3,9 @@ package otus.homework.flowcats import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { @@ -13,9 +16,13 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - - catsViewModel.catsLiveData.observe(this){ - view.populate(it) + lifecycleScope.launch { + catsViewModel.catsState.collect { catsState -> + when (catsState) { + is Result.Success<*> -> view.populate(catsState.data as Fact) + is Result.Error -> view.onError(catsState.message.toString()) + } + } } } } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/Result.kt b/flowcats/src/main/java/otus/homework/flowcats/Result.kt new file mode 100644 index 00000000..84b3b649 --- /dev/null +++ b/flowcats/src/main/java/otus/homework/flowcats/Result.kt @@ -0,0 +1,6 @@ +package otus.homework.flowcats + +sealed class Result { + data class Success(val data: T) : otus.homework.flowcats.Result() + data class Error(val message: String?, val throwable: Throwable? = null) : otus.homework.flowcats.Result() +} \ 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..e921a5bd 100644 --- a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt +++ b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt @@ -18,7 +18,13 @@ class SampleInteractor( * 6) возвращает результат */ fun task1(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .map { it * 5 } + .filter { it >= 20 } + .onEach { print("$it ") } + .filter { it % 2 != 0 } + .map { "$it won" } + .take(3) } /** @@ -29,7 +35,25 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + val produceNumbers: Flow = sampleRepository.produceNumbers() + return produceNumbers.transform { + when { + it % 15 == 0 -> { + emit(it.toString()) + emit("FizzBuzz") + } + + it % 3 == 0 -> { + emit(it.toString()) + emit("Fizz") + } + it % 5 == 0 -> { + emit(it.toString()) + emit("Buzz") + } + else -> emit(it.toString()) + } + } } /** @@ -38,7 +62,12 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + val produceForms = sampleRepository.produceForms() + val produceColors = sampleRepository.produceColors() + + return produceColors.zip(produceForms) { a, b -> + a to b + } } /** @@ -48,6 +77,18 @@ class SampleInteractor( * При любом исходе, будь то выброс исключения или успешная отработка функции вызовите метод dotsRepository.completed() */ fun task4(): Flow { - return flowOf() + return flow { + try { + emitAll(sampleRepository.produceNumbers()) + } catch (ex: Exception) { + if (ex is IllegalArgumentException) + emit(-1) + else + throw ex + } finally { + sampleRepository.completed() + } + } + } } \ No newline at end of file