expo-modules-core 0.9.2 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/CHANGELOG.md +33 -3
  2. package/README.md +3 -3
  3. package/android/CMakeLists.txt +163 -0
  4. package/android/build.gradle +343 -5
  5. package/android/src/main/cpp/CachedReferencesRegistry.cpp +65 -0
  6. package/android/src/main/cpp/CachedReferencesRegistry.h +80 -0
  7. package/android/src/main/cpp/Exceptions.cpp +22 -0
  8. package/android/src/main/cpp/Exceptions.h +38 -0
  9. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  10. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  11. package/android/src/main/cpp/JNIFunctionBody.cpp +44 -0
  12. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  13. package/android/src/main/cpp/JNIInjector.cpp +23 -0
  14. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  15. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  16. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  17. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  18. package/android/src/main/cpp/JavaScriptModuleObject.cpp +219 -0
  19. package/android/src/main/cpp/JavaScriptModuleObject.h +144 -0
  20. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  21. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  22. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  23. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  24. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  25. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  26. package/android/src/main/cpp/MethodMetadata.cpp +340 -0
  27. package/android/src/main/cpp/MethodMetadata.h +131 -0
  28. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  29. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  30. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  31. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +98 -3
  32. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  33. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  34. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +45 -3
  35. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAware.kt +49 -0
  36. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt +43 -0
  37. package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt +18 -0
  38. package/android/src/main/java/expo/modules/kotlin/activityresult/ActivityResultsManager.kt +99 -0
  39. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +25 -0
  40. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt +27 -0
  41. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultFallbackCallback.kt +17 -0
  42. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt +30 -0
  43. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +358 -0
  44. package/android/src/main/java/expo/modules/kotlin/activityresult/DataPersistor.kt +135 -0
  45. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  46. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  47. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  48. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +53 -15
  49. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +35 -7
  50. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -121
  51. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +21 -0
  52. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +21 -0
  53. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +63 -0
  54. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +36 -0
  55. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +19 -0
  56. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  57. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  58. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +46 -0
  59. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  60. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  61. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +16 -6
  62. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +5 -500
  63. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +30 -5
  64. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +271 -0
  65. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +21 -0
  66. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponent.kt +54 -0
  67. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponentBuilder.kt +32 -0
  68. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  69. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  70. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  71. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  72. package/android/src/main/java/expo/modules/kotlin/types/AnyTypeConverter.kt +36 -0
  73. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  74. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  75. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  76. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  77. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  78. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  79. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  80. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +12 -0
  81. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -41
  82. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -33
  83. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  84. package/build/NativeModulesProxy.native.js +9 -3
  85. package/build/NativeModulesProxy.native.js.map +1 -1
  86. package/build/PermissionsInterface.d.ts +29 -0
  87. package/build/PermissionsInterface.d.ts.map +1 -1
  88. package/build/PermissionsInterface.js +9 -0
  89. package/build/PermissionsInterface.js.map +1 -1
  90. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  91. package/ios/ExpoModulesCore.podspec +3 -2
  92. package/ios/JSI/EXJSIConversions.mm +6 -0
  93. package/ios/JSI/EXJSIInstaller.h +15 -21
  94. package/ios/JSI/EXJSIInstaller.mm +39 -3
  95. package/ios/JSI/EXJSIUtils.h +48 -3
  96. package/ios/JSI/EXJSIUtils.mm +88 -4
  97. package/ios/JSI/EXJavaScriptObject.h +11 -18
  98. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  99. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  100. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  101. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  102. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  103. package/ios/JSI/EXJavaScriptValue.h +3 -2
  104. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  105. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  106. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  107. package/ios/JSI/EXObjectDeallocator.h +27 -0
  108. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  109. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  110. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  111. package/ios/JSI/JavaScriptValue.swift +7 -0
  112. package/ios/JSI/TypedArray.cpp +67 -0
  113. package/ios/JSI/TypedArray.h +46 -0
  114. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  115. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  116. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +86 -77
  117. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  118. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  119. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  120. package/ios/Swift/AppContext.swift +206 -28
  121. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  122. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  123. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  124. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  125. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  126. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  127. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  128. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  129. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  130. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  131. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  132. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  133. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  134. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  135. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  136. package/ios/Swift/Exceptions/ChainableException.swift +3 -3
  137. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  138. package/ios/Swift/Exceptions/Exception.swift +8 -6
  139. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  140. package/ios/Swift/ExpoBridgeModule.m +5 -0
  141. package/ios/Swift/ExpoBridgeModule.swift +79 -0
  142. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  143. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  144. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  145. package/ios/Swift/JavaScriptUtils.swift +32 -57
  146. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  147. package/ios/Swift/Logging/LogType.swift +62 -0
  148. package/ios/Swift/Logging/Logger.swift +201 -0
  149. package/ios/Swift/ModuleHolder.swift +19 -54
  150. package/ios/Swift/ModuleRegistry.swift +7 -1
  151. package/ios/Swift/Modules/AnyModule.swift +3 -3
  152. package/ios/Swift/ModulesProvider.swift +2 -0
  153. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  154. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  155. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  156. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  157. package/ios/Swift/Promise.swift +17 -4
  158. package/ios/Swift/Records/Field.swift +2 -2
  159. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  160. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  161. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  162. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  163. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  164. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  165. package/ios/Swift/Utilities.swift +28 -0
  166. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  167. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  168. package/ios/Tests/ClassComponentSpec.swift +210 -0
  169. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  170. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  171. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  172. package/ios/Tests/FunctionSpec.swift +167 -118
  173. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  174. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  175. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  176. package/ios/Tests/TypedArraysSpec.swift +136 -0
  177. package/package.json +2 -2
  178. package/src/NativeModulesProxy.native.ts +13 -3
  179. package/src/PermissionsInterface.ts +29 -0
  180. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  181. package/tsconfig.json +1 -1
  182. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  183. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  184. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  185. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  186. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  187. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  188. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  189. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  190. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  191. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  192. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  193. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin
2
+
3
+ /**
4
+ * Simple iterator that will merge two other iterators.
5
+ */
6
+ class ConcatIterator<T>(
7
+ private val first: Iterator<T>,
8
+ private val second: Iterator<T>
9
+ ) : Iterator<T> {
10
+ override fun hasNext(): Boolean = first.hasNext() || second.hasNext()
11
+
12
+ override fun next(): T =
13
+ if (first.hasNext()) {
14
+ first.next()
15
+ } else {
16
+ second.next()
17
+ }
18
+ }
@@ -41,26 +41,25 @@ class KotlinInteropModuleRegistry(
41
41
  }
42
42
 
43
43
  fun exportedModulesConstants(): Map<ModuleName, ModuleConstants> {
44
- return registry
45
- .map { holder ->
46
- holder.name to holder.definition.constantsProvider()
47
- }
48
- .toMap()
44
+ return registry.associate { holder ->
45
+ holder.name to holder.definition.constantsProvider()
46
+ }
49
47
  }
50
48
 
51
49
  fun exportMethods(exportKey: (String, List<ModuleMethodInfo>) -> Unit = { _, _ -> }): Map<ModuleName, List<ModuleMethodInfo>> {
52
- return registry
53
- .map { holder ->
54
- val methodsInfo = holder.definition.methods.map { (name, method) ->
50
+ return registry.associate { holder ->
51
+ val methodsInfo = holder
52
+ .definition
53
+ .asyncFunctions
54
+ .map { (name, method) ->
55
55
  mapOf(
56
56
  "name" to name,
57
57
  "argumentsCount" to method.argsCount
58
58
  )
59
59
  }
60
- exportKey(holder.name, methodsInfo)
61
- holder.name to methodsInfo
62
- }
63
- .toMap()
60
+ exportKey(holder.name, methodsInfo)
61
+ holder.name to methodsInfo
62
+ }
64
63
  }
65
64
 
66
65
  fun exportViewManagers(): List<ViewManager<*, *>> {
@@ -110,4 +109,8 @@ class KotlinInteropModuleRegistry(
110
109
  fun onDestroy() {
111
110
  appContext.onDestroy()
112
111
  }
112
+
113
+ fun installJSIInterop() {
114
+ appContext.installJSIInterop()
115
+ }
113
116
  }
@@ -1,5 +1,6 @@
1
1
  package expo.modules.kotlin
2
2
 
3
+ import com.facebook.react.bridge.Arguments
3
4
  import com.facebook.react.bridge.ReadableArray
4
5
  import expo.modules.kotlin.events.BasicEventListener
5
6
  import expo.modules.kotlin.events.EventListenerWithPayload
@@ -8,19 +9,60 @@ import expo.modules.kotlin.events.EventName
8
9
  import expo.modules.kotlin.exception.FunctionCallException
9
10
  import expo.modules.kotlin.exception.MethodNotFoundException
10
11
  import expo.modules.kotlin.exception.exceptionDecorator
12
+ import expo.modules.kotlin.jni.JavaScriptModuleObject
11
13
  import expo.modules.kotlin.modules.Module
14
+ import expo.modules.kotlin.modules.ProcessedModuleDefinition
12
15
 
13
16
  class ModuleHolder(val module: Module) {
14
- val definition = module.definition()
17
+ val definition = ProcessedModuleDefinition(module.definition(), this)
18
+
15
19
  val name get() = definition.name
16
20
 
21
+ /**
22
+ * Cached instance of HybridObject used by CPP to interact with underlying [expo.modules.kotlin.modules.Module] object.
23
+ */
24
+ val jsObject by lazy {
25
+ JavaScriptModuleObject()
26
+ .apply {
27
+ val constants = definition.constantsProvider()
28
+ val convertedConstants = Arguments.makeNativeMap(constants)
29
+ exportConstants(convertedConstants)
30
+
31
+ definition
32
+ .functions
33
+ .forEach { function ->
34
+ function.attachToJSObject(module.appContext, this)
35
+ }
36
+
37
+ definition
38
+ .properties
39
+ .forEach { (_, prop) ->
40
+ prop.attachToJSObject(this)
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Invokes a function with promise. Is used in the bridge implementation of the Sweet API.
47
+ */
17
48
  fun call(methodName: String, args: ReadableArray, promise: Promise) = exceptionDecorator({
18
49
  FunctionCallException(methodName, definition.name, it)
19
50
  }) {
20
- val method = definition.methods[methodName]
51
+ val method = definition.asyncFunctions[methodName]
52
+ ?: throw MethodNotFoundException()
53
+
54
+ method.call(args, promise)
55
+ }
56
+
57
+ /**
58
+ * Invokes a function without promise.
59
+ * `callSync` was added only for test purpose and shouldn't be used anywhere else.
60
+ */
61
+ fun callSync(methodName: String, args: ReadableArray): Any? {
62
+ val method = definition.syncFunctions[methodName]
21
63
  ?: throw MethodNotFoundException()
22
64
 
23
- method.call(this, args, promise)
65
+ return method.call(args)
24
66
  }
25
67
 
26
68
  fun post(eventName: EventName) {
@@ -0,0 +1,49 @@
1
+ package expo.modules.kotlin.activityaware
2
+
3
+ import android.app.Activity
4
+ import androidx.appcompat.app.AppCompatActivity
5
+ import expo.modules.kotlin.AppContext
6
+ import kotlinx.coroutines.suspendCancellableCoroutine
7
+
8
+ /**
9
+ * Similar to [androidx.activity.contextaware.ContextAware]
10
+ *
11
+ * A [AppCompatActivityAware] class is associated with a [AppCompatActivity] sometime after
12
+ * the [Activity] is passed down to the [AppContext] in [AppContext.onHostResume].
13
+ * By adding a [OnActivityAvailableListener] you can receive a callback for that event.
14
+ */
15
+ interface AppCompatActivityAware {
16
+ /**
17
+ * Adds a listener waiting for Activity to become available.
18
+ * If Activity is available when listener is being added it will be immediately called.
19
+ */
20
+ fun addOnActivityAvailableListener(listener: OnActivityAvailableListener)
21
+
22
+ fun removeOnActivityAvailableListener(listener: OnActivityAvailableListener)
23
+ }
24
+
25
+ /**
26
+ * Similar to [androidx.activity.contextaware.ContextAware] from `androidx.activity:activity-kts`
27
+ *
28
+ * Run [onActivityAvailable] when the [AppCompatActivity] becomes available and resume with the result.
29
+ *
30
+ * If the [AppCompatActivity] is already available, [onActivityAvailable] will be synchronously
31
+ * called on the current coroutine context. Otherwise, [onActivityAvailable] will be called on the
32
+ * the UI thread immediately when the [Activity] becomes available.
33
+ *
34
+ * No matter how many times [Activity] will become available callback would be called only once.
35
+ */
36
+ suspend inline fun <R> AppCompatActivityAware.withActivityAvailable(
37
+ crossinline onActivityAvailable: (AppCompatActivity) -> R
38
+ ): R = suspendCancellableCoroutine { continuation ->
39
+ val listener = object : OnActivityAvailableListener {
40
+ override fun onActivityAvailable(activity: AppCompatActivity) {
41
+ removeOnActivityAvailableListener(this)
42
+ continuation.resumeWith(runCatching { onActivityAvailable(activity) })
43
+ }
44
+ }
45
+ addOnActivityAvailableListener(listener)
46
+ continuation.invokeOnCancellation {
47
+ removeOnActivityAvailableListener(listener)
48
+ }
49
+ }
@@ -0,0 +1,43 @@
1
+ package expo.modules.kotlin.activityaware
2
+
3
+ import androidx.appcompat.app.AppCompatActivity
4
+ import java.lang.ref.WeakReference
5
+ import java.util.concurrent.CopyOnWriteArrayList
6
+
7
+ /**
8
+ * Similar to [androidx.activity.contextaware.ContextAwareHelper]
9
+ *
10
+ * Helper class for implementing [AppCompatActivityAware].
11
+ */
12
+ class AppCompatActivityAwareHelper : AppCompatActivityAware {
13
+ val listeners = CopyOnWriteArrayList<OnActivityAvailableListener>()
14
+
15
+ var activityReference = WeakReference<AppCompatActivity>(null)
16
+
17
+ fun dispatchOnActivityAvailable(activity: AppCompatActivity) {
18
+ this.activityReference = WeakReference(activity)
19
+
20
+ activity.runOnUiThread {
21
+ for (listener in listeners) {
22
+ listener.onActivityAvailable(activity)
23
+ }
24
+ }
25
+ }
26
+
27
+ // region AppCompatActivityAware
28
+
29
+ override fun addOnActivityAvailableListener(listener: OnActivityAvailableListener) {
30
+ listeners.add(listener)
31
+ activityReference.get()?.let { activity ->
32
+ activity.runOnUiThread {
33
+ listener.onActivityAvailable(activity)
34
+ }
35
+ }
36
+ }
37
+
38
+ override fun removeOnActivityAvailableListener(listener: OnActivityAvailableListener) {
39
+ listeners.remove(listener)
40
+ }
41
+
42
+ // endregion
43
+ }
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin.activityaware
2
+
3
+ import androidx.annotation.UiThread
4
+ import androidx.appcompat.app.AppCompatActivity
5
+
6
+ /**
7
+ * Similar to [androidx.activity.contextaware.OnContextAvailableListener]
8
+ *
9
+ * Listener for receiving a callback at the first moment a [AppCompatActivity] is made
10
+ * available to the [AppCompatActivityAware] class.
11
+ */
12
+ fun interface OnActivityAvailableListener {
13
+ /**
14
+ * This callback will be called on UI thread.
15
+ */
16
+ @UiThread
17
+ fun onActivityAvailable(activity: AppCompatActivity)
18
+ }
@@ -0,0 +1,99 @@
1
+ @file:OptIn(DelicateCoroutinesApi::class)
2
+
3
+ package expo.modules.kotlin.activityresult
4
+
5
+ import android.app.Activity
6
+ import android.content.Intent
7
+ import androidx.appcompat.app.AppCompatActivity
8
+ import androidx.lifecycle.Lifecycle
9
+ import expo.modules.kotlin.AppContext
10
+ import expo.modules.kotlin.activityaware.AppCompatActivityAware
11
+ import expo.modules.kotlin.activityaware.AppCompatActivityAwareHelper
12
+ import expo.modules.kotlin.activityaware.OnActivityAvailableListener
13
+ import expo.modules.kotlin.activityaware.withActivityAvailable
14
+ import expo.modules.kotlin.providers.CurrentActivityProvider
15
+ import kotlinx.coroutines.DelicateCoroutinesApi
16
+ import kotlinx.coroutines.GlobalScope
17
+ import kotlinx.coroutines.launch
18
+ import java.io.Serializable
19
+ import java.util.concurrent.atomic.AtomicInteger
20
+
21
+ /**
22
+ * Manager class that takes care of proper communication with [AppContextActivityResultRegistry]
23
+ * It also monitors the needed lifecycle state using [AppCompatActivityAwareHelper]
24
+ */
25
+ class ActivityResultsManager(
26
+ currentActivityProvider: CurrentActivityProvider
27
+ ) : AppContextActivityResultCaller, AppCompatActivityAware {
28
+ /**
29
+ * Due to the fact that [AppContext] is not coupled directly with the [Activity]'s lifecycle
30
+ * it's impossible to subscribe all [Lifecycle]'s events properly.
31
+ * That forces us to create our own [AppContextActivityResultRegistry].
32
+ */
33
+ private val registry = AppContextActivityResultRegistry(currentActivityProvider)
34
+ private val nextLocalRequestCode = AtomicInteger()
35
+
36
+ /**
37
+ * Helper property that allows for waiting for [Activity] creation.
38
+ * It is useful when some Module wants to register itself before the current [Activity] is made available.
39
+ */
40
+ private val activityAwareHelper = AppCompatActivityAwareHelper()
41
+
42
+ init {
43
+ GlobalScope.launch {
44
+ withActivityAvailable { activity ->
45
+ registry.restoreInstanceState(activity)
46
+ }
47
+ }
48
+ }
49
+
50
+ // region Lifecycle
51
+
52
+ fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
53
+ registry.dispatchResult(requestCode, resultCode, data)
54
+ }
55
+
56
+ /**
57
+ * This function is called every time the [Activity] is resumed.
58
+ * If you want to add some mechanism that fires only once (similar to how [Activity.onCreate] works),
59
+ * then use `init` block with [withActivityAvailable].
60
+ */
61
+ fun onHostResume(activity: AppCompatActivity) {
62
+ activityAwareHelper.dispatchOnActivityAvailable(activity)
63
+ }
64
+
65
+ fun onHostDestroy(activity: AppCompatActivity) {
66
+ registry.persistInstanceState(activity)
67
+ }
68
+
69
+ // endregion
70
+
71
+ // region AppContextActivityResultCaller
72
+
73
+ override suspend fun <I : Serializable, O> registerForActivityResult(
74
+ contract: AppContextActivityResultContract<I, O>,
75
+ fallbackCallback: AppContextActivityResultFallbackCallback<I, O>
76
+ ): AppContextActivityResultLauncher<I, O> =
77
+ withActivityAvailable { activity ->
78
+ registry.register(
79
+ "AppContext_rq#${nextLocalRequestCode.getAndIncrement()}",
80
+ activity,
81
+ contract,
82
+ fallbackCallback
83
+ )
84
+ }
85
+
86
+ // endregion
87
+
88
+ // region ActivityAware
89
+
90
+ override fun addOnActivityAvailableListener(listener: OnActivityAvailableListener) {
91
+ activityAwareHelper.addOnActivityAvailableListener(listener)
92
+ }
93
+
94
+ override fun removeOnActivityAvailableListener(listener: OnActivityAvailableListener) {
95
+ activityAwareHelper.removeOnActivityAvailableListener(listener)
96
+ }
97
+
98
+ // endregion
99
+ }
@@ -0,0 +1,25 @@
1
+ package expo.modules.kotlin.activityresult
2
+
3
+ import androidx.annotation.MainThread
4
+ import java.io.Serializable
5
+
6
+ /**
7
+ * This interface is directly based on [androidx.activity.result.ActivityResultCaller], but due to incompatibility
8
+ * of ReactNative and Android's [androidx.lifecycle.Lifecycle] it needed to be adapted.
9
+ * For more information how to use it read [androidx.activity.result.ActivityResultCaller] from `androidx.activity:activity:1.4.0`
10
+ * or even better from `androidx.activity:activity-ktx:1.4.0`.
11
+ *
12
+ * @see [androidx.activity.result.ActivityResultCaller]
13
+ */
14
+ interface AppContextActivityResultCaller {
15
+ /**
16
+ * @see [androidx.activity.result.ActivityResultCaller.registerForActivityResult] from `androidx.activity:activity-ktx:1.4.0`.
17
+ * @param I - input, it is preserved across Activity calls and is delivered to fallback callback
18
+ * @param O - output
19
+ */
20
+ @MainThread
21
+ suspend fun <I : Serializable, O> registerForActivityResult(
22
+ contract: AppContextActivityResultContract<I, O>,
23
+ fallbackCallback: AppContextActivityResultFallbackCallback<I, O>,
24
+ ): AppContextActivityResultLauncher<I, O>
25
+ }
@@ -0,0 +1,27 @@
1
+ package expo.modules.kotlin.activityresult
2
+
3
+ import android.content.Context
4
+ import android.content.Intent
5
+ import java.io.Serializable
6
+
7
+ /**
8
+ * A contract specifying that an activity can be called with an input of type [I] and produce an output of type [O].
9
+ *
10
+ * Makes calling an activity for result type-safe.
11
+ *
12
+ * This interface differs from the original in terms of providing `input` parameter in the [parseResult] method.
13
+ *
14
+ * @see androidx.activity.result.contract.ActivityResultContract
15
+ */
16
+ interface AppContextActivityResultContract<I : Serializable, O> {
17
+ /**
18
+ * Create an intent that can be used for [android.app.Activity.startActivityForResult].
19
+ */
20
+ fun createIntent(context: Context, input: I): Intent
21
+
22
+ /**
23
+ * Convert result obtained from [android.app.Activity.onActivityResult] to [O].
24
+ * @param input the very same input object that is used in [createIntent] method. You can use it to add additional information to constructed result.
25
+ */
26
+ fun parseResult(input: I, resultCode: Int, intent: Intent?): O
27
+ }
@@ -0,0 +1,17 @@
1
+ package expo.modules.kotlin.activityresult
2
+
3
+ import java.io.Serializable
4
+
5
+ /**
6
+ * Interface for fallback callback that has to be registered at the very beginning of module's lifecycle
7
+ * in order to deliver all results in case launching [android.app.Activity] is killed.
8
+ *
9
+ * @see [androidx.activity.result.ActivityResultCallback]
10
+ */
11
+ fun interface AppContextActivityResultFallbackCallback<I : Serializable, O> {
12
+ /**
13
+ * @param input parameters used to construct Intent that launched the operation
14
+ * @param result output constructed by the associated [AppContextActivityResultContract]
15
+ */
16
+ fun onActivityResult(input: I, result: O)
17
+ }
@@ -0,0 +1,30 @@
1
+ package expo.modules.kotlin.activityresult
2
+
3
+ import androidx.activity.result.ActivityResultCallback
4
+ import androidx.activity.result.contract.ActivityResultContract
5
+ import java.io.Serializable
6
+ import kotlin.coroutines.resume
7
+ import kotlin.coroutines.suspendCoroutine
8
+
9
+ /**
10
+ * A launcher for a previously-[AppContextActivityResultCaller.registerForActivityResult] prepared call
11
+ * to start the process of executing an [ActivityResultContract]
12
+ *
13
+ * @param I type of the input required to launch, it also is preserved and delivered in fallback callback
14
+ * @param O result type
15
+ *
16
+ * @see [androidx.activity.result.ActivityResultLauncher]
17
+ */
18
+ abstract class AppContextActivityResultLauncher<I : Serializable, O> {
19
+ /**
20
+ * @param input This would be persisted and restored upon possible Activity destruction by the system
21
+ * to keep context of launching the mechanism.
22
+ */
23
+ abstract fun launch(input: I, callback: ActivityResultCallback<O>)
24
+
25
+ suspend fun launch(input: I): O = suspendCoroutine { continuation ->
26
+ launch(input) { output -> continuation.resume(output) }
27
+ }
28
+
29
+ abstract val contract: AppContextActivityResultContract<I, O>
30
+ }