diff --git a/flowcats/build.gradle b/flowcats/build.gradle index 0ea35e68..74e85716 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 34 buildToolsVersion "30.0.3" namespace = "otus.homework.flowcats" @@ -12,7 +12,8 @@ android { defaultConfig { applicationId "otus.homework.flowcats" minSdkVersion 23 - targetSdkVersion 30 + compileSdkVersion 34 + targetSdkVersion 34 versionCode 1 versionName "1.0" @@ -48,4 +49,5 @@ 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' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' } \ No newline at end of file diff --git a/flowcats/src/main/AndroidManifest.xml b/flowcats/src/main/AndroidManifest.xml index 2deb6454..76e5115c 100644 --- a/flowcats/src/main/AndroidManifest.xml +++ b/flowcats/src/main/AndroidManifest.xml @@ -9,8 +9,10 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.Flow" > - + android:theme="@style/Theme.Flow"> + diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt index 10fcb77d..0a8aec63 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -10,8 +10,12 @@ class CatsRepository( fun listenForCatFacts() = flow { while (true) { - val latestNews = catsService.getCatFact() - emit(latestNews) + try { + val latestNews = catsService.getCatFact() + emit(Result.Success(latestNews)) + } catch (e: Exception) { + emit(Result.Error(e.message)) + } 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..f8e78d30 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsService.kt @@ -4,6 +4,9 @@ import retrofit2.http.GET interface CatsService { - @GET("random?animal_type=cat") + /*@GET("random?animal_type=cat") + suspend fun getCatFact(): Fact*/ + + @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..75a7f72f 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( @@ -11,12 +12,21 @@ class CatsView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView { - override fun populate(fact: Fact) { - findViewById(R.id.fact_textView).text = fact.text + override fun populate(result: Result) { + when (result) { + is Result.Success<*> -> { + if (result.data is Fact) { + findViewById(R.id.fact_textView).text = result.data.fact + } + } + is Result.Error -> { + Toast.makeText(context, result.msg, Toast.LENGTH_SHORT).show() + } + } } } interface ICatsView { - fun populate(fact: Fact) + fun populate(result: Result) } \ 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..ee70a4f1 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -2,30 +2,49 @@ 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.catch import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext class CatsViewModel( private val catsRepository: CatsRepository ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + //private val _catsLiveData = MutableStateFlow(Fact("", false, "0", "", "", false, "", "", "")) + private val _catsLiveData = MutableStateFlow(value = Result.Success(Fact("", 0))) + val catsFlowData: StateFlow = _catsLiveData.asStateFlow() init { viewModelScope.launch { - withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it + catsRepository.listenForCatFacts() + .flowOn(Dispatchers.IO) + .catch { e -> + _catsLiveData.emit(Result.Error(e.message.toString())) + } + .collect { + _catsLiveData.emit(it) } - } } } } class CatsViewModelFactory(private val catsRepository: CatsRepository) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = - CatsViewModel(catsRepository) as T -} \ No newline at end of file + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(CatsViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return CatsViewModel(catsRepository) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} + +sealed class Result { + data class Success(val data: T) : Result() + data class Error(val msg: String?) : Result() + +} diff --git a/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt b/flowcats/src/main/java/otus/homework/flowcats/DiContainer.kt index 485152e2..402752b0 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..7cd73e6d 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt @@ -3,7 +3,7 @@ package otus.homework.flowcats import com.google.gson.annotations.SerializedName data class Fact( - @field:SerializedName("createdAt") + /*@field:SerializedName("createdAt") val createdAt: String, @field:SerializedName("deleted") val deleted: Boolean, @@ -20,5 +20,9 @@ data class Fact( @field:SerializedName("user") val user: String, @field:SerializedName("updatedAt") - val updatedAt: String + 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..67a360d9 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -3,19 +3,30 @@ package otus.homework.flowcats import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.flow.collectLatest +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) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + catsViewModel.catsFlowData.collectLatest { + view.populate(it) + } + } } } } \ 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..26040e45 100644 --- a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt +++ b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt @@ -1,5 +1,6 @@ package otus.homework.flow +import android.widget.Space import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* @@ -18,7 +19,16 @@ class SampleInteractor( * 6) возвращает результат */ fun task1(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .map { + it.times(5) + }.filter { + it > 20 + }.filter { + it % 2 != 0 + }.map { + "$it won" + }.take(3) } /** @@ -29,7 +39,27 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + return sampleRepository.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 +68,10 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + return sampleRepository.produceColors() + .zip(sampleRepository.produceForms()) { color, form -> + Pair(color, form) + } } /** @@ -48,6 +81,15 @@ 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