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