diff --git a/flowcats/build.gradle b/flowcats/build.gradle index 0ea35e68..6db2674c 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -4,15 +4,15 @@ plugins { } android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdk 34 + buildToolsVersion '34.0.0' namespace = "otus.homework.flowcats" defaultConfig { applicationId "otus.homework.flowcats" minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 34 versionCode 1 versionName "1.0" 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..1705555f 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -1,18 +1,31 @@ package otus.homework.flowcats +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn + +interface Repository { + suspend fun listenForCatFacts() : Flow +} class CatsRepository( private val catsService: CatsService, private val refreshIntervalMs: Long = 5000 -) { +) : Repository { - fun listenForCatFacts() = flow { - while (true) { - val latestNews = catsService.getCatFact() - emit(latestNews) - delay(refreshIntervalMs) - } + override suspend fun listenForCatFacts(): Flow { + return flow { + while (true){ + try { + val latestNews = catsService.getCatFact() + emit(LoadResult.Success(latestNews.text)) + delay(refreshIntervalMs) + } catch (e: Exception) { + emit(LoadResult.Error(e.message ?: "error")) + } + } + }.flowOn(Dispatchers.IO) } } \ 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..1e7449cd 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt @@ -1,22 +1,61 @@ package otus.homework.flowcats import android.content.Context +import android.graphics.Color import android.util.AttributeSet -import android.widget.TextView -import androidx.constraintlayout.widget.ConstraintLayout +import com.google.android.material.textview.MaterialTextView class CatsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView { +) : MaterialTextView(context, attrs, defStyleAttr), ICatsView { - override fun populate(fact: Fact) { - findViewById(R.id.fact_textView).text = fact.text + override fun populate(text: String) { + setText(text) + } + override fun showPrimaryColor() { + setTextColor(Color.BLACK) + } + + override fun showLoadingColor() { + setTextColor(Color.GREEN) + } + + override fun showErrorColor() { + setTextColor(Color.RED) } } -interface ICatsView { +interface ICatsUiState { - fun populate(fact: Fact) + fun show(catsView: ICatsView) + + data class Success(private val text: String) : ICatsUiState { + override fun show(catsView: ICatsView) { + catsView.populate(text) + catsView.showPrimaryColor() + } + } + + data class Loading(private val text: String) : ICatsUiState { + override fun show(catsView: ICatsView) { + catsView.populate(text) + catsView.showLoadingColor() + } + } + + data class Error(private val message: String) : ICatsUiState { + override fun show(catsView: ICatsView) { + catsView.populate(message) + catsView.showErrorColor() + } + } +} + +interface ICatsView { + fun populate(text: String) + fun showPrimaryColor() + fun showLoadingColor() + fun showErrorColor() } \ 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..4f7f69c8 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -2,30 +2,35 @@ package otus.homework.flowcats import androidx.lifecycle.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class CatsViewModel( - private val catsRepository: CatsRepository + private val catsRepository: CatsRepository, + private val mapper: LoadResult.Mapper ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + private val _catsFlow = MutableStateFlow(ICatsUiState.Loading("Loading...")) + val catsFlow = _catsFlow.asStateFlow() init { viewModelScope.launch { - withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it - } + catsRepository.listenForCatFacts().collect { loadResult -> + _catsFlow.emit(loadResult.map(mapper)) } } } } -class CatsViewModelFactory(private val catsRepository: CatsRepository) : +class CatsViewModelFactory(private val catsRepository: CatsRepository, + private val mapper: LoadResult.Mapper) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = - CatsViewModel(catsRepository) as T + + override fun create(modelClass: Class): T { + if (modelClass == CatsViewModel::class.java) return CatsViewModel(catsRepository, mapper) as T + throw IllegalArgumentException() + } } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/LoadResult.kt b/flowcats/src/main/java/otus/homework/flowcats/LoadResult.kt new file mode 100644 index 00000000..ea7a9a57 --- /dev/null +++ b/flowcats/src/main/java/otus/homework/flowcats/LoadResult.kt @@ -0,0 +1,45 @@ +package otus.homework.flowcats + +interface LoadResult { + fun map(mapper: Mapper): T + + interface Mapper { + + fun mapSuccess(fact: String): T + fun mapLoading(message: String): T + fun mapError(message: String): T + } + + data class Success(private val fact: String) : LoadResult { + override fun map(mapper: Mapper): T { + return mapper.mapSuccess(fact) + } + } + + data class Loading(private val message: String) : LoadResult { + override fun map(mapper: Mapper): T { + return mapper.mapLoading(message) + } + } + + data class Error(private val message: String) : LoadResult { + override fun map(mapper: Mapper): T { + return mapper.mapError(message) + } + } +} + +class ResultMapper() : LoadResult.Mapper { + + override fun mapSuccess(fact: String): ICatsUiState { + return ICatsUiState.Success(fact) + } + + override fun mapLoading(message: String): ICatsUiState { + return ICatsUiState.Loading(message) + } + + override fun mapError(message: String): ICatsUiState { + return ICatsUiState.Error(message) + } +} \ 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..c66a4ad6 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.lifecycleScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { + private lateinit var catsView: CatsView + private val diContainer = DiContainer() - private val catsViewModel by viewModels { CatsViewModelFactory(diContainer.repository) } + private val catsViewModel by viewModels { + CatsViewModelFactory(diContainer.repository, ResultMapper()) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView + val view = layoutInflater.inflate(R.layout.activity_main, null) setContentView(view) - catsViewModel.catsLiveData.observe(this){ - view.populate(it) + catsView = findViewById(R.id.fact_textView) + + lifecycleScope.launch { + catsViewModel.catsFlow.collect { result -> + result.show(catsView) + } } } } \ No newline at end of file diff --git a/flowcats/src/main/res/layout/activity_main.xml b/flowcats/src/main/res/layout/activity_main.xml index dbcedd2f..b3aa28ba 100644 --- a/flowcats/src/main/res/layout/activity_main.xml +++ b/flowcats/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + \ 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..a36abd3d 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 } + .filter { it > 20 } + .filter { it % 2 != 0 } + .map { "$it won" } + .take(3) } /** @@ -29,7 +34,15 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .transform { + emit(it.toString()) + when { + (it % 15 == 0) -> emit("FizzBuzz") + (it % 5 == 0) -> emit("Buzz") + (it % 3 == 0) -> emit("Fizz") + } + } } /** @@ -38,7 +51,11 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + return sampleRepository.produceColors() + .zip(sampleRepository.produceForms()) { + f1, f2 -> f1 to f2 + } + } /** @@ -48,6 +65,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