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 @@ -8,10 +8,15 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler

class ReactDelegateWrapper(
private val activity: ComponentActivity?,
private val reactHost: ReactHost,
resolvedReactHost: ReactHost?,
moduleName: String,
launchOptions: Bundle?
): ReactDelegate(activity, reactHost, moduleName, launchOptions){
) : ReactDelegate(
activity = activity!!,
resolvedReactHost,
appKey = moduleName,
launchOptions = launchOptions,
) {
private lateinit var hardwareBackHandler: () -> Unit
private val backBtnHandler = DefaultHardwareBackBtnHandler {
hardwareBackHandler()
Expand All @@ -25,7 +30,7 @@ class ReactDelegateWrapper(
hardwareBackHandler = backHandler
}

override fun onHostResume() {
reactHost.onHostResume(activity, backBtnHandler)
fun onReactHostResume() {
super.reactHost?.onHostResume(activity, backBtnHandler)
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
package com.callstack.reactnativebrownfield

import android.app.Application
import android.content.Context
import android.os.Bundle
import android.widget.FrameLayout
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.callstack.reactnativebrownfield.utils.VersionUtils
import com.facebook.react.ReactHost
import com.facebook.react.ReactInstanceEventListener
import com.facebook.react.ReactInstanceManager
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.ReactRootView
import com.facebook.react.bridge.ReactContext
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import java.util.concurrent.atomic.AtomicBoolean
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost

fun interface OnJSBundleLoaded {
operator fun invoke(initialized: Boolean)
operator fun invoke(initialized: Boolean)
}

/**
Expand All @@ -33,154 +29,136 @@ fun interface OnJSBundleLoaded {
*/
private const val RN_THRESHOLD_VERSION = "0.80.0"

class ReactNativeBrownfield private constructor(val reactNativeHost: ReactNativeHost) {
companion object {
private lateinit var instance: ReactNativeBrownfield
private val initialized = AtomicBoolean()
class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
companion object {
private lateinit var instance: ReactNativeBrownfield
private val initialized = AtomicBoolean()

@JvmStatic
val shared: ReactNativeBrownfield get() = instance
@JvmStatic
val shared: ReactNativeBrownfield get() = instance

private fun loadNativeLibs (application: Application) {
val rnVersion = BuildConfig.RN_VERSION
private fun loadNativeLibs(application: Application) {
val rnVersion = BuildConfig.RN_VERSION

if (VersionUtils.isVersionLessThan(rnVersion, RN_THRESHOLD_VERSION)) {
SoLoader.init(application.applicationContext, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
if (VersionUtils.isVersionLessThan(rnVersion, RN_THRESHOLD_VERSION)) {
SoLoader.init(application.applicationContext, OpenSourceMergedSoMapping)
load()
}
}
}
}

@JvmStatic
@JvmOverloads
fun initialize(application: Application, rnHost: ReactNativeHost, onJSBundleLoaded: OnJSBundleLoaded? = null) {
if (!initialized.getAndSet(true)) {
loadNativeLibs(application)
instance = ReactNativeBrownfield(rnHost)
@JvmStatic
@JvmOverloads
fun initialize(
application: Application,
reactHost: ReactHost,
onJSBundleLoaded: OnJSBundleLoaded? = null
) {
if (!initialized.getAndSet(true)) {
loadNativeLibs(application)
instance = ReactNativeBrownfield(reactHost)

preloadReactNative {
onJSBundleLoaded?.invoke(true)
}
}
}

preloadReactNative {
onJSBundleLoaded?.invoke(true)
@JvmStatic
@JvmOverloads
fun initialize(
application: Application,
options: HashMap<String, Any>,
onJSBundleLoaded: OnJSBundleLoaded? = null
) {
val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = application,
packageList = (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
.filterIsInstance<ReactPackage>(),
jsRuntimeFactory = null
)
}

initialize(application, reactHost, onJSBundleLoaded)
}
}
}

@JvmStatic
@JvmOverloads
fun initialize(application: Application, options: HashMap<String, Any>, onJSBundleLoaded: OnJSBundleLoaded? = null) {
val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(application) {
@JvmStatic
@JvmOverloads
fun initialize(
application: Application,
packages: List<ReactPackage>,
onJSBundleLoaded: OnJSBundleLoaded? = null
) {
val options = hashMapOf("packages" to packages, "mainModuleName" to "index")

override fun getJSMainModuleName(): String {
return options["mainModuleName"] as? String ?: super.getJSMainModuleName()
}
initialize(application, options, onJSBundleLoaded)
}

override fun getPackages(): List<ReactPackage> {
return (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
.filterIsInstance<ReactPackage>()
}
private fun preloadReactNative(callback: ((Boolean) -> Unit)) {
shared.reactHost.addReactInstanceEventListener(object :
ReactInstanceEventListener {
override fun onReactContextInitialized(context: ReactContext) {
callback(true)
shared.reactHost.removeReactInstanceEventListener(this)
}
})
shared.reactHost.start()
}
}

override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
fun createView(
activity: FragmentActivity?,
moduleName: String,
reactDelegate: ReactDelegateWrapper? = null,
launchOptions: Bundle? = null,
): FrameLayout {
val reactHost = shared.reactHost
val resolvedDelegate =
reactDelegate ?: ReactDelegateWrapper(activity, reactHost, moduleName, launchOptions)

val mBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// invoked for JS stack back navigation
resolvedDelegate.onBackPressed()
}
}

// Register back press callback
activity?.onBackPressedDispatcher?.addCallback(mBackPressedCallback)
// invoked on the last RN screen exit
resolvedDelegate.setHardwareBackHandler {
mBackPressedCallback.isEnabled = false
activity?.onBackPressedDispatcher?.onBackPressed()
}

override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
/**
* When createView method is called in ReactNativeFragment, a reactDelegate
* instance is required. In such a case, we use the lifeCycle events of the fragment.
* When createView method is called elsewhere, then reactDelegate is not required.
* In such a case, we set the lifeCycle observer.
*/
if (reactDelegate == null) {
activity?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
}

initialize(application, reactNativeHost, onJSBundleLoaded)
resolvedDelegate.loadApp()
return resolvedDelegate.reactRootView!!
}

@JvmStatic
@JvmOverloads
fun initialize(application: Application, packages: List<ReactPackage>, onJSBundleLoaded: OnJSBundleLoaded? = null) {
val options = hashMapOf("packages" to packages, "mainModuleName" to "index")
private fun getLifeCycleObserver(reactDelegate: ReactDelegateWrapper): DefaultLifecycleObserver {
return object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
reactDelegate.onReactHostResume()
}

initialize(application, options, onJSBundleLoaded)
}
override fun onPause(owner: LifecycleOwner) {
reactDelegate.onHostPause()
}

private fun preloadReactNative(callback: ((Boolean) -> Unit)) {
val reactInstanceManager = shared.reactNativeHost.reactInstanceManager
reactInstanceManager.addReactInstanceEventListener(object :
ReactInstanceEventListener {
override fun onReactContextInitialized(reactContext: ReactContext) {
callback(true)
reactInstanceManager.removeReactInstanceEventListener(this)
override fun onDestroy(owner: LifecycleOwner) {
reactDelegate.onHostDestroy()
owner.lifecycle.removeObserver(this) // Cleanup to avoid leaks
}
}
})
reactInstanceManager?.createReactContextInBackground()
}
}

fun createView(
context: Context,
activity: FragmentActivity?,
moduleName: String,
reactDelegate: ReactDelegateWrapper? = null,
launchOptions: Bundle? = null,
): FrameLayout {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
val reactHost = getDefaultReactHost(
context,
shared.reactNativeHost
)

val resolvedDelegate = reactDelegate ?: ReactDelegateWrapper(activity, reactHost, moduleName, launchOptions)

val mBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// invoked for JS stack back navigation
resolvedDelegate.onBackPressed()
}
}

// Register back press callback
activity?.onBackPressedDispatcher?.addCallback(mBackPressedCallback)
// invoked on the last RN screen exit
resolvedDelegate.setHardwareBackHandler {
mBackPressedCallback.isEnabled = false
activity?.onBackPressedDispatcher?.onBackPressed()
}

/**
* When createView method is called in ReactNativeFragment, a reactDelegate
* instance is required. In such a case, we use the lifeCycle events of the fragment.
* When createView method is called elsewhere, then reactDelegate is not required.
* In such a case, we set the lifeCycle observer.
*/
if (reactDelegate == null) {
activity?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
}

resolvedDelegate.loadApp()
return resolvedDelegate.reactRootView!!
}

val instanceManager: ReactInstanceManager? = shared.reactNativeHost?.reactInstanceManager
val reactView = ReactRootView(context)
reactView.startReactApplication(
instanceManager,
moduleName,
launchOptions,
)

return reactView
}

private fun getLifeCycleObserver(reactDelegate: ReactDelegateWrapper): DefaultLifecycleObserver {
return object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
reactDelegate.onHostResume()
}

override fun onPause(owner: LifecycleOwner) {
reactDelegate.onHostPause()
}

override fun onDestroy(owner: LifecycleOwner) {
reactDelegate.onHostDestroy()
owner.lifecycle.removeObserver(this) // Cleanup to avoid leaks
}
}
}
}

Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.callstack.reactnativebrownfield

import android.view.View
import java.util.Collections

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager
import java.util.Collections


class ReactNativeBrownfieldPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
return Collections.emptyList()
}
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
return Collections.emptyList()
}

override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
val modules = ArrayList<NativeModule>()
modules.add(ReactNativeBrownfieldModule(reactContext))
return modules
}
override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
val modules = ArrayList<NativeModule>()
modules.add(ReactNativeBrownfieldModule(reactContext))
return modules
}
}
Loading
Loading