From 162a895d32b0a1f2af6c6efd7b878ea6c488f6d7 Mon Sep 17 00:00:00 2001 From: Ekaterina Leonidova Date: Sun, 26 Jan 2025 12:32:50 +0300 Subject: [PATCH 1/5] task 1.1 timer on coroutines --- .../coroutineshomework/ui/timer/TimerFragment.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt index 1b7c0f1..55bac1b 100644 --- a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt +++ b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt @@ -25,6 +25,10 @@ class TimerFragment : Fragment() { binding.time.text = newValue.toDisplayString() } + private var timerJob: Job? = null + + private var timeDelay = 10.milliseconds + private var started by Delegates.observable(false) { _, _, newValue -> setButtonsState(newValue) if (newValue) { @@ -75,16 +79,23 @@ class TimerFragment : Fragment() { } private fun startTimer() { - // TODO: Start timer + timerJob = lifecycleScope.launch { + while (started) { + delay(timeDelay) + time += timeDelay + } + } } private fun stopTimer() { - // TODO: Stop timer + timerJob?.cancel() + timerJob = null } override fun onDestroyView() { super.onDestroyView() _binding = null + stopTimer() } companion object { From d5baa6bd9888f8e2239afea66678e4a6bd2ffb94 Mon Sep 17 00:00:00 2001 From: Ekaterina Leonidova Date: Sun, 26 Jan 2025 13:01:56 +0300 Subject: [PATCH 2/5] task 1.2 timer on flow --- .../ui/timer/TimerFragment.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt index 55bac1b..6638c00 100644 --- a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt +++ b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/timer/TimerFragment.kt @@ -5,10 +5,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import ru.otus.coroutineshomework.databinding.FragmentTimerBinding import java.util.Locale @@ -25,6 +28,7 @@ class TimerFragment : Fragment() { binding.time.text = newValue.toDisplayString() } + private var timeFlow = MutableStateFlow(Duration.ZERO) private var timerJob: Job? = null private var timeDelay = 10.milliseconds @@ -57,12 +61,19 @@ class TimerFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) savedInstanceState?.let { - time = it.getLong(TIME).milliseconds + timeFlow.value = it.getLong(TIME).milliseconds started = it.getBoolean(STARTED) } setButtonsState(started) with(binding) { - time.text = this@TimerFragment.time.toDisplayString() + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + timeFlow.collect { + time.text = it.toDisplayString() + } + } + } + time.text = this@TimerFragment.timeFlow.value.toDisplayString() btnStart.setOnClickListener { started = true } @@ -74,7 +85,7 @@ class TimerFragment : Fragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putLong(TIME, time.inWholeMilliseconds) + outState.putLong(TIME, timeFlow.value.inWholeMilliseconds) outState.putBoolean(STARTED, started) } @@ -82,7 +93,7 @@ class TimerFragment : Fragment() { timerJob = lifecycleScope.launch { while (started) { delay(timeDelay) - time += timeDelay + timeFlow.emit(timeFlow.value + timeDelay ) } } } From 2d9f73f91a4a41298079691988e39c65e0b73db4 Mon Sep 17 00:00:00 2001 From: Ekaterina Leonidova Date: Sun, 26 Jan 2025 13:40:28 +0300 Subject: [PATCH 3/5] task 2.1 login with coroutines --- .../ui/login/LoginViewModel.kt | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt index 5fae38a..044b885 100644 --- a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt +++ b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt @@ -3,11 +3,17 @@ package ru.otus.coroutineshomework.ui.login import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import ru.otus.coroutineshomework.ui.login.data.Credentials class LoginViewModel : ViewModel() { private val _state = MutableLiveData(LoginViewState.Login()) val state: LiveData = _state + private val loginApi = LoginApi() /** * Login to the network @@ -15,13 +21,35 @@ class LoginViewModel : ViewModel() { * @param password user password */ fun login(name: String, password: String) { - // TODO: Implement login + _state.value = LoginViewState.LoggingIn + viewModelScope.launch { + try { + val user = withContext(Dispatchers.IO) { + return@withContext loginApi.login(Credentials(name, password)) + } + _state.value = LoginViewState.Content(user) + } catch (e: Exception) { + _state.value = LoginViewState.Login(e) + } + } } /** * Logout from the network */ fun logout() { - // TODO: Implement logout + _state.value = LoginViewState.LoggingOut + + viewModelScope.launch { + try { + withContext(Dispatchers.IO) { + return@withContext loginApi.logout() + } + _state.value = LoginViewState.Login() + } catch (e: Exception) { + _state.value = LoginViewState.Login(e) + } + } + } } From 9eb7cb27bed667b386e95eafeed978eb96dd310b Mon Sep 17 00:00:00 2001 From: Ekaterina Leonidova Date: Sun, 26 Jan 2025 15:22:02 +0300 Subject: [PATCH 4/5] task 2.2 login with flow --- .../ui/login/LoginFragment.kt | 21 +++++++++---- .../ui/login/LoginViewModel.kt | 30 ++++++++++++------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginFragment.kt b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginFragment.kt index 06c3afe..e4ceae1 100644 --- a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginFragment.kt +++ b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginFragment.kt @@ -7,6 +7,11 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import ru.otus.coroutineshomework.databinding.ContentBinding import ru.otus.coroutineshomework.databinding.FragmentLoginBinding import ru.otus.coroutineshomework.databinding.LoadingBinding @@ -43,12 +48,16 @@ class LoginFragment : Fragment() { setupLogin() setupContent() - loginViewModel.state.observe(viewLifecycleOwner) { - when(it) { - is LoginViewState.Login -> showLogin(it) - LoginViewState.LoggingIn -> showLoggingIn() - is LoginViewState.Content -> showContent(it) - LoginViewState.LoggingOut -> showLoggingOut() + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + loginViewModel.state.collect { + when (it) { + is LoginViewState.Login -> showLogin(it) + LoginViewState.LoggingIn -> showLoggingIn() + is LoginViewState.Content -> showContent(it) + LoginViewState.LoggingOut -> showLoggingOut() + } + } } } } diff --git a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt index 044b885..ad07628 100644 --- a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt +++ b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/login/LoginViewModel.kt @@ -5,14 +5,18 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ru.otus.coroutineshomework.ui.login.data.Credentials class LoginViewModel : ViewModel() { - private val _state = MutableLiveData(LoginViewState.Login()) - val state: LiveData = _state + private val _state = MutableStateFlow(LoginViewState.Login()) + val state: StateFlow = _state private val loginApi = LoginApi() /** @@ -21,16 +25,22 @@ class LoginViewModel : ViewModel() { * @param password user password */ fun login(name: String, password: String) { - _state.value = LoginViewState.LoggingIn viewModelScope.launch { - try { - val user = withContext(Dispatchers.IO) { - return@withContext loginApi.login(Credentials(name, password)) - } - _state.value = LoginViewState.Content(user) - } catch (e: Exception) { - _state.value = LoginViewState.Login(e) + loginFlow(Credentials(name, password)).collect { + _state.value = it + } + } + } + + private fun loginFlow(creds: Credentials): Flow = flow { + emit(LoginViewState.LoggingIn) + try { + val user = withContext(Dispatchers.IO) { + return@withContext loginApi.login(creds) } + emit(LoginViewState.Content(user)) + } catch (e: Exception) { + emit(LoginViewState.Login(e)) } } From 9de0f0f2a91ebbbdbabc47dbb7236a80084aa819 Mon Sep 17 00:00:00 2001 From: Ekaterina Leonidova Date: Sun, 26 Jan 2025 16:24:11 +0300 Subject: [PATCH 5/5] task 3.1 network --- .../ui/network/NetworkViewModel.kt | 20 ++++++++++++++++++- app/src/main/res/values-night/themes.xml | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/network/NetworkViewModel.kt b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/network/NetworkViewModel.kt index f006e03..4c7973f 100644 --- a/app/src/main/kotlin/ru/otus/coroutineshomework/ui/network/NetworkViewModel.kt +++ b/app/src/main/kotlin/ru/otus/coroutineshomework/ui/network/NetworkViewModel.kt @@ -4,8 +4,11 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlin.random.Random @@ -18,7 +21,22 @@ class NetworkViewModel : ViewModel() { val result: LiveData = _result fun startTest(numberOfThreads: Int) { - // TODO: Implement the logic + _running.value = true + + viewModelScope.launch { + val results = mutableListOf>() + withContext(Dispatchers.IO) { + repeat(numberOfThreads) { + val result = async { emulateBlockingNetworkRequest() } + results.add(result.await()) + } + } + + val successResults = results.mapNotNull { it.getOrNull() } + + _result.value = successResults.average().toLong() + _running.value = false + } } private companion object { diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index d5ab143..f67862f 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,6 +1,6 @@ -