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.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 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 nativeModules.add(if (isVerified) ExponentAsyncStorageModule(reactContext, manifest) else ExponentUnsignedAsyncStorageModule(reactContext)) 112 113 if (isKernel) { 114 // WHEN_VERSIONING_REMOVE_FROM_HERE 115 nativeModules.add((ExponentKernelModuleProvider.newInstance(reactContext) as NativeModule?)!!) 116 // WHEN_VERSIONING_REMOVE_TO_HERE 117 } 118 if (!isKernel && !Constants.isStandaloneApp()) { 119 // We need DevMenuModule only in non-home and non-standalone apps. 120 nativeModules.add(DevMenuModule(reactContext, experienceProperties, manifest)) 121 } 122 123 if (isVerified) { 124 try { 125 val experienceKey = ExperienceKey.fromManifest(manifest) 126 val scopedContext = ScopedContext(reactContext, experienceKey) 127 nativeModules.add(NotificationsModule(reactContext, experienceKey, manifest.getStableLegacyID(), manifest.getEASProjectID())) 128 nativeModules.add(RNViewShotModule(reactContext, scopedContext)) 129 nativeModules.add(ExponentTestNativeModule(reactContext)) 130 nativeModules.add(PedometerModule(reactContext)) 131 nativeModules.add(ScreenOrientationModule(reactContext)) 132 nativeModules.add(RNGestureHandlerModule(reactContext)) 133 nativeModules.add(RNAWSCognitoModule(reactContext)) 134 nativeModules.add(RNCWebViewModule(reactContext)) 135 nativeModules.add(NetInfoModule(reactContext)) 136 nativeModules.addAll(SvgPackage().getNativeModuleIterator(reactContext).map { it.module }) 137 nativeModules.addAll(MapsPackage().createNativeModules(reactContext)) 138 nativeModules.addAll(RNDateTimePickerPackage().getNativeModuleIterator(reactContext).map { it.module }) 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 RNDateTimePickerPackage(), 179 RNCMaskedViewPackage(), 180 RNCPickerPackage(), 181 ReactSliderPackage(), 182 PagerViewPackage(), 183 stripePackage, 184 skiaPackage, 185 ReactNativeFlashListPackage() 186 ) 187 ) 188 viewManagers.addAll(moduleRegistryAdapter.createViewManagers(reactContext)) 189 return viewManagers 190 } 191 192 private fun addViewManagersFromPackages( 193 reactContext: ReactApplicationContext, 194 viewManagers: MutableList<ViewManager<*, *>>, 195 packages: List<ReactPackage> 196 ) { 197 for (pack in packages) { 198 viewManagers.addAll(pack.createViewManagers(reactContext)) 199 } 200 } 201 202 private fun createDefaultModuleRegistryAdapterForPackages( 203 packages: List<Package>, 204 singletonModules: List<SingletonModule>?, 205 modulesProvider: ModulesProvider? = null 206 ): ExpoModuleRegistryAdapter { 207 return ExpoModuleRegistryAdapter(ReactModuleRegistryProvider(packages, singletonModules), modulesProvider) 208 } 209 210 companion object { 211 private val TAG = ExponentPackage::class.java.simpleName 212 213 private val singletonModules = mutableListOf<SingletonModule>() 214 private val singletonModulesClasses = mutableSetOf<Class<*>>() 215 216 // Need to avoid initializing duplicated packages 217 private val stripePackage = StripeSdkPackage() 218 private val skiaPackage = RNSkiaPackage() 219 220 fun kernelExponentPackage( 221 context: Context, 222 manifest: Manifest, 223 expoPackages: List<Package>, 224 initialURL: String? 225 ): ExponentPackage { 226 val kernelExperienceProperties = mutableMapOf( 227 KernelConstants.LINKING_URI_KEY to "exp://", 228 KernelConstants.IS_HEADLESS_KEY to false 229 ).apply { 230 if (initialURL != null) { 231 this[KernelConstants.INTENT_URI_KEY] = initialURL 232 } 233 } 234 val singletonModules = getOrCreateSingletonModules(context, manifest, expoPackages) 235 return ExponentPackage( 236 true, 237 kernelExperienceProperties, 238 manifest, 239 expoPackages, 240 singletonModules 241 ) 242 } 243 244 fun getOrCreateSingletonModules( 245 context: Context?, 246 manifest: Manifest?, 247 providedExpoPackages: List<Package>? 248 ): List<SingletonModule> { 249 if (Looper.getMainLooper() != Looper.myLooper()) { 250 throw RuntimeException("Singleton modules must be created on the main thread.") 251 } 252 253 val expoPackages = providedExpoPackages ?: ExperiencePackagePicker.packages(manifest) 254 255 for (expoPackage in expoPackages) { 256 // For now we just accumulate more and more singleton modules, 257 // but in fact we should only return singleton modules from the requested 258 // unimodules. This solution also unnecessarily creates singleton modules 259 // which are going to be deallocated in a tick, but there's no better solution 260 // without a bigger-than-minimal refactor. In SDK32 the only singleton module 261 // is TaskService which is safe to initialize more than once. 262 val packageSingletonModules = expoPackage.createSingletonModules(context) 263 for (singletonModule in packageSingletonModules) { 264 if (!singletonModulesClasses.contains(singletonModule.javaClass)) { 265 singletonModules.add(singletonModule) 266 singletonModulesClasses.add(singletonModule.javaClass) 267 } 268 } 269 } 270 271 return singletonModules 272 } 273 } 274 } 275