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