1 package host.exp.exponent.experience
2 
3 import com.facebook.react.bridge.NativeModule
4 import com.facebook.react.bridge.ReactApplicationContext
5 import expo.modules.adapters.react.ReactModuleRegistryProvider
6 import expo.modules.core.ModuleRegistry
7 import expo.modules.core.interfaces.RegistryLifecycleListener
8 import expo.modules.notifications.notifications.categories.ExpoNotificationCategoriesModule
9 import expo.modules.notifications.notifications.handling.NotificationsHandler
10 import expo.modules.notifications.notifications.scheduling.NotificationScheduler
11 import host.exp.exponent.utils.ScopedContext
12 import host.exp.exponent.kernel.ExperienceKey
13 import expo.modules.manifests.core.Manifest
14 import versioned.host.exp.exponent.modules.universal.*
15 import versioned.host.exp.exponent.modules.universal.notifications.ScopedServerRegistrationModule
16 
17 open class DetachedModuleRegistryAdapter(moduleRegistryProvider: ReactModuleRegistryProvider) :
18   ExpoModuleRegistryAdapter(moduleRegistryProvider) {
19 
20   override fun createNativeModules(
21     scopedContext: ScopedContext,
22     experienceKey: ExperienceKey,
23     experienceProperties: Map<String, Any?>,
24     manifest: Manifest,
25     otherModules: List<NativeModule>
26   ): List<NativeModule> {
27     val reactApplicationContext = scopedContext.context as ReactApplicationContext
28 
29     // We only use React application context, because we're detached -- no scopes
30     val moduleRegistry = mModuleRegistryProvider[reactApplicationContext]
31 
32     moduleRegistry.registerInternalModule(
33       ConstantsBinding(
34         scopedContext,
35         experienceProperties,
36         manifest
37       )
38     )
39 
40     // Overriding expo-updates UpdatesService
41     moduleRegistry.registerInternalModule(UpdatesBinding(scopedContext, experienceProperties))
42 
43     // ReactAdapterPackage requires ReactContext
44     val reactContext = scopedContext.context as ReactApplicationContext
45     for (internalModule in mReactAdapterPackage.createInternalModules(reactContext)) {
46       moduleRegistry.registerInternalModule(internalModule)
47     }
48 
49     // Overriding ScopedUIManagerModuleWrapper from ReactAdapterPackage
50     moduleRegistry.registerInternalModule(ScopedUIManagerModuleWrapper(reactContext))
51 
52     // Overriding expo-secure-store
53     moduleRegistry.registerExportedModule(ScopedSecureStoreModule(scopedContext))
54 
55     // Certain notifications classes should share `SharedPreferences` object with the notifications services, so we don't want to use scoped context.
56     moduleRegistry.registerExportedModule(NotificationScheduler(scopedContext.baseContext))
57     moduleRegistry.registerExportedModule(ExpoNotificationCategoriesModule(scopedContext.baseContext))
58     moduleRegistry.registerExportedModule(NotificationsHandler(scopedContext.baseContext))
59     // We consciously pass scoped context to ScopedServerRegistrationModule
60     // so it can access legacy scoped backed-up storage and migrates
61     // the legacy UUID to scoped non-backed-up storage.
62     moduleRegistry.registerExportedModule(ScopedServerRegistrationModule(scopedContext))
63 
64     // Adding other modules (not universal) to module registry as consumers.
65     // It allows these modules to refer to universal modules.
66     for (otherModule in otherModules) {
67       if (otherModule is RegistryLifecycleListener) {
68         moduleRegistry.registerExtraListener(otherModule as RegistryLifecycleListener)
69       }
70     }
71     configureModuleRegistry(moduleRegistry, reactApplicationContext)
72     return getNativeModulesFromModuleRegistry(reactApplicationContext, moduleRegistry)
73   }
74 
75   protected open fun configureModuleRegistry(
76     moduleRegistry: ModuleRegistry,
77     reactContext: ReactApplicationContext
78   ) {
79     // Subclasses may add more modules here.
80   }
81 }
82