1 package expo.modules.devmenu 2 3 import android.app.Application 4 import android.content.Context 5 import android.util.Log 6 import com.facebook.react.ReactApplication 7 import com.facebook.react.ReactInstanceManager 8 import com.facebook.react.ReactNativeHost 9 import com.facebook.react.ReactPackage 10 import com.facebook.react.ReactPackageTurboModuleManagerDelegate 11 import com.facebook.react.bridge.JSIModulePackage 12 import com.facebook.react.bridge.JavaScriptExecutorFactory 13 import com.facebook.react.config.ReactFeatureFlags 14 import com.facebook.react.defaults.DefaultJSIModulePackage 15 import com.facebook.react.devsupport.DevMenuReactInternalSettings 16 import com.facebook.react.devsupport.DevServerHelper 17 import com.facebook.react.shell.MainReactPackage 18 import devmenu.com.th3rdwave.safeareacontext.SafeAreaProviderManager 19 import expo.modules.adapters.react.ModuleRegistryAdapter 20 import expo.modules.adapters.react.ReactModuleRegistryProvider 21 import expo.modules.devmenu.modules.DevMenuInternalModule 22 import expo.modules.devmenu.modules.DevMenuPreferences 23 import expo.modules.devmenu.react.createNonDebuggableJavaScriptExecutorFactory 24 import expo.modules.kotlin.ModulesProvider 25 import java.io.BufferedReader 26 import java.io.FileNotFoundException 27 import java.io.InputStreamReader 28 29 /** 30 * Class that represents react host used by dev menu. 31 */ 32 class DevMenuHost(application: Application) : ReactNativeHost(application) { 33 getPackagesnull34 override fun getPackages(): List<ReactPackage> { 35 val packages = mutableListOf( 36 MainReactPackage(null), 37 ModuleRegistryAdapter( 38 ReactModuleRegistryProvider(emptyList()), 39 object : ModulesProvider { 40 override fun getModulesList() = 41 listOf( 42 DevMenuInternalModule::class.java, 43 DevMenuPreferences::class.java, 44 SafeAreaProviderManager::class.java 45 ) 46 } 47 ), 48 DevMenuPackage(), 49 ) 50 51 try { 52 val devLauncherPackage = Class.forName("expo.modules.devlauncher.DevLauncherPackage") 53 val pkg = devLauncherPackage.getConstructor().newInstance() as ReactPackage 54 packages.add(pkg) 55 } catch (e: ClassNotFoundException) { 56 // dev launcher is not installed in this project 57 } 58 59 return packages 60 } 61 getUseDeveloperSupportnull62 override fun getUseDeveloperSupport() = false // change it and run `yarn start` in `expo-dev-menu` to launch dev menu from local packager 63 64 override fun getBundleAssetName() = "EXDevMenuApp.android.js" 65 66 override fun getJSMainModuleName() = "index" 67 68 fun getContext(): Context = super.getApplication() 69 70 override fun getJavaScriptExecutorFactory(): JavaScriptExecutorFactory? { 71 return createNonDebuggableJavaScriptExecutorFactory(application) 72 } 73 createReactInstanceManagernull74 override fun createReactInstanceManager(): ReactInstanceManager { 75 val reactInstanceManager = super.createReactInstanceManager() 76 77 if (useDeveloperSupport) { 78 // To use a different packager url, we need to replace internal RN objects. 79 try { 80 val serverIp = BufferedReader( 81 InputStreamReader(super.getApplication().assets.open("dev-menu-packager-host")) 82 ).use { 83 it.readLine() 84 } 85 86 val devMenuInternalReactSettings = DevMenuReactInternalSettings(serverIp, super.getApplication()) 87 88 val devSupportManager = reactInstanceManager.devSupportManager 89 val devSupportManagerBaseClass = devSupportManager.javaClass.superclass!! 90 setPrivateField( 91 obj = devSupportManager, 92 objClass = devSupportManagerBaseClass, 93 field = "mDevSettings", 94 newValue = devMenuInternalReactSettings 95 ) 96 97 val devServerHelper: DevServerHelper = getPrivateFiled(devSupportManager, devSupportManagerBaseClass, "mDevServerHelper") 98 setPrivateField( 99 obj = devServerHelper, 100 objClass = devServerHelper.javaClass, 101 field = "mSettings", 102 newValue = devMenuInternalReactSettings 103 ) 104 105 Log.i(DEV_MENU_TAG, "DevSettings was correctly injected.") 106 } catch (e: FileNotFoundException) { 107 Log.e(DEV_MENU_TAG, "Couldn't find `dev-menu-packager-host` file.", e) 108 } catch (e: Exception) { 109 Log.e(DEV_MENU_TAG, "Couldn't inject DevSettings object.", e) 110 } 111 } 112 113 return reactInstanceManager 114 } 115 getReactPackageTurboModuleManagerDelegateBuildernull116 override fun getReactPackageTurboModuleManagerDelegateBuilder(): ReactPackageTurboModuleManagerDelegate.Builder? { 117 if (!ReactFeatureFlags.useTurboModules) { 118 return null 119 } 120 val appHost = (application as ReactApplication)?.reactNativeHost ?: return null 121 val method = ReactNativeHost::class.java.getDeclaredMethod("getReactPackageTurboModuleManagerDelegateBuilder") 122 method.isAccessible = true 123 return method.invoke(appHost) as ReactPackageTurboModuleManagerDelegate.Builder 124 } getJSIModulePackagenull125 override fun getJSIModulePackage(): JSIModulePackage? = 126 if (ReactFeatureFlags.enableFabricRenderer) { 127 DefaultJSIModulePackage(this) 128 } else { 129 null 130 } 131 } 132