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 com.swmansion.reanimated.ReanimatedModule 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.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 // besides, we should not override in remote debugging mode because reanimated does not support it. 143 private fun shouldOverrideUIManagerForReanimated(): Boolean { 144 this.reactInstanceManager?.run { 145 return !(devSupportManager.devSettings?.isRemoteJSDebugEnabled ?: false) 146 } 147 148 return false 149 } 150 151 companion object { 152 private val TAG = ExpoTurboPackage::class.java.simpleName 153 154 fun kernelExpoTurboPackage(manifest: Manifest, initialURL: String?): ExpoTurboPackage { 155 val kernelExperienceProperties = mutableMapOf( 156 KernelConstants.LINKING_URI_KEY to "exp://", 157 KernelConstants.IS_HEADLESS_KEY to false, 158 ).apply { 159 if (initialURL != null) { 160 this[KernelConstants.INTENT_URI_KEY] = initialURL 161 } 162 } 163 return ExpoTurboPackage(kernelExperienceProperties, manifest) 164 } 165 } 166 } 167