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.
- package/CHANGELOG.md +39 -0
- package/README.md +6 -6
- package/android/build.gradle +30 -2
- package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +27 -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/ApplicationLifecycleListener.java +10 -0
- 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 +14 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +70 -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} +91 -18
- 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/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.kt +0 -9
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.kt +0 -11
- package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.kt +0 -51
- 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,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,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
|
+
}
|