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