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(), experienceProperties)) 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 manifest.getStableLegacyID(), 165 nativeModules 166 ) 167 ) 168 } catch (e: JSONException) { 169 EXL.e(TAG, e.toString()) 170 } catch (e: UnsupportedEncodingException) { 171 EXL.e(TAG, e.toString()) 172 } 173 } 174 return nativeModules 175 } 176 177 override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { 178 val viewManagers = mutableListOf<ViewManager<*, *>>() 179 180 // Add view manager from 3rd party library packages. 181 addViewManagersFromPackages( 182 reactContext, 183 viewManagers, 184 listOf( 185 SvgPackage(), 186 MapsPackage(), 187 LottiePackage(), 188 RNGestureHandlerPackage(), 189 RNScreensPackage(), 190 RNCWebViewPackage(), 191 SafeAreaContextPackage(), 192 RNSharedElementPackage(), 193 RNDateTimePickerPackage(), 194 RNCMaskedViewPackage(), 195 RNCPickerPackage(), 196 ReactSliderPackage(), 197 RNCViewPagerPackage(), 198 ExpoAppearancePackage(), 199 stripePackage 200 ) 201 ) 202 viewManagers.addAll(moduleRegistryAdapter.createViewManagers(reactContext)) 203 return viewManagers 204 } 205 206 private fun addViewManagersFromPackages( 207 reactContext: ReactApplicationContext, 208 viewManagers: MutableList<ViewManager<*, *>>, 209 packages: List<ReactPackage> 210 ) { 211 for (pack in packages) { 212 viewManagers.addAll(pack.createViewManagers(reactContext)) 213 } 214 } 215 216 private fun createDefaultModuleRegistryAdapterForPackages( 217 packages: List<Package>, 218 singletonModules: List<SingletonModule>? 219 ): ExpoModuleRegistryAdapter { 220 return ExpoModuleRegistryAdapter(ReactModuleRegistryProvider(packages, singletonModules)) 221 } 222 223 companion object { 224 private val TAG = ExponentPackage::class.java.simpleName 225 226 private val singletonModules = mutableListOf<SingletonModule>() 227 private val singletonModulesClasses = mutableSetOf<Class<*>>() 228 229 // Need to avoid initializing 2 StripeSdkPackages 230 private val stripePackage = StripeSdkPackage() 231 232 fun kernelExponentPackage( 233 context: Context, 234 manifest: Manifest, 235 expoPackages: List<Package>, 236 initialURL: String? 237 ): ExponentPackage { 238 val kernelExperienceProperties = mutableMapOf( 239 KernelConstants.LINKING_URI_KEY to "exp://", 240 KernelConstants.IS_HEADLESS_KEY to false 241 ).apply { 242 if (initialURL != null) { 243 this[KernelConstants.INTENT_URI_KEY] = initialURL 244 } 245 } 246 val singletonModules = getOrCreateSingletonModules(context, manifest, expoPackages) 247 return ExponentPackage( 248 true, 249 kernelExperienceProperties, 250 manifest, 251 expoPackages, 252 singletonModules 253 ) 254 } 255 256 fun getOrCreateSingletonModules( 257 context: Context?, 258 manifest: Manifest?, 259 providedExpoPackages: List<Package>? 260 ): List<SingletonModule> { 261 if (Looper.getMainLooper() != Looper.myLooper()) { 262 throw RuntimeException("Singleton modules must be created on the main thread.") 263 } 264 265 val expoPackages = providedExpoPackages ?: ExperiencePackagePicker.packages(manifest) 266 267 for (expoPackage in expoPackages) { 268 // For now we just accumulate more and more singleton modules, 269 // but in fact we should only return singleton modules from the requested 270 // unimodules. This solution also unnecessarily creates singleton modules 271 // which are going to be deallocated in a tick, but there's no better solution 272 // without a bigger-than-minimal refactor. In SDK32 the only singleton module 273 // is TaskService which is safe to initialize more than once. 274 val packageSingletonModules = expoPackage.createSingletonModules(context) 275 for (singletonModule in packageSingletonModules) { 276 if (!singletonModulesClasses.contains(singletonModule.javaClass)) { 277 singletonModules.add(singletonModule) 278 singletonModulesClasses.add(singletonModule.javaClass) 279 } 280 } 281 } 282 283 return singletonModules 284 } 285 } 286 } 287