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