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