expo-modules-core 0.4.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +6 -6
  3. package/android/build.gradle +30 -2
  4. package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +27 -5
  5. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +49 -5
  6. package/android/src/main/java/expo/modules/core/BasePackage.java +6 -0
  7. package/android/src/main/java/expo/modules/core/ModulePriorities.kt +25 -0
  8. package/android/src/main/java/expo/modules/core/interfaces/ActivityEventListener.java +3 -1
  9. package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.java +10 -0
  10. package/android/src/main/java/expo/modules/core/interfaces/Package.java +4 -0
  11. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +21 -0
  12. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +14 -0
  13. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +70 -0
  14. package/android/src/main/java/expo/modules/core/utilities/KotlinUtilities.kt +23 -0
  15. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +166 -0
  16. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +9 -0
  17. package/android/src/main/java/expo/modules/kotlin/ExpoModulesHelper.kt +18 -0
  18. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +24 -0
  19. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +98 -0
  20. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +41 -0
  21. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +56 -0
  22. package/android/src/main/java/expo/modules/kotlin/ModulesProvider.kt +7 -0
  23. package/android/src/main/java/expo/modules/kotlin/Promise.kt +13 -0
  24. package/android/src/main/java/expo/modules/kotlin/ReactLifecycleDelegate.kt +39 -0
  25. package/android/src/main/java/expo/modules/kotlin/ReadableArrayIterator.kt +14 -0
  26. package/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt +18 -0
  27. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructor.kt +5 -0
  28. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +31 -0
  29. package/android/src/main/java/expo/modules/kotlin/allocators/UnsafeAllocator.kt +49 -0
  30. package/android/src/main/java/expo/modules/kotlin/events/EventListener.kt +39 -0
  31. package/android/src/main/java/expo/modules/kotlin/events/EventName.kt +31 -0
  32. package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +3 -0
  33. package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +26 -0
  34. package/android/src/main/java/expo/modules/kotlin/events/OnActivityResultPayload.kt +8 -0
  35. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +70 -0
  36. package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +50 -0
  37. package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +14 -0
  38. package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +15 -0
  39. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +24 -0
  40. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +227 -0
  41. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +16 -0
  42. package/android/src/main/java/expo/modules/kotlin/records/Field.kt +5 -0
  43. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +3 -0
  44. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +55 -0
  45. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +14 -0
  46. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +44 -0
  47. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +60 -0
  48. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +84 -0
  49. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +25 -0
  50. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +39 -0
  51. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +28 -0
  52. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +19 -0
  53. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +107 -0
  54. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +10 -0
  55. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +17 -0
  56. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
  57. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +21 -0
  58. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +41 -0
  59. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +40 -0
  60. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +21 -0
  61. package/android/src/main/java/expo/modules/kotlin/views/ViewWrapperDelegateHolder.kt +5 -0
  62. package/build/NativeModulesProxy.native.d.ts +4 -0
  63. package/build/NativeModulesProxy.native.js +14 -1
  64. package/build/NativeModulesProxy.native.js.map +1 -1
  65. package/build/NativeModulesProxy.types.d.ts +3 -0
  66. package/build/NativeModulesProxy.types.js.map +1 -1
  67. package/build/NativeViewManagerAdapter.native.js +1 -1
  68. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  69. package/ios/AppDelegates/EXAppDelegateWrapper.h +19 -0
  70. package/ios/AppDelegates/EXAppDelegateWrapper.m +45 -0
  71. package/ios/AppDelegates/EXAppDelegatesLoader.h +15 -0
  72. package/ios/AppDelegates/EXAppDelegatesLoader.m +30 -0
  73. package/ios/AppDelegates/EXLegacyAppDelegateWrapper.h +16 -0
  74. package/ios/{EXAppDelegateWrapper.m → AppDelegates/EXLegacyAppDelegateWrapper.m} +2 -2
  75. package/ios/AppDelegates/ExpoAppDelegate.swift +282 -0
  76. package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +24 -0
  77. package/ios/EXAppDefines.h +26 -0
  78. package/ios/EXAppDefines.m +61 -0
  79. package/ios/ExpoModulesCore.podspec +8 -3
  80. package/ios/JSI/ExpoModulesProxySpec.h +24 -0
  81. package/ios/JSI/ExpoModulesProxySpec.mm +135 -0
  82. package/ios/JSI/JSIConversions.h +42 -0
  83. package/ios/JSI/JSIConversions.mm +164 -0
  84. package/ios/JSI/JSIInstaller.h +19 -0
  85. package/ios/JSI/JSIInstaller.mm +22 -0
  86. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -6
  87. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +6 -0
  88. package/ios/NativeModulesProxy/{EXNativeModulesProxy.m → EXNativeModulesProxy.mm} +91 -18
  89. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h +16 -0
  90. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.m +49 -0
  91. package/ios/ReactDelegates/EXReactDelegateWrapper+Private.h +18 -0
  92. package/ios/ReactDelegates/EXReactDelegateWrapper.h +25 -0
  93. package/ios/ReactDelegates/EXReactDelegateWrapper.m +40 -0
  94. package/ios/ReactDelegates/ExpoReactDelegate.swift +37 -0
  95. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +52 -0
  96. package/ios/ReactDelegates/ModulePriorities.swift +20 -0
  97. package/ios/Services/EXReactNativeEventEmitter.h +6 -0
  98. package/ios/Services/EXReactNativeEventEmitter.m +15 -0
  99. package/ios/Swift/AppContext.swift +14 -1
  100. package/ios/Swift/Arguments/AnyArgument.swift +14 -0
  101. package/ios/Swift/Arguments/AnyArgumentType.swift +13 -0
  102. package/ios/Swift/Arguments/ArgumentType.swift +24 -0
  103. package/ios/Swift/Arguments/ConvertibleArgument.swift +15 -0
  104. package/ios/Swift/Arguments/Convertibles.swift +107 -0
  105. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +42 -0
  106. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +16 -0
  107. package/ios/Swift/Arguments/Types/EnumArgumentType.swift +105 -0
  108. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +49 -0
  109. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +15 -0
  110. package/ios/Swift/Arguments/Types/RawArgumentType.swift +25 -0
  111. package/ios/Swift/Conversions.swift +199 -7
  112. package/ios/Swift/EventListener.swift +37 -5
  113. package/ios/Swift/Functions/AnyFunction.swift +42 -0
  114. package/ios/Swift/{Methods/ConcreteMethod.swift → Functions/ConcreteFunction.swift} +32 -34
  115. package/ios/Swift/ModuleHolder.swift +86 -20
  116. package/ios/Swift/ModuleRegistry.swift +19 -8
  117. package/ios/Swift/Modules/AnyModule.swift +8 -8
  118. package/ios/Swift/Modules/Module.swift +11 -0
  119. package/ios/Swift/Modules/ModuleDefinition.swift +55 -15
  120. package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +1 -1
  121. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +149 -54
  122. package/ios/Swift/ModulesProvider.swift +19 -0
  123. package/ios/Swift/Promise.swift +1 -1
  124. package/ios/Swift/Records/Field.swift +1 -1
  125. package/ios/Swift/Records/Record.swift +8 -1
  126. package/ios/Swift/SwiftInteropBridge.swift +46 -17
  127. package/ios/Swift/Views/AnyViewProp.swift +2 -2
  128. package/ios/Swift/Views/ConcreteViewProp.swift +37 -10
  129. package/ios/Swift/Views/ViewModuleWrapper.swift +9 -4
  130. package/ios/Swift.h +9 -0
  131. package/ios/Tests/ArgumentTypeSpec.swift +145 -0
  132. package/ios/Tests/ConstantsSpec.swift +36 -0
  133. package/ios/Tests/ConvertiblesSpec.swift +265 -0
  134. package/ios/Tests/EXAppDefinesTest.m +99 -0
  135. package/ios/Tests/{MethodSpec.swift → FunctionSpec.swift} +69 -54
  136. package/ios/Tests/FunctionWithConvertiblesSpec.swift +66 -0
  137. package/ios/Tests/Mocks/ModuleMocks.swift +21 -7
  138. package/ios/Tests/ModuleEventListenersSpec.swift +17 -16
  139. package/ios/Tests/ModuleRegistrySpec.swift +4 -7
  140. package/package.json +3 -3
  141. package/src/NativeModulesProxy.native.ts +22 -2
  142. package/src/NativeModulesProxy.types.ts +8 -0
  143. package/src/NativeViewManagerAdapter.native.tsx +1 -1
  144. package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.kt +0 -9
  145. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.kt +0 -11
  146. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.kt +0 -51
  147. package/ios/EXAppDelegateWrapper.h +0 -13
  148. package/ios/Swift/Methods/AnyArgumentType.swift +0 -48
  149. package/ios/Swift/Methods/AnyMethod.swift +0 -31
  150. package/ios/Swift/Methods/AnyMethodArgument.swift +0 -13
@@ -0,0 +1,166 @@
1
+ package expo.modules.kotlin
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import expo.modules.core.interfaces.ActivityProvider
8
+ import expo.modules.core.interfaces.services.EventEmitter
9
+ import expo.modules.interfaces.barcodescanner.BarCodeScannerInterface
10
+ import expo.modules.interfaces.camera.CameraViewInterface
11
+ import expo.modules.interfaces.constants.ConstantsInterface
12
+ import expo.modules.interfaces.filesystem.FilePermissionModuleInterface
13
+ import expo.modules.interfaces.font.FontManagerInterface
14
+ import expo.modules.interfaces.imageloader.ImageLoaderInterface
15
+ import expo.modules.interfaces.permissions.Permissions
16
+ import expo.modules.interfaces.sensors.SensorServiceInterface
17
+ import expo.modules.interfaces.taskManager.TaskManagerInterface
18
+ import expo.modules.kotlin.events.EventName
19
+ import expo.modules.kotlin.events.KEventEmitterWrapper
20
+ import expo.modules.kotlin.events.OnActivityResultPayload
21
+ import expo.modules.kotlin.modules.Module
22
+ import java.lang.ref.WeakReference
23
+
24
+ class AppContext(
25
+ modulesProvider: ModulesProvider,
26
+ val legacyModuleRegistry: expo.modules.core.ModuleRegistry,
27
+ private val reactContextHolder: WeakReference<ReactApplicationContext>
28
+ ) {
29
+ val registry = ModuleRegistry(WeakReference(this)).register(modulesProvider)
30
+ private val reactLifecycleDelegate = ReactLifecycleDelegate(this)
31
+
32
+ init {
33
+ requireNotNull(reactContextHolder.get()) {
34
+ "The app context should be created with valid react context."
35
+ }.apply {
36
+ addLifecycleEventListener(reactLifecycleDelegate)
37
+ addActivityEventListener(reactLifecycleDelegate)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Returns a legacy module implementing given interface.
43
+ */
44
+ inline fun <reified Module> legacyModule(): Module? {
45
+ return try {
46
+ legacyModuleRegistry.getModule(Module::class.java)
47
+ } catch (_: Exception) {
48
+ null
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Provides access to app's constants from the legacy module registry.
54
+ */
55
+ val constants: ConstantsInterface?
56
+ get() = legacyModule()
57
+
58
+ /**
59
+ * Provides access to the file system manager from the legacy module registry.
60
+ */
61
+ val filePermission: FilePermissionModuleInterface?
62
+ get() = legacyModule()
63
+
64
+ /**
65
+ * Provides access to the permissions manager from the legacy module registry
66
+ */
67
+ val permissions: Permissions?
68
+ get() = legacyModule()
69
+
70
+ /**
71
+ * Provides access to the image loader from the legacy module registry
72
+ */
73
+ val imageLoader: ImageLoaderInterface?
74
+ get() = legacyModule()
75
+
76
+ /**
77
+ * Provides access to the bar code scanner manager from the legacy module registry
78
+ */
79
+ val barcodeScanner: BarCodeScannerInterface?
80
+ get() = legacyModule()
81
+
82
+ /**
83
+ * Provides access to the camera view manager from the legacy module registry
84
+ */
85
+ val camera: CameraViewInterface?
86
+ get() = legacyModule()
87
+
88
+ /**
89
+ * Provides access to the font manager from the legacy module registry
90
+ */
91
+ val font: FontManagerInterface?
92
+ get() = legacyModule()
93
+
94
+ /**
95
+ * Provides access to the sensor manager from the legacy module registry
96
+ */
97
+ val sensor: SensorServiceInterface?
98
+ get() = legacyModule()
99
+
100
+ /**
101
+ * Provides access to the task manager from the legacy module registry
102
+ */
103
+ val taskManager: TaskManagerInterface?
104
+ get() = legacyModule()
105
+
106
+ /**
107
+ * Provides access to the activity provider from the legacy module registry
108
+ */
109
+ val activityProvider: ActivityProvider?
110
+ get() = legacyModule()
111
+
112
+ /**
113
+ * Provides access to the react application context
114
+ */
115
+ val reactContext: Context?
116
+ get() = reactContextHolder.get()
117
+
118
+ /**
119
+ * Provides access to the event emitter
120
+ */
121
+ fun eventEmitter(module: Module): EventEmitter? {
122
+ val legacyEventEmitter = legacyModule<EventEmitter>() ?: return null
123
+ return KEventEmitterWrapper(
124
+ requireNotNull(registry.getModuleHolder(module)) {
125
+ "Cannot create an event emitter for the module that isn't present in the module registry."
126
+ },
127
+ legacyEventEmitter
128
+ )
129
+ }
130
+
131
+ fun onDestroy() {
132
+ reactContextHolder.get()?.removeLifecycleEventListener(reactLifecycleDelegate)
133
+ registry.post(EventName.MODULE_DESTROY)
134
+ }
135
+
136
+ fun onHostResume() {
137
+ registry.post(EventName.ACTIVITY_ENTERS_FOREGROUND)
138
+ }
139
+
140
+ fun onHostPause() {
141
+ registry.post(EventName.ACTIVITY_ENTERS_BACKGROUND)
142
+ }
143
+
144
+ fun onHostDestroy() {
145
+ registry.post(EventName.ACTIVITY_DESTROYS)
146
+ }
147
+
148
+ fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
149
+ registry.post(
150
+ EventName.ON_ACTIVITY_RESULT,
151
+ activity,
152
+ OnActivityResultPayload(
153
+ requestCode,
154
+ resultCode,
155
+ data
156
+ )
157
+ )
158
+ }
159
+
160
+ fun onNewIntent(intent: Intent?) {
161
+ registry.post(
162
+ EventName.ON_NEW_INTENT,
163
+ intent
164
+ )
165
+ }
166
+ }
@@ -0,0 +1,9 @@
1
+ package expo.modules.kotlin
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+
5
+ inline fun <T> Dynamic.recycle(block: Dynamic.() -> T): T {
6
+ val result = block(this)
7
+ this.recycle()
8
+ return result
9
+ }
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin
2
+
3
+ import android.util.Log
4
+
5
+ class ExpoModulesHelper {
6
+ companion object {
7
+ @Suppress("unchecked_cast")
8
+ val modulesProvider by lazy {
9
+ try {
10
+ val expoModules = Class.forName("expo.modules.ExpoModulesPackageList")
11
+ expoModules.newInstance() as ModulesProvider
12
+ } catch (e: Exception) {
13
+ Log.e("ExpoModulesHelper", "Couldn't get expo modules list.", e)
14
+ null
15
+ }
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,24 @@
1
+ package expo.modules.kotlin
2
+
3
+ import android.os.Bundle
4
+ import com.facebook.react.bridge.Arguments
5
+
6
+ class KPromiseWrapper(
7
+ private val bridgePromise: com.facebook.react.bridge.Promise
8
+ ) : Promise {
9
+
10
+ override fun resolve(value: Any?) {
11
+ bridgePromise.resolve(
12
+ when (value) {
13
+ is Unit -> null
14
+ is Bundle -> Arguments.fromBundle(value as Bundle?)
15
+ is List<*> -> Arguments.fromList(value as List<*>?)
16
+ else -> value
17
+ }
18
+ )
19
+ }
20
+
21
+ override fun reject(code: String, message: String?, cause: Throwable?) {
22
+ bridgePromise.reject(code, message, cause)
23
+ }
24
+ }
@@ -0,0 +1,98 @@
1
+ package expo.modules.kotlin
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.uimanager.ViewManager
6
+ import expo.modules.kotlin.views.GroupViewManagerWrapper
7
+ import expo.modules.kotlin.views.SimpleViewManagerWrapper
8
+ import expo.modules.kotlin.views.ViewManagerWrapperDelegate
9
+ import expo.modules.kotlin.views.ViewWrapperDelegateHolder
10
+ import java.lang.ref.WeakReference
11
+
12
+ private typealias ModuleName = String
13
+ private typealias ModuleConstants = Map<String, Any?>
14
+ private typealias ModuleMethodInfo = Map<String, Any?>
15
+
16
+ class KotlinInteropModuleRegistry(
17
+ modulesProvider: ModulesProvider,
18
+ legacyModuleRegistry: expo.modules.core.ModuleRegistry,
19
+ reactContext: WeakReference<ReactApplicationContext>
20
+ ) {
21
+ private val appContext = AppContext(modulesProvider, legacyModuleRegistry, reactContext)
22
+
23
+ private val registry: ModuleRegistry
24
+ get() = appContext.registry
25
+
26
+ fun hasModule(name: String): Boolean = registry.hasModule(name)
27
+
28
+ fun callMethod(moduleName: String, method: String, arguments: ReadableArray, promise: Promise) {
29
+ registry
30
+ .getModuleHolder(moduleName)
31
+ ?.call(method, arguments, promise)
32
+ }
33
+
34
+ fun exportedModulesConstants(): Map<ModuleName, ModuleConstants> {
35
+ return registry
36
+ .map { holder ->
37
+ holder.name to holder.definition.constantsProvider()
38
+ }
39
+ .toMap()
40
+ }
41
+
42
+ fun exportMethods(exportKey: (String, List<ModuleMethodInfo>) -> Unit = { _, _ -> }): Map<ModuleName, List<ModuleMethodInfo>> {
43
+ return registry
44
+ .map { holder ->
45
+ val methodsInfo = holder.definition.methods.map { (name, method) ->
46
+ mapOf(
47
+ "name" to name,
48
+ "argumentsCount" to method.argsCount
49
+ )
50
+ }
51
+ exportKey(holder.name, methodsInfo)
52
+ holder.name to methodsInfo
53
+ }
54
+ .toMap()
55
+ }
56
+
57
+ fun exportViewManagers(): List<ViewManager<*, *>> {
58
+ return registry
59
+ .filter { it.definition.viewManagerDefinition != null }
60
+ .map {
61
+ val wrapperDelegate = ViewManagerWrapperDelegate(it)
62
+ when (it.definition.viewManagerDefinition!!.getViewManagerType()) {
63
+ expo.modules.core.ViewManager.ViewManagerType.SIMPLE -> SimpleViewManagerWrapper(wrapperDelegate)
64
+ expo.modules.core.ViewManager.ViewManagerType.GROUP -> GroupViewManagerWrapper(wrapperDelegate)
65
+ }
66
+ }
67
+ }
68
+
69
+ fun extractViewManagersDelegateHolders(viewManagers: List<ViewManager<*, *>>): List<ViewWrapperDelegateHolder> =
70
+ viewManagers.filterIsInstance<ViewWrapperDelegateHolder>()
71
+
72
+ /**
73
+ * Since React Native v0.55, {@link com.facebook.react.ReactPackage#createViewManagers(ReactApplicationContext)}
74
+ * gets called only once per lifetime of {@link com.facebook.react.ReactInstanceManager}.
75
+ * However, in our new architecture, users can make a bind between ViewManager and module
76
+ * (for instance, they can use `this` in prop definition). This will lead to bugs, cause
77
+ * the instance that was bound with the prop method won't be the same as the instance returned by module registry.
78
+ * To fix that we need to update all modules holder in exported view managers.
79
+ */
80
+ fun updateModuleHoldersInViewManagers(viewWrapperHolders: List<ViewWrapperDelegateHolder>) {
81
+ viewWrapperHolders
82
+ .map { it.viewWrapperDelegate }
83
+ .forEach { holderWrapper ->
84
+ holderWrapper.moduleHolder = requireNotNull(registry.getModuleHolder(holderWrapper.moduleHolder.name)) {
85
+ "Cannot update the module holder for ${holderWrapper.moduleHolder.name}."
86
+ }
87
+ }
88
+ }
89
+
90
+ fun exportedViewManagersNames(): List<String> =
91
+ registry
92
+ .filter { it.definition.viewManagerDefinition != null }
93
+ .map { it.definition.name }
94
+
95
+ fun onDestroy() {
96
+ appContext.onDestroy()
97
+ }
98
+ }
@@ -0,0 +1,41 @@
1
+ package expo.modules.kotlin
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.core.utilities.ifNull
5
+ import expo.modules.kotlin.events.BasicEventListener
6
+ import expo.modules.kotlin.events.EventListenerWithPayload
7
+ import expo.modules.kotlin.events.EventListenerWithSenderAndPayload
8
+ import expo.modules.kotlin.events.EventName
9
+ import expo.modules.kotlin.exception.MethodNotFoundException
10
+ import expo.modules.kotlin.modules.Module
11
+
12
+ class ModuleHolder(val module: Module) {
13
+ val definition = module.definition()
14
+ val name get() = definition.name
15
+
16
+ fun call(methodName: String, args: ReadableArray, promise: Promise) {
17
+ val method = definition.methods[methodName].ifNull {
18
+ promise.reject(MethodNotFoundException(methodName, definition.name))
19
+ return
20
+ }
21
+
22
+ method.call(args, promise)
23
+ }
24
+
25
+ fun post(eventName: EventName) {
26
+ val listener = definition.eventListeners[eventName] ?: return
27
+ (listener as? BasicEventListener)?.call()
28
+ }
29
+
30
+ @Suppress("UNCHECKED_CAST")
31
+ fun <Payload> post(eventName: EventName, payload: Payload) {
32
+ val listener = definition.eventListeners[eventName] ?: return
33
+ (listener as? EventListenerWithPayload<Payload>)?.call(payload)
34
+ }
35
+
36
+ @Suppress("UNCHECKED_CAST")
37
+ fun <Sender, Payload> post(eventName: EventName, sender: Sender, payload: Payload) {
38
+ val listener = definition.eventListeners[eventName] ?: return
39
+ (listener as? EventListenerWithSenderAndPayload<Sender, Payload>)?.call(sender, payload)
40
+ }
41
+ }
@@ -0,0 +1,56 @@
1
+ package expo.modules.kotlin
2
+
3
+ import expo.modules.kotlin.events.EventName
4
+ import expo.modules.kotlin.modules.Module
5
+ import java.lang.ref.WeakReference
6
+
7
+ class ModuleRegistry(
8
+ private val appContext: WeakReference<AppContext>
9
+ ) : Iterable<ModuleHolder> {
10
+ private val registry = mutableMapOf<String, ModuleHolder>()
11
+
12
+ fun register(module: Module) {
13
+ val holder = ModuleHolder(module)
14
+ module._appContext = requireNotNull(appContext.get()) { "Cannot create a module for invalid app context." }
15
+ holder.post(EventName.MODULE_CREATE)
16
+ registry[holder.name] = holder
17
+ }
18
+
19
+ fun register(provider: ModulesProvider) = apply {
20
+ provider.getModulesList().forEach { type ->
21
+ val module = type.newInstance()
22
+ register(module)
23
+ }
24
+ }
25
+
26
+ fun hasModule(name: String): Boolean = registry.containsKey(name)
27
+
28
+ fun getModule(name: String): Module? = registry[name]?.module
29
+
30
+ fun getModuleHolder(name: String): ModuleHolder? = registry[name]
31
+
32
+ fun getModuleHolder(module: Module): ModuleHolder? =
33
+ registry.values.find { it.module === module }
34
+
35
+ fun post(eventName: EventName) {
36
+ forEach {
37
+ it.post(eventName)
38
+ }
39
+ }
40
+
41
+ @Suppress("UNCHECKED_CAST")
42
+ fun <Sender> post(eventName: EventName, sender: Sender) {
43
+ forEach {
44
+ it.post(eventName, sender)
45
+ }
46
+ }
47
+
48
+ @Suppress("UNCHECKED_CAST")
49
+ fun <Sender, Payload> post(eventName: EventName, sender: Sender, payload: Payload) {
50
+ forEach {
51
+ it.post(eventName, sender, payload)
52
+ }
53
+ }
54
+
55
+ override fun iterator(): Iterator<ModuleHolder> = registry.values.iterator()
56
+ }
@@ -0,0 +1,7 @@
1
+ package expo.modules.kotlin
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+
5
+ interface ModulesProvider {
6
+ fun getModulesList(): List<Class<out Module>>
7
+ }
@@ -0,0 +1,13 @@
1
+ package expo.modules.kotlin
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ interface Promise {
6
+ fun resolve(value: Any?)
7
+
8
+ fun reject(code: String, message: String?, cause: Throwable?)
9
+
10
+ fun reject(exception: CodedException) {
11
+ reject(exception.code, exception.message, exception.cause)
12
+ }
13
+ }
@@ -0,0 +1,39 @@
1
+ package expo.modules.kotlin
2
+
3
+ import android.app.Activity
4
+ import android.content.Intent
5
+ import com.facebook.react.bridge.ActivityEventListener
6
+ import com.facebook.react.bridge.LifecycleEventListener
7
+ import java.lang.ref.WeakReference
8
+
9
+ /**
10
+ * We had to extract this from AppContext, because otherwise, we can't access AppContext
11
+ * in a package that doesn't depend on the React Native directly.
12
+ * Due to:
13
+ * Cannot access 'com.facebook.react.bridge.LifecycleEventListener'
14
+ * which is a supertype of 'expo.modules.kotlin.AppContext'.
15
+ * Check your module classpath for missing or conflicting dependencies
16
+ */
17
+ class ReactLifecycleDelegate(appContext: AppContext) : LifecycleEventListener, ActivityEventListener {
18
+ private val appContextHolder = WeakReference(appContext)
19
+
20
+ override fun onHostResume() {
21
+ appContextHolder.get()?.onHostResume()
22
+ }
23
+
24
+ override fun onHostPause() {
25
+ appContextHolder.get()?.onHostPause()
26
+ }
27
+
28
+ override fun onHostDestroy() {
29
+ appContextHolder.get()?.onHostDestroy()
30
+ }
31
+
32
+ override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
33
+ appContextHolder.get()?.onActivityResult(activity, requestCode, resultCode, data)
34
+ }
35
+
36
+ override fun onNewIntent(intent: Intent?) {
37
+ appContextHolder.get()?.onNewIntent(intent)
38
+ }
39
+ }
@@ -0,0 +1,14 @@
1
+ package expo.modules.kotlin
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import com.facebook.react.bridge.ReadableArray
5
+
6
+ class ReadableArrayIterator(private val array: ReadableArray) : Iterator<Dynamic> {
7
+ var current = 0
8
+
9
+ override fun hasNext(): Boolean = current < array.size()
10
+
11
+ override fun next(): Dynamic = array.getDynamic(current++)
12
+ }
13
+
14
+ fun ReadableArray.iterator(): ReadableArrayIterator = ReadableArrayIterator(this)
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.bridge.ReadableType
6
+ import kotlin.reflect.KType
7
+ import kotlin.reflect.full.createType
8
+
9
+ fun ReadableType.toKType(): KType {
10
+ return when (this) {
11
+ ReadableType.Null -> Any::class.createType(nullable = true)
12
+ ReadableType.Boolean -> Boolean::class.createType()
13
+ ReadableType.Number -> Number::class.createType()
14
+ ReadableType.String -> String::class.createType()
15
+ ReadableType.Map -> ReadableMap::class.createType()
16
+ ReadableType.Array -> ReadableArray::class.createType()
17
+ }
18
+ }
@@ -0,0 +1,5 @@
1
+ package expo.modules.kotlin.allocators
2
+
3
+ fun interface ObjectConstructor<T> {
4
+ fun construct(): T
5
+ }
@@ -0,0 +1,31 @@
1
+ package expo.modules.kotlin.allocators
2
+
3
+ /**
4
+ * This class was created based on https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java.
5
+ */
6
+ class ObjectConstructorFactory {
7
+ fun <T> get(clazz: Class<T>): ObjectConstructor<T> =
8
+ tryToUseDefaultConstructor(clazz) ?: useUnsafeAllocator(clazz)
9
+
10
+ private fun <T> tryToUseDefaultConstructor(clazz: Class<T>): ObjectConstructor<T>? {
11
+ return try {
12
+ val ctor = clazz.getDeclaredConstructor()
13
+ if (!ctor.isAccessible) {
14
+ ctor.isAccessible = true
15
+ }
16
+
17
+ ObjectConstructor {
18
+ ctor.newInstance() as T
19
+ }
20
+ } catch (e: NoSuchMethodException) {
21
+ null
22
+ }
23
+ }
24
+
25
+ private fun <T> useUnsafeAllocator(clazz: Class<T>): ObjectConstructor<T> {
26
+ val allocator = UnsafeAllocator.createAllocator(clazz)
27
+ return ObjectConstructor {
28
+ allocator.newInstance()
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,49 @@
1
+ package expo.modules.kotlin.allocators
2
+
3
+ import android.annotation.SuppressLint
4
+ import java.io.ObjectStreamClass
5
+ import kotlin.jvm.Throws
6
+
7
+ /**
8
+ * Base on https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java.
9
+ */
10
+ fun interface UnsafeAllocator<T> {
11
+ @Throws(Exception::class)
12
+ fun newInstance(): T
13
+
14
+ companion object {
15
+ @SuppressLint("DiscouragedPrivateApi")
16
+ @Suppress("UNCHECKED_CAST")
17
+ fun <T> createAllocator(clazz: Class<T>): UnsafeAllocator<T> {
18
+ // try DalvikVM
19
+ try {
20
+ val getConstructorId = ObjectStreamClass::class.java.getDeclaredMethod("getConstructorId", Class::class.java)
21
+ getConstructorId.isAccessible = true
22
+ val constructorId = getConstructorId.invoke(null, Object::class.java) as Int
23
+ val newInstance = ObjectStreamClass::class.java.getDeclaredMethod("newInstance", Class::class.java, Int::class.java)
24
+ newInstance.isAccessible = true
25
+ return UnsafeAllocator {
26
+ newInstance.invoke(null, clazz, constructorId) as T
27
+ }
28
+ } catch (_: Throwable) {
29
+ }
30
+
31
+ // try JVM (for unit tests)
32
+ try {
33
+ val unsafeClass = Class.forName("sun.misc.Unsafe")
34
+ val theUnsafe = unsafeClass.getDeclaredField("theUnsafe")
35
+ theUnsafe.isAccessible = true
36
+ val unsafeObj = theUnsafe.get(null)
37
+ val allocateInstance = unsafeClass.getMethod("allocateInstance", Class::class.java)
38
+ return UnsafeAllocator {
39
+ allocateInstance.invoke(unsafeObj, clazz) as T
40
+ }
41
+ } catch (_: Throwable) {
42
+ }
43
+
44
+ return UnsafeAllocator {
45
+ throw IllegalArgumentException("Cannot allocate $clazz")
46
+ }
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,39 @@
1
+ package expo.modules.kotlin.events
2
+
3
+ sealed class EventListener(val eventName: EventName)
4
+
5
+ /**
6
+ * Listener for events without sender and payload.
7
+ */
8
+ class BasicEventListener(
9
+ eventName: EventName,
10
+ val body: () -> Unit
11
+ ) : EventListener(eventName) {
12
+ fun call() {
13
+ body()
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Listener for events with payload.
19
+ */
20
+ class EventListenerWithPayload<Payload>(
21
+ eventName: EventName,
22
+ val body: (Payload) -> Unit
23
+ ) : EventListener(eventName) {
24
+ fun call(sender: Payload) {
25
+ body(sender)
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Listener for events that specify sender and payload.
31
+ */
32
+ class EventListenerWithSenderAndPayload<Sender, Payload>(
33
+ eventName: EventName,
34
+ val body: (Sender, Payload) -> Unit
35
+ ) : EventListener(eventName) {
36
+ fun call(sender: Sender, payload: Payload) {
37
+ body(sender, payload)
38
+ }
39
+ }
@@ -0,0 +1,31 @@
1
+ package expo.modules.kotlin.events
2
+
3
+ enum class EventName {
4
+ MODULE_CREATE,
5
+ MODULE_DESTROY,
6
+
7
+ /**
8
+ * Called when the host activity receives a resume event (e.g. Activity.onResume)
9
+ */
10
+ ACTIVITY_ENTERS_FOREGROUND,
11
+
12
+ /**
13
+ * Called when host activity receives pause event (e.g. Activity.onPause)
14
+ */
15
+ ACTIVITY_ENTERS_BACKGROUND,
16
+
17
+ /**
18
+ * Called when host activity receives destroy event (e.g. Activity.onDestroy)
19
+ */
20
+ ACTIVITY_DESTROYS,
21
+
22
+ /**
23
+ * Called when a new intent is passed to the activity
24
+ */
25
+ ON_NEW_INTENT,
26
+
27
+ /**
28
+ * Called when other activity returns
29
+ */
30
+ ON_ACTIVITY_RESULT
31
+ }
@@ -0,0 +1,3 @@
1
+ package expo.modules.kotlin.events
2
+
3
+ class EventsDefinition(val names: Array<out String>)