Skip to content

Commit 5a2e2a8

Browse files
authored
fix: use reflection for original StatusBar implementation discovery (#1008)
## 📜 Description Use reflection for `StatusBarModule` usage. ## 💡 Motivation and Context It seems like in RN 0.80+ the `StatusBarModule` is `internal` now (kotlin). So we can't use ir directly anymore - moreover the usage of `internal` class will lead to compilation errors. An ideal fix is to make it public again in react-native framework, but it'll take some time and some RN versions will still use `internal` modificator. So in order to assure better compatibility across different RN versions I decided to use reflection. We can not use the approach with Java's `protected` classes (where we create the same package name and export public wrapper), because kotlin has a stricter policy. So we have two options how to fix it: - reflection - copy-paste original code We already use reflection in 2 places (though they will be removed in the future) I decided to add more usage and create `StatusBarModuleProxy`. In this PR I also re-structured the fs and created `statusbar` folder where placed proxy + module implementations. ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### Android - created `StatusBarModuleProxy` based on reflection; - created `statusbar` folder; - update imports that use `StatusBarModuleCompat`; ## 🤔 How Has This Been Tested? Tested on Pixel 3A (API 33, emulator). ## 📸 Screenshots (if appropriate): https://github.com/user-attachments/assets/6581bb89-6e57-445b-afb8-6a080773e2aa ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent 46bb921 commit 5a2e2a8

6 files changed

Lines changed: 97 additions & 9 deletions

File tree

android/src/base/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.facebook.react.module.model.ReactModuleInfo
77
import com.facebook.react.module.model.ReactModuleInfoProvider
88
import com.facebook.react.uimanager.ViewManager
99
import com.reactnativekeyboardcontroller.modules.KeyboardControllerModuleImpl
10-
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
10+
import com.reactnativekeyboardcontroller.modules.statusbar.StatusBarManagerCompatModuleImpl
1111

1212
class KeyboardControllerPackage : BaseReactPackage() {
1313
override fun getModule(

android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.reactnativekeyboardcontroller
22

33
import com.facebook.react.bridge.ReactApplicationContext
4-
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
4+
import com.reactnativekeyboardcontroller.modules.statusbar.StatusBarManagerCompatModuleImpl
55

66
class StatusBarManagerCompatModule(
77
mReactContext: ReactApplicationContext,

android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt renamed to android/src/main/java/com/reactnativekeyboardcontroller/modules/statusbar/StatusBarManagerCompatModuleImpl.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.reactnativekeyboardcontroller.modules
1+
package com.reactnativekeyboardcontroller.modules.statusbar
22

33
import android.animation.ArgbEvaluator
44
import android.animation.ValueAnimator
@@ -9,7 +9,6 @@ import androidx.core.view.WindowInsetsCompat
99
import androidx.core.view.WindowInsetsControllerCompat
1010
import com.facebook.react.bridge.ReactApplicationContext
1111
import com.facebook.react.bridge.UiThreadUtil
12-
import com.facebook.react.modules.statusbar.StatusBarModule
1312
import com.reactnativekeyboardcontroller.extensions.rootView
1413
import com.reactnativekeyboardcontroller.log.Logger
1514
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
@@ -20,7 +19,7 @@ private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
2019
class StatusBarManagerCompatModuleImpl(
2120
private val mReactContext: ReactApplicationContext,
2221
) {
23-
private var original = StatusBarModule(mReactContext)
22+
private var original = StatusBarModuleProxy(mReactContext)
2423
private var controller: WindowInsetsControllerCompat? = null
2524
private var lastActivity = WeakReference<Activity?>(null)
2625

@@ -49,7 +48,10 @@ class StatusBarManagerCompatModuleImpl(
4948

5049
val activity = mReactContext.currentActivity
5150
if (activity == null) {
52-
Logger.w(TAG, "StatusBarManagerCompatModule: Ignored status bar change, current activity is null.")
51+
Logger.w(
52+
TAG,
53+
"StatusBarManagerCompatModule: Ignored status bar change, current activity is null.",
54+
)
5355
return
5456
}
5557

@@ -90,7 +92,7 @@ class StatusBarManagerCompatModuleImpl(
9092
}
9193
}
9294

93-
fun getConstants(): MutableMap<String, Any>? = original.constants
95+
fun getConstants(): MutableMap<String, Any>? = original.getConstants()
9496

9597
private fun getController(): WindowInsetsControllerCompat? {
9698
val activity = mReactContext.currentActivity
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
@file:Suppress("detekt:TooGenericExceptionCaught")
2+
3+
package com.reactnativekeyboardcontroller.modules.statusbar
4+
5+
import com.facebook.react.bridge.ReactApplicationContext
6+
import com.reactnativekeyboardcontroller.log.Logger
7+
import java.lang.reflect.Method
8+
9+
private val TAG = StatusBarModuleProxy::class.qualifiedName
10+
11+
/**
12+
* For the sake of better compatibility with the old/new version of the react-native, we are using reflection
13+
* to call the methods of the module. In RN 0.78 the `StatusBarModule` was public, but in RN 0.80
14+
* it's internal (kotlin-only). So we are using reflection to call the methods of the module.
15+
*/
16+
class StatusBarModuleProxy(
17+
reactContext: ReactApplicationContext,
18+
) {
19+
private var instance: Any? = null
20+
21+
private var setHiddenMethod: Method? = null
22+
private var setColorMethod: Method? = null
23+
private var setTranslucentMethod: Method? = null
24+
private var setStyleMethod: Method? = null
25+
private var getConstantsMethod: Method? = null
26+
27+
init {
28+
try {
29+
val clazz = Class.forName("com.facebook.react.modules.statusbar.StatusBarModule")
30+
val constructor = clazz.getConstructor(ReactApplicationContext::class.java)
31+
instance = constructor.newInstance(reactContext)
32+
33+
setHiddenMethod = clazz.getMethod("setHidden", Boolean::class.java)
34+
setColorMethod = clazz.getMethod("setColor", Double::class.java, Boolean::class.java)
35+
setTranslucentMethod = clazz.getMethod("setTranslucent", Boolean::class.java)
36+
setStyleMethod = clazz.getMethod("setStyle", String::class.java)
37+
getConstantsMethod = clazz.getMethod("getConstants")
38+
} catch (e: Exception) {
39+
Logger.w(TAG, "Failed to initialize StatusBarModule via reflection", e)
40+
}
41+
}
42+
43+
fun setHidden(hidden: Boolean) {
44+
try {
45+
setHiddenMethod?.invoke(instance, hidden)
46+
} catch (e: Exception) {
47+
Logger.w(TAG, "Error invoking StatusBarModule.setHidden method", e)
48+
}
49+
}
50+
51+
fun setColor(
52+
color: Double,
53+
animated: Boolean,
54+
) {
55+
try {
56+
setColorMethod?.invoke(instance, color, animated)
57+
} catch (e: Exception) {
58+
Logger.w(TAG, "Error invoking StatusBarModule.setColor method", e)
59+
}
60+
}
61+
62+
fun setTranslucent(translucent: Boolean) {
63+
try {
64+
setTranslucentMethod?.invoke(instance, translucent)
65+
} catch (e: Exception) {
66+
Logger.w(TAG, "Error invoking StatusBarModule.setTranslucent method", e)
67+
}
68+
}
69+
70+
fun setStyle(style: String) {
71+
try {
72+
setStyleMethod?.invoke(instance, style)
73+
} catch (e: Exception) {
74+
Logger.w(TAG, "Error invoking StatusBarModule.setStyle method", e)
75+
}
76+
}
77+
78+
fun getConstants(): MutableMap<String, Any>? =
79+
try {
80+
@Suppress("UNCHECKED_CAST")
81+
getConstantsMethod?.invoke(instance) as? MutableMap<String, Any>
82+
} catch (e: Exception) {
83+
Logger.w(TAG, "Error invoking StatusBarModule.getConstants method", e)
84+
null
85+
}
86+
}

android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import androidx.annotation.RequiresApi
55
import com.facebook.react.bridge.ReactApplicationContext
66
import com.facebook.react.bridge.ReactContextBaseJavaModule
77
import com.facebook.react.bridge.ReactMethod
8-
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
8+
import com.reactnativekeyboardcontroller.modules.statusbar.StatusBarManagerCompatModuleImpl
99

1010
class StatusBarManagerCompatModule(
1111
mReactContext: ReactApplicationContext,

android/src/turbo/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.facebook.react.module.model.ReactModuleInfo
77
import com.facebook.react.module.model.ReactModuleInfoProvider
88
import com.facebook.react.uimanager.ViewManager
99
import com.reactnativekeyboardcontroller.modules.KeyboardControllerModuleImpl
10-
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
10+
import com.reactnativekeyboardcontroller.modules.statusbar.StatusBarManagerCompatModuleImpl
1111

1212
class KeyboardControllerPackage : TurboReactPackage() {
1313
override fun getModule(

0 commit comments

Comments
 (0)