From 89f541f8fcf9a3bc109e708e4181b29893709852 Mon Sep 17 00:00:00 2001 From: Kirill Nayduik Date: Tue, 21 Sep 2021 01:06:56 +0300 Subject: [PATCH] Fix keyboard change detection: separate StatefulKeyboardResizeableFragment and KeyboardResizeableFragment, add ability to handle keyboard detection inside nested fragments --- .../core/MviKeyboardResizableFragment.kt | 38 ++++---- .../KeyboardBehaviorDetector.kt | 22 +++-- .../KeyboardResizeableFragment.kt | 53 +++++------ .../StatefulKeyboardResizeableFragment.kt | 93 +++++++++++++++++++ 4 files changed, 155 insertions(+), 51 deletions(-) create mode 100644 navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/StatefulKeyboardResizeableFragment.kt diff --git a/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/MviKeyboardResizableFragment.kt b/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/MviKeyboardResizableFragment.kt index c41e850..1c30f88 100644 --- a/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/MviKeyboardResizableFragment.kt +++ b/mvi-arch/src/main/java/ru/touchin/roboswag/mvi_arch/core/MviKeyboardResizableFragment.kt @@ -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( 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( } } + 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( 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) } } diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardBehaviorDetector.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardBehaviorDetector.kt index ca79892..13e6088 100644 --- a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardBehaviorDetector.kt +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardBehaviorDetector.kt @@ -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 = mutableListOf() + private val keyboardShowListeners: MutableList = 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()} } } diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt index e43d405..7043d13 100644 --- a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/KeyboardResizeableFragment.kt @@ -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( +abstract class KeyboardResizeableFragment( @LayoutRes layoutRes: Int -) : StatefulFragment( +) : BaseFragment( 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 + onKeyboardShow(diff) + isKeyboardVisible = true + } + private var isHideKeyboardOnBackEnabled = false protected open fun onKeyboardShow(diff: Int = 0) {} @@ -41,50 +51,35 @@ abstract class KeyboardResizeableFragment= 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 - } - } diff --git a/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/StatefulKeyboardResizeableFragment.kt b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/StatefulKeyboardResizeableFragment.kt new file mode 100644 index 0000000..d14ec8e --- /dev/null +++ b/navigation-base/src/main/java/ru/touchin/roboswag/navigation_base/keyboard_resizeable/StatefulKeyboardResizeableFragment.kt @@ -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( + @LayoutRes layoutRes: Int +) : StatefulFragment( + 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