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