react-native-theoplayer 9.9.0 → 9.10.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 +23 -0
- package/README.md +3 -0
- package/android/build.gradle +9 -7
- package/android/src/main/java/com/theoplayer/ReactTHEOplayerPackage.kt +3 -1
- package/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt +32 -0
- package/android/src/main/java/com/theoplayer/presentation/FullscreenLayoutObserver.kt +41 -6
- package/android/src/main/java/com/theoplayer/presentation/PresentationManager.kt +75 -67
- package/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +11 -31
- package/android/src/main/java/com/theoplayer/theolive/EndpointAdapter.kt +2 -1
- package/android/src/main/java/com/theoplayer/theolive/THEOliveEventAdapter.kt +6 -2
- package/android/src/main/java/com/theoplayer/theolive/THEOliveModule.kt +49 -0
- package/ios/THEOplayerRCTBridge.m +4 -0
- package/ios/THEOplayerRCTMainEventHandler.swift +1 -1
- package/ios/THEOplayerRCTPlayerAPI.swift +14 -0
- package/ios/THEOplayerRCTTypeUtils.swift +13 -0
- package/ios/theolive/THEOplayerRCTTHEOliveAPI.swift +17 -0
- package/ios/theolive/THEOplayerRCTTHEOliveEventAdapter.swift +62 -0
- package/ios/theolive/THEOplayerRCTTHEOliveEventHandler.swift +6 -2
- package/lib/commonjs/api/event/TheoLiveEvent.js +6 -0
- package/lib/commonjs/api/event/TheoLiveEvent.js.map +1 -1
- package/lib/commonjs/api/player/PlayerEventMap.js.map +1 -1
- package/lib/commonjs/internal/THEOplayerView.js +8 -3
- package/lib/commonjs/internal/THEOplayerView.js.map +1 -1
- package/lib/commonjs/internal/THEOplayerView.web.js +28 -3
- package/lib/commonjs/internal/THEOplayerView.web.js.map +1 -1
- package/lib/commonjs/internal/adapter/THEOplayerAdapter.js +4 -2
- package/lib/commonjs/internal/adapter/THEOplayerAdapter.js.map +1 -1
- package/lib/commonjs/internal/adapter/WebEventForwarder.js +5 -0
- package/lib/commonjs/internal/adapter/WebEventForwarder.js.map +1 -1
- package/lib/commonjs/internal/adapter/event/PlayerEvents.js +9 -1
- package/lib/commonjs/internal/adapter/event/PlayerEvents.js.map +1 -1
- package/lib/commonjs/internal/adapter/event/native/NativeTheoLiveEvent.js +2 -0
- package/lib/commonjs/internal/adapter/event/native/NativeTheoLiveEvent.js.map +1 -1
- package/lib/commonjs/internal/adapter/theolive/TheoLiveNativeAdapter.js +9 -15
- package/lib/commonjs/internal/adapter/theolive/TheoLiveNativeAdapter.js.map +1 -1
- package/lib/commonjs/internal/adapter/theolive/TheoLiveWebAdapter.js +8 -0
- package/lib/commonjs/internal/adapter/theolive/TheoLiveWebAdapter.js.map +1 -1
- package/lib/commonjs/manifest.json +1 -1
- package/lib/module/api/event/TheoLiveEvent.js +7 -0
- package/lib/module/api/event/TheoLiveEvent.js.map +1 -1
- package/lib/module/api/player/PlayerEventMap.js.map +1 -1
- package/lib/module/internal/THEOplayerView.js +8 -3
- package/lib/module/internal/THEOplayerView.js.map +1 -1
- package/lib/module/internal/THEOplayerView.web.js +28 -3
- package/lib/module/internal/THEOplayerView.web.js.map +1 -1
- package/lib/module/internal/adapter/THEOplayerAdapter.js +4 -2
- package/lib/module/internal/adapter/THEOplayerAdapter.js.map +1 -1
- package/lib/module/internal/adapter/WebEventForwarder.js +6 -1
- package/lib/module/internal/adapter/WebEventForwarder.js.map +1 -1
- package/lib/module/internal/adapter/event/PlayerEvents.js +7 -0
- package/lib/module/internal/adapter/event/PlayerEvents.js.map +1 -1
- package/lib/module/internal/adapter/event/native/NativeTheoLiveEvent.js +3 -1
- package/lib/module/internal/adapter/event/native/NativeTheoLiveEvent.js.map +1 -1
- package/lib/module/internal/adapter/theolive/TheoLiveNativeAdapter.js +10 -16
- package/lib/module/internal/adapter/theolive/TheoLiveNativeAdapter.js.map +1 -1
- package/lib/module/internal/adapter/theolive/TheoLiveWebAdapter.js +8 -0
- package/lib/module/internal/adapter/theolive/TheoLiveWebAdapter.js.map +1 -1
- package/lib/module/manifest.json +1 -1
- package/lib/typescript/api/event/TheoLiveEvent.d.ts +13 -3
- package/lib/typescript/api/event/TheoLiveEvent.d.ts.map +1 -1
- package/lib/typescript/api/player/PlayerEventMap.d.ts +3 -3
- package/lib/typescript/api/player/PlayerEventMap.d.ts.map +1 -1
- package/lib/typescript/api/theolive/TheoLiveAPI.d.ts +4 -0
- package/lib/typescript/api/theolive/TheoLiveAPI.d.ts.map +1 -1
- package/lib/typescript/api/theolive/TheoLiveEndpoint.d.ts +1 -0
- package/lib/typescript/api/theolive/TheoLiveEndpoint.d.ts.map +1 -1
- package/lib/typescript/internal/THEOplayerView.d.ts +1 -0
- package/lib/typescript/internal/THEOplayerView.d.ts.map +1 -1
- package/lib/typescript/internal/THEOplayerView.web.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/THEOplayerAdapter.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/WebEventForwarder.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts +6 -1
- package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/event/native/NativeTheoLiveEvent.d.ts +5 -1
- package/lib/typescript/internal/adapter/event/native/NativeTheoLiveEvent.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/theolive/TheoLiveNativeAdapter.d.ts +3 -0
- package/lib/typescript/internal/adapter/theolive/TheoLiveNativeAdapter.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/theolive/TheoLiveWebAdapter.d.ts +2 -0
- package/lib/typescript/internal/adapter/theolive/TheoLiveWebAdapter.d.ts.map +1 -1
- package/package.json +2 -2
- package/react-native-theoplayer.podspec +7 -7
- package/src/api/event/TheoLiveEvent.ts +14 -3
- package/src/api/player/PlayerEventMap.ts +4 -2
- package/src/api/theolive/TheoLiveAPI.ts +4 -0
- package/src/api/theolive/TheoLiveEndpoint.ts +1 -0
- package/src/internal/THEOplayerView.tsx +10 -2
- package/src/internal/THEOplayerView.web.tsx +24 -2
- package/src/internal/adapter/THEOplayerAdapter.ts +6 -2
- package/src/internal/adapter/WebEventForwarder.ts +5 -1
- package/src/internal/adapter/event/PlayerEvents.ts +10 -0
- package/src/internal/adapter/event/native/NativeTheoLiveEvent.ts +14 -2
- package/src/internal/adapter/theolive/TheoLiveNativeAdapter.ts +13 -16
- package/src/internal/adapter/theolive/TheoLiveWebAdapter.ts +10 -0
- package/src/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [9.10.0] - 25-08-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fixed an issue on Android where transitioning into picture-in-picture presentation mode with the `THEOplayer_reparent_on_PiP` flag enabled would sometimes break the lay-out.
|
|
13
|
+
- Fixed an issue where the player's `currentTime` would not be immediately updated when dispatching `seeking` and `seeked` events.
|
|
14
|
+
- Fixed an issue on iOS where a listener for `seeked` events was not properly created.
|
|
15
|
+
|
|
16
|
+
## [9.9.1] - 25-07-31
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fixed an issue on Web in case the `aspect-ratio` CSS property is not supported on older browsers.
|
|
21
|
+
- Prevented passing infinite or NaN for intializationDelay in TheoAdDescription, on the Android bridge.
|
|
22
|
+
- Fixed and issue on Android where transitioning automatically into PiP presentation mode would fail on Android 16+.
|
|
23
|
+
- Fixed sources without an explicit `type` not being correctly recognized on Android when the URL contains query parameters.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Added support for Clearkey DRM on Android.
|
|
28
|
+
- Added `endpoint` property to THEOlive `EndpointLoaded` event.
|
|
29
|
+
- Added support for `ABRStrategyConfiguration` on iOS.
|
|
30
|
+
|
|
8
31
|
## [9.9.0] - 25-07-14
|
|
9
32
|
|
|
10
33
|
### Added
|
package/README.md
CHANGED
|
@@ -145,8 +145,11 @@ functionality. Currently, the following connectors are available:
|
|
|
145
145
|
| Agama analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-agama) | [`Agama`](https://github.com/THEOplayer/react-native-connectors/tree/main/agama) |
|
|
146
146
|
| Comscore analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-comscore) | [`Comscore`](https://github.com/THEOplayer/react-native-connectors/tree/main/comscore) |
|
|
147
147
|
| Conviva analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-conviva) | [`Conviva`](https://github.com/THEOplayer/react-native-connectors/tree/main/conviva) |
|
|
148
|
+
| Engage | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-engage) | [`Engage`](https://github.com/THEOplayer/react-native-connectors/tree/main/engage) |
|
|
149
|
+
| Gemius | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-gemius) | [`Gemius`](https://github.com/THEOplayer/react-native-connectors/tree/main/gemius) |
|
|
148
150
|
| Mux analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-mux) | [`Mux`](https://github.com/THEOplayer/react-native-connectors/tree/main/mux) |
|
|
149
151
|
| Nielsen analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-nielsen) | [`Nielsen`](https://github.com/THEOplayer/react-native-connectors/tree/main/nielsen) |
|
|
152
|
+
| Nielsen AdScript analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-adscript) | [`AdScript`](https://github.com/THEOplayer/react-native-connectors/tree/main/adscript) |
|
|
150
153
|
| Youbora analytics | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-youbora) | [`Youbora`](https://github.com/THEOplayer/react-native-connectors/tree/main/youbora) |
|
|
151
154
|
| Yospace SSAI | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-yospace) | [`Yospace`](https://github.com/THEOplayer/react-native-connectors/tree/main/yospace) |
|
|
152
155
|
| Content protection (DRM) | [](https://www.npmjs.com/package/%40theoplayer%2Freact-native-drm) | [`DRM`](https://github.com/THEOplayer/react-native-theoplayer-drm) |
|
package/android/build.gradle
CHANGED
|
@@ -43,12 +43,12 @@ def enabledMediaSession = safeExtGet("THEOplayer_extensionMediaSession", 'true')
|
|
|
43
43
|
def enabledMillicast = safeExtGet("THEOplayer_extensionMillicast", 'false').toBoolean()
|
|
44
44
|
|
|
45
45
|
android {
|
|
46
|
-
compileSdk safeExtGet('THEOplayer_compileSdkVersion',
|
|
46
|
+
compileSdk safeExtGet('THEOplayer_compileSdkVersion', 35)
|
|
47
47
|
namespace "com.theoplayer"
|
|
48
48
|
|
|
49
49
|
defaultConfig {
|
|
50
50
|
minSdkVersion safeExtGet('THEOplayer_minSdkVersion', 21)
|
|
51
|
-
targetSdkVersion safeExtGet('THEOplayer_targetSdkVersion',
|
|
51
|
+
targetSdkVersion safeExtGet('THEOplayer_targetSdkVersion', 35)
|
|
52
52
|
versionCode 1
|
|
53
53
|
versionName "1.0"
|
|
54
54
|
|
|
@@ -124,15 +124,16 @@ repositories {
|
|
|
124
124
|
mavenLocal()
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
// The minimum supported THEOplayer version is 9.
|
|
128
|
-
def theoVersion = safeExtGet('THEOplayer_sdk', '[9.
|
|
127
|
+
// The minimum supported THEOplayer version is 9.9.0
|
|
128
|
+
def theoVersion = safeExtGet('THEOplayer_sdk', '[9.9.0, 10.0.0)')
|
|
129
129
|
def theoMediaSessionVersion = safeExtGet('THEOplayer_mediasession', '[8.0.0, 10.0.0)')
|
|
130
130
|
def theoAdsWrapperVersion = "9.0.0"
|
|
131
|
-
def coroutinesVersion = safeExtGet('coroutinesVersion', '1.
|
|
131
|
+
def coroutinesVersion = safeExtGet('coroutinesVersion', '1.10.2')
|
|
132
132
|
def appcompatVersion = safeExtGet('appcompatVersion', '1.7.1')
|
|
133
133
|
def corektxVersion = safeExtGet('corektxVersion', '1.13.1')
|
|
134
|
-
def gsonVersion =
|
|
135
|
-
def
|
|
134
|
+
def gsonVersion = safeExtGet('gsonVersion', '2.13.1')
|
|
135
|
+
def activityktxVersion = safeExtGet('activityktxVersion', '1.10.1')
|
|
136
|
+
def millicastVersion = safeExtGet('millicastVersion', '2.4.3')
|
|
136
137
|
|
|
137
138
|
dependencies {
|
|
138
139
|
def addOptiViewIntegration = { enabled, notation, additional = null ->
|
|
@@ -156,6 +157,7 @@ dependencies {
|
|
|
156
157
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
|
|
157
158
|
implementation("androidx.appcompat:appcompat:$appcompatVersion")
|
|
158
159
|
implementation("androidx.core:core-ktx:$corektxVersion")
|
|
160
|
+
implementation("androidx.activity:activity-ktx:$activityktxVersion")
|
|
159
161
|
implementation("com.google.code.gson:gson:$gsonVersion")
|
|
160
162
|
|
|
161
163
|
println("Using THEOplayer (${versionString(theoVersion)})")
|
|
@@ -11,6 +11,7 @@ import com.theoplayer.cast.CastModule
|
|
|
11
11
|
import com.theoplayer.broadcast.EventBroadcastModule
|
|
12
12
|
import com.theoplayer.playback.PlaybackSettingsModule
|
|
13
13
|
import com.theoplayer.player.PlayerModule
|
|
14
|
+
import com.theoplayer.theolive.THEOliveModule
|
|
14
15
|
|
|
15
16
|
class ReactTHEOplayerPackage : ReactPackage {
|
|
16
17
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
@@ -21,7 +22,8 @@ class ReactTHEOplayerPackage : ReactPackage {
|
|
|
21
22
|
CastModule(reactContext),
|
|
22
23
|
CacheModule(reactContext),
|
|
23
24
|
EventBroadcastModule(reactContext),
|
|
24
|
-
PlaybackSettingsModule(reactContext)
|
|
25
|
+
PlaybackSettingsModule(reactContext),
|
|
26
|
+
THEOliveModule(reactContext)
|
|
25
27
|
)
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -9,6 +9,7 @@ import com.google.gson.Gson
|
|
|
9
9
|
import com.theoplayer.android.api.contentprotection.KeySystemId
|
|
10
10
|
import com.theoplayer.android.api.contentprotection.Request
|
|
11
11
|
import com.theoplayer.android.api.contentprotection.Response
|
|
12
|
+
import com.theoplayer.android.api.source.drm.ClearkeyKeySystemConfiguration
|
|
12
13
|
import com.theoplayer.android.api.source.drm.DRMConfiguration
|
|
13
14
|
import com.theoplayer.android.api.source.drm.DRMIntegrationId
|
|
14
15
|
import com.theoplayer.android.api.source.drm.KeySystemConfiguration
|
|
@@ -27,6 +28,7 @@ const val PROP_KEYSYSTEM_ID: String = "keySystemId"
|
|
|
27
28
|
const val PROP_DRM_CONFIG: String = "drmConfig"
|
|
28
29
|
const val PROP_PLAYREADY: String = "playready"
|
|
29
30
|
const val PROP_WIDEVINE: String = "widevine"
|
|
31
|
+
const val PROP_CLEARKEY: String = "clearkey"
|
|
30
32
|
const val PROP_REQUEST_ID: String = "requestId"
|
|
31
33
|
const val PROP_URL: String = "url"
|
|
32
34
|
const val PROP_METHOD: String = "method"
|
|
@@ -42,6 +44,9 @@ const val PROP_LICENSE_TYPE_TEMPORARY: String = "temporary"
|
|
|
42
44
|
const val PROP_LICENSE_TYPE_PERSISTENT: String = "persistent"
|
|
43
45
|
const val PROP_QUERY_PARAMETERS: String = "queryParameters"
|
|
44
46
|
const val PROP_CERTIFICATE: String = "certificate"
|
|
47
|
+
const val PROP_KEYS: String = "keys"
|
|
48
|
+
const val PROP_ID: String = "id"
|
|
49
|
+
const val PROP_VALUE: String = "value"
|
|
45
50
|
|
|
46
51
|
object ContentProtectionAdapter {
|
|
47
52
|
|
|
@@ -93,6 +98,9 @@ object ContentProtectionAdapter {
|
|
|
93
98
|
if (jsonConfig.has(PROP_PLAYREADY)) {
|
|
94
99
|
playready(keySystemConfigurationFromJson(jsonConfig.getJSONObject(PROP_PLAYREADY)))
|
|
95
100
|
}
|
|
101
|
+
if (jsonConfig.has(PROP_CLEARKEY)) {
|
|
102
|
+
clearkey(clearkeyKeySystemConfigurationFromJson(jsonConfig.getJSONObject(PROP_CLEARKEY)))
|
|
103
|
+
}
|
|
96
104
|
if (jsonConfig.has(PROP_INTEGRATION_PARAMETERS)) {
|
|
97
105
|
integrationParameters(fromJSONObjectToMap(jsonConfig.getJSONObject(PROP_INTEGRATION_PARAMETERS)))
|
|
98
106
|
}
|
|
@@ -113,6 +121,30 @@ object ContentProtectionAdapter {
|
|
|
113
121
|
}.build()
|
|
114
122
|
}
|
|
115
123
|
|
|
124
|
+
private fun clearkeyKeySystemConfigurationFromJson(config: JSONObject): ClearkeyKeySystemConfiguration {
|
|
125
|
+
val jsonKeys = config.optJSONArray(PROP_KEYS)
|
|
126
|
+
val keys = mutableListOf<ClearkeyKeySystemConfiguration.ClearkeyDecryptionKey>()
|
|
127
|
+
if (jsonKeys != null) {
|
|
128
|
+
for (i in 0 until jsonKeys.length()) {
|
|
129
|
+
val jsonKey = jsonKeys.optJSONObject(i)
|
|
130
|
+
if (jsonKey != null) {
|
|
131
|
+
val id = jsonKey.optString(PROP_ID)
|
|
132
|
+
val value = jsonKey.optString(PROP_VALUE)
|
|
133
|
+
if (!id.isNullOrEmpty() && !value.isNullOrEmpty()) {
|
|
134
|
+
keys.add(ClearkeyKeySystemConfiguration.ClearkeyDecryptionKey(id, value))
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ClearkeyKeySystemConfiguration.Builder(config.optString(PROP_LICENSE_ACQUISITION_URL))
|
|
140
|
+
.apply {
|
|
141
|
+
useCredentials(config.optBoolean(PROP_USE_CREDENTIALS))
|
|
142
|
+
headers(fromJSONObjectToMap(config.optJSONObject(PROP_HEADERS)))
|
|
143
|
+
queryParameters(fromJSONObjectToMap(config.optJSONObject(PROP_QUERY_PARAMETERS)))
|
|
144
|
+
keys(keys.toTypedArray())
|
|
145
|
+
}.build()
|
|
146
|
+
}
|
|
147
|
+
|
|
116
148
|
private fun licenseTypeFromString(str: String?): LicenseType? {
|
|
117
149
|
return when (str) {
|
|
118
150
|
PROP_LICENSE_TYPE_PERSISTENT -> LicenseType.PERSISTENT
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
package com.theoplayer.presentation
|
|
2
2
|
|
|
3
3
|
import android.util.Log
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
4
6
|
import android.view.ViewTreeObserver
|
|
7
|
+
import androidx.core.view.children
|
|
5
8
|
import com.facebook.react.views.view.ReactViewGroup
|
|
9
|
+
import com.theoplayer.ReactTHEOplayerView
|
|
6
10
|
|
|
7
|
-
private val TAG = "FSLayoutObserver"
|
|
11
|
+
private const val TAG = "FSLayoutObserver"
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* FullScreenLayoutObserver makes sure that the React Native view does not get the layout
|
|
11
|
-
* defined in
|
|
15
|
+
* defined in ReactNative during fullscreen presentation mode. We want to enforce fullscreen
|
|
12
16
|
* position & size.
|
|
17
|
+
* Similarly for picture-in-picture presentation mode, enforce a view that stretches the whole screen.
|
|
13
18
|
*/
|
|
14
19
|
class FullScreenLayoutObserver {
|
|
15
20
|
private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
|
@@ -20,13 +25,22 @@ class FullScreenLayoutObserver {
|
|
|
20
25
|
Log.w(TAG, "A previously attached ViewGroup was not properly detached.")
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
viewGroup?.let {
|
|
28
|
+
viewGroup?.let { reactPlayerGroup ->
|
|
24
29
|
globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
val root = getRootViewFrom(reactPlayerGroup)
|
|
31
|
+
reactPlayerGroup.post {
|
|
32
|
+
applyOnViewTree(reactPlayerGroup) { view ->
|
|
33
|
+
if (view == reactPlayerGroup || view is ReactTHEOplayerView) {
|
|
34
|
+
view.measure(
|
|
35
|
+
View.MeasureSpec.makeMeasureSpec(root.width, View.MeasureSpec.EXACTLY),
|
|
36
|
+
View.MeasureSpec.makeMeasureSpec(root.height, View.MeasureSpec.EXACTLY)
|
|
37
|
+
)
|
|
38
|
+
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
27
41
|
}
|
|
28
42
|
}
|
|
29
|
-
|
|
43
|
+
reactPlayerGroup.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
|
|
30
44
|
attached = viewGroup
|
|
31
45
|
}
|
|
32
46
|
}
|
|
@@ -37,3 +51,24 @@ class FullScreenLayoutObserver {
|
|
|
37
51
|
globalLayoutListener = null
|
|
38
52
|
}
|
|
39
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Find the view root, most likely the decorView
|
|
57
|
+
*/
|
|
58
|
+
fun getRootViewFrom(view: View): View {
|
|
59
|
+
var current = view
|
|
60
|
+
while (current.parent is View) {
|
|
61
|
+
current = current.parent as View
|
|
62
|
+
}
|
|
63
|
+
return current
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Conditionally apply an operation on each view in a hierarchy.
|
|
68
|
+
*/
|
|
69
|
+
fun applyOnViewTree(view: View, doOp: (View) -> Unit) {
|
|
70
|
+
doOp(view)
|
|
71
|
+
if (view is ViewGroup) {
|
|
72
|
+
view.children.forEach { ch -> applyOnViewTree(ch, doOp) }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -26,6 +26,12 @@ import com.theoplayer.android.api.error.ErrorCode
|
|
|
26
26
|
import com.theoplayer.android.api.error.THEOplayerException
|
|
27
27
|
import com.theoplayer.android.api.player.PresentationMode
|
|
28
28
|
|
|
29
|
+
const val IS_TRANSITION_INTO_PIP = "isTransitioningToPip"
|
|
30
|
+
const val IS_IN_PIP_MODE = "isInPictureInPictureMode"
|
|
31
|
+
const val ON_USER_LEAVE_HINT = "onUserLeaveHint"
|
|
32
|
+
const val ON_PIP_MODE_CHANGED = "onPictureInPictureModeChanged"
|
|
33
|
+
|
|
34
|
+
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
|
|
29
35
|
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
30
36
|
class PresentationManager(
|
|
31
37
|
private val viewCtx: ReactTHEOplayerContext,
|
|
@@ -34,34 +40,28 @@ class PresentationManager(
|
|
|
34
40
|
) {
|
|
35
41
|
private var supportsPip = false
|
|
36
42
|
private var onUserLeaveHintReceiver: BroadcastReceiver? = null
|
|
43
|
+
private var onUserLeaveHintRunnable: Runnable = Runnable {
|
|
44
|
+
if (pipConfig.startsAutomatically == true) {
|
|
45
|
+
setPresentation(PresentationMode.PICTURE_IN_PICTURE)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
37
48
|
private var onPictureInPictureModeChanged: BroadcastReceiver? = null
|
|
38
49
|
private val pipUtils: PipUtils = PipUtils(viewCtx, reactContext)
|
|
39
50
|
private val fullScreenLayoutObserver = FullScreenLayoutObserver()
|
|
40
51
|
private val playerGroupRestoreOptions by lazy {
|
|
41
52
|
PlayerGroupRestoreOptions()
|
|
42
53
|
}
|
|
43
|
-
|
|
44
54
|
var currentPresentationMode: PresentationMode = PresentationMode.INLINE
|
|
45
55
|
private set
|
|
46
56
|
var currentPresentationModeChangeContext: PresentationModeChangeContext? = null
|
|
47
57
|
private set
|
|
48
|
-
|
|
49
58
|
var pipConfig: PipConfig = PipConfig()
|
|
50
59
|
|
|
51
60
|
init {
|
|
52
|
-
onUserLeaveHintReceiver = object : BroadcastReceiver() {
|
|
53
|
-
override fun onReceive(context: Context?, intent: Intent?) {
|
|
54
|
-
// Optionally into PiP mode when the app goes to background.
|
|
55
|
-
if (pipConfig.startsAutomatically == true) {
|
|
56
|
-
setPresentation(PresentationMode.PICTURE_IN_PICTURE)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
61
|
onPictureInPictureModeChanged = object : BroadcastReceiver() {
|
|
61
62
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
62
|
-
val transitioningToPip = intent
|
|
63
|
-
|
|
64
|
-
val inPip = intent?.getBooleanExtra("isInPictureInPictureMode", false) ?: false
|
|
63
|
+
val transitioningToPip = intent?.getBooleanExtra(IS_TRANSITION_INTO_PIP, false) ?: false
|
|
64
|
+
val inPip = intent?.getBooleanExtra(IS_IN_PIP_MODE, false) ?: false
|
|
65
65
|
// Dispatch event on every PiP mode change
|
|
66
66
|
when {
|
|
67
67
|
transitioningToPip -> onEnterPip(true)
|
|
@@ -75,35 +75,50 @@ class PresentationManager(
|
|
|
75
75
|
reactContext.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
reactContext.currentActivity?.let { activity ->
|
|
79
|
+
// On Android 16+, the broadcasted onUserLeaveHint comes too late to activate PiP presentation
|
|
80
|
+
// mode, and the activity will not go into PiP when the user backgrounds the app. In this case
|
|
81
|
+
// we rely on the newer addOnUserLeaveHintListener and ignore the broadcast event.
|
|
82
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
83
|
+
onUserLeaveHintReceiver = object : BroadcastReceiver() {
|
|
84
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
85
|
+
onUserLeaveHintRunnable.run()
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
(activity as? ComponentActivity)?.addOnUserLeaveHintListener(onUserLeaveHintRunnable)
|
|
90
|
+
}
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
93
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
94
|
+
activity.registerReceiver(
|
|
95
|
+
onUserLeaveHintReceiver,
|
|
96
|
+
IntentFilter(ON_USER_LEAVE_HINT),
|
|
97
|
+
Context.RECEIVER_EXPORTED
|
|
98
|
+
)
|
|
99
|
+
activity.registerReceiver(
|
|
100
|
+
onPictureInPictureModeChanged,
|
|
101
|
+
IntentFilter(ON_PIP_MODE_CHANGED),
|
|
102
|
+
Context.RECEIVER_EXPORTED
|
|
103
|
+
)
|
|
104
|
+
} else {
|
|
105
|
+
activity.registerReceiver(onUserLeaveHintReceiver, IntentFilter(ON_USER_LEAVE_HINT))
|
|
106
|
+
activity.registerReceiver(onPictureInPictureModeChanged, IntentFilter(ON_PIP_MODE_CHANGED))
|
|
107
|
+
}
|
|
97
108
|
}
|
|
98
109
|
}
|
|
99
110
|
|
|
100
111
|
fun destroy() {
|
|
101
112
|
try {
|
|
102
|
-
reactContext.currentActivity?.
|
|
103
|
-
|
|
113
|
+
reactContext.currentActivity?.let { activity ->
|
|
114
|
+
activity.unregisterReceiver(onUserLeaveHintReceiver)
|
|
115
|
+
(activity as? ComponentActivity)?.addOnUserLeaveHintListener(onUserLeaveHintRunnable)
|
|
116
|
+
activity.unregisterReceiver(onPictureInPictureModeChanged)
|
|
117
|
+
}
|
|
118
|
+
|
|
104
119
|
fullScreenLayoutObserver.remove()
|
|
105
120
|
pipUtils.destroy()
|
|
106
|
-
} catch (
|
|
121
|
+
} catch (_: Exception) {
|
|
107
122
|
}
|
|
108
123
|
}
|
|
109
124
|
|
|
@@ -126,62 +141,60 @@ class PresentationManager(
|
|
|
126
141
|
|
|
127
142
|
private fun enterPip() {
|
|
128
143
|
// PiP not supported
|
|
129
|
-
if (!supportsPip || Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
|
130
|
-
return
|
|
131
|
-
}
|
|
144
|
+
if (!supportsPip || Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
132
145
|
|
|
133
146
|
// Already in right PiP state?
|
|
134
|
-
if (currentPresentationMode == PresentationMode.PICTURE_IN_PICTURE)
|
|
135
|
-
return
|
|
136
|
-
}
|
|
147
|
+
if (currentPresentationMode == PresentationMode.PICTURE_IN_PICTURE) return
|
|
137
148
|
|
|
138
149
|
// Check to see whether this activity is in the process of finishing, either because you
|
|
139
150
|
// called finish on it or someone else has requested that it finished.
|
|
140
|
-
if (reactContext.currentActivity?.isFinishing == true)
|
|
141
|
-
return
|
|
142
|
-
}
|
|
151
|
+
if (reactContext.currentActivity?.isFinishing == true) return
|
|
143
152
|
|
|
144
153
|
// Check whether the special permission Picture-in-Picture is given.
|
|
145
|
-
if (!hasPipPermission())
|
|
146
|
-
return
|
|
147
|
-
}
|
|
154
|
+
if (!hasPipPermission()) return
|
|
148
155
|
|
|
149
156
|
try {
|
|
150
157
|
pipUtils.enable()
|
|
151
158
|
reactContext.currentActivity?.enterPictureInPictureMode(pipUtils.getPipParams())
|
|
152
|
-
if (BuildConfig.REPARENT_ON_PIP && pipConfig.reparentPip == true) {
|
|
153
|
-
reparentPlayerToRoot()
|
|
154
|
-
}
|
|
155
159
|
} catch (_: Exception) {
|
|
156
160
|
onPipError()
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Called when the transition into PiP either starts (transitioningToPip = true)
|
|
166
|
+
* or after it ends (transitioningToPip = false)
|
|
167
|
+
*/
|
|
160
168
|
private fun onEnterPip(transitioningToPip: Boolean = false) {
|
|
169
|
+
if (BuildConfig.REPARENT_ON_PIP && !transitioningToPip && pipConfig.reparentPip == true) {
|
|
170
|
+
reparentPlayerToRoot()
|
|
171
|
+
}
|
|
172
|
+
|
|
161
173
|
updatePresentationMode(
|
|
162
174
|
PresentationMode.PICTURE_IN_PICTURE,
|
|
163
|
-
if (transitioningToPip)
|
|
164
|
-
PresentationModeChangeContext(PresentationModeChangePipContext.TRANSITIONING_TO_PIP)
|
|
175
|
+
if (transitioningToPip) PresentationModeChangeContext(PresentationModeChangePipContext.TRANSITIONING_TO_PIP)
|
|
165
176
|
else null
|
|
166
177
|
)
|
|
167
178
|
}
|
|
168
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Called when the PiP exit transition starts.
|
|
182
|
+
*/
|
|
169
183
|
private fun onExitPip() {
|
|
170
184
|
val pipCtx: PresentationModeChangePipContext =
|
|
171
|
-
if ((reactContext.currentActivity as? ComponentActivity)
|
|
172
|
-
?.lifecycle?.currentState == Lifecycle.State.CREATED
|
|
173
|
-
) {
|
|
185
|
+
if ((reactContext.currentActivity as? ComponentActivity)?.lifecycle?.currentState == Lifecycle.State.CREATED) {
|
|
174
186
|
PresentationModeChangePipContext.CLOSED
|
|
175
187
|
} else {
|
|
176
188
|
PresentationModeChangePipContext.RESTORED
|
|
177
189
|
}
|
|
178
|
-
if (BuildConfig.REPARENT_ON_PIP) {
|
|
190
|
+
if (BuildConfig.REPARENT_ON_PIP && pipConfig.reparentPip == true) {
|
|
179
191
|
reparentPlayerToOriginal()
|
|
180
192
|
}
|
|
181
193
|
updatePresentationMode(PresentationMode.INLINE, PresentationModeChangeContext(pipCtx))
|
|
182
194
|
pipUtils.disable()
|
|
183
195
|
}
|
|
184
196
|
|
|
197
|
+
@Suppress("DEPRECATION")
|
|
185
198
|
private fun hasPipPermission(): Boolean {
|
|
186
199
|
val appOps = reactContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager?
|
|
187
200
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
@@ -192,7 +205,6 @@ class PresentationManager(
|
|
|
192
205
|
reactContext.packageName
|
|
193
206
|
) == AppOpsManager.MODE_ALLOWED
|
|
194
207
|
} else {
|
|
195
|
-
@Suppress("DEPRECATION")
|
|
196
208
|
appOps?.checkOpNoThrow(
|
|
197
209
|
AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
|
|
198
210
|
reactContext.applicationInfo.uid,
|
|
@@ -210,9 +222,7 @@ class PresentationManager(
|
|
|
210
222
|
}
|
|
211
223
|
|
|
212
224
|
private fun setFullscreen(fullscreen: Boolean) {
|
|
213
|
-
if ((fullscreen && currentPresentationMode == PresentationMode.FULLSCREEN) ||
|
|
214
|
-
(!fullscreen && currentPresentationMode == PresentationMode.INLINE)
|
|
215
|
-
) {
|
|
225
|
+
if ((fullscreen && currentPresentationMode == PresentationMode.FULLSCREEN) || (!fullscreen && currentPresentationMode == PresentationMode.INLINE)) {
|
|
216
226
|
return
|
|
217
227
|
}
|
|
218
228
|
val activity = reactContext.currentActivity ?: return
|
|
@@ -254,8 +264,7 @@ class PresentationManager(
|
|
|
254
264
|
val activity = reactContext.currentActivity ?: return null
|
|
255
265
|
// Try to search in parents and as a fallback option from root to bottom using depth-first order
|
|
256
266
|
return reactPlayerGroup?.getClosestParentOfType()
|
|
257
|
-
?: (activity.window.decorView.rootView as? ViewGroup)
|
|
258
|
-
?.getClosestParentOfType(false)
|
|
267
|
+
?: (activity.window.decorView.rootView as? ViewGroup)?.getClosestParentOfType(false)
|
|
259
268
|
}
|
|
260
269
|
|
|
261
270
|
private fun reparentPlayerToRoot() {
|
|
@@ -281,7 +290,9 @@ class PresentationManager(
|
|
|
281
290
|
|
|
282
291
|
// Re-parent the playerViewGroup from the root node to its original parent
|
|
283
292
|
removeView(playerGroup)
|
|
284
|
-
playerGroupRestoreOptions.parentNode?.addView(
|
|
293
|
+
playerGroupRestoreOptions.parentNode?.addView(
|
|
294
|
+
playerGroup, playerGroupRestoreOptions.childIndex ?: 0
|
|
295
|
+
)
|
|
285
296
|
playerGroupRestoreOptions.reset()
|
|
286
297
|
}
|
|
287
298
|
}
|
|
@@ -289,12 +300,9 @@ class PresentationManager(
|
|
|
289
300
|
// endregion
|
|
290
301
|
|
|
291
302
|
private fun updatePresentationMode(
|
|
292
|
-
presentationMode: PresentationMode,
|
|
293
|
-
context: PresentationModeChangeContext? = null
|
|
303
|
+
presentationMode: PresentationMode, context: PresentationModeChangeContext? = null
|
|
294
304
|
) {
|
|
295
|
-
if (presentationMode == currentPresentationMode &&
|
|
296
|
-
context == currentPresentationModeChangeContext
|
|
297
|
-
) {
|
|
305
|
+
if (presentationMode == currentPresentationMode && context == currentPresentationModeChangeContext) {
|
|
298
306
|
return
|
|
299
307
|
}
|
|
300
308
|
val prevPresentationMode = currentPresentationMode
|
|
@@ -2,6 +2,7 @@ package com.theoplayer.source
|
|
|
2
2
|
|
|
3
3
|
import android.text.TextUtils
|
|
4
4
|
import android.util.Log
|
|
5
|
+
import androidx.core.net.toUri
|
|
5
6
|
import com.google.gson.Gson
|
|
6
7
|
import com.theoplayer.android.api.ads.theoads.TheoAdDescription
|
|
7
8
|
import com.theoplayer.android.api.error.THEOplayerException
|
|
@@ -271,38 +272,17 @@ class SourceAdapter {
|
|
|
271
272
|
private fun parseSourceType(jsonTypedSource: JSONObject): SourceType? {
|
|
272
273
|
val type = jsonTypedSource.optString(PROP_TYPE)
|
|
273
274
|
if (type.isNotEmpty()) {
|
|
274
|
-
|
|
275
|
-
return SourceType.DASH
|
|
276
|
-
}
|
|
277
|
-
if ("application/x-mpegurl" == type) {
|
|
278
|
-
return SourceType.HLSX
|
|
279
|
-
}
|
|
280
|
-
if ("application/vnd.theo.hesp+json" == type) {
|
|
281
|
-
return SourceType.HESP
|
|
282
|
-
}
|
|
283
|
-
if ("application/vnd.apple.mpegurl" == type) {
|
|
284
|
-
return SourceType.HLS
|
|
285
|
-
}
|
|
286
|
-
if ("video/mp4" == type) {
|
|
287
|
-
return SourceType.MP4
|
|
288
|
-
}
|
|
289
|
-
if ("audio/mpeg" == type) {
|
|
290
|
-
return SourceType.MP3
|
|
291
|
-
}
|
|
275
|
+
SourceType.entries.find { it.mimeType == type }?.let { return it }
|
|
292
276
|
} else {
|
|
293
277
|
// No type given, check for known extension.
|
|
294
|
-
val src = jsonTypedSource.optString(PROP_SRC)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return SourceType.MP4
|
|
303
|
-
}
|
|
304
|
-
if (src.endsWith(".mp3")) {
|
|
305
|
-
return SourceType.MP3
|
|
278
|
+
val src = jsonTypedSource.optString(PROP_SRC).toUri()
|
|
279
|
+
src.lastPathSegment?.let { fileName ->
|
|
280
|
+
when {
|
|
281
|
+
fileName.endsWith(".mpd") -> return SourceType.DASH
|
|
282
|
+
fileName.endsWith(".m3u8") -> return SourceType.HLSX
|
|
283
|
+
fileName.endsWith(".mp4") -> return SourceType.MP4
|
|
284
|
+
fileName.endsWith(".mp3") -> return SourceType.MP3
|
|
285
|
+
}
|
|
306
286
|
}
|
|
307
287
|
}
|
|
308
288
|
return null
|
|
@@ -366,7 +346,7 @@ class SourceAdapter {
|
|
|
366
346
|
overrideLayout = parseOverrideLayout(jsonAdDescription.optString(PROP_OVERRIDE_LAYOUT)),
|
|
367
347
|
useId3 = jsonAdDescription.optBoolean(PROP_USE_ID3, false),
|
|
368
348
|
retrievePodIdURI = jsonAdDescription.optString(PROP_RETRIEVE_POD_ID_URI).takeIf { it.isNotEmpty() },
|
|
369
|
-
initializationDelay = jsonAdDescription.optDouble(PROP_INITIALIZATION_DELAY),
|
|
349
|
+
initializationDelay = jsonAdDescription.optDouble(PROP_INITIALIZATION_DELAY).takeIf { it.isFinite() },
|
|
370
350
|
sseEndpoint = jsonAdDescription.optString(PROP_SSE_ENDPOINT).takeIf { it.isNotEmpty() },
|
|
371
351
|
)
|
|
372
352
|
}
|
|
@@ -10,12 +10,12 @@ import com.theoplayer.android.api.theolive.KeySystemConfiguration
|
|
|
10
10
|
private const val PROP_HESP_SRC = "hespSrc"
|
|
11
11
|
private const val PROP_HLS_SRC = "hlsSrc"
|
|
12
12
|
private const val PROP_AD_SRC = "adSrc"
|
|
13
|
+
private const val PROP_CDN = "cdn"
|
|
13
14
|
private const val PROP_TARGET_LATENCY = "targetLatency"
|
|
14
15
|
private const val PROP_WEIGHT = "weight"
|
|
15
16
|
private const val PROP_PRIORITY = "priority"
|
|
16
17
|
private const val PROP_CONTENT_PROTECTION = "contentProtection"
|
|
17
18
|
private const val PROP_INTEGRATION = "integration"
|
|
18
|
-
|
|
19
19
|
private const val PROP_WIDEVINE = "widevine"
|
|
20
20
|
private const val PROP_PLAYREADY = "playready"
|
|
21
21
|
private const val PROP_FAIRPLAY = "fairplay"
|
|
@@ -29,6 +29,7 @@ object EndpointAdapter {
|
|
|
29
29
|
endPoint.hespSrc?.let { putString(PROP_HESP_SRC, it) }
|
|
30
30
|
endPoint.hlsSrc?.let { putString(PROP_HLS_SRC, it) }
|
|
31
31
|
endPoint.adSrc?.let { putString(PROP_AD_SRC, it) }
|
|
32
|
+
endPoint.cdn?.let { putString(PROP_CDN, it) }
|
|
32
33
|
endPoint.targetLatency?.let { putDouble(PROP_TARGET_LATENCY, it) }
|
|
33
34
|
putInt(PROP_WEIGHT, endPoint.weight)
|
|
34
35
|
putInt(PROP_PRIORITY, endPoint.priority)
|
|
@@ -8,11 +8,12 @@ import com.theoplayer.android.api.event.player.theolive.EndpointLoadedEvent
|
|
|
8
8
|
import com.theoplayer.android.api.event.player.theolive.IntentToFallbackEvent
|
|
9
9
|
import com.theoplayer.android.api.event.player.theolive.TheoLiveEventTypes
|
|
10
10
|
import com.theoplayer.android.api.player.theolive.TheoLive
|
|
11
|
+
import com.theoplayer.util.PayloadBuilder
|
|
11
12
|
|
|
12
13
|
private const val EVENT_PROP_TYPE = "type"
|
|
13
|
-
|
|
14
14
|
private const val EVENT_PROP_DISTRIBUTION_ID = "distributionId"
|
|
15
15
|
private const val EVENT_PROP_ENDPOINT = "endpoint"
|
|
16
|
+
private const val EVENT_PROP_REASON = "reason"
|
|
16
17
|
|
|
17
18
|
class THEOliveEventAdapter(private val theoLiveApi: TheoLive, private val emitter: Emitter) {
|
|
18
19
|
|
|
@@ -55,9 +56,12 @@ class THEOliveEventAdapter(private val theoLiveApi: TheoLive, private val emitte
|
|
|
55
56
|
})
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
private fun onIntentOfFallback(
|
|
59
|
+
private fun onIntentOfFallback(event: IntentToFallbackEvent) {
|
|
59
60
|
emitter.emit(Arguments.createMap().apply {
|
|
60
61
|
putString(EVENT_PROP_TYPE, "intenttofallback")
|
|
62
|
+
event.reason?.let {
|
|
63
|
+
putMap(EVENT_PROP_REASON, PayloadBuilder().error(it.code.id.toString(), it.message).build())
|
|
64
|
+
}
|
|
61
65
|
})
|
|
62
66
|
}
|
|
63
67
|
}
|