Skip to content

Commit

Permalink
refactor: new way for handling insets (#207)
Browse files Browse the repository at this point in the history
## 馃摐 Description

Setup `onApplyWindowInsetsListener` that manages `systemBars` and
`navigationBar` on `rootView` to get correct and constant insets.

## 馃挕 Motivation and Context

If we setup `onApplyWindowInsetsListener` on `EdgeToEdgeReactViewGroup`,
then sometimes insets are including keyboard size (it's is `0, 1225`
(top, bottom) instead of expected `144, 84`).

I discovered that if we setup `onApplyWindowInsetsListener` on
`rootVIew` - in this case insets are always stable. So I reworked this
part (worth to note that `onApplyWindowInsetsListener` that detects
keyboard size changes still should listen to `EdgeToEdgeReactViewGroup`
because otherwise it's not working).

It fixes a problem with content jump, however the old approach (setting
`padding` for `action_bar_root`) is not working anymore - instead we
should apply margin. Last, but not least - we should setup it
asynchronously, because if we setup it in `onAttachedToWindow`/`init`
then it'll add undesired padding:

<p align="center">
<img width="257" alt="image"
src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/bac5586a-c91b-4162-8098-831b63607323">
</p>
<p align="center">
<i>Area in blue - undesired margin</i>
</p>

Fixes
#51

## 馃摙 Changelog

### Android
- apply `margin` to `action_bar_root` instead of `padding`;
- for managing paddings setup `onApplyWindowInsetsListener` on
`rootView`, keep `onApplyWindowInsetsListener` on `EdgeToEdgeView` just
for detecting keyboard size changes;
- setup `onApplyWindowInsetsListener` on `rootView` asynchronously (to
avoid unnecessary margin on mount);
- added `ThemedReactContext` extension that returns `rootView`;

## 馃 How Has This Been Tested?

Tested (fabric/paper) on:
- Pixel 7 Pro (Android 13);
- Redmi note 5 Pro (Android 9);

## 馃摳 Screenshots (if appropriate):

|Before|After|
|------|-----|

|![telegram-cloud-photo-size-2-5402402906965133775-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/e89ac555-4ef6-463d-81b6-7f66178fe506)|![telegram-cloud-photo-size-2-5402402906965133774-y](https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/1b94cb28-ce09-4977-967a-c7d1487377f9)|

## 馃摑 Checklist

- [x] CI successfully passed
  • Loading branch information
kirillzyusko committed Aug 18, 2023
1 parent 896d232 commit dfe353d
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class KeyboardAnimationCallback(
val deferredInsetTypes: Int,
dispatchMode: Int = DISPATCH_MODE_STOP,
val context: ThemedReactContext?,
val onApplyWindowInsetsListener: OnApplyWindowInsetsListener,
) : WindowInsetsAnimationCompat.Callback(dispatchMode), OnApplyWindowInsetsListener {
private var persistentKeyboardHeight = 0.0
private var isKeyboardVisible = false
Expand Down Expand Up @@ -158,7 +157,7 @@ class KeyboardAnimationCallback(
this.persistentKeyboardHeight = keyboardHeight
}

return onApplyWindowInsetsListener.onApplyWindowInsets(v, insets)
return insets
}

override fun onStart(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.reactnativekeyboardcontroller.extensions

import android.view.View
import com.facebook.react.uimanager.ThemedReactContext

val ThemedReactContext.rootView: View?
get() = this.currentActivity?.window?.decorView?.rootView
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.reactnativekeyboardcontroller.views

import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.FrameLayout
import androidx.appcompat.widget.FitWindowsLinearLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
Expand All @@ -10,8 +13,8 @@ import androidx.core.view.WindowInsetsCompat
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.views.view.ReactViewGroup
import com.reactnativekeyboardcontroller.KeyboardAnimationCallback
import com.reactnativekeyboardcontroller.R
import com.reactnativekeyboardcontroller.extensions.requestApplyInsetsWhenAttached
import com.reactnativekeyboardcontroller.extensions.rootView

private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName

Expand All @@ -24,7 +27,6 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
val activity = reactContext.currentActivity

if (activity != null) {

val callback = KeyboardAnimationCallback(
view = this,
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
Expand All @@ -33,30 +35,6 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
// child views, so that step 2.5 below receives the call
dispatchMode = WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE,
context = reactContext,
onApplyWindowInsetsListener = { v, insets ->
val content =
reactContext.currentActivity?.window?.decorView?.rootView?.findViewById<FitWindowsLinearLayout>(
androidx.appcompat.R.id.action_bar_root,
)
content?.setPadding(
0,
if (this.isStatusBarTranslucent) {
0
} else {
insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.top
?: 0
},
0,
if (this.isNavigationBarTranslucent) {
0
} else {
insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom
?: 0
},
)

insets
},
)
ViewCompat.setWindowInsetsAnimationCallback(this, callback)
ViewCompat.setOnApplyWindowInsetsListener(this, callback)
Expand All @@ -66,9 +44,47 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
}
}

private fun setupWindowInsets() {
val rootView = reactContext.rootView
if (rootView != null) {
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
val content =
reactContext.rootView?.findViewById<FitWindowsLinearLayout>(
androidx.appcompat.R.id.action_bar_root,
)
val params = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT,
)

params.setMargins(
0,
if (this.isStatusBarTranslucent) {
0
} else {
insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.top
?: 0
},
0,
if (this.isNavigationBarTranslucent) {
0
} else {
insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom
?: 0
},
)

content?.layoutParams = params

insets
}
}
}

override fun onAttachedToWindow() {
super.onAttachedToWindow()

Handler(Looper.getMainLooper()).post(this::setupWindowInsets)
reactContext.currentActivity?.let {
WindowCompat.setDecorFitsSystemWindows(
it.window,
Expand Down

0 comments on commit dfe353d

Please sign in to comment.