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