1 // Copyright 2020-present 650 Industries. All rights reserved. 2 package versioned.host.exp.exponent 3 4 import com.facebook.react.ReactInstanceManager 5 import com.facebook.react.ReactRootView 6 import com.facebook.react.TurboReactPackage 7 import com.facebook.react.bridge.NativeModule 8 import com.facebook.react.bridge.ReactApplicationContext 9 import com.facebook.react.bridge.ReactMarker 10 import com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_END 11 import com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_START 12 import com.facebook.react.module.annotations.ReactModule 13 import com.facebook.react.module.annotations.ReactModuleList 14 import com.facebook.react.module.model.ReactModuleInfo 15 import com.facebook.react.module.model.ReactModuleInfoProvider 16 import com.facebook.react.modules.intent.IntentModule 17 import com.facebook.react.turbomodule.core.interfaces.TurboModule 18 import com.facebook.react.uimanager.ReanimatedUIManager 19 import com.facebook.react.uimanager.UIManagerModule 20 import com.facebook.react.uimanager.ViewManager 21 import com.facebook.systrace.Systrace 22 import com.swmansion.reanimated.ReanimatedModule 23 import expo.modules.manifests.core.Manifest 24 import host.exp.exponent.experience.ReactNativeActivity 25 import host.exp.exponent.kernel.KernelConstants 26 import host.exp.expoview.Exponent 27 import versioned.host.exp.exponent.modules.internal.ExponentIntentModule 28 29 /** Package defining basic modules and view managers. */ 30 @ReactModuleList( 31 nativeModules = [ 32 ExponentIntentModule::class, 33 ReanimatedModule::class, 34 ReanimatedUIManager::class, 35 ] 36 ) 37 class ExpoTurboPackage( 38 private val experienceProperties: Map<String, Any?>, 39 private val manifest: Manifest 40 ) : TurboReactPackage() { 41 // Get the hosted `ReactInstanceManager` by current Activity 42 private val reactInstanceManager: ReactInstanceManager? 43 get() { 44 val currentActivity = Exponent.instance.currentActivity as? ReactNativeActivity ?: return null 45 return (currentActivity.rootView as? ReactRootView)?.reactInstanceManager 46 } 47 48 override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { 49 return listOf() 50 } 51 52 override fun getModule(name: String, context: ReactApplicationContext): NativeModule? { 53 val isVerified = manifest.isVerified() 54 return when (name) { 55 IntentModule.NAME -> ExponentIntentModule( 56 context, 57 experienceProperties 58 ) 59 ReanimatedModule.NAME -> ReanimatedModule(context) 60 ReanimatedUIManager.NAME -> createReanimatedUIManager(context) ?: throw RuntimeException("Cannot create reanimated uimanager") 61 else -> null 62 } 63 } 64 65 override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { 66 return try { 67 // TODO(Bacon): Does this need to reflect ExpoTurboPackage$$ReactModuleInfoProvider ? 68 val reactModuleInfoProviderClass = Class.forName("com.facebook.react.shell.MainReactPackage$\$ReactModuleInfoProvider") 69 reactModuleInfoProviderClass.newInstance() as ReactModuleInfoProvider 70 } catch (e: ClassNotFoundException) { 71 // In OSS case, the annotation processor does not run. We fall back on creating this by hand 72 val moduleList: Array<Class<out NativeModule?>> = arrayOf( 73 ExponentIntentModule::class.java, 74 ReanimatedModule::class.java, 75 ReanimatedUIManager::class.java, 76 ) 77 val reactModuleInfoMap = mutableMapOf<String, ReactModuleInfo>() 78 for (moduleClass in moduleList) { 79 val reactModule = moduleClass.getAnnotation(ReactModule::class.java)!! 80 var canOverrideExistingModule = reactModule.canOverrideExistingModule 81 val isTurbo = TurboModule::class.java.isAssignableFrom(moduleClass) 82 if (reactModule.name == ReanimatedUIManager.NAME) { 83 if (!shouldOverrideUIManagerForReanimated()) { 84 continue 85 } 86 canOverrideExistingModule = true 87 } 88 reactModuleInfoMap[reactModule.name] = ReactModuleInfo( 89 reactModule.name, 90 moduleClass.name, 91 canOverrideExistingModule, 92 reactModule.needsEagerInit, 93 reactModule.hasConstants, 94 reactModule.isCxxModule, 95 isTurbo 96 ) 97 } 98 ReactModuleInfoProvider { reactModuleInfoMap } 99 } catch (e: InstantiationException) { 100 throw RuntimeException( 101 "No ReactModuleInfoProvider for CoreModulesPackage$\$ReactModuleInfoProvider", e 102 ) 103 } catch (e: IllegalAccessException) { 104 throw RuntimeException( 105 "No ReactModuleInfoProvider for CoreModulesPackage$\$ReactModuleInfoProvider", e 106 ) 107 } 108 } 109 110 private fun createReanimatedUIManager(reactContext: ReactApplicationContext): UIManagerModule? { 111 val reactInstanceManager = this.reactInstanceManager ?: return null 112 ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_START) 113 Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createUIManagerModule") 114 val minTimeLeftInFrameForNonBatchedOperationMs = -1 115 return try { 116 ReanimatedUIManager( 117 reactContext, 118 reactInstanceManager.getOrCreateViewManagers(reactContext), 119 minTimeLeftInFrameForNonBatchedOperationMs 120 ) 121 } finally { 122 Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE) 123 ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_END) 124 } 125 } 126 127 // Reanimated UIManager requires `ReactInstanceManager`, 128 // for headless mode we cannot get the hosted `ReactInstanceManager` from current Activity, 129 // in this case, we don't override UIManager for reanimated. 130 // besides, we should not override in remote debugging mode because reanimated does not support it. 131 private fun shouldOverrideUIManagerForReanimated(): Boolean { 132 this.reactInstanceManager?.run { 133 return !(devSupportManager.devSettings?.isRemoteJSDebugEnabled ?: false) 134 } 135 136 return false 137 } 138 139 companion object { 140 private val TAG = ExpoTurboPackage::class.java.simpleName 141 142 fun kernelExpoTurboPackage(manifest: Manifest, initialURL: String?): ExpoTurboPackage { 143 val kernelExperienceProperties = mutableMapOf( 144 KernelConstants.LINKING_URI_KEY to "exp://", 145 KernelConstants.IS_HEADLESS_KEY to false, 146 ).apply { 147 if (initialURL != null) { 148 this[KernelConstants.INTENT_URI_KEY] = initialURL 149 } 150 } 151 return ExpoTurboPackage(kernelExperienceProperties, manifest) 152 } 153 } 154 } 155