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