Hide edit text cursor on list scroll
This commit is contained in:
@@ -2,19 +2,11 @@ package gq.kirmanak.mealient.extensions
|
|||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
fun <T> Fragment.collectWhenViewResumed(flow: Flow<T>, collector: FlowCollector<T>) {
|
fun <T> Fragment.collectWhenViewResumed(flow: Flow<T>, collector: FlowCollector<T>) {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.collectWhenResumed(flow, collector)
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
flow.collect(collector)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Fragment.showLongToast(@StringRes text: Int) = context?.showLongToast(text) != null
|
fun Fragment.showLongToast(@StringRes text: Int) = context?.showLongToast(text) != null
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ import android.widget.Toast
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import gq.kirmanak.mealient.logging.Logger
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
@@ -24,10 +21,7 @@ import kotlinx.coroutines.channels.ChannelResult
|
|||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.channels.onClosed
|
import kotlinx.coroutines.channels.onClosed
|
||||||
import kotlinx.coroutines.channels.onFailure
|
import kotlinx.coroutines.channels.onFailure
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
fun SwipeRefreshLayout.refreshRequestFlow(logger: Logger): Flow<Unit> = callbackFlow {
|
fun SwipeRefreshLayout.refreshRequestFlow(logger: Logger): Flow<Unit> = callbackFlow {
|
||||||
@@ -124,4 +118,12 @@ fun Context.isDarkThemeOn(): Boolean {
|
|||||||
resources.configuration.isNightModeActive
|
resources.configuration.isNightModeActive
|
||||||
else
|
else
|
||||||
resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
|
resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> LifecycleOwner.collectWhenResumed(flow: Flow<T>, collector: FlowCollector<T>) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
flow.collect(collector)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalBaseURLFrag
|
|||||||
import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalRecipesListFragment
|
import gq.kirmanak.mealient.NavGraphDirections.Companion.actionGlobalRecipesListFragment
|
||||||
import gq.kirmanak.mealient.R
|
import gq.kirmanak.mealient.R
|
||||||
import gq.kirmanak.mealient.databinding.MainActivityBinding
|
import gq.kirmanak.mealient.databinding.MainActivityBinding
|
||||||
|
import gq.kirmanak.mealient.extensions.collectWhenResumed
|
||||||
import gq.kirmanak.mealient.extensions.isDarkThemeOn
|
import gq.kirmanak.mealient.extensions.isDarkThemeOn
|
||||||
import gq.kirmanak.mealient.extensions.observeOnce
|
import gq.kirmanak.mealient.extensions.observeOnce
|
||||||
import gq.kirmanak.mealient.logging.Logger
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
@@ -69,6 +70,10 @@ class MainActivity : AppCompatActivity(R.layout.main_activity) {
|
|||||||
isAppearanceLightStatusBars = isAppearanceLightBars
|
isAppearanceLightStatusBars = isAppearanceLightBars
|
||||||
}
|
}
|
||||||
viewModel.uiStateLive.observe(this, ::onUiStateChange)
|
viewModel.uiStateLive.observe(this, ::onUiStateChange)
|
||||||
|
collectWhenResumed(viewModel.clearSearchViewFocus) {
|
||||||
|
logger.d { "clearSearchViewFocus(): received event" }
|
||||||
|
binding.toolbar.clearSearchFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
|
private fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
|||||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||||
import gq.kirmanak.mealient.logging.Logger
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -32,6 +35,9 @@ class MainActivityViewModel @Inject constructor(
|
|||||||
private val _startDestination = MutableLiveData<Int>()
|
private val _startDestination = MutableLiveData<Int>()
|
||||||
val startDestination: LiveData<Int> = _startDestination
|
val startDestination: LiveData<Int> = _startDestination
|
||||||
|
|
||||||
|
private val _clearSearchViewFocusChannel = Channel<Unit>()
|
||||||
|
val clearSearchViewFocus: Flow<Unit> = _clearSearchViewFocusChannel.receiveAsFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
authRepo.isAuthorizedFlow
|
authRepo.isAuthorizedFlow
|
||||||
.onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } }
|
.onEach { isAuthorized -> updateUiState { it.copy(isAuthorized = isAuthorized) } }
|
||||||
@@ -59,4 +65,9 @@ class MainActivityViewModel @Inject constructor(
|
|||||||
logger.v { "onSearchQuery() called with: query = $query" }
|
logger.v { "onSearchQuery() called with: query = $query" }
|
||||||
recipeRepo.updateNameQuery(query)
|
recipeRepo.updateNameQuery(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearSearchViewFocus() {
|
||||||
|
logger.v { "clearSearchViewFocus() called" }
|
||||||
|
_clearSearchViewFocusChannel.trySend(Unit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -61,8 +61,8 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
|
|||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun hideKeyboardOnScroll() {
|
private fun hideKeyboardOnScroll() {
|
||||||
binding.recipes.setOnTouchListener { view, _ ->
|
binding.recipes.setOnTouchListener { _, _ ->
|
||||||
view?.hideKeyboard()
|
activityViewModel.clearSearchViewFocus()
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,18 +155,12 @@ private fun Throwable.toLoadErrorReasonText(): Int? = when (this) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.refreshErrors(): Flow<Throwable> {
|
private fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.refreshErrors(): Flow<Throwable> {
|
||||||
return loadStateFlow
|
return loadStateFlow.map { it.refresh }.valueUpdatesOnly().filterIsInstance<LoadState.Error>()
|
||||||
.map { it.refresh }
|
|
||||||
.valueUpdatesOnly()
|
|
||||||
.filterIsInstance<LoadState.Error>()
|
|
||||||
.map { it.error }
|
.map { it.error }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.appendPaginationEnd(): Flow<Unit> {
|
private fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.appendPaginationEnd(): Flow<Unit> {
|
||||||
return loadStateFlow
|
return loadStateFlow.map { it.append.endOfPaginationReached }.valueUpdatesOnly().filter { it }
|
||||||
.map { it.append.endOfPaginationReached }
|
|
||||||
.valueUpdatesOnly()
|
|
||||||
.filter { it }
|
|
||||||
.map { }
|
.map { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user