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