Replace AccountManager with EncryptedSharedPreferences
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui.addaccount
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AddAccountActivity : AppCompatActivity() {
|
||||
|
||||
private val viewModel by viewModels<AddAccountViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||
super.onCreate(savedInstanceState, persistentState)
|
||||
supportActionBar?.title = getString(R.string.app_name)
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui.addaccount
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
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 kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AddAccountFragment : Fragment(R.layout.fragment_authentication) {
|
||||
|
||||
private val binding by viewBinding(FragmentAuthenticationBinding::bind)
|
||||
private val viewModel by activityViewModels<AddAccountViewModel>()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
binding.button.setOnClickListener { onLoginClicked() }
|
||||
}
|
||||
|
||||
private fun onLoginClicked(): Unit = with(binding) {
|
||||
Timber.v("onLoginClicked() called")
|
||||
|
||||
val email: String = emailInput.checkIfInputIsEmpty(
|
||||
inputLayout = emailInputLayout,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_authentication_email_input_empty,
|
||||
) ?: return
|
||||
|
||||
val pass: String = passwordInput.checkIfInputIsEmpty(
|
||||
inputLayout = passwordInputLayout,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_authentication_password_input_empty,
|
||||
trim = false,
|
||||
) ?: return
|
||||
|
||||
button.isClickable = false
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
onAuthenticationResult(viewModel.authenticate(email, pass))
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAuthenticationResult(result: Result<Unit>) {
|
||||
Timber.v("onAuthenticationResult() called with: result = $result")
|
||||
if (result.isSuccess) {
|
||||
TODO("Implement authentication success")
|
||||
}
|
||||
|
||||
binding.passwordInputLayout.error = when (result.exceptionOrNull()) {
|
||||
is NetworkError.Unauthorized -> getString(R.string.fragment_authentication_credentials_incorrect)
|
||||
else -> null
|
||||
}
|
||||
|
||||
binding.button.isClickable = true
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui.addaccount
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import timber.log.Timber
|
||||
|
||||
@HiltViewModel
|
||||
class AddAccountViewModel(
|
||||
private val authRepo: AuthRepo,
|
||||
) : ViewModel() {
|
||||
|
||||
suspend fun authenticate(username: String, password: String) = runCatchingExceptCancel {
|
||||
Timber.v("authenticate() called with: username = $username, password = $password")
|
||||
authRepo.authenticate(username, password)
|
||||
}.onFailure {
|
||||
Timber.e(it, "authenticate: can't authenticate")
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -13,8 +12,7 @@ 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.executeOnceOnBackPressed
|
||||
import kotlinx.coroutines.launch
|
||||
import gq.kirmanak.mealient.extensions.launchWithViewLifecycle
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -22,12 +20,6 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
private val binding by viewBinding(FragmentAuthenticationBinding::bind)
|
||||
private val viewModel by activityViewModels<AuthenticationViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||
executeOnceOnBackPressed { viewModel.authRequested = false }
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
@@ -53,9 +45,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
) ?: return
|
||||
|
||||
button.isClickable = false
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
onAuthenticationResult(viewModel.authenticate(email, pass))
|
||||
}
|
||||
launchWithViewLifecycle { onAuthenticationResult(viewModel.authenticate(email, pass)) }
|
||||
}
|
||||
|
||||
private fun onAuthenticationResult(result: Result<Unit>) {
|
||||
|
||||
@@ -4,22 +4,19 @@ import timber.log.Timber
|
||||
|
||||
enum class AuthenticationState {
|
||||
AUTHORIZED,
|
||||
AUTH_REQUESTED,
|
||||
UNAUTHORIZED,
|
||||
UNKNOWN;
|
||||
|
||||
companion object {
|
||||
|
||||
fun determineState(
|
||||
isLoginRequested: Boolean,
|
||||
showLoginButton: Boolean,
|
||||
isAuthorized: Boolean,
|
||||
): AuthenticationState {
|
||||
Timber.v("determineState() called with: isLoginRequested = $isLoginRequested, showLoginButton = $showLoginButton, isAuthorized = $isAuthorized")
|
||||
Timber.v("determineState() called with: showLoginButton = $showLoginButton, isAuthorized = $isAuthorized")
|
||||
val result = when {
|
||||
!showLoginButton -> UNKNOWN
|
||||
isAuthorized -> AUTHORIZED
|
||||
isLoginRequested -> AUTH_REQUESTED
|
||||
else -> UNAUTHORIZED
|
||||
}
|
||||
Timber.v("determineState() returned: $result")
|
||||
|
||||
@@ -18,21 +18,18 @@ class AuthenticationViewModel @Inject constructor(
|
||||
private val authRepo: AuthRepo,
|
||||
) : ViewModel() {
|
||||
|
||||
private val authRequestsFlow = MutableStateFlow(false)
|
||||
private val showLoginButtonFlow = MutableStateFlow(false)
|
||||
private val authenticationStateFlow = combine(
|
||||
authRequestsFlow,
|
||||
showLoginButtonFlow,
|
||||
authRepo.isAuthorizedFlow,
|
||||
AuthenticationState::determineState
|
||||
)
|
||||
val authenticationStateLive: LiveData<AuthenticationState>
|
||||
get() = authenticationStateFlow.asLiveData()
|
||||
var authRequested: Boolean by authRequestsFlow::value
|
||||
var showLoginButton: Boolean by showLoginButtonFlow::value
|
||||
|
||||
suspend fun authenticate(username: String, password: String) = runCatchingExceptCancel {
|
||||
authRepo.authenticate(username, password)
|
||||
suspend fun authenticate(email: String, password: String) = runCatchingExceptCancel {
|
||||
authRepo.authenticate(email, password)
|
||||
}.onFailure {
|
||||
Timber.e(it, "authenticate: can't authenticate")
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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
|
||||
@@ -22,7 +23,6 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
viewModel.screenState.observe(viewLifecycleOwner, ::updateState)
|
||||
binding.button.setOnClickListener(::onProceedClick)
|
||||
}
|
||||
|
||||
@@ -33,16 +33,16 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_baseurl_url_input_empty,
|
||||
) ?: return
|
||||
viewModel.saveBaseUrl(url)
|
||||
launchWithViewLifecycle { onCheckURLResult(viewModel.saveBaseUrl(url)) }
|
||||
}
|
||||
|
||||
private fun updateState(baseURLScreenState: BaseURLScreenState) {
|
||||
Timber.v("updateState() called with: baseURLScreenState = $baseURLScreenState")
|
||||
if (baseURLScreenState.navigateNext) {
|
||||
private fun onCheckURLResult(result: Result<Unit>) {
|
||||
Timber.v("onCheckURLResult() called with: result = $result")
|
||||
if (result.isSuccess) {
|
||||
findNavController().navigate(BaseURLFragmentDirections.actionBaseURLFragmentToRecipesFragment())
|
||||
return
|
||||
}
|
||||
binding.urlInputLayout.error = when (val exception = baseURLScreenState.error) {
|
||||
binding.urlInputLayout.error = when (val exception = result.exceptionOrNull()) {
|
||||
is NetworkError.NoServerConnection -> getString(R.string.fragment_base_url_no_connection)
|
||||
is NetworkError.NotMealie -> getString(R.string.fragment_base_url_unexpected_response)
|
||||
is NetworkError.MalformedUrl -> {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui.baseurl
|
||||
|
||||
import gq.kirmanak.mealient.data.network.NetworkError
|
||||
|
||||
data class BaseURLScreenState(
|
||||
val error: NetworkError? = null,
|
||||
val navigateNext: Boolean = false,
|
||||
)
|
||||
@@ -1,14 +1,10 @@
|
||||
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.data.network.NetworkError
|
||||
import kotlinx.coroutines.launch
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -18,35 +14,21 @@ class BaseURLViewModel @Inject constructor(
|
||||
private val versionDataSource: VersionDataSource,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _screenState = MutableLiveData(BaseURLScreenState())
|
||||
var currentScreenState: BaseURLScreenState
|
||||
get() = _screenState.value!!
|
||||
private set(value) {
|
||||
_screenState.value = value
|
||||
}
|
||||
val screenState: LiveData<BaseURLScreenState>
|
||||
get() = _screenState
|
||||
|
||||
fun saveBaseUrl(baseURL: String) {
|
||||
suspend fun saveBaseUrl(baseURL: String): Result<Unit> {
|
||||
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)
|
||||
viewModelScope.launch { checkBaseURL(url) }
|
||||
return checkBaseURL(url)
|
||||
}
|
||||
|
||||
private suspend fun checkBaseURL(baseURL: String) {
|
||||
private suspend fun checkBaseURL(baseURL: String): Result<Unit> {
|
||||
Timber.v("checkBaseURL() called with: baseURL = $baseURL")
|
||||
val version = try {
|
||||
val result = runCatchingExceptCancel {
|
||||
// If it returns proper version info then it must be a Mealie
|
||||
versionDataSource.getVersionInfo(baseURL)
|
||||
} catch (e: NetworkError) {
|
||||
Timber.e(e, "checkBaseURL: can't get version info")
|
||||
currentScreenState = BaseURLScreenState(e, false)
|
||||
return
|
||||
}
|
||||
Timber.d("checkBaseURL: version is $version")
|
||||
baseURLStorage.storeBaseURL(baseURL)
|
||||
currentScreenState = BaseURLScreenState(null, true)
|
||||
return result.map { }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -14,7 +14,6 @@ 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.AuthenticationState
|
||||
import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel
|
||||
import timber.log.Timber
|
||||
|
||||
@@ -24,19 +23,6 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
private val viewModel by viewModels<RecipeViewModel>()
|
||||
private val authViewModel by activityViewModels<AuthenticationViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||
authViewModel.authenticationStateLive.observe(this, ::onAuthStateChange)
|
||||
}
|
||||
|
||||
private fun onAuthStateChange(authenticationState: AuthenticationState) {
|
||||
Timber.v("onAuthStateChange() called with: authenticationState = $authenticationState")
|
||||
if (authenticationState == AuthenticationState.AUTH_REQUESTED) {
|
||||
findNavController().navigate(RecipesFragmentDirections.actionRecipesFragmentToAuthenticationFragment())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
|
||||
Reference in New Issue
Block a user