Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.reactnativekeyboardcontroller.extensions

import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.common.UIManagerType
Expand All @@ -28,3 +29,12 @@ val ReactContext.content: ViewGroup?
this.currentActivity?.window?.decorView?.rootView?.findViewById(
androidx.appcompat.R.id.action_bar_root,
)

val ReactContext.windowSoftInputMode: Int
get() =
this
.currentActivity
?.window
?.attributes
?.softInputMode
?: WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import android.annotation.SuppressLint
import android.graphics.Rect
import android.os.Build
import android.view.View
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.reactnativekeyboardcontroller.log.Logger

/**
Expand Down Expand Up @@ -61,41 +59,3 @@ val View.screenLocation get(): IntArray {

return point
}

/**
* Safely replaces status bar insets so that when we edge-to-edge mode gets disabled/enabled
* the app content is not jumping/resizing a window.
* */
@Suppress("DEPRECATION")
fun View.replaceStatusBarInsets(
insets: WindowInsetsCompat,
isStatusBarTranslucent: Boolean,
active: Boolean,
): WindowInsetsCompat {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val sysBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
val navBars = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
val ime = insets.getInsets(WindowInsetsCompat.Type.ime())
val adjustedTop = if (isStatusBarTranslucent) 0 else sysBars.top
// pick bottom: use IME if present, otherwise nav bar bottom (respect translucency)
val bottomFromImeOrNav = if (ime.bottom > 0) ime.bottom else navBars.bottom
val adjustedInsets =
WindowInsetsCompat
.Builder(insets)
.setInsets(
WindowInsetsCompat.Type.systemBars(),
Insets.of(sysBars.left, adjustedTop, sysBars.right, if (active) sysBars.bottom else bottomFromImeOrNav),
).build()

return ViewCompat.onApplyWindowInsets(this, adjustedInsets)
} else {
val defaultInsets = ViewCompat.onApplyWindowInsets(this, insets)

return defaultInsets.replaceSystemWindowInsets(
defaultInsets.systemWindowInsetLeft,
if (isStatusBarTranslucent) 0 else defaultInsets.systemWindowInsetTop,
defaultInsets.systemWindowInsetRight,
defaultInsets.systemWindowInsetBottom,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.reactnativekeyboardcontroller.modules
import android.content.Context
import android.os.Build
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
Expand All @@ -12,6 +11,7 @@ import com.facebook.react.bridge.UiThreadUtil
import com.reactnativekeyboardcontroller.extensions.dp
import com.reactnativekeyboardcontroller.extensions.screenLocation
import com.reactnativekeyboardcontroller.extensions.uiManager
import com.reactnativekeyboardcontroller.extensions.windowSoftInputMode
import com.reactnativekeyboardcontroller.interactive.KeyboardAnimationController
import com.reactnativekeyboardcontroller.traversal.FocusedInputHolder
import com.reactnativekeyboardcontroller.traversal.ViewHierarchyNavigator
Expand All @@ -21,7 +21,7 @@ class KeyboardControllerModuleImpl(
) {
private val uiManager = mReactContext.uiManager
private val controller = KeyboardAnimationController()
private val mDefaultMode: Int = getCurrentMode()
private val mDefaultMode: Int = mReactContext.windowSoftInputMode

// region Module methods
fun setInputMode(mode: Int) {
Expand Down Expand Up @@ -108,19 +108,11 @@ class KeyboardControllerModuleImpl(
// region Helpers
private fun setSoftInputMode(mode: Int) {
UiThreadUtil.runOnUiThread {
if (getCurrentMode() != mode) {
if (mReactContext.windowSoftInputMode != mode) {
mReactContext.currentActivity?.window?.setSoftInputMode(mode)
}
}
}

private fun getCurrentMode(): Int =
mReactContext
.currentActivity
?.window
?.attributes
?.softInputMode
?: WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
// endregion

// region Module constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.content.res.Configuration
import android.os.Handler
import android.os.Looper
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
import android.widget.FrameLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
Expand All @@ -14,14 +15,15 @@ import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.views.view.ReactViewGroup
import com.reactnativekeyboardcontroller.extensions.content
import com.reactnativekeyboardcontroller.extensions.removeSelf
import com.reactnativekeyboardcontroller.extensions.replaceStatusBarInsets
import com.reactnativekeyboardcontroller.extensions.requestApplyInsetsWhenAttached
import com.reactnativekeyboardcontroller.extensions.rootView
import com.reactnativekeyboardcontroller.extensions.windowSoftInputMode
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallbackConfig
import com.reactnativekeyboardcontroller.log.Logger
import com.reactnativekeyboardcontroller.modal.ModalAttachedWatcher
import java.lang.ref.WeakReference
import kotlin.math.max

private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName

Expand All @@ -44,7 +46,6 @@ class EdgeToEdgeReactViewGroup(
private var isStatusBarTranslucent = false
private var isNavigationBarTranslucent = false
private var isPreservingEdgeToEdge = false
private var isEdgeToEdge = false
var active: Boolean = false
set(value) {
field = value
Expand Down Expand Up @@ -110,47 +111,43 @@ class EdgeToEdgeReactViewGroup(
FrameLayout.LayoutParams.MATCH_PARENT,
)

val shouldApplyZeroPaddingTop = !active || this.isStatusBarTranslucent
val shouldApplyZeroPaddingBottom = !active || this.isNavigationBarTranslucent
val shouldApplyBottomPadding =
!active && reactContext.windowSoftInputMode == SOFT_INPUT_ADJUST_RESIZE && !isPreservingEdgeToEdge
val navBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
val keyboardInsets =
if (!shouldApplyBottomPadding) 0 else insets.getInsets(WindowInsetsCompat.Type.ime()).bottom

params.setMargins(
navBarInsets.left,
if (shouldApplyZeroPaddingTop) {
if (this.isStatusBarTranslucent) {
0
} else {
systemBarInsets.top
},
navBarInsets.right,
if (shouldApplyZeroPaddingBottom) {
0
if (this.isNavigationBarTranslucent) {
keyboardInsets
} else {
navBarInsets.bottom
max(navBarInsets.bottom, keyboardInsets)
},
)
content?.layoutParams = params

v.replaceStatusBarInsets(insets, this.isStatusBarTranslucent, active)
ViewCompat.onApplyWindowInsets(v, insets)
}
}
}

fun setEdgeToEdge() {
val nextValue = active || isPreservingEdgeToEdge

if (isEdgeToEdge != nextValue) {
isEdgeToEdge = nextValue

reactContext.currentActivity?.let {
WindowCompat.setDecorFitsSystemWindows(
it.window,
!isEdgeToEdge,
)
}
// unclear legacy flag if it was set earlier
reactContext.currentActivity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
reactContext.currentActivity?.let {
WindowCompat.setDecorFitsSystemWindows(
it.window,
false,
)
}
// unclear legacy flag if it was set earlier
reactContext.currentActivity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}

private fun setupKeyboardCallbacks() {
Expand Down
Loading