1 // Copyright 2015-present 650 Industries. All rights reserved.
2 package versioned.host.exp.exponent
3 
4 import android.content.Context
5 import android.os.Looper
6 import com.facebook.react.ReactPackage
7 import com.facebook.react.bridge.NativeModule
8 import com.facebook.react.bridge.ReactApplicationContext
9 import com.facebook.react.uimanager.ViewManager
10 import expo.modules.adapters.react.ReactModuleRegistryProvider
11 import expo.modules.core.interfaces.Package
12 import expo.modules.core.interfaces.SingletonModule
13 import expo.modules.kotlin.ModulesProvider
14 import expo.modules.manifests.core.Manifest
15 import host.exp.exponent.Constants
16 import host.exp.exponent.analytics.EXL
17 import host.exp.exponent.kernel.ExperienceKey
18 // WHEN_VERSIONING_REMOVE_FROM_HERE
19 import host.exp.exponent.kernel.ExponentKernelModuleProvider
20 // WHEN_VERSIONING_REMOVE_TO_HERE
21 import host.exp.exponent.kernel.KernelConstants
22 import host.exp.exponent.utils.ScopedContext
23 import org.json.JSONException
24 import versioned.host.exp.exponent.modules.api.*
25 import versioned.host.exp.exponent.modules.api.cognito.RNAWSCognitoModule
26 import versioned.host.exp.exponent.modules.api.components.datetimepicker.RNDateTimePickerPackage
27 import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerModule
28 import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerPackage
29 import versioned.host.exp.exponent.modules.api.components.lottie.LottiePackage
30 import versioned.host.exp.exponent.modules.api.components.maps.MapsPackage
31 import versioned.host.exp.exponent.modules.api.components.maskedview.RNCMaskedViewPackage
32 import versioned.host.exp.exponent.modules.api.components.picker.RNCPickerPackage
33 import versioned.host.exp.exponent.modules.api.components.reactnativestripesdk.StripeSdkPackage
34 import versioned.host.exp.exponent.modules.api.components.sharedelement.RNSharedElementModule
35 import versioned.host.exp.exponent.modules.api.components.sharedelement.RNSharedElementPackage
36 import versioned.host.exp.exponent.modules.api.components.slider.ReactSliderPackage
37 import versioned.host.exp.exponent.modules.api.components.svg.SvgPackage
38 import versioned.host.exp.exponent.modules.api.components.pagerview.PagerViewPackage
39 import versioned.host.exp.exponent.modules.api.components.webview.RNCWebViewModule
40 import versioned.host.exp.exponent.modules.api.components.webview.RNCWebViewPackage
41 import versioned.host.exp.exponent.modules.api.netinfo.NetInfoModule
42 import versioned.host.exp.exponent.modules.api.notifications.NotificationsModule
43 import versioned.host.exp.exponent.modules.api.safeareacontext.SafeAreaContextPackage
44 import versioned.host.exp.exponent.modules.api.screens.RNScreensPackage
45 import versioned.host.exp.exponent.modules.api.viewshot.RNViewShotModule
46 import versioned.host.exp.exponent.modules.internal.DevMenuModule
47 import versioned.host.exp.exponent.modules.test.ExponentTestNativeModule
48 import versioned.host.exp.exponent.modules.universal.ExpoModuleRegistryAdapter
49 import versioned.host.exp.exponent.modules.universal.ScopedModuleRegistryAdapter
50 import java.io.UnsupportedEncodingException
51 
52 // This is an Expo module but not a unimodule
53 class ExponentPackage : ReactPackage {
54   private val isKernel: Boolean
55   private val experienceProperties: Map<String, Any?>
56   private val manifest: Manifest
57   private val moduleRegistryAdapter: ScopedModuleRegistryAdapter
58 
59   private constructor(
60     isKernel: Boolean,
61     experienceProperties: Map<String, Any?>,
62     manifest: Manifest,
63     expoPackages: List<Package>,
64     singletonModules: List<SingletonModule>?
65   ) {
66     this.isKernel = isKernel
67     this.experienceProperties = experienceProperties
68     this.manifest = manifest
69     moduleRegistryAdapter = createDefaultModuleRegistryAdapterForPackages(expoPackages, singletonModules)
70   }
71 
72   constructor(
73     experienceProperties: Map<String, Any?>,
74     manifest: Manifest,
75     expoPackages: List<Package>?,
76     delegate: ExponentPackageDelegate?,
77     singletonModules: List<SingletonModule>
78   ) {
79     isKernel = false
80     this.experienceProperties = experienceProperties
81     this.manifest = manifest
82     val packages = expoPackages ?: ExperiencePackagePicker.packages(manifest)
83     // Delegate may not be null only when the app is detached
84     moduleRegistryAdapter = createModuleRegistryAdapter(delegate, singletonModules, packages)
85   }
86 
87   private fun createModuleRegistryAdapter(
88     delegate: ExponentPackageDelegate?,
89     singletonModules: List<SingletonModule>,
90     packages: List<Package>
91   ): ScopedModuleRegistryAdapter {
92     var registryAdapter: ScopedModuleRegistryAdapter? = null
93     if (delegate != null) {
94       registryAdapter = delegate.getScopedModuleRegistryAdapterForPackages(packages, singletonModules)
95     }
96     if (registryAdapter == null) {
97       registryAdapter = createDefaultModuleRegistryAdapterForPackages(packages, singletonModules, ExperiencePackagePicker)
98     }
99     return registryAdapter
100   }
101 
102   override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
103     val isVerified = manifest.isVerified() ?: false
104     val nativeModules: MutableList<NativeModule> = mutableListOf(
105       URLHandlerModule(reactContext),
106       ShakeModule(reactContext),
107       KeyboardModule(reactContext)
108     )
109 
110     if (isKernel) {
111       // WHEN_VERSIONING_REMOVE_FROM_HERE
112       nativeModules.add((ExponentKernelModuleProvider.newInstance(reactContext) as NativeModule?)!!)
113       // WHEN_VERSIONING_REMOVE_TO_HERE
114     }
115     if (!isKernel && !Constants.isStandaloneApp()) {
116       // We need DevMenuModule only in non-home and non-standalone apps.
117       nativeModules.add(DevMenuModule(reactContext, experienceProperties, manifest))
118     }
119 
120     if (isVerified) {
121       try {
122         val experienceKey = ExperienceKey.fromManifest(manifest)
123         val scopedContext = ScopedContext(reactContext, experienceKey)
124         nativeModules.add(NotificationsModule(reactContext, experienceKey, manifest.getStableLegacyID(), manifest.getEASProjectID()))
125         nativeModules.add(RNViewShotModule(reactContext, scopedContext))
126         nativeModules.add(ExponentTestNativeModule(reactContext))
127         nativeModules.add(PedometerModule(reactContext))
128         nativeModules.add(ScreenOrientationModule(reactContext))
129         nativeModules.add(RNGestureHandlerModule(reactContext))
130         nativeModules.add(RNAWSCognitoModule(reactContext))
131         nativeModules.add(RNCWebViewModule(reactContext))
132         nativeModules.add(NetInfoModule(reactContext))
133         nativeModules.add(RNSharedElementModule(reactContext))
134         nativeModules.addAll(SvgPackage().createNativeModules(reactContext))
135         nativeModules.addAll(MapsPackage().createNativeModules(reactContext))
136         nativeModules.addAll(RNDateTimePickerPackage().createNativeModules(reactContext))
137         nativeModules.addAll(stripePackage.createNativeModules(reactContext))
138 
139         // Call to create native modules has to be at the bottom --
140         // -- ExpoModuleRegistryAdapter uses the list of native modules
141         // to create Bindings for internal modules.
142         nativeModules.addAll(
143           moduleRegistryAdapter.createNativeModules(
144             scopedContext,
145             experienceKey,
146             experienceProperties,
147             manifest,
148             nativeModules
149           )
150         )
151       } catch (e: JSONException) {
152         EXL.e(TAG, e.toString())
153       } catch (e: UnsupportedEncodingException) {
154         EXL.e(TAG, e.toString())
155       }
156     }
157     return nativeModules
158   }
159 
160   override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
161     val viewManagers = mutableListOf<ViewManager<*, *>>()
162 
163     // Add view manager from 3rd party library packages.
164     addViewManagersFromPackages(
165       reactContext,
166       viewManagers,
167       listOf(
168         SvgPackage(),
169         MapsPackage(),
170         LottiePackage(),
171         RNGestureHandlerPackage(),
172         RNScreensPackage(),
173         RNCWebViewPackage(),
174         SafeAreaContextPackage(),
175         RNSharedElementPackage(),
176         RNDateTimePickerPackage(),
177         RNCMaskedViewPackage(),
178         RNCPickerPackage(),
179         ReactSliderPackage(),
180         PagerViewPackage(),
181         stripePackage
182       )
183     )
184     viewManagers.addAll(moduleRegistryAdapter.createViewManagers(reactContext))
185     return viewManagers
186   }
187 
188   private fun addViewManagersFromPackages(
189     reactContext: ReactApplicationContext,
190     viewManagers: MutableList<ViewManager<*, *>>,
191     packages: List<ReactPackage>
192   ) {
193     for (pack in packages) {
194       viewManagers.addAll(pack.createViewManagers(reactContext))
195     }
196   }
197 
198   private fun createDefaultModuleRegistryAdapterForPackages(
199     packages: List<Package>,
200     singletonModules: List<SingletonModule>?,
201     modulesProvider: ModulesProvider? = null
202   ): ExpoModuleRegistryAdapter {
203     return ExpoModuleRegistryAdapter(ReactModuleRegistryProvider(packages, singletonModules), modulesProvider)
204   }
205 
206   companion object {
207     private val TAG = ExponentPackage::class.java.simpleName
208 
209     private val singletonModules = mutableListOf<SingletonModule>()
210     private val singletonModulesClasses = mutableSetOf<Class<*>>()
211 
212     // Need to avoid initializing 2 StripeSdkPackages
213     private val stripePackage = StripeSdkPackage()
214 
215     fun kernelExponentPackage(
216       context: Context,
217       manifest: Manifest,
218       expoPackages: List<Package>,
219       initialURL: String?
220     ): ExponentPackage {
221       val kernelExperienceProperties = mutableMapOf(
222         KernelConstants.LINKING_URI_KEY to "exp://",
223         KernelConstants.IS_HEADLESS_KEY to false
224       ).apply {
225         if (initialURL != null) {
226           this[KernelConstants.INTENT_URI_KEY] = initialURL
227         }
228       }
229       val singletonModules = getOrCreateSingletonModules(context, manifest, expoPackages)
230       return ExponentPackage(
231         true,
232         kernelExperienceProperties,
233         manifest,
234         expoPackages,
235         singletonModules
236       )
237     }
238 
239     fun getOrCreateSingletonModules(
240       context: Context?,
241       manifest: Manifest?,
242       providedExpoPackages: List<Package>?
243     ): List<SingletonModule> {
244       if (Looper.getMainLooper() != Looper.myLooper()) {
245         throw RuntimeException("Singleton modules must be created on the main thread.")
246       }
247 
248       val expoPackages = providedExpoPackages ?: ExperiencePackagePicker.packages(manifest)
249 
250       for (expoPackage in expoPackages) {
251         // For now we just accumulate more and more singleton modules,
252         // but in fact we should only return singleton modules from the requested
253         // unimodules. This solution also unnecessarily creates singleton modules
254         // which are going to be deallocated in a tick, but there's no better solution
255         // without a bigger-than-minimal refactor. In SDK32 the only singleton module
256         // is TaskService which is safe to initialize more than once.
257         val packageSingletonModules = expoPackage.createSingletonModules(context)
258         for (singletonModule in packageSingletonModules) {
259           if (!singletonModulesClasses.contains(singletonModule.javaClass)) {
260             singletonModules.add(singletonModule)
261             singletonModulesClasses.add(singletonModule.javaClass)
262           }
263         }
264       }
265 
266       return singletonModules
267     }
268   }
269 }
270