@callstack/react-native-brownfield 1.2.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,13 @@
31
31
  - Compatible with all native languages **Objective-C**, **Swift**, **Java** and **Kotlin**
32
32
  - Supports UIKit and SwiftUI on iOS and Fragments and Jetpack Compose on Android
33
33
 
34
+ ## React Native version compatibility matrix
35
+
36
+ | Tested React Native Version | React Native Brownfield Version |
37
+ | --------------------------- | ------------------------------- |
38
+ | 0.81.x, 0.82.x | ^2.0.0-rc.0 |
39
+ | 0.78.x | ^1.2.0 |
40
+
34
41
  ## Installation
35
42
 
36
43
  The React Native Brownfield library is intended to be installed in a React Native app that is later consumed as a framework artifact by your native iOS or Android app.
@@ -51,16 +58,16 @@ npm install @callstack/react-native-brownfield
51
58
 
52
59
  First, we need to package our React Native app as an XCFramework or Fat-AAR.
53
60
 
54
- #### With RNEF
61
+ #### With Rock
55
62
 
56
- Follow [Integrating with Native Apps](https://www.rnef.dev/docs/brownfield/intro) steps in RNEF docs and run:
63
+ Follow [Integrating with Native Apps](https://www.rockjs.dev/docs/brownfield/intro) steps in Rock docs and run:
57
64
 
58
- - `rnef package:ios` for iOS
59
- - `rnef package:aar` for Android
65
+ - `rock package:ios` for iOS
66
+ - `rock package:aar` for Android
60
67
 
61
68
  #### With custom scripts
62
69
 
63
- Instead of using RNEF, you can create your own custom packaging scripts. Here are base versions for iOS and Android that you'll need to adjust for your project-specific setup:
70
+ Instead of using Rock, you can create your own custom packaging scripts. Here are base versions for iOS and Android that you'll need to adjust for your project-specific setup:
64
71
 
65
72
  - [Example iOS script](https://github.com/callstackincubator/modern-brownfield-ref/blob/main/scripts/build-xcframework.sh)
66
73
  - [Example Android script](https://github.com/callstackincubator/modern-brownfield-ref/blob/main/scripts/build-aar.sh)
@@ -121,17 +128,13 @@ import android.view.LayoutInflater
121
128
  import android.view.View
122
129
  import android.view.ViewGroup
123
130
  import androidx.fragment.app.Fragment
124
- import com.callstack.rnbrownfield.RNViewFactory # exposed by RN app framework
125
131
 
126
132
  class RNAppFragment : Fragment() {
127
133
  override fun onCreateView(
128
134
  inflater: LayoutInflater,
129
135
  container: ViewGroup?,
130
136
  savedInstanceState: Bundle?,
131
- ): View? =
132
- this.context?.let {
133
- RNViewFactory.createFrameLayout(it)
134
- }
137
+ ): View? = ReactNativeBrownfield.shared.createView(activity, "BrownFieldTest")
135
138
  }
136
139
  ```
137
140
 
@@ -166,7 +169,9 @@ class MainActivity : AppCompatActivity() {
166
169
 
167
170
  override fun onCreate(savedInstanceState: Bundle?) {
168
171
  super.onCreate(savedInstanceState)
169
- ReactNativeHostManager.shared.initialize(this.application)
172
+ ReactNativeHostManager.shared.initialize(this.application) {
173
+ println("JS bundle loaded")
174
+ }
170
175
 
171
176
  showRNAppBtn = findViewById(R.id.show_rn_app_btn)
172
177
  showRNAppBtn.setOnClickListener {
@@ -228,7 +233,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
228
233
  <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
229
234
  <!-- prettier-ignore -->
230
235
  | [<img src="https://avatars0.githubusercontent.com/u/7837457?s=460&v=4" width="100px;" alt="Michał Chudziak"/><br /><sub><b>Michał Chudziak</b></sub>](https://twitter.com/michalchudziak)<br />[💻](https://github.com/callstack/react-native-brownfield/commits?author=michalchudziak "Code") [📖](https://github.com/callstack/react-native-brownfield/commits?author=michalchudziak "Documentation") [🤔](#ideas-michalchudziak "Ideas, Planning, & Feedback") | [<img src="https://avatars1.githubusercontent.com/u/16336501?s=400&v=4" width="100px;" alt="Piotr Drapich"/><br /><sub><b>Piotr Drapich</b></sub>](https://twitter.com/dratwas)<br />[💻](https://github.com/callstack/react-native-brownfield/commits?author=dratwas "Code") [🤔](#ideas-dratwas "Ideas, Planning, & Feedback") |
231
- | :---: | :---: |
236
+ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
232
237
 
233
238
  <!-- ALL-CONTRIBUTORS-LIST:END -->
234
239
 
@@ -8,10 +8,15 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
8
8
 
9
9
  class ReactDelegateWrapper(
10
10
  private val activity: ComponentActivity?,
11
- private val reactHost: ReactHost,
11
+ resolvedReactHost: ReactHost?,
12
12
  moduleName: String,
13
13
  launchOptions: Bundle?
14
- ): ReactDelegate(activity, reactHost, moduleName, launchOptions){
14
+ ) : ReactDelegate(
15
+ activity = activity!!,
16
+ resolvedReactHost,
17
+ appKey = moduleName,
18
+ launchOptions = launchOptions,
19
+ ) {
15
20
  private lateinit var hardwareBackHandler: () -> Unit
16
21
  private val backBtnHandler = DefaultHardwareBackBtnHandler {
17
22
  hardwareBackHandler()
@@ -25,7 +30,7 @@ class ReactDelegateWrapper(
25
30
  hardwareBackHandler = backHandler
26
31
  }
27
32
 
28
- override fun onHostResume() {
29
- reactHost.onHostResume(activity, backBtnHandler)
33
+ fun onReactHostResume() {
34
+ super.reactHost?.onHostResume(activity, backBtnHandler)
30
35
  }
31
36
  }
@@ -1,7 +1,6 @@
1
1
  package com.callstack.reactnativebrownfield
2
2
 
3
3
  import android.app.Application
4
- import android.content.Context
5
4
  import android.os.Bundle
6
5
  import android.widget.FrameLayout
7
6
  import androidx.activity.OnBackPressedCallback
@@ -9,21 +8,19 @@ import androidx.fragment.app.FragmentActivity
9
8
  import androidx.lifecycle.DefaultLifecycleObserver
10
9
  import androidx.lifecycle.LifecycleOwner
11
10
  import com.callstack.reactnativebrownfield.utils.VersionUtils
11
+ import com.facebook.react.ReactHost
12
12
  import com.facebook.react.ReactInstanceEventListener
13
- import com.facebook.react.ReactInstanceManager
14
- import com.facebook.react.ReactNativeHost
15
13
  import com.facebook.react.ReactPackage
16
- import com.facebook.react.ReactRootView
17
14
  import com.facebook.react.bridge.ReactContext
18
- import com.facebook.react.defaults.DefaultReactNativeHost
15
+ import com.facebook.react.common.build.ReactBuildConfig
16
+ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
17
+ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
19
18
  import com.facebook.react.soloader.OpenSourceMergedSoMapping
20
19
  import com.facebook.soloader.SoLoader
21
20
  import java.util.concurrent.atomic.AtomicBoolean
22
- import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
23
- import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
24
21
 
25
22
  fun interface OnJSBundleLoaded {
26
- operator fun invoke(initialized: Boolean)
23
+ operator fun invoke(initialized: Boolean)
27
24
  }
28
25
 
29
26
  /**
@@ -33,154 +30,140 @@ fun interface OnJSBundleLoaded {
33
30
  */
34
31
  private const val RN_THRESHOLD_VERSION = "0.80.0"
35
32
 
36
- class ReactNativeBrownfield private constructor(val reactNativeHost: ReactNativeHost) {
37
- companion object {
38
- private lateinit var instance: ReactNativeBrownfield
39
- private val initialized = AtomicBoolean()
33
+ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
34
+ companion object {
35
+ private lateinit var instance: ReactNativeBrownfield
36
+ private val initialized = AtomicBoolean()
40
37
 
41
- @JvmStatic
42
- val shared: ReactNativeBrownfield get() = instance
38
+ @JvmStatic
39
+ val shared: ReactNativeBrownfield get() = instance
43
40
 
44
- private fun loadNativeLibs (application: Application) {
45
- val rnVersion = BuildConfig.RN_VERSION
41
+ private fun loadNativeLibs(application: Application) {
42
+ val rnVersion = BuildConfig.RN_VERSION
46
43
 
47
- if (VersionUtils.isVersionLessThan(rnVersion, RN_THRESHOLD_VERSION)) {
48
- SoLoader.init(application.applicationContext, OpenSourceMergedSoMapping)
49
- if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
50
- // If you opted-in for the New Architecture, we load the native entry point for this app.
51
- load()
44
+ if (VersionUtils.isVersionLessThan(rnVersion, RN_THRESHOLD_VERSION)) {
45
+ SoLoader.init(application.applicationContext, OpenSourceMergedSoMapping)
46
+ load()
47
+ }
52
48
  }
53
- }
54
- }
55
49
 
56
- @JvmStatic
57
- @JvmOverloads
58
- fun initialize(application: Application, rnHost: ReactNativeHost, onJSBundleLoaded: OnJSBundleLoaded? = null) {
59
- if (!initialized.getAndSet(true)) {
60
- loadNativeLibs(application)
61
- instance = ReactNativeBrownfield(rnHost)
50
+ @JvmStatic
51
+ @JvmOverloads
52
+ fun initialize(
53
+ application: Application,
54
+ reactHost: ReactHost,
55
+ onJSBundleLoaded: OnJSBundleLoaded? = null
56
+ ) {
57
+ if (!initialized.getAndSet(true)) {
58
+ loadNativeLibs(application)
59
+ instance = ReactNativeBrownfield(reactHost)
60
+
61
+ preloadReactNative {
62
+ onJSBundleLoaded?.invoke(true)
63
+ }
64
+ }
65
+ }
62
66
 
63
- preloadReactNative {
64
- onJSBundleLoaded?.invoke(true)
67
+ @JvmStatic
68
+ @JvmOverloads
69
+ fun initialize(
70
+ application: Application,
71
+ options: HashMap<String, Any>,
72
+ onJSBundleLoaded: OnJSBundleLoaded? = null
73
+ ) {
74
+ val reactHost: ReactHost by lazy {
75
+ getDefaultReactHost(
76
+ context = application,
77
+ packageList = (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
78
+ .filterIsInstance<ReactPackage>(),
79
+ jsMainModulePath = options["mainModuleName"] as? String ?: "index",
80
+ jsBundleAssetPath = options["bundleAssetPath"] as? String ?: "index.android.bundle",
81
+ useDevSupport = options["useDeveloperSupport"] as? Boolean
82
+ ?: ReactBuildConfig.DEBUG,
83
+ jsRuntimeFactory = null
84
+ )
85
+ }
86
+
87
+ initialize(application, reactHost, onJSBundleLoaded)
65
88
  }
66
- }
67
- }
68
89
 
69
- @JvmStatic
70
- @JvmOverloads
71
- fun initialize(application: Application, options: HashMap<String, Any>, onJSBundleLoaded: OnJSBundleLoaded? = null) {
72
- val reactNativeHost: ReactNativeHost =
73
- object : DefaultReactNativeHost(application) {
90
+ @JvmStatic
91
+ @JvmOverloads
92
+ fun initialize(
93
+ application: Application,
94
+ packages: List<ReactPackage>,
95
+ onJSBundleLoaded: OnJSBundleLoaded? = null
96
+ ) {
97
+ val options = hashMapOf("packages" to packages, "mainModuleName" to "index")
74
98
 
75
- override fun getJSMainModuleName(): String {
76
- return options["mainModuleName"] as? String ?: super.getJSMainModuleName()
77
- }
99
+ initialize(application, options, onJSBundleLoaded)
100
+ }
78
101
 
79
- override fun getPackages(): List<ReactPackage> {
80
- return (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
81
- .filterIsInstance<ReactPackage>()
82
- }
102
+ private fun preloadReactNative(callback: ((Boolean) -> Unit)) {
103
+ shared.reactHost.addReactInstanceEventListener(object :
104
+ ReactInstanceEventListener {
105
+ override fun onReactContextInitialized(context: ReactContext) {
106
+ callback(true)
107
+ shared.reactHost.removeReactInstanceEventListener(this)
108
+ }
109
+ })
110
+ shared.reactHost.start()
111
+ }
112
+ }
83
113
 
84
- override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
114
+ fun createView(
115
+ activity: FragmentActivity?,
116
+ moduleName: String,
117
+ reactDelegate: ReactDelegateWrapper? = null,
118
+ launchOptions: Bundle? = null,
119
+ ): FrameLayout {
120
+ val reactHost = shared.reactHost
121
+ val resolvedDelegate =
122
+ reactDelegate ?: ReactDelegateWrapper(activity, reactHost, moduleName, launchOptions)
123
+
124
+ val mBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
125
+ override fun handleOnBackPressed() {
126
+ // invoked for JS stack back navigation
127
+ resolvedDelegate.onBackPressed()
128
+ }
129
+ }
130
+
131
+ // Register back press callback
132
+ activity?.onBackPressedDispatcher?.addCallback(mBackPressedCallback)
133
+ // invoked on the last RN screen exit
134
+ resolvedDelegate.setHardwareBackHandler {
135
+ mBackPressedCallback.isEnabled = false
136
+ activity?.onBackPressedDispatcher?.onBackPressed()
137
+ }
85
138
 
86
- override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
87
- override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
139
+ /**
140
+ * When createView method is called in ReactNativeFragment, a reactDelegate
141
+ * instance is required. In such a case, we use the lifeCycle events of the fragment.
142
+ * When createView method is called elsewhere, then reactDelegate is not required.
143
+ * In such a case, we set the lifeCycle observer.
144
+ */
145
+ if (reactDelegate == null) {
146
+ activity?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
88
147
  }
89
148
 
90
- initialize(application, reactNativeHost, onJSBundleLoaded)
149
+ resolvedDelegate.loadApp()
150
+ return resolvedDelegate.reactRootView!!
91
151
  }
92
152
 
93
- @JvmStatic
94
- @JvmOverloads
95
- fun initialize(application: Application, packages: List<ReactPackage>, onJSBundleLoaded: OnJSBundleLoaded? = null) {
96
- val options = hashMapOf("packages" to packages, "mainModuleName" to "index")
153
+ private fun getLifeCycleObserver(reactDelegate: ReactDelegateWrapper): DefaultLifecycleObserver {
154
+ return object : DefaultLifecycleObserver {
155
+ override fun onResume(owner: LifecycleOwner) {
156
+ reactDelegate.onReactHostResume()
157
+ }
97
158
 
98
- initialize(application, options, onJSBundleLoaded)
99
- }
159
+ override fun onPause(owner: LifecycleOwner) {
160
+ reactDelegate.onHostPause()
161
+ }
100
162
 
101
- private fun preloadReactNative(callback: ((Boolean) -> Unit)) {
102
- val reactInstanceManager = shared.reactNativeHost.reactInstanceManager
103
- reactInstanceManager.addReactInstanceEventListener(object :
104
- ReactInstanceEventListener {
105
- override fun onReactContextInitialized(reactContext: ReactContext) {
106
- callback(true)
107
- reactInstanceManager.removeReactInstanceEventListener(this)
163
+ override fun onDestroy(owner: LifecycleOwner) {
164
+ reactDelegate.onHostDestroy()
165
+ owner.lifecycle.removeObserver(this) // Cleanup to avoid leaks
166
+ }
108
167
  }
109
- })
110
- reactInstanceManager?.createReactContextInBackground()
111
168
  }
112
- }
113
-
114
- fun createView(
115
- context: Context,
116
- activity: FragmentActivity?,
117
- moduleName: String,
118
- reactDelegate: ReactDelegateWrapper? = null,
119
- launchOptions: Bundle? = null,
120
- ): FrameLayout {
121
- if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
122
- val reactHost = getDefaultReactHost(
123
- context,
124
- shared.reactNativeHost
125
- )
126
-
127
- val resolvedDelegate = reactDelegate ?: ReactDelegateWrapper(activity, reactHost, moduleName, launchOptions)
128
-
129
- val mBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
130
- override fun handleOnBackPressed() {
131
- // invoked for JS stack back navigation
132
- resolvedDelegate.onBackPressed()
133
- }
134
- }
135
-
136
- // Register back press callback
137
- activity?.onBackPressedDispatcher?.addCallback(mBackPressedCallback)
138
- // invoked on the last RN screen exit
139
- resolvedDelegate.setHardwareBackHandler {
140
- mBackPressedCallback.isEnabled = false
141
- activity?.onBackPressedDispatcher?.onBackPressed()
142
- }
143
-
144
- /**
145
- * When createView method is called in ReactNativeFragment, a reactDelegate
146
- * instance is required. In such a case, we use the lifeCycle events of the fragment.
147
- * When createView method is called elsewhere, then reactDelegate is not required.
148
- * In such a case, we set the lifeCycle observer.
149
- */
150
- if (reactDelegate == null) {
151
- activity?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
152
- }
153
-
154
- resolvedDelegate.loadApp()
155
- return resolvedDelegate.reactRootView!!
156
- }
157
-
158
- val instanceManager: ReactInstanceManager? = shared.reactNativeHost?.reactInstanceManager
159
- val reactView = ReactRootView(context)
160
- reactView.startReactApplication(
161
- instanceManager,
162
- moduleName,
163
- launchOptions,
164
- )
165
-
166
- return reactView
167
- }
168
-
169
- private fun getLifeCycleObserver(reactDelegate: ReactDelegateWrapper): DefaultLifecycleObserver {
170
- return object : DefaultLifecycleObserver {
171
- override fun onResume(owner: LifecycleOwner) {
172
- reactDelegate.onHostResume()
173
- }
174
-
175
- override fun onPause(owner: LifecycleOwner) {
176
- reactDelegate.onHostPause()
177
- }
178
-
179
- override fun onDestroy(owner: LifecycleOwner) {
180
- reactDelegate.onHostDestroy()
181
- owner.lifecycle.removeObserver(this) // Cleanup to avoid leaks
182
- }
183
- }
184
- }
185
169
  }
186
-
@@ -1,23 +1,22 @@
1
1
  package com.callstack.reactnativebrownfield
2
2
 
3
3
  import android.view.View
4
- import java.util.Collections
5
-
6
4
  import com.facebook.react.ReactPackage
7
5
  import com.facebook.react.bridge.NativeModule
8
6
  import com.facebook.react.bridge.ReactApplicationContext
9
- import com.facebook.react.uimanager.ViewManager
10
7
  import com.facebook.react.uimanager.ReactShadowNode
8
+ import com.facebook.react.uimanager.ViewManager
9
+ import java.util.Collections
11
10
 
12
11
 
13
12
  class ReactNativeBrownfieldPackage : ReactPackage {
14
- override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
15
- return Collections.emptyList()
16
- }
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
14
+ return Collections.emptyList()
15
+ }
17
16
 
18
- override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
19
- val modules = ArrayList<NativeModule>()
20
- modules.add(ReactNativeBrownfieldModule(reactContext))
21
- return modules
22
- }
17
+ override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
18
+ val modules = ArrayList<NativeModule>()
19
+ modules.add(ReactNativeBrownfieldModule(reactContext))
20
+ return modules
21
+ }
23
22
  }
@@ -1,164 +1,140 @@
1
- package com.callstack.reactnativebrownfield;
1
+ package com.callstack.reactnativebrownfield
2
2
 
3
- import android.annotation.TargetApi
4
- import android.os.Build
5
3
  import android.os.Bundle
6
4
  import android.util.Log
7
- import android.view.KeyEvent
8
5
  import android.view.LayoutInflater
9
6
  import android.view.View
10
7
  import android.view.ViewGroup
11
- import com.facebook.infer.annotation.Assertions
8
+ import com.callstack.reactnativebrownfield.constants.ReactNativeFragmentArgNames
12
9
  import com.facebook.react.ReactFragment
13
10
  import com.facebook.react.ReactHost
14
- import com.facebook.react.ReactNativeHost
15
11
  import com.facebook.react.bridge.Callback
16
12
  import com.facebook.react.bridge.WritableMap
17
- import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
18
- import com.facebook.react.devsupport.DoubleTapReloadRecognizer
19
13
  import com.facebook.react.modules.core.PermissionAwareActivity
20
14
  import com.facebook.react.modules.core.PermissionListener
21
15
 
22
16
  class ReactNativeFragment : ReactFragment(), PermissionAwareActivity {
23
- private lateinit var doubleTapReloadRecognizer: DoubleTapReloadRecognizer
24
- private lateinit var permissionsCallback: Callback
25
- private var permissionListener: PermissionListener? = null
26
- private lateinit var moduleName: String
27
-
28
- override fun onCreate(savedInstanceState: Bundle?) {
29
- /**
30
- * ReactFragment.onCreate will throw an exception if we do not provide arg_component_name as arguments.
31
- * We silently catch this exception. The reason is we want to invoke the super<Fragment>.onCreate in
32
- * ReactFragment. Then initialise the mReactDelegate with ReactDelegateWrapper instead of ReactDelegate.
33
- *
34
- * So we purposely force ReactFragment.onCreate to throw an exception, so that we can provide our own
35
- * implementation for mReactDelegate: ReactDelegateWrapper
36
- */
37
- try{
38
- super.onCreate(savedInstanceState)
39
- } catch (e: IllegalStateException){
40
- Log.w("ReactNativeFragment", "ReactFragment threw due to missing arg_component_name: ${e.message} - This is an expected behaviour.")
17
+ private lateinit var permissionsCallback: Callback
18
+ private var permissionListener: PermissionListener? = null
19
+ private lateinit var moduleName: String
20
+
21
+ override fun onCreate(savedInstanceState: Bundle?) {
22
+ /**
23
+ * ReactFragment.onCreate will throw an exception if we do not provide arg_component_name as arguments.
24
+ * We silently catch this exception. The reason is we want to invoke the super<Fragment>.onCreate in
25
+ * ReactFragment. Then initialise the mReactDelegate with ReactDelegateWrapper instead of ReactDelegate.
26
+ *
27
+ * So we purposely force ReactFragment.onCreate to throw an exception, so that we can provide our own
28
+ * implementation for mReactDelegate: ReactDelegateWrapper
29
+ */
30
+ try {
31
+ super.onCreate(savedInstanceState)
32
+ } catch (e: IllegalStateException) {
33
+ Log.w(
34
+ "ReactNativeFragment",
35
+ "ReactFragment threw due to missing arg_component_name: ${e.message} - This is an expected behaviour."
36
+ )
37
+ }
38
+
39
+ moduleName = arguments?.getString(ReactNativeFragmentArgNames.ARG_MODULE_NAME)!!
40
+ this.reactDelegate =
41
+ ReactDelegateWrapper(
42
+ activity,
43
+ this.reactHost,
44
+ moduleName,
45
+ arguments?.getBundle(ReactNativeFragmentArgNames.ARG_LAUNCH_OPTIONS)
46
+ )
41
47
  }
42
48
 
43
- moduleName = arguments?.getString(ARG_MODULE_NAME)!!
44
- this.mReactDelegate = this.reactHost?.let {
45
- ReactDelegateWrapper(activity,
46
- it, moduleName, arguments?.getBundle("arg_launch_options"))
49
+ override fun onCreateView(
50
+ inflater: LayoutInflater,
51
+ container: ViewGroup?,
52
+ savedInstanceState: Bundle?
53
+ ): View {
54
+ return ReactNativeBrownfield.shared.createView(
55
+ activity,
56
+ moduleName,
57
+ this.reactDelegate as ReactDelegateWrapper
58
+ )
47
59
  }
48
60
 
49
- doubleTapReloadRecognizer = DoubleTapReloadRecognizer()
50
- }
51
-
52
- override fun onCreateView(
53
- inflater: LayoutInflater,
54
- container: ViewGroup?,
55
- savedInstanceState: Bundle?
56
- ): View {
57
- return ReactNativeBrownfield.shared.createView(this.requireContext(), activity, moduleName, this.mReactDelegate as ReactDelegateWrapper)
58
- }
61
+ override val reactHost: ReactHost?
62
+ get() = ReactNativeBrownfield.shared.reactHost
59
63
 
60
- override fun getReactHost(): ReactHost? {
61
- return activity?.let {
62
- getDefaultReactHost(
63
- it.applicationContext,
64
- ReactNativeBrownfield.shared.reactNativeHost
65
- )
64
+ override fun onResume() {
65
+ try {
66
+ super.onResume()
67
+ } catch (_: ClassCastException) {
68
+ (this.reactDelegate as ReactDelegateWrapper).onReactHostResume()
69
+ }
66
70
  }
67
- }
68
-
69
- override fun getReactNativeHost(): ReactNativeHost? {
70
- return ReactNativeBrownfield.shared.reactNativeHost
71
- }
72
-
73
- override fun onRequestPermissionsResult(
74
- requestCode: Int,
75
- permissions: Array<String>,
76
- grantResults: IntArray
77
- ) {
78
- permissionsCallback = Callback {
79
- if (permissionListener != null) {
80
- permissionListener?.onRequestPermissionsResult(
81
- requestCode,
82
- permissions,
83
- grantResults
84
- )
85
71
 
86
- permissionListener = null
87
- }
72
+ override fun onRequestPermissionsResult(
73
+ requestCode: Int,
74
+ permissions: Array<String>,
75
+ grantResults: IntArray
76
+ ) {
77
+ permissionsCallback = Callback {
78
+ if (permissionListener != null) {
79
+ permissionListener?.onRequestPermissionsResult(
80
+ requestCode,
81
+ permissions,
82
+ grantResults
83
+ )
84
+
85
+ permissionListener = null
86
+ }
87
+ }
88
88
  }
89
- }
90
89
 
91
- override fun checkPermission(permission: String, pid: Int, uid: Int): Int {
92
- return requireActivity().checkPermission(permission, pid, uid)
93
- }
94
-
95
- @TargetApi(Build.VERSION_CODES.M)
96
- override fun checkSelfPermission(permission: String): Int {
97
- return requireActivity().checkSelfPermission(permission)
98
- }
99
-
100
- override fun requestPermissions(
101
- permissions: Array<String>,
102
- requestCode: Int,
103
- listener: PermissionListener?
104
- ) {
105
- permissionListener = listener
106
- this.requestPermissions(permissions, requestCode)
107
- }
108
-
109
- override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
110
- var handled = false
111
- if (ReactNativeBrownfield.shared.reactNativeHost.useDeveloperSupport && ReactNativeBrownfield.shared.reactNativeHost.hasInstance()) {
112
- if (keyCode == KeyEvent.KEYCODE_MENU) {
113
- ReactNativeBrownfield.shared.reactNativeHost.reactInstanceManager.showDevOptionsDialog()
114
- handled = true
115
- }
116
- val didDoubleTapR = activity?.currentFocus?.let {
117
- Assertions.assertNotNull(doubleTapReloadRecognizer)
118
- .didDoubleTapR(keyCode, it)
119
- }
120
- if (didDoubleTapR == true) {
121
- reactDelegate.reload()
122
- handled = true
123
- }
90
+ override fun checkPermission(permission: String, pid: Int, uid: Int): Int {
91
+ return requireActivity().checkPermission(permission, pid, uid)
124
92
  }
125
- return handled
126
- }
127
93
 
128
- companion object {
129
- private const val ARG_MODULE_NAME = "arg_module_name"
130
-
131
- @JvmStatic
132
- @JvmOverloads
133
- fun createReactNativeFragment(
134
- moduleName: String,
135
- initialProps: Bundle? = null
136
- ): ReactNativeFragment {
137
- val fragment = ReactNativeFragment()
138
- val args = Bundle()
139
- args.putString(ARG_MODULE_NAME, moduleName)
140
- if (initialProps != null) {
141
- args.putBundle(ARG_LAUNCH_OPTIONS, initialProps)
142
- }
143
- fragment.arguments = args
144
- return fragment
94
+ override fun checkSelfPermission(permission: String): Int {
95
+ return requireActivity().checkSelfPermission(permission)
145
96
  }
146
97
 
147
- @JvmStatic
148
- fun createReactNativeFragment(
149
- moduleName: String,
150
- initialProps: HashMap<String, *>
151
- ): ReactNativeFragment {
152
- return createReactNativeFragment(moduleName, PropsBundle.fromHashMap(initialProps))
98
+ override fun requestPermissions(
99
+ permissions: Array<String>,
100
+ requestCode: Int,
101
+ listener: PermissionListener?
102
+ ) {
103
+ permissionListener = listener
104
+ this.requestPermissions(permissions, requestCode)
153
105
  }
154
106
 
155
- @JvmStatic
156
- fun createReactNativeFragment(
157
- moduleName: String,
158
- initialProps: WritableMap
159
- ): ReactNativeFragment {
160
- return createReactNativeFragment(moduleName, initialProps.toHashMap())
107
+ companion object {
108
+ @JvmStatic
109
+ @JvmOverloads
110
+ fun createReactNativeFragment(
111
+ moduleName: String,
112
+ initialProps: Bundle? = null
113
+ ): ReactNativeFragment {
114
+ val fragment = ReactNativeFragment()
115
+ val args = Bundle()
116
+ args.putString(ReactNativeFragmentArgNames.ARG_MODULE_NAME, moduleName)
117
+ if (initialProps != null) {
118
+ args.putBundle(ReactNativeFragmentArgNames.ARG_LAUNCH_OPTIONS, initialProps)
119
+ }
120
+ fragment.arguments = args
121
+ return fragment
122
+ }
123
+
124
+ @JvmStatic
125
+ fun createReactNativeFragment(
126
+ moduleName: String,
127
+ initialProps: HashMap<String, *>
128
+ ): ReactNativeFragment {
129
+ return createReactNativeFragment(moduleName, PropsBundle.fromHashMap(initialProps))
130
+ }
131
+
132
+ @JvmStatic
133
+ fun createReactNativeFragment(
134
+ moduleName: String,
135
+ initialProps: WritableMap
136
+ ): ReactNativeFragment {
137
+ return createReactNativeFragment(moduleName, initialProps.toHashMap())
138
+ }
161
139
  }
162
- }
163
-
164
140
  }
@@ -0,0 +1,20 @@
1
+ package com.callstack.reactnativebrownfield.constants
2
+
3
+ import com.facebook.react.ReactFragment
4
+
5
+ /**
6
+ * Convenience export of arguments that can be used
7
+ */
8
+ class ReactNativeFragmentArgNames private constructor() :
9
+ ReactFragment() // subclass to gain access to protected constants
10
+ {
11
+ companion object {
12
+ /**
13
+ * The module name to be loaded
14
+ */
15
+ const val ARG_MODULE_NAME = "arg_module_name"
16
+
17
+ // re-export constants from ReactFragment to enable access
18
+ const val ARG_LAUNCH_OPTIONS: String = "arg_launch_options"
19
+ }
20
+ }
@@ -4,34 +4,34 @@ import com.facebook.react.bridge.ReactApplicationContext
4
4
  import com.facebook.react.bridge.ReactMethod
5
5
 
6
6
  class ReactNativeBrownfieldModule(reactContext: ReactApplicationContext) :
7
- NativeReactNativeBrownfieldModuleSpec(reactContext) {
8
- companion object {
9
- var shouldPopToNative: Boolean = false
10
- }
7
+ NativeReactNativeBrownfieldModuleSpec(reactContext) {
8
+ companion object {
9
+ var shouldPopToNative: Boolean = false
10
+ }
11
11
 
12
- @ReactMethod
13
- override fun popToNative(animated: Boolean) {
14
- shouldPopToNative = true
15
- onBackPressed()
16
- }
12
+ @ReactMethod
13
+ override fun popToNative(animated: Boolean) {
14
+ shouldPopToNative = true
15
+ onBackPressed()
16
+ }
17
17
 
18
- @ReactMethod
19
- override fun setPopGestureRecognizerEnabled(enabled: Boolean) {
20
- shouldPopToNative = enabled
21
- }
18
+ @ReactMethod
19
+ override fun setPopGestureRecognizerEnabled(enabled: Boolean) {
20
+ shouldPopToNative = enabled
21
+ }
22
22
 
23
- @ReactMethod
24
- override fun setHardwareBackButtonEnabled(enabled: Boolean) {
25
- shouldPopToNative = enabled
26
- }
23
+ @ReactMethod
24
+ override fun setHardwareBackButtonEnabled(enabled: Boolean) {
25
+ shouldPopToNative = enabled
26
+ }
27
27
 
28
- private fun onBackPressed() {
29
- reactApplicationContext.currentActivity?.runOnUiThread {
30
- reactApplicationContext.currentActivity?.onBackPressed()
28
+ private fun onBackPressed() {
29
+ reactApplicationContext.currentActivity?.runOnUiThread {
30
+ reactApplicationContext.currentActivity?.onBackPressed()
31
+ }
31
32
  }
32
- }
33
33
 
34
- override fun getName(): String {
35
- return "ReactNativeBrownfield"
36
- }
34
+ override fun getName(): String {
35
+ return "ReactNativeBrownfield"
36
+ }
37
37
  }
@@ -88,7 +88,7 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
88
88
  initialProps: [AnyHashable: Any]?,
89
89
  launchOptions: [AnyHashable: Any]? = nil
90
90
  ) -> UIView? {
91
- reactNativeFactory?.rootViewFactory.view(
91
+ rootViewFactory?.view(
92
92
  withModuleName: moduleName,
93
93
  initialProperties: initialProps,
94
94
  launchOptions: launchOptions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@callstack/react-native-brownfield",
3
- "version": "1.2.0",
3
+ "version": "2.0.1",
4
4
  "license": "MIT",
5
5
  "author": "Michal Chudziak <mike.chudziak@callstack.com>",
6
6
  "contributors": [
@@ -60,7 +60,7 @@
60
60
  "access": "public"
61
61
  },
62
62
  "resolutions": {
63
- "@types/react": "19.0.0"
63
+ "@types/react": "19.1.1"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "react": "*",
@@ -70,25 +70,25 @@
70
70
  "@babel/core": "^7.25.2",
71
71
  "@babel/preset-env": "^7.25.3",
72
72
  "@babel/runtime": "^7.25.0",
73
- "@react-native/babel-preset": "0.78.0",
74
- "@react-native/eslint-config": "0.78.0",
75
- "@react-native/typescript-config": "0.78.0",
73
+ "@react-native/babel-preset": "0.82.1",
74
+ "@react-native/eslint-config": "0.82.1",
75
+ "@react-native/typescript-config": "0.82.1",
76
76
  "@release-it/conventional-changelog": "^5.0.0",
77
77
  "@types/jest": "^29.5.13",
78
- "@types/react": "^19.0.0",
79
- "@types/react-test-renderer": "^19.0.0",
78
+ "@types/react": "^19.1.1",
79
+ "@types/react-test-renderer": "^19.1.0",
80
80
  "babel-plugin-module-resolver": "5.0.0",
81
81
  "eslint": "^8.19.0",
82
82
  "eslint-config-prettier": "^9.1.0",
83
83
  "eslint-plugin-prettier": "^5.1.3",
84
84
  "jest": "^29.6.3",
85
85
  "prettier": "^3.5.3",
86
- "react": "19.0.0",
87
- "react-native": "0.78.0",
86
+ "react": "19.1.1",
87
+ "react-native": "0.82.1",
88
88
  "react-native-builder-bob": "^0.37.0",
89
- "react-test-renderer": "19.0.0",
89
+ "react-test-renderer": "19.1.1",
90
90
  "release-it": "^18.1.2",
91
- "typescript": "5.0.4"
91
+ "typescript": "5.8.3"
92
92
  },
93
93
  "release-it": {
94
94
  "git": {
@@ -172,7 +172,7 @@
172
172
  ]
173
173
  },
174
174
  "engines": {
175
- "node": ">=18"
175
+ "node": ">=20"
176
176
  },
177
177
  "packageManager": "yarn@3.6.4"
178
178
  }