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..0d95826 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 @@ -11,6 +11,8 @@ import ru.otus.coroutineshomework.databinding.ContentBinding import ru.otus.coroutineshomework.databinding.FragmentLoginBinding import ru.otus.coroutineshomework.databinding.LoadingBinding import ru.otus.coroutineshomework.databinding.LoginBinding +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch class LoginFragment : Fragment() { @@ -39,16 +41,17 @@ class LoginFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupLogin() setupContent() - loginViewModel.state.observe(viewLifecycleOwner) { - when(it) { - is LoginViewState.Login -> showLogin(it) - LoginViewState.LoggingIn -> showLoggingIn() - is LoginViewState.Content -> showContent(it) - LoginViewState.LoggingOut -> showLoggingOut() + lifecycleScope.launch { + loginViewModel.stateFlow.collect { viewState -> + when (viewState) { + is LoginViewState.Login -> showLogin(viewState) + LoginViewState.LoggingIn -> showLoggingIn() + is LoginViewState.Content -> showContent(viewState) + 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 5fae38a..c0f573f 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,41 @@ 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.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +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 _stateFlow = MutableStateFlow(LoginViewState.Login()) + val stateFlow: StateFlow = _stateFlow.asStateFlow() + val loginApi = LoginApi() + + fun loginFlow(name: String, password: String) = flow { + emit(LoginViewState.LoggingIn) + + val credentials = Credentials(name, password) + + try { + val user = loginApi.login(credentials) + emit(LoginViewState.Content(user)) + } catch (e: Exception){ + emit(LoginViewState.Login(e)) + } + }.flowOn(Dispatchers.IO) + + fun logoutFlow() = flow { + emit(LoginViewState.LoggingOut) + loginApi.logout() + emit(LoginViewState.Login()) + }.flowOn(Dispatchers.IO) /** * Login to the network @@ -15,13 +45,21 @@ class LoginViewModel : ViewModel() { * @param password user password */ fun login(name: String, password: String) { - // TODO: Implement login + viewModelScope.launch { + loginFlow(name, password).collect { + _stateFlow.emit(it) + } + } } /** * Logout from the network */ fun logout() { - // TODO: Implement logout + viewModelScope.launch { + logoutFlow().collect { + _stateFlow.emit(it) + } + } } } 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..27ed1ba 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,24 @@ class NetworkViewModel : ViewModel() { val result: LiveData = _result fun startTest(numberOfThreads: Int) { - // TODO: Implement the logic + viewModelScope.launch { + _running.value = true + _result.value = null + + val jobs = List(numberOfThreads) { + async { emulateBlockingNetworkRequest() } + } + + val successfulTimeList = jobs.mapNotNull { it.await().getOrNull() } + + val averageTime = if (successfulTimeList.isNotEmpty()) { + successfulTimeList.average().toLong() + } else { + 0L + } + _result.value = averageTime + _running.value = false + } } private companion object { 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..c5a7ba1 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 @@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import ru.otus.coroutineshomework.databinding.FragmentTimerBinding @@ -18,6 +19,8 @@ import kotlin.time.Duration.Companion.milliseconds class TimerFragment : Fragment() { + private var timerJob: Job? = null + private val timeFlow = MutableStateFlow(Duration.ZERO) private var _binding: FragmentTimerBinding? = null private val binding get() = _binding!! @@ -53,12 +56,16 @@ 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() + lifecycleScope.launch { + timeFlow.collect { + time.text = this@TimerFragment.timeFlow.value.toDisplayString() + } + } btnStart.setOnClickListener { started = true } @@ -70,16 +77,21 @@ 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) } private fun startTimer() { - // TODO: Start timer + timerJob = viewLifecycleOwner.lifecycleScope.launch { + while (true){ + delay(10) + timeFlow.emit(timeFlow.value + 10.milliseconds) + } + } } private fun stopTimer() { - // TODO: Stop timer + timerJob?.cancel() } override fun onDestroyView() {