From d2029438d7a019ded5d4c1a2b29ca90ce6a33477 Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Fri, 8 Apr 2022 21:19:05 +0500 Subject: [PATCH] Fix handling result in BaseURLFragment and AuthenticationFragment --- app/src/main/AndroidManifest.xml | 2 +- .../{ => ui/activity}/MainActivity.kt | 10 +++--- .../ui/activity/MainActivityViewModel.kt | 34 +++++++++++++++++++ .../ui/auth/AuthenticationFragment.kt | 8 ++--- .../mealient/ui/auth/AuthenticationState.kt | 4 +-- .../ui/auth/AuthenticationViewModel.kt | 32 ++++++----------- .../mealient/ui/baseurl/BaseURLFragment.kt | 4 +-- .../mealient/ui/baseurl/BaseURLViewModel.kt | 18 +++++++--- .../mealient/ui/recipes/RecipesFragment.kt | 8 ++--- app/src/main/res/layout/main_activity.xml | 2 +- 10 files changed, 77 insertions(+), 45 deletions(-) rename app/src/main/java/gq/kirmanak/mealient/{ => ui/activity}/MainActivity.kt (92%) create mode 100644 app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 26f6c08..a72e4e7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ tools:ignore="UnusedAttribute" android:theme="@style/Theme.Mealient"> diff --git a/app/src/main/java/gq/kirmanak/mealient/MainActivity.kt b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt similarity index 92% rename from app/src/main/java/gq/kirmanak/mealient/MainActivity.kt rename to app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt index eaf1669..478002d 100644 --- a/app/src/main/java/gq/kirmanak/mealient/MainActivity.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivity.kt @@ -1,4 +1,4 @@ -package gq.kirmanak.mealient +package gq.kirmanak.mealient.ui.activity import android.os.Bundle import android.view.Menu @@ -10,17 +10,17 @@ import androidx.navigation.findNavController import com.google.android.material.shape.CornerFamily import com.google.android.material.shape.MaterialShapeDrawable import dagger.hilt.android.AndroidEntryPoint +import gq.kirmanak.mealient.R import gq.kirmanak.mealient.databinding.MainActivityBinding import gq.kirmanak.mealient.ui.auth.AuthenticationState import gq.kirmanak.mealient.ui.auth.AuthenticationState.AUTHORIZED import gq.kirmanak.mealient.ui.auth.AuthenticationState.UNAUTHORIZED -import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel import timber.log.Timber @AndroidEntryPoint class MainActivity : AppCompatActivity() { private lateinit var binding: MainActivityBinding - private val authViewModel by viewModels() + private val viewModel by viewModels() private var lastAuthenticationState: AuthenticationState? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -53,7 +53,7 @@ class MainActivity : AppCompatActivity() { private fun listenToAuthStatuses() { Timber.v("listenToAuthStatuses() called") - authViewModel.authenticationStateLive.observe(this, ::onAuthStateUpdate) + viewModel.authenticationStateLive.observe(this, ::onAuthStateUpdate) } private fun onAuthStateUpdate(authState: AuthenticationState) { @@ -78,7 +78,7 @@ class MainActivity : AppCompatActivity() { true } R.id.logout -> { - authViewModel.logout() + viewModel.logout() true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt new file mode 100644 index 0000000..34fdf1e --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealient/ui/activity/MainActivityViewModel.kt @@ -0,0 +1,34 @@ +package gq.kirmanak.mealient.ui.activity + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import gq.kirmanak.mealient.data.auth.AuthRepo +import gq.kirmanak.mealient.ui.auth.AuthenticationState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class MainActivityViewModel @Inject constructor( + private val authRepo: AuthRepo, +) : ViewModel() { + + private val showLoginButtonFlow = MutableStateFlow(false) + var showLoginButton: Boolean by showLoginButtonFlow::value + + private val authenticationStateFlow = combine( + showLoginButtonFlow, + authRepo.isAuthorizedFlow, + AuthenticationState::determineState + ) + val authenticationStateLive = authenticationStateFlow.asLiveData() + + fun logout() { + Timber.v("logout() called") + viewModelScope.launch { authRepo.logout() } + } +} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationFragment.kt index ef84b24..1aa024c 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationFragment.kt @@ -4,7 +4,7 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import by.kirich1409.viewbindingdelegate.viewBinding import dagger.hilt.android.AndroidEntryPoint @@ -12,13 +12,12 @@ import gq.kirmanak.mealient.R import gq.kirmanak.mealient.data.network.NetworkError import gq.kirmanak.mealient.databinding.FragmentAuthenticationBinding import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty -import gq.kirmanak.mealient.extensions.launchWithViewLifecycle import timber.log.Timber @AndroidEntryPoint class AuthenticationFragment : Fragment(R.layout.fragment_authentication) { private val binding by viewBinding(FragmentAuthenticationBinding::bind) - private val viewModel by activityViewModels() + private val viewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -26,6 +25,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) { binding.button.setOnClickListener { onLoginClicked() } (requireActivity() as? AppCompatActivity)?.supportActionBar?.title = getString(R.string.app_name) + viewModel.authenticationResult.observe(viewLifecycleOwner, ::onAuthenticationResult) } private fun onLoginClicked(): Unit = with(binding) { @@ -45,7 +45,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) { ) ?: return button.isClickable = false - launchWithViewLifecycle { onAuthenticationResult(viewModel.authenticate(email, pass)) } + viewModel.authenticate(email, pass) } private fun onAuthenticationResult(result: Result) { diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationState.kt b/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationState.kt index 45ae5cc..72429c7 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationState.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationState.kt @@ -5,7 +5,7 @@ import timber.log.Timber enum class AuthenticationState { AUTHORIZED, UNAUTHORIZED, - UNKNOWN; + HIDDEN; companion object { @@ -15,7 +15,7 @@ enum class AuthenticationState { ): AuthenticationState { Timber.v("determineState() called with: showLoginButton = $showLoginButton, isAuthorized = $isAuthorized") val result = when { - !showLoginButton -> UNKNOWN + !showLoginButton -> HIDDEN isAuthorized -> AUTHORIZED else -> UNAUTHORIZED } diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationViewModel.kt index 542cefb..3b975f5 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/auth/AuthenticationViewModel.kt @@ -1,14 +1,12 @@ package gq.kirmanak.mealient.ui.auth import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import gq.kirmanak.mealient.data.auth.AuthRepo import gq.kirmanak.mealient.extensions.runCatchingExceptCancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -18,24 +16,16 @@ class AuthenticationViewModel @Inject constructor( private val authRepo: AuthRepo, ) : ViewModel() { - private val showLoginButtonFlow = MutableStateFlow(false) - private val authenticationStateFlow = combine( - showLoginButtonFlow, - authRepo.isAuthorizedFlow, - AuthenticationState::determineState - ) - val authenticationStateLive: LiveData - get() = authenticationStateFlow.asLiveData() - var showLoginButton: Boolean by showLoginButtonFlow::value + private val _authenticationResult = MutableLiveData>() + val authenticationResult: LiveData> + get() = _authenticationResult - suspend fun authenticate(email: String, password: String) = runCatchingExceptCancel { - authRepo.authenticate(email, password) - }.onFailure { - Timber.e(it, "authenticate: can't authenticate") - } - - fun logout() { - Timber.v("logout() called") - viewModelScope.launch { authRepo.logout() } + fun authenticate(email: String, password: String) { + Timber.v("authenticate() called with: email = $email, password = $password") + viewModelScope.launch { + _authenticationResult.value = runCatchingExceptCancel { + authRepo.authenticate(email, password) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLFragment.kt index 8edeb96..d30441e 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLFragment.kt @@ -11,7 +11,6 @@ import gq.kirmanak.mealient.R import gq.kirmanak.mealient.data.network.NetworkError import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty -import gq.kirmanak.mealient.extensions.launchWithViewLifecycle import timber.log.Timber @AndroidEntryPoint @@ -24,6 +23,7 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) { super.onViewCreated(view, savedInstanceState) Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState") binding.button.setOnClickListener(::onProceedClick) + viewModel.checkURLResult.observe(viewLifecycleOwner, ::onCheckURLResult) } private fun onProceedClick(view: View) { @@ -33,7 +33,7 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) { lifecycleOwner = viewLifecycleOwner, stringId = R.string.fragment_baseurl_url_input_empty, ) ?: return - launchWithViewLifecycle { onCheckURLResult(viewModel.saveBaseUrl(url)) } + viewModel.saveBaseUrl(url) } private fun onCheckURLResult(result: Result) { diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLViewModel.kt index e5aac34..38a8257 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/baseurl/BaseURLViewModel.kt @@ -1,10 +1,14 @@ package gq.kirmanak.mealient.ui.baseurl +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import gq.kirmanak.mealient.data.baseurl.BaseURLStorage import gq.kirmanak.mealient.data.baseurl.VersionDataSource import gq.kirmanak.mealient.extensions.runCatchingExceptCancel +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -14,21 +18,25 @@ class BaseURLViewModel @Inject constructor( private val versionDataSource: VersionDataSource, ) : ViewModel() { - suspend fun saveBaseUrl(baseURL: String): Result { + private val _checkURLResult = MutableLiveData>() + val checkURLResult: LiveData> get() = _checkURLResult + + fun saveBaseUrl(baseURL: String) { Timber.v("saveBaseUrl() called with: baseURL = $baseURL") val hasPrefix = ALLOWED_PREFIXES.any { baseURL.startsWith(it) } val url = baseURL.takeIf { hasPrefix } ?: WITH_PREFIX_FORMAT.format(baseURL) - return checkBaseURL(url) + viewModelScope.launch { checkBaseURL(url) } } - private suspend fun checkBaseURL(baseURL: String): Result { + private suspend fun checkBaseURL(baseURL: String) { Timber.v("checkBaseURL() called with: baseURL = $baseURL") val result = runCatchingExceptCancel { // If it returns proper version info then it must be a Mealie versionDataSource.getVersionInfo(baseURL) + baseURLStorage.storeBaseURL(baseURL) } - baseURLStorage.storeBaseURL(baseURL) - return result.map { } + Timber.i("checkBaseURL: result is $result") + _checkURLResult.value = result } companion object { diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt index f7dc93e..14ba126 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt @@ -14,19 +14,19 @@ import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity import gq.kirmanak.mealient.databinding.FragmentRecipesBinding import gq.kirmanak.mealient.extensions.collectWithViewLifecycle import gq.kirmanak.mealient.extensions.refreshRequestFlow -import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel +import gq.kirmanak.mealient.ui.activity.MainActivityViewModel import timber.log.Timber @AndroidEntryPoint class RecipesFragment : Fragment(R.layout.fragment_recipes) { private val binding by viewBinding(FragmentRecipesBinding::bind) private val viewModel by viewModels() - private val authViewModel by activityViewModels() + private val activityViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState") - authViewModel.showLoginButton = true + activityViewModel.showLoginButton = true setupRecipeAdapter() (requireActivity() as? AppCompatActivity)?.supportActionBar?.title = null } @@ -64,6 +64,6 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) { Timber.v("onDestroyView() called") // Prevent RV leaking through mObservers list in adapter binding.recipes.adapter = null - authViewModel.showLoginButton = false + activityViewModel.showLoginButton = false } } \ No newline at end of file diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index 3986213..041006d 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".ui.activity.MainActivity">