expo-modules-core 0.4.9 → 0.6.2
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.
- package/CHANGELOG.md +33 -2
- package/android/build.gradle +30 -2
- package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +42 -5
- package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +49 -5
- package/android/src/main/java/expo/modules/core/BasePackage.java +6 -0
- package/android/src/main/java/expo/modules/core/ModulePriorities.kt +25 -0
- package/android/src/main/java/expo/modules/core/interfaces/ActivityEventListener.java +3 -1
- package/android/src/main/java/expo/modules/core/interfaces/Package.java +4 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +21 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +23 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +14 -0
- package/android/src/main/java/expo/modules/core/utilities/KotlinUtilities.kt +23 -0
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +166 -0
- package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +9 -0
- package/android/src/main/java/expo/modules/kotlin/ExpoModulesHelper.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +24 -0
- package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +98 -0
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +41 -0
- package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +56 -0
- package/android/src/main/java/expo/modules/kotlin/ModulesProvider.kt +7 -0
- package/android/src/main/java/expo/modules/kotlin/Promise.kt +13 -0
- package/android/src/main/java/expo/modules/kotlin/ReactLifecycleDelegate.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/ReadableArrayIterator.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructor.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +31 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/UnsafeAllocator.kt +49 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventListener.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventName.kt +31 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +26 -0
- package/android/src/main/java/expo/modules/kotlin/events/OnActivityResultPayload.kt +8 -0
- package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +70 -0
- package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +50 -0
- package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +24 -0
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +227 -0
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +16 -0
- package/android/src/main/java/expo/modules/kotlin/records/Field.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/records/Record.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +55 -0
- package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +44 -0
- package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +60 -0
- package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +84 -0
- package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +25 -0
- package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +28 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +19 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +107 -0
- package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +10 -0
- package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +17 -0
- package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
- package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +21 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +41 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +40 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +21 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewWrapperDelegateHolder.kt +5 -0
- package/build/NativeModulesProxy.native.d.ts +4 -0
- package/build/NativeModulesProxy.native.js +14 -1
- package/build/NativeModulesProxy.native.js.map +1 -1
- package/build/NativeModulesProxy.types.d.ts +3 -0
- package/build/NativeModulesProxy.types.js.map +1 -1
- package/build/NativeViewManagerAdapter.native.js +1 -1
- package/build/NativeViewManagerAdapter.native.js.map +1 -1
- package/ios/AppDelegates/EXAppDelegateWrapper.h +19 -0
- package/ios/AppDelegates/EXAppDelegateWrapper.m +45 -0
- package/ios/AppDelegates/EXAppDelegatesLoader.h +15 -0
- package/ios/AppDelegates/EXAppDelegatesLoader.m +30 -0
- package/ios/AppDelegates/EXLegacyAppDelegateWrapper.h +16 -0
- package/ios/{EXAppDelegateWrapper.m → AppDelegates/EXLegacyAppDelegateWrapper.m} +2 -2
- package/ios/AppDelegates/ExpoAppDelegate.swift +282 -0
- package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +24 -0
- package/ios/EXAppDefines.h +26 -0
- package/ios/EXAppDefines.m +61 -0
- package/ios/ExpoModulesCore.podspec +8 -3
- package/ios/JSI/ExpoModulesProxySpec.h +24 -0
- package/ios/JSI/ExpoModulesProxySpec.mm +135 -0
- package/ios/JSI/JSIConversions.h +42 -0
- package/ios/JSI/JSIConversions.mm +164 -0
- package/ios/JSI/JSIInstaller.h +19 -0
- package/ios/JSI/JSIInstaller.mm +22 -0
- package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -6
- package/ios/NativeModulesProxy/EXNativeModulesProxy.h +6 -0
- package/ios/NativeModulesProxy/{EXNativeModulesProxy.m → EXNativeModulesProxy.mm} +63 -17
- package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h +16 -0
- package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.m +49 -0
- package/ios/ReactDelegates/EXReactDelegateWrapper+Private.h +18 -0
- package/ios/ReactDelegates/EXReactDelegateWrapper.h +25 -0
- package/ios/ReactDelegates/EXReactDelegateWrapper.m +40 -0
- package/ios/ReactDelegates/ExpoReactDelegate.swift +37 -0
- package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +52 -0
- package/ios/ReactDelegates/ModulePriorities.swift +20 -0
- package/ios/Services/EXReactNativeEventEmitter.h +6 -0
- package/ios/Services/EXReactNativeEventEmitter.m +15 -0
- package/ios/Swift/AppContext.swift +14 -1
- package/ios/Swift/Arguments/AnyArgument.swift +14 -0
- package/ios/Swift/Arguments/AnyArgumentType.swift +13 -0
- package/ios/Swift/Arguments/ArgumentType.swift +24 -0
- package/ios/Swift/Arguments/ConvertibleArgument.swift +15 -0
- package/ios/Swift/Arguments/Convertibles.swift +107 -0
- package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +42 -0
- package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +16 -0
- package/ios/Swift/Arguments/Types/EnumArgumentType.swift +105 -0
- package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +49 -0
- package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +15 -0
- package/ios/Swift/Arguments/Types/RawArgumentType.swift +25 -0
- package/ios/Swift/Conversions.swift +199 -7
- package/ios/Swift/EventListener.swift +37 -5
- package/ios/Swift/Functions/AnyFunction.swift +42 -0
- package/ios/Swift/{Methods/ConcreteMethod.swift → Functions/ConcreteFunction.swift} +32 -34
- package/ios/Swift/ModuleHolder.swift +86 -20
- package/ios/Swift/ModuleRegistry.swift +19 -8
- package/ios/Swift/Modules/AnyModule.swift +8 -8
- package/ios/Swift/Modules/Module.swift +11 -0
- package/ios/Swift/Modules/ModuleDefinition.swift +55 -15
- package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +1 -1
- package/ios/Swift/Modules/ModuleDefinitionComponents.swift +149 -54
- package/ios/Swift/ModulesProvider.swift +19 -0
- package/ios/Swift/Promise.swift +1 -1
- package/ios/Swift/Records/Field.swift +1 -1
- package/ios/Swift/Records/Record.swift +8 -1
- package/ios/Swift/SwiftInteropBridge.swift +46 -17
- package/ios/Swift/Views/AnyViewProp.swift +2 -2
- package/ios/Swift/Views/ConcreteViewProp.swift +37 -10
- package/ios/Swift/Views/ViewModuleWrapper.swift +9 -4
- package/ios/Swift.h +9 -0
- package/ios/Tests/ArgumentTypeSpec.swift +145 -0
- package/ios/Tests/ConstantsSpec.swift +36 -0
- package/ios/Tests/ConvertiblesSpec.swift +265 -0
- package/ios/Tests/EXAppDefinesTest.m +99 -0
- package/ios/Tests/{MethodSpec.swift → FunctionSpec.swift} +69 -54
- package/ios/Tests/FunctionWithConvertiblesSpec.swift +66 -0
- package/ios/Tests/Mocks/ModuleMocks.swift +21 -7
- package/ios/Tests/ModuleEventListenersSpec.swift +17 -16
- package/ios/Tests/ModuleRegistrySpec.swift +4 -7
- package/package.json +3 -3
- package/src/NativeModulesProxy.native.ts +22 -2
- package/src/NativeModulesProxy.types.ts +8 -0
- package/src/NativeViewManagerAdapter.native.tsx +1 -1
- package/ios/EXAppDelegateWrapper.h +0 -13
- package/ios/Swift/Methods/AnyArgumentType.swift +0 -48
- package/ios/Swift/Methods/AnyMethod.swift +0 -31
- package/ios/Swift/Methods/AnyMethodArgument.swift +0 -13
|
@@ -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,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,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,26 @@
|
|
|
1
|
+
package expo.modules.kotlin.events
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import expo.modules.core.interfaces.services.EventEmitter
|
|
5
|
+
import expo.modules.kotlin.ModuleHolder
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* In Swift, we check if the event is supported. Otherwise, we can't send such event through the bridge.
|
|
9
|
+
* However, in Kotlin this is unnecessary, but to make our API consistent, we added similar check.
|
|
10
|
+
* But because of that, we had to create a wrapper for EventEmitter.
|
|
11
|
+
*/
|
|
12
|
+
class KEventEmitterWrapper(
|
|
13
|
+
private val moduleHolder: ModuleHolder,
|
|
14
|
+
private val legacyEventEmitter: EventEmitter
|
|
15
|
+
) : EventEmitter by legacyEventEmitter {
|
|
16
|
+
override fun emit(eventName: String, eventBody: Bundle?) {
|
|
17
|
+
require(
|
|
18
|
+
moduleHolder
|
|
19
|
+
.definition
|
|
20
|
+
.eventsDefinition
|
|
21
|
+
?.names
|
|
22
|
+
?.contains(eventName) == true
|
|
23
|
+
) { "Unsupported event: $eventName." }
|
|
24
|
+
legacyEventEmitter.emit(eventName, eventBody)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package expo.modules.kotlin.exception
|
|
2
|
+
|
|
3
|
+
import java.util.*
|
|
4
|
+
import kotlin.reflect.KType
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A class for errors specifying its `code` and providing the `description`.
|
|
8
|
+
*/
|
|
9
|
+
open class CodedException(
|
|
10
|
+
message: String?,
|
|
11
|
+
cause: Throwable?
|
|
12
|
+
) : Exception(message, cause) {
|
|
13
|
+
// We need that secondary property, cause we can't access
|
|
14
|
+
// the javaClass property in the constructor.
|
|
15
|
+
private var providedCode: String? = null
|
|
16
|
+
|
|
17
|
+
val code
|
|
18
|
+
get() = providedCode ?: inferCode(javaClass)
|
|
19
|
+
|
|
20
|
+
constructor(code: String, message: String, cause: Throwable?) : this(message, cause) {
|
|
21
|
+
providedCode = code
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor(message: String) : this(message, null)
|
|
25
|
+
|
|
26
|
+
constructor(cause: Throwable) : this(cause.localizedMessage, cause)
|
|
27
|
+
|
|
28
|
+
constructor() : this(null, null)
|
|
29
|
+
|
|
30
|
+
companion object {
|
|
31
|
+
/**
|
|
32
|
+
* The code is inferred from the class name — e.g. the code of `ModuleNotFoundException` becomes `ERR_MODULE_NOT_FOUND`.
|
|
33
|
+
*/
|
|
34
|
+
private fun inferCode(clazz: Class<*>): String {
|
|
35
|
+
val name = requireNotNull(clazz.simpleName) { "Cannot infer code name from class name. We don't support anonymous classes." }
|
|
36
|
+
|
|
37
|
+
return "ERR_" + name
|
|
38
|
+
.replace("(Exception)$".toRegex(), "")
|
|
39
|
+
.replace("(.)([A-Z])".toRegex(), "$1_$2")
|
|
40
|
+
.toUpperCase(Locale.ROOT)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
internal class IncompatibleArgTypeException(
|
|
46
|
+
argumentType: KType,
|
|
47
|
+
desiredType: KType,
|
|
48
|
+
cause: Throwable? = null
|
|
49
|
+
) : CodedException(
|
|
50
|
+
message = "Argument type $argumentType is not compatible with expected type $desiredType.",
|
|
51
|
+
cause = cause
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
internal class MissingTypeConverter(
|
|
55
|
+
forType: KType
|
|
56
|
+
) : CodedException(
|
|
57
|
+
message = "Cannot find type converter for $forType.",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
internal class InvalidArgsNumberException(received: Int, expected: Int) :
|
|
61
|
+
CodedException(message = "Received $received arguments, but $expected was expected.")
|
|
62
|
+
|
|
63
|
+
internal class MethodNotFoundException(methodName: String, moduleName: String) :
|
|
64
|
+
CodedException(message = "Cannot fund method $methodName in module $moduleName")
|
|
65
|
+
|
|
66
|
+
internal class NullArgumentException(desiredType: KType) :
|
|
67
|
+
CodedException(message = "Cannot assigned null to not nullable type $desiredType")
|
|
68
|
+
|
|
69
|
+
internal class UnexpectedException(val throwable: Throwable) :
|
|
70
|
+
CodedException(throwable)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
package expo.modules.kotlin.methods
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableArray
|
|
4
|
+
import expo.modules.kotlin.Promise
|
|
5
|
+
import expo.modules.kotlin.exception.CodedException
|
|
6
|
+
import expo.modules.kotlin.exception.InvalidArgsNumberException
|
|
7
|
+
import expo.modules.kotlin.exception.UnexpectedException
|
|
8
|
+
import expo.modules.kotlin.iterator
|
|
9
|
+
import expo.modules.kotlin.recycle
|
|
10
|
+
import expo.modules.kotlin.types.AnyType
|
|
11
|
+
import kotlin.jvm.Throws
|
|
12
|
+
|
|
13
|
+
abstract class AnyMethod(
|
|
14
|
+
protected val name: String,
|
|
15
|
+
private val desiredArgsTypes: Array<AnyType>
|
|
16
|
+
) {
|
|
17
|
+
fun call(args: ReadableArray, promise: Promise) {
|
|
18
|
+
if (desiredArgsTypes.size != args.size()) {
|
|
19
|
+
promise.reject(InvalidArgsNumberException(args.size(), desiredArgsTypes.size))
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
val convertedArgs = convertArgs(args)
|
|
24
|
+
callImplementation(convertedArgs, promise)
|
|
25
|
+
} catch (codedError: CodedException) {
|
|
26
|
+
promise.reject(codedError)
|
|
27
|
+
} catch (e: Throwable) {
|
|
28
|
+
promise.reject(UnexpectedException(e))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Throws(CodedException::class)
|
|
33
|
+
internal abstract fun callImplementation(args: Array<out Any?>, promise: Promise)
|
|
34
|
+
|
|
35
|
+
val argsCount get() = desiredArgsTypes.size
|
|
36
|
+
|
|
37
|
+
@Throws(CodedException::class)
|
|
38
|
+
private fun convertArgs(args: ReadableArray): Array<out Any?> {
|
|
39
|
+
val finalArgs = Array<Any?>(desiredArgsTypes.size) { null }
|
|
40
|
+
val argIterator = args.iterator()
|
|
41
|
+
desiredArgsTypes
|
|
42
|
+
.withIndex()
|
|
43
|
+
.forEach { (index, type) ->
|
|
44
|
+
argIterator.next().recycle {
|
|
45
|
+
finalArgs[index] = type.convert(this)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return finalArgs
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package expo.modules.kotlin.methods
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.Promise
|
|
4
|
+
import expo.modules.kotlin.types.AnyType
|
|
5
|
+
|
|
6
|
+
class Method(
|
|
7
|
+
name: String,
|
|
8
|
+
argsType: Array<AnyType>,
|
|
9
|
+
private val body: (args: Array<out Any?>) -> Any?
|
|
10
|
+
) : AnyMethod(name, argsType) {
|
|
11
|
+
override fun callImplementation(args: Array<out Any?>, promise: Promise) {
|
|
12
|
+
promise.resolve(body(args))
|
|
13
|
+
}
|
|
14
|
+
}
|