Merge pull request #225 from TouchInstinct/fix/nested_keyboard_detection

Keyboard detector refactor
This commit is contained in:
Kirill Nayduik 2021-09-21 17:10:58 +03:00 committed by GitHub
commit 4af62593e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 155 additions and 51 deletions

View File

@ -9,10 +9,12 @@ import ru.touchin.roboswag.components.utils.hideSoftInput
import ru.touchin.roboswag.mvi_arch.marker.ViewAction
import ru.touchin.roboswag.mvi_arch.marker.ViewState
import ru.touchin.roboswag.navigation_base.activities.OnBackPressedListener
import ru.touchin.roboswag.navigation_base.keyboard_resizeable.OnHideListener
import ru.touchin.roboswag.navigation_base.keyboard_resizeable.OnShowListener
// CPD-OFF
/**
* Same code as in [ru.touchin.roboswag.navigation_base.keyboard_resizeable.KeyboardResizeableFragment] but inherited from MviFragment
* Same code as in [ru.touchin.roboswag.navigation_base.keyboard_resizeable.StatefulKeyboardResizeableFragment] but inherited from MviFragment
* Used to detect IME events (show, hide)
*/
@ -26,7 +28,7 @@ abstract class MviKeyboardResizableFragment<NavArgs, State, Action, VM>(
private var isKeyboardVisible: Boolean = false
private val keyboardHideListener = OnBackPressedListener {
private val onBackPressedListener = OnBackPressedListener {
if (isKeyboardVisible) {
activity.hideSoftInput()
true
@ -35,6 +37,18 @@ abstract class MviKeyboardResizableFragment<NavArgs, State, Action, VM>(
}
}
private val keyboardHideListener: OnHideListener = {
if (isKeyboardVisible) {
onKeyboardHide()
}
isKeyboardVisible = false
}
private val keyboardShowListener: OnShowListener = { diff ->
onKeyboardShow(diff)
isKeyboardVisible = true
}
private var isHideKeyboardOnBackEnabled = false
protected open fun onKeyboardShow(diff: Int = 0) {}
@ -54,36 +68,28 @@ abstract class MviKeyboardResizableFragment<NavArgs, State, Action, VM>(
override fun onResume() {
super.onResume()
if (isHideKeyboardOnBackEnabled) activity.addOnBackPressedListener(keyboardHideListener)
if (isHideKeyboardOnBackEnabled) activity.addOnBackPressedListener(onBackPressedListener)
}
override fun onPause() {
super.onPause()
if (isKeyboardVisible) activity.hideSoftInput()
if (isHideKeyboardOnBackEnabled) activity.removeOnBackPressedListener(keyboardHideListener)
if (isHideKeyboardOnBackEnabled) activity.removeOnBackPressedListener(onBackPressedListener)
}
override fun onStart() {
super.onStart()
activity.keyboardBehaviorDetector?.apply {
keyboardHideListener = {
if (isKeyboardVisible) {
onKeyboardHide()
}
isKeyboardVisible = false
}
keyboardShowListener = { diff ->
onKeyboardShow(diff)
isKeyboardVisible = true
}
addOnHideListener(keyboardHideListener)
addOnShowListener(keyboardShowListener)
}
}
override fun onStop() {
super.onStop()
activity.keyboardBehaviorDetector?.apply {
keyboardHideListener = null
keyboardShowListener = null
removeOnHideListener(keyboardHideListener)
removeOnShowListener(keyboardShowListener)
}
}

View File

@ -12,14 +12,26 @@ import ru.touchin.roboswag.navigation_base.activities.BaseActivity
*
* Your activity must have android:windowSoftInputMode="adjustResize" at least, otherwise listeners won't be called
*/
typealias OnHideListener = () -> Unit
typealias OnShowListener = (Int) -> Unit
class KeyboardBehaviorDetector(
activity: BaseActivity
) : LifecycleObserver {
private val view = activity.window.decorView
var keyboardHideListener: (() -> Unit)? = null
var keyboardShowListener: ((Int) -> Unit)? = null
private val keyboardHideListeners: MutableList<OnHideListener> = mutableListOf()
private val keyboardShowListeners: MutableList<OnShowListener> = mutableListOf()
fun addOnHideListener(listener: OnHideListener) { keyboardHideListeners.add(listener) }
fun addOnShowListener(listener: OnShowListener) { keyboardShowListeners.add(listener) }
fun removeOnHideListener(listener: OnHideListener) { keyboardHideListeners.remove(listener) }
fun removeOnShowListener(listener: OnShowListener) { keyboardShowListeners.remove(listener) }
// -1 when we never measure insets yet
var startNavigationBarHeight = -1
@ -27,11 +39,9 @@ class KeyboardBehaviorDetector(
private val listener = { isKeyboardOpen: Boolean, windowInsets: WindowInsetsCompat ->
if (isKeyboardOpen) {
keyboardShowListener?.invoke(
windowInsets.systemWindowInsetBottom - startNavigationBarHeight
)
keyboardShowListeners.forEach { it.invoke(windowInsets.systemWindowInsetBottom - startNavigationBarHeight) }
} else {
keyboardHideListener?.invoke()
keyboardHideListeners.forEach { it.invoke()}
}
}

View File

@ -1,25 +1,23 @@
package ru.touchin.roboswag.navigation_base.keyboard_resizeable
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.annotation.LayoutRes
import androidx.lifecycle.LifecycleObserver
import ru.touchin.roboswag.components.utils.hideSoftInput
import ru.touchin.roboswag.navigation_base.activities.BaseActivity
import ru.touchin.roboswag.navigation_base.activities.OnBackPressedListener
import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment
import ru.touchin.roboswag.navigation_base.fragments.BaseFragment
abstract class KeyboardResizeableFragment<TActivity : BaseActivity, TState : Parcelable>(
abstract class KeyboardResizeableFragment<TActivity : BaseActivity>(
@LayoutRes layoutRes: Int
) : StatefulFragment<TActivity, TState>(
) : BaseFragment<TActivity>(
layoutRes
) {
private var isKeyboardVisible: Boolean = false
private val keyboardHideListener = OnBackPressedListener {
private val onBackPressedListener = OnBackPressedListener {
if (isKeyboardVisible) {
activity.hideSoftInput()
true
@ -28,6 +26,18 @@ abstract class KeyboardResizeableFragment<TActivity : BaseActivity, TState : Par
}
}
private val keyboardHideListener: OnHideListener = {
if (isKeyboardVisible) {
onKeyboardHide()
}
isKeyboardVisible = false
}
private val keyboardShowListener: OnShowListener = { diff ->
onKeyboardShow(diff)
isKeyboardVisible = true
}
private var isHideKeyboardOnBackEnabled = false
protected open fun onKeyboardShow(diff: Int = 0) {}
@ -41,50 +51,35 @@ abstract class KeyboardResizeableFragment<TActivity : BaseActivity, TState : Par
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
view.requestApplyInsets()
}
view.requestApplyInsets()
lifecycle.addObserver(activity.keyboardBehaviorDetector as LifecycleObserver)
}
override fun onResume() {
super.onResume()
if (isHideKeyboardOnBackEnabled) activity.addOnBackPressedListener(keyboardHideListener)
if (isHideKeyboardOnBackEnabled) activity.addOnBackPressedListener(onBackPressedListener)
}
override fun onPause() {
super.onPause()
notifyKeyboardHidden()
if (isHideKeyboardOnBackEnabled) activity.removeOnBackPressedListener(keyboardHideListener)
if (isKeyboardVisible) activity.hideSoftInput()
if (isHideKeyboardOnBackEnabled) activity.removeOnBackPressedListener(onBackPressedListener)
}
override fun onStart() {
super.onStart()
activity.keyboardBehaviorDetector?.apply {
keyboardHideListener = {
if (isKeyboardVisible) {
onKeyboardHide()
}
isKeyboardVisible = false
}
keyboardShowListener = { diff ->
onKeyboardShow(diff)
isKeyboardVisible = true
}
addOnHideListener(keyboardHideListener)
addOnShowListener(keyboardShowListener)
}
}
override fun onStop() {
super.onStop()
activity.keyboardBehaviorDetector?.apply {
keyboardHideListener = null
keyboardShowListener = null
removeOnHideListener(keyboardHideListener)
removeOnShowListener(keyboardShowListener)
}
}
private fun notifyKeyboardHidden() {
if (isKeyboardVisible) onKeyboardHide()
isKeyboardVisible = false
}
}

View File

@ -0,0 +1,93 @@
package ru.touchin.roboswag.navigation_base.keyboard_resizeable
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.annotation.LayoutRes
import androidx.lifecycle.LifecycleObserver
import ru.touchin.roboswag.components.utils.hideSoftInput
import ru.touchin.roboswag.navigation_base.activities.BaseActivity
import ru.touchin.roboswag.navigation_base.activities.OnBackPressedListener
import ru.touchin.roboswag.navigation_base.fragments.StatefulFragment
// CPD-OFF
/**
* Same code as in [KeyboardResizeableFragment] but inherited from StatefulFragment
* Used to detect IME events (show, hide)
*/
abstract class StatefulKeyboardResizeableFragment<TActivity : BaseActivity, TState : Parcelable>(
@LayoutRes layoutRes: Int
) : StatefulFragment<TActivity, TState>(
layoutRes
) {
private var isKeyboardVisible: Boolean = false
private val onBackPressedListener = OnBackPressedListener {
if (isKeyboardVisible) {
activity.hideSoftInput()
true
} else {
false
}
}
private val keyboardHideListener: OnHideListener = {
if (isKeyboardVisible) {
onKeyboardHide()
}
isKeyboardVisible = false
}
private val keyboardShowListener: OnShowListener = { diff ->
onKeyboardShow(diff)
isKeyboardVisible = true
}
private var isHideKeyboardOnBackEnabled = false
protected open fun onKeyboardShow(diff: Int = 0) {}
protected open fun onKeyboardHide() {}
protected fun hideKeyboardOnBackPressed() {
isHideKeyboardOnBackEnabled = true
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requestApplyInsets()
lifecycle.addObserver(activity.keyboardBehaviorDetector as LifecycleObserver)
}
override fun onResume() {
super.onResume()
if (isHideKeyboardOnBackEnabled) activity.addOnBackPressedListener(onBackPressedListener)
}
override fun onPause() {
super.onPause()
if (isKeyboardVisible) activity.hideSoftInput()
if (isHideKeyboardOnBackEnabled) activity.removeOnBackPressedListener(onBackPressedListener)
}
override fun onStart() {
super.onStart()
activity.keyboardBehaviorDetector?.apply {
addOnHideListener(keyboardHideListener)
addOnShowListener(keyboardShowListener)
}
}
override fun onStop() {
super.onStop()
activity.keyboardBehaviorDetector?.apply {
removeOnHideListener(keyboardHideListener)
removeOnShowListener(keyboardShowListener)
}
}
}
// CPD-ON