Fix pressing back on AuthenticationFragment
If login request isn't disabled when user presses back then they might get navigated back to authentication again.
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
package gq.kirmanak.mealient.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.ChannelResult
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.onClosed
|
||||
import kotlinx.coroutines.channels.onFailure
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import timber.log.Timber
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun SwipeRefreshLayout.refreshesLiveData(): LiveData<Unit> {
|
||||
val callbackFlow: Flow<Unit> = callbackFlow {
|
||||
val listener = SwipeRefreshLayout.OnRefreshListener {
|
||||
Timber.v("Refresh requested")
|
||||
trySend(Unit).logErrors("refreshesFlow")
|
||||
}
|
||||
Timber.v("Adding refresh request listener")
|
||||
setOnRefreshListener(listener)
|
||||
awaitClose {
|
||||
Timber.v("Removing refresh request listener")
|
||||
setOnRefreshListener(null)
|
||||
}
|
||||
}
|
||||
return callbackFlow.asLiveData()
|
||||
}
|
||||
|
||||
fun Activity.setSystemUiVisibility(isVisible: Boolean) {
|
||||
Timber.v("setSystemUiVisibility() called with: isVisible = $isVisible")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) setSystemUiVisibilityV30(isVisible)
|
||||
else setSystemUiVisibilityV1(isVisible)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun Activity.setSystemUiVisibilityV1(isVisible: Boolean) {
|
||||
Timber.v("setSystemUiVisibilityV1() called with: isVisible = $isVisible")
|
||||
window.decorView.systemUiVisibility = if (isVisible) 0 else View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
private fun Activity.setSystemUiVisibilityV30(isVisible: Boolean) {
|
||||
Timber.v("setSystemUiVisibilityV30() called with: isVisible = $isVisible")
|
||||
val systemBars = WindowInsets.Type.systemBars()
|
||||
window.insetsController?.apply { if (isVisible) show(systemBars) else hide(systemBars) }
|
||||
?: Timber.w("setSystemUiVisibilityV30: insets controller is null")
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setActionBarVisibility(isVisible: Boolean) {
|
||||
Timber.v("setActionBarVisibility() called with: isVisible = $isVisible")
|
||||
supportActionBar?.apply { if (isVisible) show() else hide() }
|
||||
?: Timber.w("setActionBarVisibility: action bar is null")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun TextView.textChangesFlow(): Flow<CharSequence?> = callbackFlow {
|
||||
Timber.v("textChangesFlow() called")
|
||||
val textWatcher = doAfterTextChanged {
|
||||
trySend(it).logErrors("textChangesFlow")
|
||||
}
|
||||
awaitClose {
|
||||
Timber.d("textChangesFlow: flow is closing")
|
||||
removeTextChangedListener(textWatcher)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> ChannelResult<T>.logErrors(methodName: String): ChannelResult<T> {
|
||||
onFailure { Timber.e(it, "$methodName: can't send event") }
|
||||
onClosed { Timber.e(it, "$methodName: flow has been closed") }
|
||||
return this
|
||||
}
|
||||
|
||||
fun EditText.checkIfInputIsEmpty(
|
||||
inputLayout: TextInputLayout,
|
||||
lifecycleCoroutineScope: LifecycleCoroutineScope,
|
||||
errorText: () -> String
|
||||
): String? {
|
||||
Timber.v("checkIfInputIsEmpty() called with: input = $this, inputLayout = $inputLayout, errorText = $errorText")
|
||||
val text = text?.toString()
|
||||
Timber.d("Input text is \"$text\"")
|
||||
if (text.isNullOrEmpty()) {
|
||||
inputLayout.error = errorText()
|
||||
lifecycleCoroutineScope.launchWhenResumed {
|
||||
waitUntilNotEmpty()
|
||||
inputLayout.error = null
|
||||
}
|
||||
return null
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
suspend fun EditText.waitUntilNotEmpty() {
|
||||
Timber.v("waitUntilNotEmpty() called with: input = $this")
|
||||
textChangesFlow().filterNotNull().first { it.isNotEmpty() }
|
||||
Timber.v("waitUntilNotEmpty() returned")
|
||||
}
|
||||
@@ -13,7 +13,8 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.network.NetworkError.Unauthorized
|
||||
import gq.kirmanak.mealient.databinding.FragmentAuthenticationBinding
|
||||
import gq.kirmanak.mealient.ui.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.executeOnceOnBackPressed
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -28,6 +29,7 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||
authStatuses.observe(this, ::onAuthStatusChange)
|
||||
executeOnceOnBackPressed { viewModel.disableLoginRequest() }
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
@@ -45,8 +45,17 @@ class AuthenticationViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun login() {
|
||||
Timber.v("login() called")
|
||||
viewModelScope.launch { loginRequestsFlow.emit(true) }
|
||||
fun enableLoginRequest() {
|
||||
Timber.v("enableLoginRequest() called")
|
||||
updateIsLoginRequested(true)
|
||||
}
|
||||
|
||||
fun disableLoginRequest() {
|
||||
Timber.v("disableLoginRequest() called")
|
||||
updateIsLoginRequested(false)
|
||||
}
|
||||
|
||||
private fun updateIsLoginRequested(isRequested: Boolean) {
|
||||
viewModelScope.launch { loginRequestsFlow.emit(isRequested) }
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.network.NetworkError
|
||||
import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding
|
||||
import gq.kirmanak.mealient.ui.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
||||
@@ -13,9 +13,9 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.FragmentRecipesBinding
|
||||
import gq.kirmanak.mealient.extensions.refreshesLiveData
|
||||
import gq.kirmanak.mealient.ui.auth.AuthenticationState
|
||||
import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel
|
||||
import gq.kirmanak.mealient.ui.refreshesLiveData
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.setActionBarVisibility
|
||||
import gq.kirmanak.mealient.ui.setSystemUiVisibility
|
||||
import gq.kirmanak.mealient.extensions.setActionBarVisibility
|
||||
import gq.kirmanak.mealient.extensions.setSystemUiVisibility
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
||||
Reference in New Issue
Block a user