react-native-theoplayer 3.5.0 → 3.7.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 +17 -0
- package/README.md +1 -0
- package/android/src/main/java/com/theoplayer/PlayerEventEmitter.kt +1 -1
- package/android/src/main/java/com/theoplayer/broadcast/DefaultEventDispatcher.kt +2 -1
- package/android/src/main/java/com/theoplayer/cast/CastEventAdapter.kt +1 -0
- package/android/src/main/java/com/theoplayer/drm/ContentProtectionModule.kt +2 -0
- package/android/src/main/java/com/theoplayer/media/MediaNotificationBuilder.kt +11 -0
- package/android/src/main/java/com/theoplayer/media/MediaPlaybackService.kt +31 -19
- package/android/src/main/java/com/theoplayer/presentation/PresentationManager.kt +41 -1
- package/android/src/main/res/drawable/ic_notification_large.png +0 -0
- package/ios/THEOplayerRCTMainEventHandler.swift +6 -24
- package/ios/THEOplayerRCTPlayerAPI.swift +3 -16
- package/ios/THEOplayerRCTTextTrackEventHandler.swift +0 -9
- package/ios/THEOplayerRCTTrackMetadataAggregator.swift +1 -1
- package/ios/THEOplayerRCTView.swift +12 -10
- package/ios/Theoplayer-Bridging-Header.h +1 -0
- package/ios/eventBroadcasting/THEOplayerRCTBroadcastEventHandler.swift +1 -1
- package/ios/pip/THEOplayerRCTView+PipConfig.swift +2 -2
- package/ios/{THEOplayerRCTPresentationModeContext.swift → presentationMode/THEOplayerRCTPresentationModeContext.swift} +4 -8
- package/ios/presentationMode/THEOplayerRCTPresentationModeManager.swift +159 -0
- package/ios/presentationMode/THEOplayerRCTView+PresentationMode.swift +11 -0
- package/ios/{THEOplayerRCTSideloadedMetadataTrackHandler.swift → sideloadedMetadata/THEOplayerRCTSideloadedMetadataProcessor.swift} +14 -4
- package/ios/sideloadedMetadata/THEOplayerRCTSideloadedMetadataTrackEventHandler.swift +28 -0
- package/lib/commonjs/internal/THEOplayerView.js +24 -3
- package/lib/commonjs/internal/THEOplayerView.js.map +1 -1
- package/lib/commonjs/internal/adapter/THEOplayerAdapter.js +3 -0
- package/lib/commonjs/internal/adapter/THEOplayerAdapter.js.map +1 -1
- package/lib/commonjs/internal/utils/Dimensions.js +31 -0
- package/lib/commonjs/internal/utils/Dimensions.js.map +1 -0
- package/lib/module/internal/THEOplayerView.js +26 -5
- package/lib/module/internal/THEOplayerView.js.map +1 -1
- package/lib/module/internal/adapter/THEOplayerAdapter.js +5 -2
- package/lib/module/internal/adapter/THEOplayerAdapter.js.map +1 -1
- package/lib/module/internal/utils/Dimensions.js +26 -0
- package/lib/module/internal/utils/Dimensions.js.map +1 -0
- package/lib/typescript/internal/THEOplayerView.d.ts +7 -1
- package/lib/typescript/internal/utils/Dimensions.d.ts +5 -0
- package/package.json +1 -1
- package/react-native-theoplayer.podspec +5 -5
- package/src/internal/THEOplayerView.tsx +25 -5
- package/src/internal/adapter/THEOplayerAdapter.ts +11 -7
- package/src/internal/utils/Dimensions.ts +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ 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
|
+
## [3.7.0] - 24-02-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added fullscreen presentation mode support for iOS and Android. More info on the [documentation](./doc/fullscreen.md) page.
|
|
13
|
+
|
|
14
|
+
## [3.6.0] - 24-02-02
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Fixed a build issue on the iOS bridge caused by the deprecated DispatchDispatch protocol.
|
|
19
|
+
- Fixed an issue on Android where the `MediaPlaybackService` would sometimes crash with a `ForegroundServiceDidNotStartInTimeException` exception.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Added the ability to override both small and large notification icons in Android with `ic_notification_small` and `ic_notification_large` resources respectively.
|
|
24
|
+
|
|
8
25
|
## [3.5.0] - 24-01-30
|
|
9
26
|
|
|
10
27
|
### Added
|
package/README.md
CHANGED
|
@@ -101,6 +101,7 @@ and discussed in the next section. Finally, an overview of features, limitations
|
|
|
101
101
|
- [Casting with Chromecast and Airplay](./doc/cast.md)
|
|
102
102
|
- [Custom iOS framework](./doc/custom-ios-framework.md)
|
|
103
103
|
- [Digital Rights Management (DRM)](./doc/drm.md)
|
|
104
|
+
- [Fullscreen presentation](./doc/fullscreen.md)
|
|
104
105
|
- [Media Caching](./doc/media_caching.md)
|
|
105
106
|
- [Migrating to `react-native-theoplayer` v2.x](./doc/migrating_v2.md)
|
|
106
107
|
- [Picture-in-Picture (PiP)](./doc/pip.md)
|
|
@@ -294,7 +294,7 @@ class PlayerEventEmitter internal constructor(
|
|
|
294
294
|
)
|
|
295
295
|
payload.putMap(EVENT_PROP_VERSION, WritableNativeMap().apply {
|
|
296
296
|
putString(EVENT_PROP_VERSION, THEOplayerGlobal.getVersion())
|
|
297
|
-
putString(EVENT_PROP_SUITE_VERSION,
|
|
297
|
+
putString(EVENT_PROP_SUITE_VERSION, "")
|
|
298
298
|
})
|
|
299
299
|
|
|
300
300
|
// Notify the player is ready
|
|
@@ -21,7 +21,8 @@ open class DefaultEventDispatcher: EventDispatcher<Event<*>> {
|
|
|
21
21
|
|
|
22
22
|
fun dispatchEvent(event: Event<*>) {
|
|
23
23
|
_listeners[event.type]?.forEach { listener ->
|
|
24
|
-
|
|
24
|
+
@Suppress("UNCHECKED_CAST")
|
|
25
|
+
(listener as EventListener<Event<*>>).handleEvent(event)
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -41,6 +41,7 @@ class CastEventAdapter(private val castApi: Cast, private val emitter: Emitter)
|
|
|
41
41
|
|
|
42
42
|
private fun serializeError(error: CastError): WritableMap {
|
|
43
43
|
val errorPayload = Arguments.createMap()
|
|
44
|
+
@Suppress("SENSELESS_NULL_IN_WHEN")
|
|
44
45
|
errorPayload.putString(
|
|
45
46
|
EVENT_PROP_ERROR_CODE,
|
|
46
47
|
when (error.errorCode) {
|
|
@@ -216,10 +216,12 @@ class ContentProtectionModule(private val context: ReactApplicationContext) :
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
@ReactMethod
|
|
219
|
+
@Suppress("UNUSED_PARAMETER")
|
|
219
220
|
fun addListener(eventName: String?) {
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
@ReactMethod
|
|
224
|
+
@Suppress("UNUSED_PARAMETER")
|
|
223
225
|
fun removeListeners(count: Int?) {
|
|
224
226
|
}
|
|
225
227
|
|
|
@@ -5,6 +5,7 @@ import android.app.NotificationChannel
|
|
|
5
5
|
import android.app.NotificationManager
|
|
6
6
|
import android.content.Context
|
|
7
7
|
import android.graphics.Bitmap
|
|
8
|
+
import android.graphics.BitmapFactory
|
|
8
9
|
import android.net.Uri
|
|
9
10
|
import android.os.Build
|
|
10
11
|
import android.support.v4.media.session.MediaSessionCompat
|
|
@@ -195,3 +196,13 @@ fun fetchImageFromUri(uri: Uri?, block: (Bitmap?) -> Unit) {
|
|
|
195
196
|
)
|
|
196
197
|
}
|
|
197
198
|
}
|
|
199
|
+
|
|
200
|
+
fun loadPlaceHolderIcon(context: Context, res: Int = R.drawable.ic_notification_large): Bitmap? {
|
|
201
|
+
return try {
|
|
202
|
+
BitmapFactory.decodeResource(context.resources, res)
|
|
203
|
+
} catch(e: Exception) {
|
|
204
|
+
// Make sure we never crash on trying to decode a possibly overridden icon resource.
|
|
205
|
+
Log.w(TAG, "Failed to decode placeHolderIcon: ${e.message}")
|
|
206
|
+
null
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -3,6 +3,7 @@ package com.theoplayer.media
|
|
|
3
3
|
import android.app.*
|
|
4
4
|
import android.content.Intent
|
|
5
5
|
import android.content.pm.ServiceInfo
|
|
6
|
+
import android.graphics.Bitmap
|
|
6
7
|
import android.os.Binder
|
|
7
8
|
import android.os.Build
|
|
8
9
|
import android.os.Bundle
|
|
@@ -96,6 +97,8 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
|
|
|
96
97
|
Log.w(TAG, "Failed to start foreground service: ${e.message}")
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
// Quickly post a notification and already call startForeground. This has to happen within 5s
|
|
101
|
+
// after creating the service to avoid a ForegroundServiceDidNotStartInTimeException
|
|
99
102
|
updateNotification(PlaybackStateCompat.STATE_PLAYING)
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -222,6 +225,7 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
|
|
|
222
225
|
}
|
|
223
226
|
}
|
|
224
227
|
|
|
228
|
+
@Suppress("UNUSED_PARAMETER")
|
|
225
229
|
private fun allowBrowsing(clientPackageName: String, clientUid: Int): Boolean {
|
|
226
230
|
// Only allow browsing from the same package
|
|
227
231
|
return TextUtils.equals(clientPackageName, packageName)
|
|
@@ -264,26 +268,13 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
|
|
|
264
268
|
// When a service runs in the foreground, it must display a notification, ideally
|
|
265
269
|
// with one or more transport controls. The notification should also include useful
|
|
266
270
|
// information from the session's metadata.
|
|
267
|
-
|
|
271
|
+
|
|
272
|
+
// Get the foreground service started in time before fetching an icon.
|
|
273
|
+
startForegroundWithPlaybackState(playbackState, loadPlaceHolderIcon(this))
|
|
274
|
+
|
|
275
|
+
// Fetch the correct large icon asynchronously.
|
|
268
276
|
fetchImageFromUri(mediaSession.controller.metadata?.description?.iconUri) { largeIcon ->
|
|
269
|
-
|
|
270
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
271
|
-
startForeground(
|
|
272
|
-
NOTIFICATION_ID,
|
|
273
|
-
notificationBuilder.build(playbackState, largeIcon, enableMediaControls),
|
|
274
|
-
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
|
275
|
-
)
|
|
276
|
-
} else {
|
|
277
|
-
startForeground(
|
|
278
|
-
NOTIFICATION_ID,
|
|
279
|
-
notificationBuilder.build(playbackState, largeIcon, enableMediaControls)
|
|
280
|
-
)
|
|
281
|
-
}
|
|
282
|
-
} catch (e: IllegalStateException) {
|
|
283
|
-
// Make sure that app does not crash in case anything goes wrong with starting the service.
|
|
284
|
-
// https://issuetracker.google.com/issues/229000935
|
|
285
|
-
Log.w(TAG, "Failed to start foreground service: ${e.message}")
|
|
286
|
-
}
|
|
277
|
+
startForegroundWithPlaybackState(playbackState, largeIcon)
|
|
287
278
|
}
|
|
288
279
|
}
|
|
289
280
|
PlaybackStateCompat.STATE_STOPPED -> {
|
|
@@ -303,4 +294,25 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
|
|
|
303
294
|
}
|
|
304
295
|
}
|
|
305
296
|
}
|
|
297
|
+
|
|
298
|
+
private fun startForegroundWithPlaybackState(@PlaybackStateCompat.State playbackState: Int, largeIcon: Bitmap? = null) {
|
|
299
|
+
try {
|
|
300
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
301
|
+
startForeground(
|
|
302
|
+
NOTIFICATION_ID,
|
|
303
|
+
notificationBuilder.build(playbackState, largeIcon, enableMediaControls),
|
|
304
|
+
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
|
305
|
+
)
|
|
306
|
+
} else {
|
|
307
|
+
startForeground(
|
|
308
|
+
NOTIFICATION_ID,
|
|
309
|
+
notificationBuilder.build(playbackState, largeIcon, enableMediaControls)
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
} catch (e: IllegalStateException) {
|
|
313
|
+
// Make sure that app does not crash in case anything goes wrong with starting the service.
|
|
314
|
+
// https://issuetracker.google.com/issues/229000935
|
|
315
|
+
Log.w(TAG, "Failed to start foreground service: ${e.message}")
|
|
316
|
+
}
|
|
317
|
+
}
|
|
306
318
|
}
|
|
@@ -8,11 +8,20 @@ import android.content.Intent
|
|
|
8
8
|
import android.content.IntentFilter
|
|
9
9
|
import android.content.pm.PackageManager
|
|
10
10
|
import android.os.Build
|
|
11
|
+
import android.view.View
|
|
12
|
+
import android.view.View.OnLayoutChangeListener
|
|
13
|
+
import android.view.ViewGroup
|
|
14
|
+
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
15
|
+
import android.view.ViewParent
|
|
11
16
|
import androidx.activity.ComponentActivity
|
|
12
17
|
import androidx.core.view.WindowInsetsCompat
|
|
13
18
|
import androidx.core.view.WindowInsetsControllerCompat
|
|
19
|
+
import androidx.core.view.children
|
|
20
|
+
import androidx.core.view.updateLayoutParams
|
|
14
21
|
import androidx.lifecycle.Lifecycle
|
|
22
|
+
import com.facebook.react.ReactRootView
|
|
15
23
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
24
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
16
25
|
import com.theoplayer.PlayerEventEmitter
|
|
17
26
|
import com.theoplayer.ReactTHEOplayerContext
|
|
18
27
|
import com.theoplayer.android.api.error.ErrorCode
|
|
@@ -28,7 +37,8 @@ class PresentationManager(
|
|
|
28
37
|
private var supportsPip = false
|
|
29
38
|
private var onUserLeaveHintReceiver: BroadcastReceiver? = null
|
|
30
39
|
private var onPictureInPictureModeChanged: BroadcastReceiver? = null
|
|
31
|
-
|
|
40
|
+
private var playerGroupParentNode: ViewGroup? = null
|
|
41
|
+
private var playerGroupChildIndex: Int? = null
|
|
32
42
|
private val pipUtils: PipUtils = PipUtils(viewCtx, reactContext)
|
|
33
43
|
|
|
34
44
|
var currentPresentationMode: PresentationMode = PresentationMode.INLINE
|
|
@@ -189,16 +199,38 @@ class PresentationManager(
|
|
|
189
199
|
}
|
|
190
200
|
val activity = reactContext.currentActivity ?: return
|
|
191
201
|
val window = activity.window
|
|
202
|
+
|
|
203
|
+
// Get the player's ReactViewGroup parent, which contains THEOplayerView and its children (typically the UI).
|
|
204
|
+
val reactPlayerGroup: ReactViewGroup? = getClosestParentOfType(this.viewCtx.playerView)
|
|
205
|
+
|
|
206
|
+
// Get ReactNative's root node or the render hiearchy
|
|
207
|
+
val root: ReactRootView? = getClosestParentOfType(reactPlayerGroup)
|
|
208
|
+
|
|
192
209
|
if (fullscreen) {
|
|
193
210
|
WindowInsetsControllerCompat(window, window.decorView).apply {
|
|
194
211
|
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
|
195
212
|
}.hide(WindowInsetsCompat.Type.systemBars())
|
|
196
213
|
updatePresentationMode(PresentationMode.FULLSCREEN)
|
|
214
|
+
|
|
215
|
+
playerGroupParentNode = (reactPlayerGroup?.parent as ReactViewGroup?)?.also { parent ->
|
|
216
|
+
playerGroupChildIndex = parent.indexOfChild(reactPlayerGroup)
|
|
217
|
+
// Re-parent the playerViewGroup to the root node
|
|
218
|
+
parent.removeView(reactPlayerGroup)
|
|
219
|
+
root?.addView(reactPlayerGroup)
|
|
220
|
+
}
|
|
197
221
|
} else {
|
|
198
222
|
WindowInsetsControllerCompat(window, window.decorView).show(
|
|
199
223
|
WindowInsetsCompat.Type.systemBars()
|
|
200
224
|
)
|
|
201
225
|
updatePresentationMode(PresentationMode.INLINE)
|
|
226
|
+
|
|
227
|
+
root?.run {
|
|
228
|
+
// Re-parent the playerViewGroup from the root node to its original parent
|
|
229
|
+
removeView(reactPlayerGroup)
|
|
230
|
+
playerGroupParentNode?.addView(reactPlayerGroup, playerGroupChildIndex ?: 0)
|
|
231
|
+
playerGroupParentNode = null
|
|
232
|
+
playerGroupChildIndex = null
|
|
233
|
+
}
|
|
202
234
|
}
|
|
203
235
|
}
|
|
204
236
|
|
|
@@ -224,3 +256,11 @@ class PresentationManager(
|
|
|
224
256
|
}
|
|
225
257
|
}
|
|
226
258
|
}
|
|
259
|
+
|
|
260
|
+
inline fun <reified T : View> getClosestParentOfType(view: View?): T? {
|
|
261
|
+
var parent: ViewParent? = view?.parent
|
|
262
|
+
while (parent != null && parent !is T) {
|
|
263
|
+
parent = parent.parent
|
|
264
|
+
}
|
|
265
|
+
return parent as? T
|
|
266
|
+
}
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// THEOplayerRCTMainEventHandler.swift
|
|
2
2
|
|
|
3
3
|
import Foundation
|
|
4
4
|
import THEOplayerSDK
|
|
@@ -7,7 +7,7 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
7
7
|
// MARK: Members
|
|
8
8
|
private weak var player: THEOplayer?
|
|
9
9
|
private weak var presentationModeContext: THEOplayerRCTPresentationModeContext?
|
|
10
|
-
private var
|
|
10
|
+
private var loadedMetadataTracksInfo: [[String:Any]] = []
|
|
11
11
|
|
|
12
12
|
// MARK: Events
|
|
13
13
|
var onNativePlay: RCTDirectEventBlock?
|
|
@@ -29,7 +29,6 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
29
29
|
var onNativeRateChange: RCTDirectEventBlock?
|
|
30
30
|
var onNativeWaiting: RCTDirectEventBlock?
|
|
31
31
|
var onNativeCanPlay: RCTDirectEventBlock?
|
|
32
|
-
var onNativePresentationModeChange: RCTDirectEventBlock?
|
|
33
32
|
|
|
34
33
|
// MARK: player Listeners
|
|
35
34
|
private var playListener: EventListener?
|
|
@@ -60,16 +59,15 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
// MARK: - player setup / breakdown
|
|
63
|
-
func setPlayer(_ player: THEOplayer
|
|
62
|
+
func setPlayer(_ player: THEOplayer) {
|
|
64
63
|
self.player = player
|
|
65
|
-
self.presentationModeContext = presentationModeContext
|
|
66
64
|
|
|
67
65
|
// attach listeners
|
|
68
66
|
self.attachListeners()
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
func
|
|
72
|
-
self.
|
|
69
|
+
func setLoadedMetadataTracksInfo(_ metadataTracksInfo: [[String:Any]]) {
|
|
70
|
+
self.loadedMetadataTracksInfo = metadataTracksInfo
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
// MARK: - attach/dettach main player Listeners
|
|
@@ -273,7 +271,7 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
273
271
|
if let wplayer = player,
|
|
274
272
|
let welf = self,
|
|
275
273
|
let forwardedLoadedMetadataEvent = self?.onNativeLoadedMetadata {
|
|
276
|
-
let metadata = THEOplayerRCTTrackMetadataAggregator.aggregateTrackMetadata(player: wplayer, metadataTracksInfo: welf.
|
|
274
|
+
let metadata = THEOplayerRCTTrackMetadataAggregator.aggregateTrackMetadata(player: wplayer, metadataTracksInfo: welf.loadedMetadataTracksInfo)
|
|
277
275
|
print(metadata)
|
|
278
276
|
forwardedLoadedMetadataEvent(metadata)
|
|
279
277
|
}
|
|
@@ -306,16 +304,6 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
306
304
|
}
|
|
307
305
|
}
|
|
308
306
|
if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] Waiting listener attached to THEOplayer") }
|
|
309
|
-
|
|
310
|
-
// PRESENTATION_MODE_CHANGE
|
|
311
|
-
self.presentationModeChangeListener = player.addEventListener(type: PlayerEventTypes.PRESENTATION_MODE_CHANGE) { [weak self] event in
|
|
312
|
-
if DEBUG_THEOPLAYER_EVENTS || true { PrintUtils.printLog(logText: "[NATIVE] Received PRESENTATION_MODE_CHANGE event from THEOplayer (to \(event.presentationMode._rawValue))") }
|
|
313
|
-
if let forwardedPresentationModeChangeEvent = self?.onNativePresentationModeChange,
|
|
314
|
-
let presentationModeContext = self?.presentationModeContext {
|
|
315
|
-
forwardedPresentationModeChangeEvent(presentationModeContext.eventContextForNewPresentationMode(event.presentationMode))
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] PresentationModeChange listener attached to THEOplayer") }
|
|
319
307
|
}
|
|
320
308
|
|
|
321
309
|
private func dettachListeners() {
|
|
@@ -430,11 +418,5 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
430
418
|
player.removeEventListener(type: PlayerEventTypes.CAN_PLAY, listener: canPlayListener)
|
|
431
419
|
if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] CanPlay listener dettached from THEOplayer") }
|
|
432
420
|
}
|
|
433
|
-
|
|
434
|
-
// PRESENTATION_MODE_CHANGE
|
|
435
|
-
if let presentationModeChangeListener = self.presentationModeChangeListener {
|
|
436
|
-
player.removeEventListener(type: PlayerEventTypes.PRESENTATION_MODE_CHANGE, listener: presentationModeChangeListener)
|
|
437
|
-
if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] PresentationModeChange listener dettached from THEOplayer") }
|
|
438
|
-
}
|
|
439
421
|
}
|
|
440
422
|
}
|
|
@@ -12,7 +12,6 @@ import THEOplayerConnectorSideloadedSubtitle
|
|
|
12
12
|
|
|
13
13
|
let ERROR_MESSAGE_PLAYER_ABR_UNSUPPORTED_FEATURE: String = "Setting an ABRconfig is not supported on iOS/tvOS."
|
|
14
14
|
let ERROR_MESSAGE_PLAYER_QUALITY_UNSUPPORTED_FEATURE: String = "Setting a target video quality is not supported on iOS/tvOS."
|
|
15
|
-
let ERROR_MESSAGE_PLAYER_FULLSCREEN_UNSUPPORTED_FEATURE: String = "Fullscreen presentationmode should be implemented on the RN level for iOS/tvOS."
|
|
16
15
|
|
|
17
16
|
let TTS_PROP_BACKGROUND_COLOR = "backgroundColor"
|
|
18
17
|
let TTS_PROP_EDGE_STYLE = "edgeStyle"
|
|
@@ -85,7 +84,6 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
85
84
|
@objc(setABRConfig:abrConfig:)
|
|
86
85
|
func setABRConfig(_ node: NSNumber, setABRConfig: NSDictionary) -> Void {
|
|
87
86
|
if DEBUG_PLAYER_API { print(ERROR_MESSAGE_PLAYER_ABR_UNSUPPORTED_FEATURE) }
|
|
88
|
-
return
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
@objc(setCurrentTime:time:)
|
|
@@ -135,19 +133,11 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
135
133
|
@objc(setPresentationMode:presentationMode:)
|
|
136
134
|
func setPresentationMode(_ node: NSNumber, presentationMode: String) -> Void {
|
|
137
135
|
DispatchQueue.main.async {
|
|
138
|
-
let
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
if let theView = self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView,
|
|
143
|
-
let player = theView.player {
|
|
144
|
-
if player.presentationMode != newPresentationMode {
|
|
145
|
-
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Changing TheoPlayer to \(presentationMode)") }
|
|
146
|
-
player.presentationMode = newPresentationMode
|
|
147
|
-
}
|
|
136
|
+
if let theView = self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView {
|
|
137
|
+
let newPresentationMode: PresentationMode = THEOplayerRCTTypeUtils.presentationModeFromString(presentationMode)
|
|
138
|
+
theView.setPresentationMode(newPresentationMode: newPresentationMode)
|
|
148
139
|
}
|
|
149
140
|
}
|
|
150
|
-
return
|
|
151
141
|
}
|
|
152
142
|
|
|
153
143
|
@objc(setAspectRatio:ratio:)
|
|
@@ -162,7 +152,6 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
162
152
|
}
|
|
163
153
|
}
|
|
164
154
|
}
|
|
165
|
-
return
|
|
166
155
|
}
|
|
167
156
|
|
|
168
157
|
@objc(setPipConfig:pipConfig:)
|
|
@@ -173,7 +162,6 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
173
162
|
theView.pipConfig = pipConfig
|
|
174
163
|
}
|
|
175
164
|
}
|
|
176
|
-
return
|
|
177
165
|
}
|
|
178
166
|
|
|
179
167
|
private func parsePipConfig(configDict: NSDictionary) -> PipConfig {
|
|
@@ -190,7 +178,6 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
190
178
|
theView.backgroundAudioConfig = newBackgroundAudioConfig
|
|
191
179
|
}
|
|
192
180
|
}
|
|
193
|
-
return
|
|
194
181
|
}
|
|
195
182
|
|
|
196
183
|
private func parseBackgroundAudioConfig(configDict: NSDictionary) -> BackgroundAudioConfig {
|
|
@@ -37,15 +37,6 @@ class THEOplayerRCTTextTrackEventHandler {
|
|
|
37
37
|
self.attachListeners()
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
func triggerAddMetadataTrackEvent(metadataTrackInfo: [String:Any]) {
|
|
41
|
-
if let addTrackEvent = self.onNativeTextTrackListEvent {
|
|
42
|
-
addTrackEvent([
|
|
43
|
-
"track" : metadataTrackInfo,
|
|
44
|
-
"type" : TrackListEventType.ADD_TRACK.rawValue
|
|
45
|
-
])
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
40
|
// MARK: - attach/dettach textTrackList Listeners
|
|
50
41
|
private func attachListeners() {
|
|
51
42
|
guard let player = self.player else {
|
|
@@ -247,7 +247,7 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
247
247
|
guard trackDescription.kind == .metadata, trackDescription.format == .WebVTT else { continue }
|
|
248
248
|
|
|
249
249
|
let urlString = trackDescription.src.absoluteString
|
|
250
|
-
|
|
250
|
+
THEOplayerRCTSideloadedMetadataProcessor.parseVtt(urlString) { cueArray in
|
|
251
251
|
if let cues = cueArray {
|
|
252
252
|
var track: [String:Any] = [:]
|
|
253
253
|
let trackUid = 1000 + trackIndex
|
|
@@ -11,12 +11,13 @@ public class THEOplayerRCTView: UIView {
|
|
|
11
11
|
public private(set) var broadcastEventHandler: THEOplayerRCTBroadcastEventHandler
|
|
12
12
|
var textTrackEventHandler: THEOplayerRCTTextTrackEventHandler
|
|
13
13
|
var mediaTrackEventHandler: THEOplayerRCTMediaTrackEventHandler
|
|
14
|
+
var metadataTrackEventHandler: THEOplayerRCTSideloadedMetadataTrackEventHandler
|
|
14
15
|
var adEventHandler: THEOplayerRCTAdsEventHandler
|
|
15
16
|
var castEventHandler: THEOplayerRCTCastEventHandler
|
|
17
|
+
var presentationModeManager: THEOplayerRCTPresentationModeManager
|
|
16
18
|
var nowPlayingManager: THEOplayerRCTNowPlayingManager
|
|
17
19
|
var remoteCommandsManager: THEOplayerRCTRemoteCommandsManager
|
|
18
20
|
var pipControlsManager: THEOplayerRCTPipControlsManager
|
|
19
|
-
var presentationModeContext = THEOplayerRCTPresentationModeContext()
|
|
20
21
|
var adsConfig = AdsConfig()
|
|
21
22
|
var castConfig = CastConfig()
|
|
22
23
|
var uiConfig = UIConfig()
|
|
@@ -48,8 +49,10 @@ public class THEOplayerRCTView: UIView {
|
|
|
48
49
|
self.broadcastEventHandler = THEOplayerRCTBroadcastEventHandler()
|
|
49
50
|
self.textTrackEventHandler = THEOplayerRCTTextTrackEventHandler()
|
|
50
51
|
self.mediaTrackEventHandler = THEOplayerRCTMediaTrackEventHandler()
|
|
52
|
+
self.metadataTrackEventHandler = THEOplayerRCTSideloadedMetadataTrackEventHandler()
|
|
51
53
|
self.adEventHandler = THEOplayerRCTAdsEventHandler()
|
|
52
54
|
self.castEventHandler = THEOplayerRCTCastEventHandler()
|
|
55
|
+
self.presentationModeManager = THEOplayerRCTPresentationModeManager()
|
|
53
56
|
self.nowPlayingManager = THEOplayerRCTNowPlayingManager()
|
|
54
57
|
self.remoteCommandsManager = THEOplayerRCTRemoteCommandsManager()
|
|
55
58
|
self.pipControlsManager = THEOplayerRCTPipControlsManager()
|
|
@@ -75,9 +78,10 @@ public class THEOplayerRCTView: UIView {
|
|
|
75
78
|
// Create new player instance
|
|
76
79
|
if let player = self.initPlayer() {
|
|
77
80
|
// Attach player instance to event handlers
|
|
78
|
-
self.mainEventHandler.setPlayer(player
|
|
81
|
+
self.mainEventHandler.setPlayer(player)
|
|
79
82
|
self.textTrackEventHandler.setPlayer(player)
|
|
80
83
|
self.mediaTrackEventHandler.setPlayer(player)
|
|
84
|
+
self.presentationModeManager.setPlayer(player, view: self)
|
|
81
85
|
self.adEventHandler.setPlayer(player)
|
|
82
86
|
self.castEventHandler.setPlayer(player)
|
|
83
87
|
self.nowPlayingManager.setPlayer(player)
|
|
@@ -157,13 +161,9 @@ public class THEOplayerRCTView: UIView {
|
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
func processMetadataTracks(metadataTrackDescriptions: [TextTrackDescription]?) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
for trackInfo in tracksInfo {
|
|
164
|
-
self.textTrackEventHandler.triggerAddMetadataTrackEvent(metadataTrackInfo: trackInfo)
|
|
165
|
-
}
|
|
166
|
-
}
|
|
164
|
+
THEOplayerRCTSideloadedMetadataProcessor.loadTrackInfoFromTrackDescriptions(metadataTrackDescriptions) { tracksInfo in
|
|
165
|
+
self.mainEventHandler.setLoadedMetadataTracksInfo(tracksInfo)
|
|
166
|
+
self.metadataTrackEventHandler.setLoadedMetadataTracksInfo(tracksInfo)
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -312,9 +312,11 @@ public class THEOplayerRCTView: UIView {
|
|
|
312
312
|
if DEBUG_VIEW { PrintUtils.printLog(logText: "[NATIVE] nativeCanPlay prop set.") }
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
// MARK: - Listener based PRESENTATIONMODE event bridging
|
|
316
|
+
|
|
315
317
|
@objc(setOnNativePresentationModeChange:)
|
|
316
318
|
func setOnNativePresentationModeChange(nativePresentationMode: @escaping RCTDirectEventBlock) {
|
|
317
|
-
self.
|
|
319
|
+
self.presentationModeManager.onNativePresentationModeChange = nativePresentationMode
|
|
318
320
|
if DEBUG_VIEW { PrintUtils.printLog(logText: "[NATIVE] nativePresentationMode prop set.") }
|
|
319
321
|
}
|
|
320
322
|
|
|
@@ -27,7 +27,7 @@ public class THEOplayerRCTBroadcastEventHandler: DefaultEventDispatcher {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
public class DefaultEventDispatcher: NSObject, THEOplayerSDK.EventDispatcherProtocol
|
|
30
|
+
public class DefaultEventDispatcher: NSObject, THEOplayerSDK.EventDispatcherProtocol {
|
|
31
31
|
private var eventListeners = [String: [EventListenerWrapper]]()
|
|
32
32
|
|
|
33
33
|
public func addEventListener<E>(type: THEOplayerSDK.EventType<E>, listener: @escaping (_ : E) -> ()) -> THEOplayerSDK.EventListener {
|
|
@@ -40,12 +40,12 @@ extension THEOplayerRCTView: AVPictureInPictureControllerDelegate {
|
|
|
40
40
|
// MARK: - AVPictureInPictureControllerDelegate
|
|
41
41
|
@available(tvOS 14.0, *)
|
|
42
42
|
public func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
|
43
|
-
self.presentationModeContext.pipContext = .PIP_CLOSED
|
|
43
|
+
self.presentationModeManager.presentationModeContext.pipContext = .PIP_CLOSED
|
|
44
44
|
self.pipControlsManager.willStartPip()
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
@available(tvOS 14.0, *)
|
|
48
48
|
public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
|
49
|
-
self.presentationModeContext.pipContext = .PIP_RESTORED
|
|
49
|
+
self.presentationModeManager.presentationModeContext.pipContext = .PIP_RESTORED
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -10,18 +10,14 @@ enum PipContext: String {
|
|
|
10
10
|
|
|
11
11
|
class THEOplayerRCTPresentationModeContext {
|
|
12
12
|
// MARK: Members
|
|
13
|
-
var currentPresentationMode: THEOplayerSDK.PresentationMode = .inline // TheoPlayer's initial presentationMode
|
|
14
13
|
var pipContext: PipContext = .PIP_CLOSED
|
|
15
14
|
|
|
16
|
-
func eventContextForNewPresentationMode(
|
|
17
|
-
let previousPresentationMode = self.currentPresentationMode
|
|
18
|
-
self.currentPresentationMode = newPresentationMode
|
|
19
|
-
|
|
15
|
+
func eventContextForNewPresentationMode(oldPresentationMode: PresentationMode, newPresentationMode: PresentationMode) -> [String:Any] {
|
|
20
16
|
var eventContext: [String:Any] = [
|
|
21
|
-
"presentationMode": THEOplayerRCTTypeUtils.presentationModeToString(
|
|
22
|
-
"previousPresentationMode": THEOplayerRCTTypeUtils.presentationModeToString(
|
|
17
|
+
"presentationMode": THEOplayerRCTTypeUtils.presentationModeToString(newPresentationMode),
|
|
18
|
+
"previousPresentationMode": THEOplayerRCTTypeUtils.presentationModeToString(oldPresentationMode),
|
|
23
19
|
]
|
|
24
|
-
if
|
|
20
|
+
if oldPresentationMode == .pictureInPicture {
|
|
25
21
|
eventContext["context"] = ["pip" : self.pipContext.rawValue]
|
|
26
22
|
}
|
|
27
23
|
return eventContext
|