Files
Mealient/app/src/main/java/gq/kirmanak/mealient/extensions/ViewExtensions.kt
Kirill Kamakin f14afd2ebe Fix IllegalStateException when clicking login after logout
The previous login result was stored as live data and
prevented AuthenticationFragment from being shown
properly. However, an attempt to destroy RecipesFragment
was made. This attempt caused IllegalStateException
when accessing view in onDestroyView.
2022-04-04 20:52:14 +05:00

107 lines
4.0 KiB
Kotlin

package gq.kirmanak.mealient.extensions
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.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.refreshRequestFlow(): Flow<Unit> = callbackFlow {
Timber.v("refreshRequestFlow() called")
val listener = SwipeRefreshLayout.OnRefreshListener {
Timber.v("refreshRequestFlow: listener called")
trySend(Unit).logErrors("refreshesFlow")
}
setOnRefreshListener(listener)
awaitClose {
Timber.v("Removing refresh request listener")
setOnRefreshListener(null)
}
}
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")
}