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.random.RandomModule 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.appearance.ExpoAppearanceModule 26 import versioned.host.exp.exponent.modules.api.appearance.ExpoAppearancePackage 27 import versioned.host.exp.exponent.modules.api.appearance.rncappearance.RNCAppearanceModule 28 import versioned.host.exp.exponent.modules.api.cognito.RNAWSCognitoModule 29 import versioned.host.exp.exponent.modules.api.components.datetimepicker.RNDateTimePickerPackage 30 import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerModule 31 import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerPackage 32 import versioned.host.exp.exponent.modules.api.components.lottie.LottiePackage 33 import versioned.host.exp.exponent.modules.api.components.maps.MapsPackage 34 import versioned.host.exp.exponent.modules.api.components.maskedview.RNCMaskedViewPackage 35 import versioned.host.exp.exponent.modules.api.components.picker.RNCPickerPackage 36 import versioned.host.exp.exponent.modules.api.components.reactnativestripesdk.StripeSdkPackage 37 import versioned.host.exp.exponent.modules.api.components.sharedelement.RNSharedElementModule 38 import versioned.host.exp.exponent.modules.api.components.sharedelement.RNSharedElementPackage 39 import versioned.host.exp.exponent.modules.api.components.slider.ReactSliderPackage 40 import versioned.host.exp.exponent.modules.api.components.svg.SvgPackage 41 import versioned.host.exp.exponent.modules.api.components.viewpager.RNCViewPagerPackage 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.reanimated.ReanimatedModule 47 import versioned.host.exp.exponent.modules.api.safeareacontext.SafeAreaContextPackage 48 import versioned.host.exp.exponent.modules.api.screens.RNScreensPackage 49 import versioned.host.exp.exponent.modules.api.viewshot.RNViewShotModule 50 import versioned.host.exp.exponent.modules.internal.DevMenuModule 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) 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 114 if (isKernel) { 115 // WHEN_VERSIONING_REMOVE_FROM_HERE 116 nativeModules.add((ExponentKernelModuleProvider.newInstance(reactContext) as NativeModule?)!!) 117 // WHEN_VERSIONING_REMOVE_TO_HERE 118 } 119 if (!isKernel && !Constants.isStandaloneApp()) { 120 // We need DevMenuModule only in non-home and non-standalone apps. 121 nativeModules.add(DevMenuModule(reactContext, experienceProperties, manifest)) 122 } 123 124 if (isVerified) { 125 try { 126 val experienceKey = ExperienceKey.fromManifest(manifest) 127 val scopedContext = ScopedContext(reactContext, experienceKey) 128 nativeModules.add(NotificationsModule(reactContext, experienceKey, manifest.getStableLegacyID(), manifest.getEASProjectID())) 129 nativeModules.add(RNViewShotModule(reactContext, scopedContext)) 130 nativeModules.add(RandomModule(reactContext)) 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(ReanimatedModule(reactContext)) 137 nativeModules.add(RNCWebViewModule(reactContext)) 138 nativeModules.add(NetInfoModule(reactContext)) 139 nativeModules.add(RNSharedElementModule(reactContext)) 140 141 // @tsapeta: Using ExpoAppearanceModule in home app causes some issues with the dev menu, 142 // when home's setting is set to automatic and the system theme is different 143 // than this supported by the experience in which we opened the dev menu. 144 if (isKernel) { 145 nativeModules.add(RNCAppearanceModule(reactContext)) 146 } else { 147 nativeModules.add(ExpoAppearanceModule(reactContext)) 148 } 149 150 nativeModules.addAll(SvgPackage().createNativeModules(reactContext)) 151 nativeModules.addAll(MapsPackage().createNativeModules(reactContext)) 152 nativeModules.addAll(RNDateTimePickerPackage().createNativeModules(reactContext)) 153 nativeModules.addAll(stripePackage.createNativeModules(reactContext)) 154 155 // Call to create native modules has to be at the bottom -- 156 // -- ExpoModuleRegistryAdapter uses the list of native modules 157 // to create Bindings for internal modules. 158 nativeModules.addAll( 159 moduleRegistryAdapter.createNativeModules( 160 scopedContext, 161 experienceKey, 162 experienceProperties, 163 manifest, 164 nativeModules 165 ) 166 ) 167 } catch (e: JSONException) { 168 EXL.e(TAG, e.toString()) 169 } catch (e: UnsupportedEncodingException) { 170 EXL.e(TAG, e.toString()) 171 } 172 } 173 return nativeModules 174 } 175 176 override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { 177 val viewManagers = mutableListOf<ViewManager<*, *>>() 178 179 // Add view manager from 3rd party library packages. 180 addViewManagersFromPackages( 181 reactContext, 182 viewManagers, 183 listOf( 184 SvgPackage(), 185 MapsPackage(), 186 LottiePackage(), 187 RNGestureHandlerPackage(), 188 RNScreensPackage(), 189 RNCWebViewPackage(), 190 SafeAreaContextPackage(), 191 RNSharedElementPackage(), 192 RNDateTimePickerPackage(), 193 RNCMaskedViewPackage(), 194 RNCPickerPackage(), 195 ReactSliderPackage(), 196 RNCViewPagerPackage(), 197 ExpoAppearancePackage(), 198 stripePackage 199 ) 200 ) 201 viewManagers.addAll(moduleRegistryAdapter.createViewManagers(reactContext)) 202 return viewManagers 203 } 204 205 private fun addViewManagersFromPackages( 206 reactContext: ReactApplicationContext, 207 viewManagers: MutableList<ViewManager<*, *>>, 208 packages: List<ReactPackage> 209 ) { 210 for (pack in packages) { 211 viewManagers.addAll(pack.createViewManagers(reactContext)) 212 } 213 } 214 215 private fun createDefaultModuleRegistryAdapterForPackages( 216 packages: List<Package>, 217 singletonModules: List<SingletonModule>? 218 ): ExpoModuleRegistryAdapter { 219 return ExpoModuleRegistryAdapter(ReactModuleRegistryProvider(packages, singletonModules)) 220 } 221 222 companion object { 223 private val TAG = ExponentPackage::class.java.simpleName 224 225 private val singletonModules = mutableListOf<SingletonModule>() 226 private val singletonModulesClasses = mutableSetOf<Class<*>>() 227 228 // Need to avoid initializing 2 StripeSdkPackages 229 private val stripePackage = StripeSdkPackage() 230 231 fun kernelExponentPackage( 232 context: Context, 233 manifest: Manifest, 234 expoPackages: List<Package>, 235 initialURL: String? 236 ): ExponentPackage { 237 val kernelExperienceProperties = mutableMapOf( 238 KernelConstants.LINKING_URI_KEY to "exp://", 239 KernelConstants.IS_HEADLESS_KEY to false 240 ).apply { 241 if (initialURL != null) { 242 this[KernelConstants.INTENT_URI_KEY] = initialURL 243 } 244 } 245 val singletonModules = getOrCreateSingletonModules(context, manifest, expoPackages) 246 return ExponentPackage( 247 true, 248 kernelExperienceProperties, 249 manifest, 250 expoPackages, 251 singletonModules 252 ) 253 } 254 255 fun getOrCreateSingletonModules( 256 context: Context?, 257 manifest: Manifest?, 258 providedExpoPackages: List<Package>? 259 ): List<SingletonModule> { 260 if (Looper.getMainLooper() != Looper.myLooper()) { 261 throw RuntimeException("Singleton modules must be created on the main thread.") 262 } 263 264 val expoPackages = providedExpoPackages ?: ExperiencePackagePicker.packages(manifest) 265 266 for (expoPackage in expoPackages) { 267 // For now we just accumulate more and more singleton modules, 268 // but in fact we should only return singleton modules from the requested 269 // unimodules. This solution also unnecessarily creates singleton modules 270 // which are going to be deallocated in a tick, but there's no better solution 271 // without a bigger-than-minimal refactor. In SDK32 the only singleton module 272 // is TaskService which is safe to initialize more than once. 273 val packageSingletonModules = expoPackage.createSingletonModules(context) 274 for (singletonModule in packageSingletonModules) { 275 if (!singletonModulesClasses.contains(singletonModule.javaClass)) { 276 singletonModules.add(singletonModule) 277 singletonModulesClasses.add(singletonModule.javaClass) 278 } 279 } 280 } 281 282 return singletonModules 283 } 284 } 285 } 286