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.appearance.ExpoAppearanceModule 27 import versioned.host.exp.exponent.modules.api.appearance.ExpoAppearancePackage 28 import versioned.host.exp.exponent.modules.api.appearance.rncappearance.RNCAppearanceModule 29 import versioned.host.exp.exponent.modules.api.cognito.RNAWSCognitoModule 30 import versioned.host.exp.exponent.modules.api.components.datetimepicker.RNDateTimePickerPackage 31 import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerModule 32 import versioned.host.exp.exponent.modules.api.components.gesturehandler.react.RNGestureHandlerPackage 33 import versioned.host.exp.exponent.modules.api.components.lottie.LottiePackage 34 import versioned.host.exp.exponent.modules.api.components.maps.MapsPackage 35 import versioned.host.exp.exponent.modules.api.components.maskedview.RNCMaskedViewPackage 36 import versioned.host.exp.exponent.modules.api.components.picker.RNCPickerPackage 37 import versioned.host.exp.exponent.modules.api.components.reactnativestripesdk.StripeSdkPackage 38 import versioned.host.exp.exponent.modules.api.components.sharedelement.RNSharedElementModule 39 import versioned.host.exp.exponent.modules.api.components.sharedelement.RNSharedElementPackage 40 import versioned.host.exp.exponent.modules.api.components.slider.ReactSliderPackage 41 import versioned.host.exp.exponent.modules.api.components.svg.SvgPackage 42 import versioned.host.exp.exponent.modules.api.components.pagerview.PagerViewPackage 43 import versioned.host.exp.exponent.modules.api.components.webview.RNCWebViewModule 44 import versioned.host.exp.exponent.modules.api.components.webview.RNCWebViewPackage 45 import versioned.host.exp.exponent.modules.api.netinfo.NetInfoModule 46 import versioned.host.exp.exponent.modules.api.notifications.NotificationsModule 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, ExperiencePackagePicker) 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(RNCWebViewModule(reactContext)) 137 nativeModules.add(NetInfoModule(reactContext)) 138 nativeModules.add(RNSharedElementModule(reactContext)) 139 140 // @tsapeta: Using ExpoAppearanceModule in home app causes some issues with the dev menu, 141 // when home's setting is set to automatic and the system theme is different 142 // than this supported by the experience in which we opened the dev menu. 143 if (isKernel) { 144 nativeModules.add(RNCAppearanceModule(reactContext)) 145 } else { 146 nativeModules.add(ExpoAppearanceModule(reactContext)) 147 } 148 149 nativeModules.addAll(SvgPackage().createNativeModules(reactContext)) 150 nativeModules.addAll(MapsPackage().createNativeModules(reactContext)) 151 nativeModules.addAll(RNDateTimePickerPackage().createNativeModules(reactContext)) 152 nativeModules.addAll(stripePackage.createNativeModules(reactContext)) 153 154 // Call to create native modules has to be at the bottom -- 155 // -- ExpoModuleRegistryAdapter uses the list of native modules 156 // to create Bindings for internal modules. 157 nativeModules.addAll( 158 moduleRegistryAdapter.createNativeModules( 159 scopedContext, 160 experienceKey, 161 experienceProperties, 162 manifest, 163 nativeModules 164 ) 165 ) 166 } catch (e: JSONException) { 167 EXL.e(TAG, e.toString()) 168 } catch (e: UnsupportedEncodingException) { 169 EXL.e(TAG, e.toString()) 170 } 171 } 172 return nativeModules 173 } 174 175 override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { 176 val viewManagers = mutableListOf<ViewManager<*, *>>() 177 178 // Add view manager from 3rd party library packages. 179 addViewManagersFromPackages( 180 reactContext, 181 viewManagers, 182 listOf( 183 SvgPackage(), 184 MapsPackage(), 185 LottiePackage(), 186 RNGestureHandlerPackage(), 187 RNScreensPackage(), 188 RNCWebViewPackage(), 189 SafeAreaContextPackage(), 190 RNSharedElementPackage(), 191 RNDateTimePickerPackage(), 192 RNCMaskedViewPackage(), 193 RNCPickerPackage(), 194 ReactSliderPackage(), 195 PagerViewPackage(), 196 ExpoAppearancePackage(), 197 stripePackage 198 ) 199 ) 200 viewManagers.addAll(moduleRegistryAdapter.createViewManagers(reactContext)) 201 return viewManagers 202 } 203 204 private fun addViewManagersFromPackages( 205 reactContext: ReactApplicationContext, 206 viewManagers: MutableList<ViewManager<*, *>>, 207 packages: List<ReactPackage> 208 ) { 209 for (pack in packages) { 210 viewManagers.addAll(pack.createViewManagers(reactContext)) 211 } 212 } 213 214 private fun createDefaultModuleRegistryAdapterForPackages( 215 packages: List<Package>, 216 singletonModules: List<SingletonModule>?, 217 modulesProvider: ModulesProvider? = null 218 ): ExpoModuleRegistryAdapter { 219 return ExpoModuleRegistryAdapter(ReactModuleRegistryProvider(packages, singletonModules), modulesProvider) 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