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,15 @@
|
|
|
1
|
+
package expo.modules.kotlin.methods
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.Promise
|
|
4
|
+
import expo.modules.kotlin.types.AnyType
|
|
5
|
+
|
|
6
|
+
class PromiseMethod(
|
|
7
|
+
name: String,
|
|
8
|
+
argsType: Array<AnyType>,
|
|
9
|
+
private val body: (args: Array<out Any?>, promise: Promise) -> Unit
|
|
10
|
+
) : AnyMethod(name, argsType) {
|
|
11
|
+
|
|
12
|
+
override fun callImplementation(args: Array<out Any?>, promise: Promise) {
|
|
13
|
+
body(args, promise)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package expo.modules.kotlin.modules
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import expo.modules.kotlin.AppContext
|
|
5
|
+
|
|
6
|
+
abstract class Module {
|
|
7
|
+
internal var _appContext: AppContext? = null
|
|
8
|
+
|
|
9
|
+
private val moduleEventEmitter by lazy { appContext.eventEmitter(this) }
|
|
10
|
+
|
|
11
|
+
val appContext: AppContext
|
|
12
|
+
get() = requireNotNull(_appContext) { "The module wasn't created! You can't access the app context." }
|
|
13
|
+
|
|
14
|
+
fun sendEvent(name: String, body: Bundle?) {
|
|
15
|
+
moduleEventEmitter?.emit(name, body)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
abstract fun definition(): ModuleDefinitionData
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Suppress("FunctionName")
|
|
22
|
+
inline fun ModuleDefinition(block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
|
|
23
|
+
return ModuleDefinitionBuilder().also(block).build()
|
|
24
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* We used a function from the experimental STD API - typeOf (see kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/type-of.html).
|
|
3
|
+
* We shouldn't have any problem with that function, cause it's widely used in other libraries created by JetBrains like kotlinx-serializer.
|
|
4
|
+
* This function is super handy if we want to receive a collection type.
|
|
5
|
+
* For example, it's very hard to obtain the generic parameter type from the list class.
|
|
6
|
+
* In plain Java, it's almost impossible. There is a trick to getting such information using something called TypeToken.
|
|
7
|
+
* For instance, the Gson library uses this workaround. But there still will be a problem with nullability.
|
|
8
|
+
* We didn't find a good solution to distinguish between List<Any?> and List<Any>.
|
|
9
|
+
* Mainly because from the JVM perspective it's the same type.
|
|
10
|
+
* That's why we used typeOf. It solves all problems described above.
|
|
11
|
+
*/
|
|
12
|
+
@file:OptIn(ExperimentalStdlibApi::class)
|
|
13
|
+
|
|
14
|
+
package expo.modules.kotlin.modules
|
|
15
|
+
|
|
16
|
+
import android.app.Activity
|
|
17
|
+
import android.content.Intent
|
|
18
|
+
import expo.modules.kotlin.Promise
|
|
19
|
+
import expo.modules.kotlin.events.BasicEventListener
|
|
20
|
+
import expo.modules.kotlin.events.EventListener
|
|
21
|
+
import expo.modules.kotlin.events.EventListenerWithPayload
|
|
22
|
+
import expo.modules.kotlin.events.EventListenerWithSenderAndPayload
|
|
23
|
+
import expo.modules.kotlin.events.EventName
|
|
24
|
+
import expo.modules.kotlin.events.EventsDefinition
|
|
25
|
+
import expo.modules.kotlin.events.OnActivityResultPayload
|
|
26
|
+
import expo.modules.kotlin.methods.AnyMethod
|
|
27
|
+
import expo.modules.kotlin.methods.Method
|
|
28
|
+
import expo.modules.kotlin.methods.PromiseMethod
|
|
29
|
+
import expo.modules.kotlin.types.toAnyType
|
|
30
|
+
import expo.modules.kotlin.views.ViewManagerDefinition
|
|
31
|
+
import expo.modules.kotlin.views.ViewManagerDefinitionBuilder
|
|
32
|
+
import kotlin.reflect.typeOf
|
|
33
|
+
|
|
34
|
+
class ModuleDefinitionBuilder {
|
|
35
|
+
private var name: String? = null
|
|
36
|
+
private var constantsProvider = { emptyMap<String, Any?>() }
|
|
37
|
+
private var eventsDefinition: EventsDefinition? = null
|
|
38
|
+
|
|
39
|
+
@PublishedApi
|
|
40
|
+
internal var methods = mutableMapOf<String, AnyMethod>()
|
|
41
|
+
|
|
42
|
+
@PublishedApi
|
|
43
|
+
internal var viewManagerDefinition: ViewManagerDefinition? = null
|
|
44
|
+
|
|
45
|
+
@PublishedApi
|
|
46
|
+
internal val eventListeners = mutableMapOf<EventName, EventListener>()
|
|
47
|
+
|
|
48
|
+
fun build(): ModuleDefinitionData {
|
|
49
|
+
return ModuleDefinitionData(
|
|
50
|
+
requireNotNull(name),
|
|
51
|
+
constantsProvider,
|
|
52
|
+
methods,
|
|
53
|
+
viewManagerDefinition,
|
|
54
|
+
eventListeners,
|
|
55
|
+
eventsDefinition
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fun name(name: String) {
|
|
60
|
+
this.name = name
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fun constants(constantsProvider: () -> Map<String, Any?>) {
|
|
64
|
+
this.constantsProvider = constantsProvider
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@JvmName("methodWithoutArgs")
|
|
68
|
+
inline fun function(
|
|
69
|
+
name: String,
|
|
70
|
+
crossinline body: () -> Any?
|
|
71
|
+
) {
|
|
72
|
+
methods[name] = Method(name, arrayOf()) { body() }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
inline fun <reified R> function(
|
|
76
|
+
name: String,
|
|
77
|
+
crossinline body: () -> R
|
|
78
|
+
) {
|
|
79
|
+
methods[name] = Method(name, arrayOf()) { body() }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
inline fun <reified R, reified P0> function(
|
|
83
|
+
name: String,
|
|
84
|
+
crossinline body: (p0: P0) -> R
|
|
85
|
+
) {
|
|
86
|
+
methods[name] = if (P0::class == Promise::class) {
|
|
87
|
+
PromiseMethod(name, arrayOf()) { _, promise -> body(promise as P0) }
|
|
88
|
+
} else {
|
|
89
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType())) { body(it[0] as P0) }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
inline fun <reified R, reified P0, reified P1> function(
|
|
94
|
+
name: String,
|
|
95
|
+
crossinline body: (p0: P0, p1: P1) -> R
|
|
96
|
+
) {
|
|
97
|
+
methods[name] = if (P1::class == Promise::class) {
|
|
98
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType())) { args, promise -> body(args[0] as P0, promise as P1) }
|
|
99
|
+
} else {
|
|
100
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { body(it[0] as P0, it[1] as P1) }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
inline fun <reified R, reified P0, reified P1, reified P2> function(
|
|
105
|
+
name: String,
|
|
106
|
+
crossinline body: (p0: P0, p1: P1, p2: P2) -> R
|
|
107
|
+
) {
|
|
108
|
+
methods[name] = if (P2::class == Promise::class) {
|
|
109
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, promise as P2) }
|
|
110
|
+
} else {
|
|
111
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2) }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
inline fun <reified R, reified P0, reified P1, reified P2, reified P3> function(
|
|
116
|
+
name: String,
|
|
117
|
+
crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R
|
|
118
|
+
) {
|
|
119
|
+
methods[name] = if (P3::class == Promise::class) {
|
|
120
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, promise as P3) }
|
|
121
|
+
} else {
|
|
122
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> function(
|
|
127
|
+
name: String,
|
|
128
|
+
crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R
|
|
129
|
+
) {
|
|
130
|
+
methods[name] = if (P4::class == Promise::class) {
|
|
131
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, promise as P4) }
|
|
132
|
+
} else {
|
|
133
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> function(
|
|
138
|
+
name: String,
|
|
139
|
+
crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R
|
|
140
|
+
) {
|
|
141
|
+
methods[name] = if (P5::class == Promise::class) {
|
|
142
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, promise as P5) }
|
|
143
|
+
} else {
|
|
144
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> function(
|
|
149
|
+
name: String,
|
|
150
|
+
crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R
|
|
151
|
+
) {
|
|
152
|
+
methods[name] = if (P6::class == Promise::class) {
|
|
153
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, promise as P6) }
|
|
154
|
+
} else {
|
|
155
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> function(
|
|
160
|
+
name: String,
|
|
161
|
+
crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R
|
|
162
|
+
) {
|
|
163
|
+
methods[name] = if (P7::class == Promise::class) {
|
|
164
|
+
PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, args[6] as P6, promise as P7) }
|
|
165
|
+
} else {
|
|
166
|
+
Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType(), typeOf<P7>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
inline fun viewManager(body: ViewManagerDefinitionBuilder.() -> Unit) {
|
|
171
|
+
require(viewManagerDefinition == null) { "The module definition may have exported only one view manager." }
|
|
172
|
+
|
|
173
|
+
val viewManagerDefinitionBuilder = ViewManagerDefinitionBuilder()
|
|
174
|
+
body.invoke(viewManagerDefinitionBuilder)
|
|
175
|
+
viewManagerDefinition = viewManagerDefinitionBuilder.build()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
inline fun onCreate(crossinline body: () -> Unit) {
|
|
179
|
+
eventListeners[EventName.MODULE_CREATE] = BasicEventListener(EventName.MODULE_CREATE) { body() }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
inline fun onDestroy(crossinline body: () -> Unit) {
|
|
183
|
+
eventListeners[EventName.MODULE_DESTROY] = BasicEventListener(EventName.MODULE_DESTROY) { body() }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
inline fun onActivityEntersForeground(crossinline body: () -> Unit) {
|
|
187
|
+
eventListeners[EventName.ACTIVITY_ENTERS_FOREGROUND] = BasicEventListener(EventName.ACTIVITY_ENTERS_FOREGROUND) { body() }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
inline fun onActivityEntersBackground(crossinline body: () -> Unit) {
|
|
191
|
+
eventListeners[EventName.ACTIVITY_ENTERS_BACKGROUND] = BasicEventListener(EventName.ACTIVITY_ENTERS_BACKGROUND) { body() }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
inline fun onActivityDestroys(crossinline body: () -> Unit) {
|
|
195
|
+
eventListeners[EventName.ACTIVITY_DESTROYS] = BasicEventListener(EventName.ACTIVITY_DESTROYS) { body() }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Defines event names that this module can send to JavaScript.
|
|
200
|
+
*/
|
|
201
|
+
fun events(vararg events: String) {
|
|
202
|
+
eventsDefinition = EventsDefinition(events)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Method that is invoked when the first event listener is added.
|
|
207
|
+
*/
|
|
208
|
+
inline fun onStartObserving(crossinline body: () -> Unit) {
|
|
209
|
+
function("startObserving", body)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Method that is invoked when all event listeners are removed.
|
|
214
|
+
*/
|
|
215
|
+
inline fun onStopObserving(crossinline body: () -> Unit) {
|
|
216
|
+
function("stopObserving", body)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
inline fun onNewIntent(crossinline body: (Intent) -> Unit) {
|
|
220
|
+
eventListeners[EventName.ON_NEW_INTENT] = EventListenerWithPayload<Intent>(EventName.ON_NEW_INTENT) { body(it) }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
inline fun onActivityResult(crossinline body: (Activity, OnActivityResultPayload) -> Unit) {
|
|
224
|
+
eventListeners[EventName.ON_ACTIVITY_RESULT] =
|
|
225
|
+
EventListenerWithSenderAndPayload<Activity, OnActivityResultPayload>(EventName.ON_ACTIVITY_RESULT) { sender, payload -> body(sender, payload) }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package expo.modules.kotlin.modules
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.events.EventListener
|
|
4
|
+
import expo.modules.kotlin.events.EventName
|
|
5
|
+
import expo.modules.kotlin.events.EventsDefinition
|
|
6
|
+
import expo.modules.kotlin.methods.AnyMethod
|
|
7
|
+
import expo.modules.kotlin.views.ViewManagerDefinition
|
|
8
|
+
|
|
9
|
+
class ModuleDefinitionData(
|
|
10
|
+
val name: String,
|
|
11
|
+
val constantsProvider: () -> Map<String, Any?>,
|
|
12
|
+
val methods: Map<String, AnyMethod>,
|
|
13
|
+
val viewManagerDefinition: ViewManagerDefinition? = null,
|
|
14
|
+
val eventListeners: Map<EventName, EventListener> = emptyMap(),
|
|
15
|
+
val eventsDefinition: EventsDefinition? = null
|
|
16
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package expo.modules.kotlin.records
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import expo.modules.kotlin.allocators.ObjectConstructor
|
|
5
|
+
import expo.modules.kotlin.allocators.ObjectConstructorFactory
|
|
6
|
+
import expo.modules.kotlin.types.TypeConverter
|
|
7
|
+
import expo.modules.kotlin.types.TypeConverterProvider
|
|
8
|
+
import kotlin.reflect.KClass
|
|
9
|
+
import kotlin.reflect.KType
|
|
10
|
+
import kotlin.reflect.full.findAnnotation
|
|
11
|
+
import kotlin.reflect.full.memberProperties
|
|
12
|
+
import kotlin.reflect.jvm.javaField
|
|
13
|
+
|
|
14
|
+
// TODO(@lukmccall): create all converters during initialization
|
|
15
|
+
class RecordTypeConverter<T : Record>(
|
|
16
|
+
private val converterProvider: TypeConverterProvider,
|
|
17
|
+
val type: KType,
|
|
18
|
+
) : TypeConverter<T>(type.isMarkedNullable) {
|
|
19
|
+
private val objectConstructorFactory = ObjectConstructorFactory()
|
|
20
|
+
|
|
21
|
+
override fun convertNonOptional(value: Dynamic): T {
|
|
22
|
+
val jsMap = value.asMap()
|
|
23
|
+
|
|
24
|
+
val kClass = type.classifier as KClass<*>
|
|
25
|
+
val instance = getObjectConstructor(kClass.java).construct()
|
|
26
|
+
|
|
27
|
+
kClass
|
|
28
|
+
.memberProperties
|
|
29
|
+
.map { property ->
|
|
30
|
+
val filedInformation = property.findAnnotation<Field>() ?: return@map
|
|
31
|
+
val jsKey = filedInformation.key.takeUnless { it == "" } ?: property.name
|
|
32
|
+
|
|
33
|
+
if (!jsMap.hasKey(jsKey)) {
|
|
34
|
+
// TODO(@lukmccall): handle required keys
|
|
35
|
+
return@map
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
val value = jsMap.getDynamic(jsKey)
|
|
39
|
+
val javaField = property.javaField!!
|
|
40
|
+
|
|
41
|
+
val elementConverter = converterProvider.obtainTypeConverter(property.returnType)
|
|
42
|
+
val casted = elementConverter.convert(value)
|
|
43
|
+
|
|
44
|
+
javaField.isAccessible = true
|
|
45
|
+
javaField.set(instance, casted)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Suppress("UNCHECKED_CAST")
|
|
49
|
+
return instance as T
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private fun <T> getObjectConstructor(clazz: Class<T>): ObjectConstructor<T> {
|
|
53
|
+
return objectConstructorFactory.get(clazz)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import kotlin.reflect.KType
|
|
5
|
+
|
|
6
|
+
fun KType.toAnyType(): AnyType = AnyType(this)
|
|
7
|
+
|
|
8
|
+
class AnyType(val kType: KType) {
|
|
9
|
+
private val converter: TypeConverter<*> by lazy {
|
|
10
|
+
TypeConverterProviderImpl.obtainTypeConverter(kType)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
fun convert(value: Dynamic): Any? = converter.convert(value)
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import expo.modules.kotlin.recycle
|
|
5
|
+
import kotlin.reflect.KClass
|
|
6
|
+
import kotlin.reflect.KType
|
|
7
|
+
|
|
8
|
+
class ArrayTypeConverter(
|
|
9
|
+
converterProvider: TypeConverterProvider,
|
|
10
|
+
private val type: KType,
|
|
11
|
+
) : TypeConverter<Array<*>>(type.isMarkedNullable) {
|
|
12
|
+
private val arrayElementConverter = converterProvider.obtainTypeConverter(
|
|
13
|
+
requireNotNull(type.arguments.first().type) {
|
|
14
|
+
"The array type should contain the type of the elements."
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
override fun convertNonOptional(value: Dynamic): Array<*> {
|
|
19
|
+
val jsArray = value.asArray()
|
|
20
|
+
val array = createTypedArray(jsArray.size())
|
|
21
|
+
for (i in 0 until jsArray.size()) {
|
|
22
|
+
array[i] = jsArray
|
|
23
|
+
.getDynamic(i)
|
|
24
|
+
.recycle {
|
|
25
|
+
arrayElementConverter.convert(this)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return array
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* We can't use a Array<Any?> here. We have to create a typed array.
|
|
33
|
+
* Otherwise, cast which is done before calling lambda provided by the user will always fail.
|
|
34
|
+
* For JVM, Array<String> is a different type than Array<Any?>.
|
|
35
|
+
* The first one is translated to `[Ljava.lang.String;` but the second one is translated to `[java.lang.Object;`.
|
|
36
|
+
*/
|
|
37
|
+
@Suppress("UNCHECKED_CAST")
|
|
38
|
+
private fun createTypedArray(size: Int): Array<Any?> {
|
|
39
|
+
return java.lang.reflect.Array.newInstance(
|
|
40
|
+
(type.arguments.first().type!!.classifier as KClass<*>).java,
|
|
41
|
+
size
|
|
42
|
+
) as Array<Any?>
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
|
|
7
|
+
class IntTypeConverter(isOptional: Boolean) : TypeConverter<Int>(isOptional) {
|
|
8
|
+
override fun convertNonOptional(value: Dynamic): Int = value.asInt()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class DoubleTypeConverter(isOptional: Boolean) : TypeConverter<Double>(isOptional) {
|
|
12
|
+
override fun convertNonOptional(value: Dynamic): Double = value.asDouble()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class FloatTypeConverter(isOptional: Boolean) : TypeConverter<Float>(isOptional) {
|
|
16
|
+
override fun convertNonOptional(value: Dynamic): Float = value.asDouble().toFloat()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class BoolTypeConverter(isOptional: Boolean) : TypeConverter<Boolean>(isOptional) {
|
|
20
|
+
override fun convertNonOptional(value: Dynamic): Boolean = value.asBoolean()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class StringTypeConverter(isOptional: Boolean) : TypeConverter<String>(isOptional) {
|
|
24
|
+
override fun convertNonOptional(value: Dynamic): String = value.asString()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class ReadableArrayTypeConverter(isOptional: Boolean) : TypeConverter<ReadableArray>(isOptional) {
|
|
28
|
+
override fun convertNonOptional(value: Dynamic): ReadableArray = value.asArray()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class ReadableMapTypeConverter(isOptional: Boolean) : TypeConverter<ReadableMap>(isOptional) {
|
|
32
|
+
override fun convertNonOptional(value: Dynamic): ReadableMap = value.asMap()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class PrimitiveIntArrayTypeConverter(isOptional: Boolean) : TypeConverter<IntArray>(isOptional) {
|
|
36
|
+
override fun convertNonOptional(value: Dynamic): IntArray {
|
|
37
|
+
val jsArray = value.asArray()
|
|
38
|
+
return IntArray(jsArray.size()) { index ->
|
|
39
|
+
jsArray.getInt(index)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class PrimitiveDoubleArrayTypeConverter(isOptional: Boolean) : TypeConverter<DoubleArray>(isOptional) {
|
|
45
|
+
override fun convertNonOptional(value: Dynamic): DoubleArray {
|
|
46
|
+
val jsArray = value.asArray()
|
|
47
|
+
return DoubleArray(jsArray.size()) { index ->
|
|
48
|
+
jsArray.getDouble(index)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class PrimitiveFloatArrayTypeConverter(isOptional: Boolean) : TypeConverter<FloatArray>(isOptional) {
|
|
54
|
+
override fun convertNonOptional(value: Dynamic): FloatArray {
|
|
55
|
+
val jsArray = value.asArray()
|
|
56
|
+
return FloatArray(jsArray.size()) { index ->
|
|
57
|
+
jsArray.getDouble(index).toFloat()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import expo.modules.kotlin.exception.IncompatibleArgTypeException
|
|
5
|
+
import expo.modules.kotlin.toKType
|
|
6
|
+
import kotlin.reflect.KClass
|
|
7
|
+
import kotlin.reflect.full.createType
|
|
8
|
+
import kotlin.reflect.full.declaredMemberProperties
|
|
9
|
+
import kotlin.reflect.full.primaryConstructor
|
|
10
|
+
|
|
11
|
+
class EnumTypeConverter(
|
|
12
|
+
private val enumClass: KClass<Enum<*>>,
|
|
13
|
+
isOptional: Boolean
|
|
14
|
+
) : TypeConverter<Enum<*>>(isOptional) {
|
|
15
|
+
override fun convertNonOptional(value: Dynamic): Enum<*> {
|
|
16
|
+
@Suppress("UNCHECKED_CAST")
|
|
17
|
+
val enumConstants = requireNotNull(enumClass.java.enumConstants) {
|
|
18
|
+
"Passed type is not an enum type."
|
|
19
|
+
}
|
|
20
|
+
require(enumConstants.isNotEmpty()) {
|
|
21
|
+
"Passed enum type is empty."
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
val primaryConstructor = requireNotNull(enumClass.primaryConstructor) {
|
|
25
|
+
"Cannot convert js value to enum without the primary constructor."
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (primaryConstructor.parameters.isEmpty()) {
|
|
29
|
+
return convertEnumWithoutParameter(value, enumConstants)
|
|
30
|
+
} else if (primaryConstructor.parameters.size == 1) {
|
|
31
|
+
return convertEnumWithParameter(
|
|
32
|
+
value,
|
|
33
|
+
enumConstants,
|
|
34
|
+
primaryConstructor.parameters.first().name!!
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
throw IncompatibleArgTypeException(value.type.toKType(), enumClass.createType())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* If the primary constructor doesn't take any parameters, we treat the name of each enum as a value.
|
|
43
|
+
* So the jsValue has to contain string.
|
|
44
|
+
*/
|
|
45
|
+
private fun convertEnumWithoutParameter(
|
|
46
|
+
jsValue: Dynamic,
|
|
47
|
+
enumConstants: Array<out Enum<*>>
|
|
48
|
+
): Enum<*> {
|
|
49
|
+
val unwrappedJsValue = jsValue.asString()
|
|
50
|
+
return requireNotNull(
|
|
51
|
+
enumConstants.find { it.name == unwrappedJsValue }
|
|
52
|
+
) { "Couldn't convert ${jsValue.asString()} to ${enumClass.simpleName}." }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* If the primary constructor take one parameter, we treat this parameter as a enum value.
|
|
57
|
+
* In that case, we handles two different types: Int and String.
|
|
58
|
+
*/
|
|
59
|
+
private fun convertEnumWithParameter(
|
|
60
|
+
jsValue: Dynamic,
|
|
61
|
+
enumConstants: Array<out Enum<*>>,
|
|
62
|
+
parameterName: String
|
|
63
|
+
): Enum<*> {
|
|
64
|
+
// To obtain the value of parameter, we have to find a property that is connected with this parameter.
|
|
65
|
+
@Suppress("UNCHECKED_CAST")
|
|
66
|
+
val parameterProperty = enumClass
|
|
67
|
+
.declaredMemberProperties
|
|
68
|
+
.find { it.name == parameterName }
|
|
69
|
+
requireNotNull(parameterProperty) { "Cannot find a property for $parameterName parameter." }
|
|
70
|
+
|
|
71
|
+
val parameterType = parameterProperty.returnType.classifier
|
|
72
|
+
val jsUnwrapValue = if (parameterType == String::class) {
|
|
73
|
+
jsValue.asString()
|
|
74
|
+
} else {
|
|
75
|
+
jsValue.asInt()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return requireNotNull(
|
|
79
|
+
enumConstants.find {
|
|
80
|
+
parameterProperty.get(it) == jsUnwrapValue
|
|
81
|
+
}
|
|
82
|
+
) { "Couldn't convert ${jsValue.asString()} to ${enumClass.simpleName} where $parameterName is the enum parameter. " }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import expo.modules.kotlin.recycle
|
|
5
|
+
import kotlin.reflect.KType
|
|
6
|
+
|
|
7
|
+
class ListTypeConverter(
|
|
8
|
+
converterProvider: TypeConverterProvider,
|
|
9
|
+
type: KType,
|
|
10
|
+
) : TypeConverter<List<*>>(type.isMarkedNullable) {
|
|
11
|
+
private val elementConverter = converterProvider.obtainTypeConverter(
|
|
12
|
+
requireNotNull(type.arguments.first().type) {
|
|
13
|
+
"The list type should contain the type of elements."
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
override fun convertNonOptional(value: Dynamic): List<*> {
|
|
18
|
+
val jsArray = value.asArray()
|
|
19
|
+
return List(jsArray.size()) { index ->
|
|
20
|
+
jsArray.getDynamic(index).recycle {
|
|
21
|
+
elementConverter.convert(this)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@file:OptIn(ExperimentalStdlibApi::class)
|
|
2
|
+
|
|
3
|
+
package expo.modules.kotlin.types
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.bridge.Dynamic
|
|
6
|
+
import com.facebook.react.bridge.DynamicFromObject
|
|
7
|
+
import expo.modules.kotlin.recycle
|
|
8
|
+
import kotlin.reflect.KType
|
|
9
|
+
import kotlin.reflect.typeOf
|
|
10
|
+
|
|
11
|
+
class MapTypeConverter(
|
|
12
|
+
converterProvider: TypeConverterProvider,
|
|
13
|
+
type: KType
|
|
14
|
+
) : TypeConverter<Map<*, *>>(type.isMarkedNullable) {
|
|
15
|
+
init {
|
|
16
|
+
require(type.arguments.first().type == typeOf<String>()) {
|
|
17
|
+
"The map key type should be String, but received ${type.arguments.first()}."
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private val valueConverter = converterProvider.obtainTypeConverter(
|
|
22
|
+
requireNotNull(type.arguments.getOrNull(1)?.type) {
|
|
23
|
+
"The map type should contain the key type."
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
override fun convertNonOptional(value: Dynamic): Map<*, *> {
|
|
28
|
+
val jsMap = value.asMap()
|
|
29
|
+
val result = mutableMapOf<String, Any?>()
|
|
30
|
+
|
|
31
|
+
jsMap.entryIterator.forEach { (key, value) ->
|
|
32
|
+
DynamicFromObject(value).recycle {
|
|
33
|
+
result[key] = valueConverter.convert(this)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import kotlin.reflect.KType
|
|
5
|
+
|
|
6
|
+
class PairTypeConverter(
|
|
7
|
+
converterProvider: TypeConverterProvider,
|
|
8
|
+
type: KType,
|
|
9
|
+
) : TypeConverter<Pair<*, *>>(type.isMarkedNullable) {
|
|
10
|
+
private val firstConverter = converterProvider.obtainTypeConverter(
|
|
11
|
+
requireNotNull(type.arguments.getOrNull(0)?.type) {
|
|
12
|
+
"The pair type should contain the type of the first parameter."
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
private val secondConverter = converterProvider.obtainTypeConverter(
|
|
16
|
+
requireNotNull(type.arguments.getOrNull(1)?.type) {
|
|
17
|
+
"The pair type should contain the type of the second parameter."
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
override fun convertNonOptional(value: Dynamic): Pair<*, *> {
|
|
22
|
+
val jsArray = value.asArray()
|
|
23
|
+
return Pair(
|
|
24
|
+
firstConverter.convert(jsArray.getDynamic(0)),
|
|
25
|
+
secondConverter.convert(jsArray.getDynamic(1)),
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package expo.modules.kotlin.types
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
|
|
5
|
+
abstract class TypeConverter<Type : Any>(
|
|
6
|
+
private val isOptional: Boolean
|
|
7
|
+
) {
|
|
8
|
+
open fun convert(value: Dynamic): Type? {
|
|
9
|
+
if (value.isNull) {
|
|
10
|
+
if (isOptional) {
|
|
11
|
+
return null
|
|
12
|
+
}
|
|
13
|
+
throw IllegalArgumentException()
|
|
14
|
+
}
|
|
15
|
+
return convertNonOptional(value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
abstract fun convertNonOptional(value: Dynamic): Type
|
|
19
|
+
}
|