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.
Files changed (42) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -0
  3. package/android/src/main/java/com/theoplayer/PlayerEventEmitter.kt +1 -1
  4. package/android/src/main/java/com/theoplayer/broadcast/DefaultEventDispatcher.kt +2 -1
  5. package/android/src/main/java/com/theoplayer/cast/CastEventAdapter.kt +1 -0
  6. package/android/src/main/java/com/theoplayer/drm/ContentProtectionModule.kt +2 -0
  7. package/android/src/main/java/com/theoplayer/media/MediaNotificationBuilder.kt +11 -0
  8. package/android/src/main/java/com/theoplayer/media/MediaPlaybackService.kt +31 -19
  9. package/android/src/main/java/com/theoplayer/presentation/PresentationManager.kt +41 -1
  10. package/android/src/main/res/drawable/ic_notification_large.png +0 -0
  11. package/ios/THEOplayerRCTMainEventHandler.swift +6 -24
  12. package/ios/THEOplayerRCTPlayerAPI.swift +3 -16
  13. package/ios/THEOplayerRCTTextTrackEventHandler.swift +0 -9
  14. package/ios/THEOplayerRCTTrackMetadataAggregator.swift +1 -1
  15. package/ios/THEOplayerRCTView.swift +12 -10
  16. package/ios/Theoplayer-Bridging-Header.h +1 -0
  17. package/ios/eventBroadcasting/THEOplayerRCTBroadcastEventHandler.swift +1 -1
  18. package/ios/pip/THEOplayerRCTView+PipConfig.swift +2 -2
  19. package/ios/{THEOplayerRCTPresentationModeContext.swift → presentationMode/THEOplayerRCTPresentationModeContext.swift} +4 -8
  20. package/ios/presentationMode/THEOplayerRCTPresentationModeManager.swift +159 -0
  21. package/ios/presentationMode/THEOplayerRCTView+PresentationMode.swift +11 -0
  22. package/ios/{THEOplayerRCTSideloadedMetadataTrackHandler.swift → sideloadedMetadata/THEOplayerRCTSideloadedMetadataProcessor.swift} +14 -4
  23. package/ios/sideloadedMetadata/THEOplayerRCTSideloadedMetadataTrackEventHandler.swift +28 -0
  24. package/lib/commonjs/internal/THEOplayerView.js +24 -3
  25. package/lib/commonjs/internal/THEOplayerView.js.map +1 -1
  26. package/lib/commonjs/internal/adapter/THEOplayerAdapter.js +3 -0
  27. package/lib/commonjs/internal/adapter/THEOplayerAdapter.js.map +1 -1
  28. package/lib/commonjs/internal/utils/Dimensions.js +31 -0
  29. package/lib/commonjs/internal/utils/Dimensions.js.map +1 -0
  30. package/lib/module/internal/THEOplayerView.js +26 -5
  31. package/lib/module/internal/THEOplayerView.js.map +1 -1
  32. package/lib/module/internal/adapter/THEOplayerAdapter.js +5 -2
  33. package/lib/module/internal/adapter/THEOplayerAdapter.js.map +1 -1
  34. package/lib/module/internal/utils/Dimensions.js +26 -0
  35. package/lib/module/internal/utils/Dimensions.js.map +1 -0
  36. package/lib/typescript/internal/THEOplayerView.d.ts +7 -1
  37. package/lib/typescript/internal/utils/Dimensions.d.ts +5 -0
  38. package/package.json +1 -1
  39. package/react-native-theoplayer.podspec +5 -5
  40. package/src/internal/THEOplayerView.tsx +25 -5
  41. package/src/internal/adapter/THEOplayerAdapter.ts +11 -7
  42. 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, THEOplayerGlobal.getPlayerSuiteVersion())
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
- (listener as EventListener<Event<*>>).handleEvent(event)
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
- // Fetch large icon asynchronously
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
- try {
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
+ }
@@ -1,4 +1,4 @@
1
- // THEOplayerRCTView.swift
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 metadataTracksInfo: [[String:Any]] = []
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, presentationModeContext: THEOplayerRCTPresentationModeContext) {
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 setMetadataTracksInfo(metadataTracksInfo: [[String:Any]]) {
72
- self.metadataTracksInfo = metadataTracksInfo
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.metadataTracksInfo)
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 newPresentationMode: PresentationMode = THEOplayerRCTTypeUtils.presentationModeFromString(presentationMode)
139
- if newPresentationMode == .fullscreen {
140
- print(ERROR_MESSAGE_PLAYER_FULLSCREEN_UNSUPPORTED_FEATURE)
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
- THEOplayerRCTSideloadedMetadataTrackHandler.parseVtt(urlString) { cueArray in
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, presentationModeContext: self.presentationModeContext)
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
- if let trackDescriptions = metadataTrackDescriptions {
161
- THEOplayerRCTTrackMetadataAggregator.aggregatedMetadataTrackInfo(metadataTrackDescriptions: trackDescriptions) { tracksInfo in
162
- self.mainEventHandler.setMetadataTracksInfo(metadataTracksInfo: tracksInfo)
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.mainEventHandler.onNativePresentationModeChange = nativePresentationMode
319
+ self.presentationModeManager.onNativePresentationModeChange = nativePresentationMode
318
320
  if DEBUG_VIEW { PrintUtils.printLog(logText: "[NATIVE] nativePresentationMode prop set.") }
319
321
  }
320
322
 
@@ -6,3 +6,4 @@
6
6
  #import <React/RCTUIManager.h>
7
7
  #import <React/RCTBridgeModule.h>
8
8
  #import <React/RCTEventEmitter.h>
9
+ #import <React/RCTRootContentView.h>
@@ -27,7 +27,7 @@ public class THEOplayerRCTBroadcastEventHandler: DefaultEventDispatcher {
27
27
  }
28
28
  }
29
29
 
30
- public class DefaultEventDispatcher: NSObject, THEOplayerSDK.EventDispatcherProtocol, THEOplayerSDK.DispatchDispatch {
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(_ newPresentationMode: PresentationMode) -> [String:Any] {
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(self.currentPresentationMode),
22
- "previousPresentationMode": THEOplayerRCTTypeUtils.presentationModeToString(previousPresentationMode),
17
+ "presentationMode": THEOplayerRCTTypeUtils.presentationModeToString(newPresentationMode),
18
+ "previousPresentationMode": THEOplayerRCTTypeUtils.presentationModeToString(oldPresentationMode),
23
19
  ]
24
- if previousPresentationMode == .pictureInPicture {
20
+ if oldPresentationMode == .pictureInPicture {
25
21
  eventContext["context"] = ["pip" : self.pipContext.rawValue]
26
22
  }
27
23
  return eventContext