diff --git a/build.gradle b/build.gradle index e47bb55b..4fb1f3d6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.4.32" + ext.kotlin_version = "1.6.0" repositories { google() jcenter() diff --git a/flowcats/build.gradle b/flowcats/build.gradle index cefa21e0..c16e5a8b 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -4,13 +4,13 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 32 buildToolsVersion "30.0.3" defaultConfig { applicationId "otus.homework.flowcats" minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 32 versionCode 1 versionName "1.0" @@ -46,4 +46,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.5.0") } \ No newline at end of file diff --git a/flowcats/src/main/AndroidManifest.xml b/flowcats/src/main/AndroidManifest.xml index 2deb6454..6531e25f 100644 --- a/flowcats/src/main/AndroidManifest.xml +++ b/flowcats/src/main/AndroidManifest.xml @@ -10,7 +10,8 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" 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..3da59438 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -1,18 +1,26 @@ package otus.homework.flowcats +import android.util.Log +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn class CatsRepository( private val catsService: CatsService, private val refreshIntervalMs: Long = 5000 ) { - fun listenForCatFacts() = flow { + fun listenForCatFacts() = flow> { while (true) { val latestNews = catsService.getCatFact() - emit(latestNews) + emit(Result.Success(latestNews)) delay(refreshIntervalMs) } - } + }.flowOn(Dispatchers.IO) + .catch { e -> + Log.d(this@CatsRepository.javaClass.canonicalName, "Error", e) + emit(Result.Error(e.message ?: "Error")) + } } \ 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..347de5fd 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( @@ -14,9 +15,15 @@ class CatsView @JvmOverloads constructor( override fun populate(fact: Fact) { findViewById(R.id.fact_textView).text = fact.text } + + override fun error(message: String) { + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } } interface ICatsView { fun populate(fact: Fact) + + fun error(message: 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..d4f3830c 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -1,31 +1,33 @@ package otus.homework.flowcats -import androidx.lifecycle.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow 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 _catsStateFlow = MutableStateFlow?>(null) + val catsStateFlow: StateFlow?> = _catsStateFlow init { viewModelScope.launch { - withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it + catsRepository.listenForCatFacts() + .collect { + _catsStateFlow.emit(it) } - } } } } class CatsViewModelFactory(private val catsRepository: CatsRepository) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = - CatsViewModel(catsRepository) as T + + override fun create(modelClass: Class): T { + return CatsViewModel(catsRepository) as T + } } \ 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..9e5e96ef 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -1,8 +1,12 @@ package otus.homework.flowcats -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle 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 +18,16 @@ 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 { + when (it) { + is Result.Success -> view.populate(it.data) + is Result.Error -> view.error(it.message) + null -> return@collect + } + } + } } } } \ 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..cf681b03 --- /dev/null +++ b/flowcats/src/main/java/otus/homework/flowcats/Result.kt @@ -0,0 +1,12 @@ +package otus.homework.flowcats + +sealed class Result { + + data class Success( + val data: T + ) : Result() + + data class Error( + val message: String + ) : Result() +} diff --git a/operators/build.gradle b/operators/build.gradle index 39c4b952..c4c5fc16 100644 --- a/operators/build.gradle +++ b/operators/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 32 buildToolsVersion "30.0.3" defaultConfig { minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 32 versionCode 1 versionName "1.0" } diff --git a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt index 1993c064..81813dd1 100644 --- a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt +++ b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt @@ -18,7 +18,12 @@ class SampleInteractor( * 6) возвращает результат */ fun task1(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .map { it * 5 } + .filterNot { it <= 20 } + .filterNot { it % 2 == 0 } + .map { "$it won" } + .take(3) } /** @@ -29,16 +34,40 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .transform { value -> + when { + value isDividedBy 15 -> { + emit(value.toString()) + emit("FizzBuzz") + } + value isDividedBy 3 -> { + emit(value.toString()) + emit("Fizz") + } + value isDividedBy 5 -> { + emit(value.toString()) + emit("Buzz") + } + else -> emit(value.toString()) + } + } + } + + private infix fun Int.isDividedBy(divider: Int): Boolean { + return this % divider == 0 } /** * Реализуйте функцию task3, которая объединяет эмиты из двух flow и возвращает кортеж Pair(f1,f2), * где f1 айтем из первого флоу, f2 айтем из второго флоу. - * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится + * Если айтемы в одном из флоу кончились, то результирующий флоу также должен закончиться */ fun task3(): Flow> { - return flowOf() + return sampleRepository.produceColors() + .zip(sampleRepository.produceForms()) { i, j -> + i to j + } } /** @@ -48,6 +77,14 @@ class SampleInteractor( * При любом исходе, будь то выброс исключения или успешная отработка функции вызовите метод dotsRepository.completed() */ fun task4(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .catch { e -> + when (e) { + is IllegalArgumentException -> emit(-1) + else -> throw e + } + }.onCompletion { + sampleRepository.completed() + } } } \ No newline at end of file