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.updates.manifest.raw.RawManifest 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 import java.util.* 56 57 // This is an Expo module but not a unimodule 58 class ExponentPackage : ReactPackage { 59 private val isKernel: Boolean 60 private val experienceProperties: Map<String, Any?> 61 private val manifest: RawManifest 62 private val moduleRegistryAdapter: ScopedModuleRegistryAdapter 63 64 private constructor( 65 isKernel: Boolean, 66 experienceProperties: Map<String, Any?>, 67 manifest: RawManifest, 68 expoPackages: List<Package>, 69 singletonModules: List<SingletonModule>? 70 ) { 71 this.isKernel = isKernel 72 this.experienceProperties = experienceProperties 73 this.manifest = manifest 74 moduleRegistryAdapter = createDefaultModuleRegistryAdapterForPackages(expoPackages, singletonModules) 75 } 76 77 constructor( 78 experienceProperties: Map<String, Any?>, 79 manifest: RawManifest, 80 expoPackages: List<Package>?, 81 delegate: ExponentPackageDelegate?, 82 singletonModules: List<SingletonModule> 83 ) { 84 isKernel = false 85 this.experienceProperties = experienceProperties 86 this.manifest = manifest 87 val packages = expoPackages ?: ExperiencePackagePicker.packages(manifest) 88 // Delegate may not be null only when the app is detached 89 moduleRegistryAdapter = createModuleRegistryAdapter(delegate, singletonModules, packages) 90 } 91 92 private fun createModuleRegistryAdapter( 93 delegate: ExponentPackageDelegate?, 94 singletonModules: List<SingletonModule>, 95 packages: List<Package> 96 ): ScopedModuleRegistryAdapter { 97 var registryAdapter: ScopedModuleRegistryAdapter? = null 98 if (delegate != null) { 99 registryAdapter = delegate.getScopedModuleRegistryAdapterForPackages(packages, singletonModules) 100 } 101 if (registryAdapter == null) { 102 registryAdapter = createDefaultModuleRegistryAdapterForPackages(packages, singletonModules) 103 } 104 return registryAdapter 105 } 106 107 override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> { 108 val isVerified = manifest.isVerified() ?: false 109 val nativeModules: MutableList<NativeModule> = mutableListOf( 110 URLHandlerModule(reactContext), 111 ShakeModule(reactContext), 112 KeyboardModule(reactContext) 113 ) 114 115 if (isKernel) { 116 // WHEN_VERSIONING_REMOVE_FROM_HERE 117 nativeModules.add((ExponentKernelModuleProvider.newInstance(reactContext) as NativeModule?)!!) 118 // WHEN_VERSIONING_REMOVE_TO_HERE 119 } 120 if (!isKernel && !Constants.isStandaloneApp()) { 121 // We need DevMenuModule only in non-home and non-standalone apps. 122 nativeModules.add(DevMenuModule(reactContext, experienceProperties, manifest)) 123 } 124 125 if (isVerified) { 126 try { 127 val experienceKey = ExperienceKey.fromRawManifest(manifest) 128 val scopedContext = ScopedContext(reactContext, experienceKey) 129 nativeModules.add(NotificationsModule(reactContext, experienceKey, manifest.getStableLegacyID(), experienceProperties)) 130 nativeModules.add(RNViewShotModule(reactContext, scopedContext)) 131 nativeModules.add(RandomModule(reactContext)) 132 nativeModules.add(ExponentTestNativeModule(reactContext)) 133 nativeModules.add(PedometerModule(reactContext)) 134 nativeModules.add(ScreenOrientationModule(reactContext)) 135 nativeModules.add(RNGestureHandlerModule(reactContext)) 136 nativeModules.add(RNAWSCognitoModule(reactContext)) 137 nativeModules.add(ReanimatedModule(reactContext)) 138 nativeModules.add(RNCWebViewModule(reactContext)) 139 nativeModules.add(NetInfoModule(reactContext)) 140 nativeModules.add(RNSharedElementModule(reactContext)) 141 142 // @tsapeta: Using ExpoAppearanceModule in home app causes some issues with the dev menu, 143 // when home's setting is set to automatic and the system theme is different 144 // than this supported by the experience in which we opened the dev menu. 145 if (isKernel) { 146 nativeModules.add(RNCAppearanceModule(reactContext)) 147 } else { 148 nativeModules.add(ExpoAppearanceModule(reactContext)) 149 } 150 151 nativeModules.addAll(SvgPackage().createNativeModules(reactContext)) 152 nativeModules.addAll(MapsPackage().createNativeModules(reactContext)) 153 nativeModules.addAll(RNDateTimePickerPackage().createNativeModules(reactContext)) 154 nativeModules.addAll(stripePackage.createNativeModules(reactContext)) 155 156 // Call to create native modules has to be at the bottom -- 157 // -- ExpoModuleRegistryAdapter uses the list of native modules 158 // to create Bindings for internal modules. 159 nativeModules.addAll( 160 moduleRegistryAdapter.createNativeModules( 161 scopedContext, 162 experienceKey, 163 experienceProperties, 164 manifest, 165 manifest.getStableLegacyID(), 166 nativeModules 167 ) 168 ) 169 } catch (e: JSONException) { 170 EXL.e(TAG, e.toString()) 171 } catch (e: UnsupportedEncodingException) { 172 EXL.e(TAG, e.toString()) 173 } 174 } 175 return nativeModules 176 } 177 178 override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { 179 val viewManagers = mutableListOf<ViewManager<*, *>>() 180 181 // Add view manager from 3rd party library packages. 182 addViewManagersFromPackages( 183 reactContext, 184 viewManagers, 185 listOf( 186 SvgPackage(), 187 MapsPackage(), 188 LottiePackage(), 189 RNGestureHandlerPackage(), 190 RNScreensPackage(), 191 RNCWebViewPackage(), 192 SafeAreaContextPackage(), 193 RNSharedElementPackage(), 194 RNDateTimePickerPackage(), 195 RNCMaskedViewPackage(), 196 RNCPickerPackage(), 197 ReactSliderPackage(), 198 RNCViewPagerPackage(), 199 ExpoAppearancePackage(), 200 stripePackage 201 ) 202 ) 203 viewManagers.addAll(moduleRegistryAdapter.createViewManagers(reactContext)) 204 return viewManagers 205 } 206 207 private fun addViewManagersFromPackages( 208 reactContext: ReactApplicationContext, 209 viewManagers: MutableList<ViewManager<*, *>>, 210 packages: List<ReactPackage> 211 ) { 212 for (pack in packages) { 213 viewManagers.addAll(pack.createViewManagers(reactContext)) 214 } 215 } 216 217 private fun createDefaultModuleRegistryAdapterForPackages( 218 packages: List<Package>, 219 singletonModules: List<SingletonModule>? 220 ): ExpoModuleRegistryAdapter { 221 return ExpoModuleRegistryAdapter(ReactModuleRegistryProvider(packages, singletonModules)) 222 } 223 224 companion object { 225 private val TAG = ExponentPackage::class.java.simpleName 226 227 private val singletonModules = mutableListOf<SingletonModule>() 228 private val singletonModulesClasses = mutableSetOf<Class<*>>() 229 230 // Need to avoid initializing 2 StripeSdkPackages 231 private val stripePackage = StripeSdkPackage() 232 233 fun kernelExponentPackage( 234 context: Context, 235 manifest: RawManifest, 236 expoPackages: List<Package>, 237 initialURL: String? 238 ): ExponentPackage { 239 val kernelExperienceProperties = mutableMapOf( 240 KernelConstants.LINKING_URI_KEY to "exp://", 241 KernelConstants.IS_HEADLESS_KEY to false 242 ).apply { 243 if (initialURL != null) { 244 this[KernelConstants.INTENT_URI_KEY] = initialURL 245 } 246 } 247 val singletonModules = getOrCreateSingletonModules(context, manifest, expoPackages) 248 return ExponentPackage( 249 true, 250 kernelExperienceProperties, 251 manifest, 252 expoPackages, 253 singletonModules 254 ) 255 } 256 257 fun getOrCreateSingletonModules( 258 context: Context?, 259 manifest: RawManifest?, 260 providedExpoPackages: List<Package>? 261 ): List<SingletonModule> { 262 if (Looper.getMainLooper() != Looper.myLooper()) { 263 throw RuntimeException("Singleton modules must be created on the main thread.") 264 } 265 266 val expoPackages = providedExpoPackages ?: ExperiencePackagePicker.packages(manifest) 267 268 for (expoPackage in expoPackages) { 269 // For now we just accumulate more and more singleton modules, 270 // but in fact we should only return singleton modules from the requested 271 // unimodules. This solution also unnecessarily creates singleton modules 272 // which are going to be deallocated in a tick, but there's no better solution 273 // without a bigger-than-minimal refactor. In SDK32 the only singleton module 274 // is TaskService which is safe to initialize more than once. 275 val packageSingletonModules = expoPackage.createSingletonModules(context) 276 for (singletonModule in packageSingletonModules) { 277 if (!singletonModulesClasses.contains(singletonModule.javaClass)) { 278 singletonModules.add(singletonModule) 279 singletonModulesClasses.add(singletonModule.javaClass) 280 } 281 } 282 } 283 284 return singletonModules 285 } 286 } 287 } 288