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