expo-modules-core 0.10.0 → 0.11.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 +23 -0
- package/README.md +3 -3
- package/android/CMakeLists.txt +14 -5
- package/android/build.gradle +78 -28
- package/android/src/main/cpp/CachedReferencesRegistry.cpp +67 -0
- package/android/src/main/cpp/CachedReferencesRegistry.h +80 -0
- package/android/src/main/cpp/JNIFunctionBody.cpp +28 -12
- package/android/src/main/cpp/JNIFunctionBody.h +2 -2
- package/android/src/main/cpp/JNIInjector.cpp +4 -0
- package/android/src/main/cpp/JavaScriptModuleObject.cpp +86 -5
- package/android/src/main/cpp/JavaScriptModuleObject.h +27 -5
- package/android/src/main/cpp/JavaScriptRuntime.cpp +10 -12
- package/android/src/main/cpp/MethodMetadata.cpp +181 -40
- package/android/src/main/cpp/MethodMetadata.h +43 -3
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +63 -10
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +6 -0
- package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAware.kt +49 -0
- package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt +43 -0
- package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/ActivityResultsManager.kt +99 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +25 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt +27 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultFallbackCallback.kt +17 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt +30 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +358 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/DataPersistor.kt +135 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +34 -1
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +7 -1
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +0 -108
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +5 -2
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +5 -2
- package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +9 -2
- package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +9 -1
- package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +1 -0
- package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +2 -2
- package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +4 -2
- package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +5 -454
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +7 -15
- package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +271 -0
- package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +21 -0
- package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponent.kt +54 -0
- package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponentBuilder.kt +32 -0
- package/android/src/main/java/expo/modules/kotlin/types/AnyTypeConverter.kt +36 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +7 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -41
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -33
- package/build/PermissionsInterface.d.ts +29 -0
- package/build/PermissionsInterface.d.ts.map +1 -1
- package/build/PermissionsInterface.js +9 -0
- package/build/PermissionsInterface.js.map +1 -1
- package/ios/ExpoModulesCore.podspec +2 -1
- package/ios/JSI/EXJSIInstaller.mm +2 -0
- package/ios/JSI/EXJSIUtils.h +1 -0
- package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +4 -3
- package/ios/Swift/AppContext.swift +2 -4
- package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +2 -2
- package/ios/Swift/Exceptions/ChainableException.swift +3 -3
- package/ios/Swift/ExpoBridgeModule.swift +16 -2
- package/ios/Swift/Logging/Logger.swift +3 -0
- package/ios/Swift/Promise.swift +5 -1
- package/package.json +2 -2
- package/src/PermissionsInterface.ts +29 -0
|
@@ -5,6 +5,7 @@ package expo.modules.kotlin
|
|
|
5
5
|
import android.app.Activity
|
|
6
6
|
import android.content.Context
|
|
7
7
|
import android.content.Intent
|
|
8
|
+
import androidx.annotation.MainThread
|
|
8
9
|
import androidx.appcompat.app.AppCompatActivity
|
|
9
10
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
11
|
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
|
|
@@ -19,6 +20,11 @@ import expo.modules.interfaces.imageloader.ImageLoaderInterface
|
|
|
19
20
|
import expo.modules.interfaces.permissions.Permissions
|
|
20
21
|
import expo.modules.interfaces.sensors.SensorServiceInterface
|
|
21
22
|
import expo.modules.interfaces.taskManager.TaskManagerInterface
|
|
23
|
+
import expo.modules.kotlin.activityresult.ActivityResultsManager
|
|
24
|
+
import expo.modules.kotlin.activityresult.AppContextActivityResultFallbackCallback
|
|
25
|
+
import expo.modules.kotlin.activityresult.AppContextActivityResultCaller
|
|
26
|
+
import expo.modules.kotlin.activityresult.AppContextActivityResultContract
|
|
27
|
+
import expo.modules.kotlin.activityresult.AppContextActivityResultLauncher
|
|
22
28
|
import expo.modules.kotlin.defaultmodules.ErrorManagerModule
|
|
23
29
|
import expo.modules.kotlin.events.EventEmitter
|
|
24
30
|
import expo.modules.kotlin.events.EventName
|
|
@@ -34,45 +40,64 @@ import kotlinx.coroutines.DelicateCoroutinesApi
|
|
|
34
40
|
import kotlinx.coroutines.SupervisorJob
|
|
35
41
|
import kotlinx.coroutines.cancel
|
|
36
42
|
import kotlinx.coroutines.newSingleThreadContext
|
|
43
|
+
import java.io.Serializable
|
|
37
44
|
import java.lang.ref.WeakReference
|
|
38
45
|
|
|
39
46
|
class AppContext(
|
|
40
47
|
modulesProvider: ModulesProvider,
|
|
41
48
|
val legacyModuleRegistry: expo.modules.core.ModuleRegistry,
|
|
42
49
|
private val reactContextHolder: WeakReference<ReactApplicationContext>
|
|
43
|
-
) : CurrentActivityProvider {
|
|
50
|
+
) : CurrentActivityProvider, AppContextActivityResultCaller {
|
|
44
51
|
val registry = ModuleRegistry(WeakReference(this)).apply {
|
|
45
|
-
register(ErrorManagerModule())
|
|
46
|
-
register(modulesProvider)
|
|
47
52
|
}
|
|
48
53
|
private val reactLifecycleDelegate = ReactLifecycleDelegate(this)
|
|
54
|
+
|
|
49
55
|
// We postpone creating the `JSIInteropModuleRegistry` to not load so files in unit tests.
|
|
50
56
|
private lateinit var jsiInterop: JSIInteropModuleRegistry
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A queue used to dispatch all async methods that are called via JSI.
|
|
60
|
+
*/
|
|
51
61
|
internal val modulesQueue = CoroutineScope(
|
|
62
|
+
// TODO(@lukmccall): maybe it will be better to use a thread pool
|
|
52
63
|
newSingleThreadContext("ExpoModulesCoreQueue") +
|
|
53
64
|
SupervisorJob() +
|
|
54
65
|
CoroutineName("ExpoModulesCoreCoroutineQueue")
|
|
55
66
|
)
|
|
56
67
|
|
|
68
|
+
private val activityResultsManager = ActivityResultsManager(this)
|
|
69
|
+
|
|
57
70
|
init {
|
|
58
71
|
requireNotNull(reactContextHolder.get()) {
|
|
59
72
|
"The app context should be created with valid react context."
|
|
60
73
|
}.apply {
|
|
61
74
|
addLifecycleEventListener(reactLifecycleDelegate)
|
|
62
75
|
addActivityEventListener(reactLifecycleDelegate)
|
|
76
|
+
|
|
77
|
+
// Registering modules has to happen at the very end of `AppContext` creation. Some modules need to access
|
|
78
|
+
// `AppContext` during their initialisation (or during `OnCreate` method), so we need to ensure all `AppContext`'s
|
|
79
|
+
// properties are initialized first. Not having that would trigger NPE.
|
|
80
|
+
registry.register(ErrorManagerModule())
|
|
81
|
+
registry.register(modulesProvider)
|
|
63
82
|
}
|
|
64
83
|
}
|
|
65
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Initializes a JSI part of the module registry.
|
|
87
|
+
* It will be a NOOP if the remote debugging was activated.
|
|
88
|
+
*/
|
|
66
89
|
fun installJSIInterop() {
|
|
67
90
|
jsiInterop = JSIInteropModuleRegistry(this)
|
|
68
91
|
val reactContext = reactContextHolder.get() ?: return
|
|
69
|
-
reactContext.javaScriptContextHolder?.get()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
92
|
+
reactContext.javaScriptContextHolder?.get()
|
|
93
|
+
?.takeIf { it != 0L }
|
|
94
|
+
?.let {
|
|
95
|
+
jsiInterop.installJSI(
|
|
96
|
+
it,
|
|
97
|
+
reactContext.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl,
|
|
98
|
+
reactContext.catalystInstance.nativeCallInvokerHolder as CallInvokerHolderImpl
|
|
99
|
+
)
|
|
100
|
+
}
|
|
76
101
|
}
|
|
77
102
|
|
|
78
103
|
/**
|
|
@@ -185,6 +210,11 @@ class AppContext(
|
|
|
185
210
|
}
|
|
186
211
|
|
|
187
212
|
fun onHostResume() {
|
|
213
|
+
activityResultsManager.onHostResume(
|
|
214
|
+
requireNotNull(currentActivity) {
|
|
215
|
+
"Current Activity is not available at this moment. This is an invalid state and this should never happen"
|
|
216
|
+
}
|
|
217
|
+
)
|
|
188
218
|
registry.post(EventName.ACTIVITY_ENTERS_FOREGROUND)
|
|
189
219
|
}
|
|
190
220
|
|
|
@@ -193,10 +223,16 @@ class AppContext(
|
|
|
193
223
|
}
|
|
194
224
|
|
|
195
225
|
fun onHostDestroy() {
|
|
226
|
+
activityResultsManager.onHostDestroy(
|
|
227
|
+
requireNotNull(currentActivity) {
|
|
228
|
+
"Current Activity is not available at this moment. This is an invalid state and this should never happen"
|
|
229
|
+
}
|
|
230
|
+
)
|
|
196
231
|
registry.post(EventName.ACTIVITY_DESTROYS)
|
|
197
232
|
}
|
|
198
233
|
|
|
199
234
|
fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
|
235
|
+
activityResultsManager.onActivityResult(activity, requestCode, resultCode, data)
|
|
200
236
|
registry.post(
|
|
201
237
|
EventName.ON_ACTIVITY_RESULT,
|
|
202
238
|
activity,
|
|
@@ -228,5 +264,22 @@ class AppContext(
|
|
|
228
264
|
return currentActivity
|
|
229
265
|
}
|
|
230
266
|
|
|
267
|
+
// endregion
|
|
268
|
+
|
|
269
|
+
// region AppContextActivityResultCaller
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* For the time being [fallbackCallback] is not working.
|
|
273
|
+
* There are some problems with saving and restoring the state of [activityResultsManager]
|
|
274
|
+
* connected with [Activity]'s lifecycle and [AppContext] lifespan. So far, we've failed with identifying
|
|
275
|
+
* what parts of the application outlives the Activity destruction (especially [AppContext] and other [Bridge]-related parts).
|
|
276
|
+
*/
|
|
277
|
+
@MainThread
|
|
278
|
+
override suspend fun <I : Serializable, O> registerForActivityResult(
|
|
279
|
+
contract: AppContextActivityResultContract<I, O>,
|
|
280
|
+
fallbackCallback: AppContextActivityResultFallbackCallback<I, O>
|
|
281
|
+
): AppContextActivityResultLauncher<I, O> =
|
|
282
|
+
activityResultsManager.registerForActivityResult(contract, fallbackCallback)
|
|
283
|
+
|
|
231
284
|
// endregion
|
|
232
285
|
}
|
|
@@ -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
|
+
}
|
package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt
ADDED
|
@@ -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
|
+
}
|