diff --git a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java
index aea3db7..1ec0db3 100644
--- a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java
+++ b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java
@@ -505,6 +505,28 @@ public void execute (NativeViewHierarchyManager nvhm) {
}
}
+
+ @ReactMethod
+ public void getCurrentCaptions(final int reactTag, final Promise promise) {
+ try {
+ UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class);
+ uiManager.addUIBlock(new UIBlock() {
+ public void execute (NativeViewHierarchyManager nvhm) {
+ RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag);
+
+ if (playerView != null && playerView.mPlayer != null) {
+ promise.resolve(playerView.mPlayer.getCurrentCaptions());
+ } else {
+ promise.reject("RNJW Error", "Player is null");
+ }
+ }
+ });
+ } catch (IllegalViewOperationException e) {
+ throw e;
+ }
+ }
+
+
private int stateToInt(PlayerState playerState) {
switch (playerState) {
case IDLE:
diff --git a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java
index 50c3993..46ff9e9 100755
--- a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java
+++ b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java
@@ -35,6 +35,7 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
@@ -110,6 +111,7 @@
import com.jwplayer.pub.api.fullscreen.delegates.DialogLayoutDelegate;
import com.jwplayer.pub.api.fullscreen.delegates.SystemUiDelegate;
import com.jwplayer.pub.api.license.LicenseUtil;
+import com.jwplayer.pub.api.media.captions.Caption;
import com.jwplayer.pub.api.media.playlists.PlaylistItem;
import com.jwplayer.ui.views.CueMarkerSeekbar;
@@ -1437,6 +1439,38 @@ public void onAudioTrackChanged(AudioTrackChangedEvent audioTrackChangedEvent) {
}
+ // Captions Events
+
+ @Override
+ public void onCaptionsChanged(CaptionsChangedEvent captionsChangedEvent) {
+ WritableMap event = Arguments.createMap();
+ event.putString("message", "onCaptionsChanged");
+ event.putInt("index", captionsChangedEvent.getCurrentTrack());
+ getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCaptionsChanged", event);
+ }
+
+ @Override
+ public void onCaptionsList(CaptionsListEvent captionsListEvent) {
+ WritableMap event = Arguments.createMap();
+ List
captionTrackList = captionsListEvent.getCaptions();
+ WritableArray captionTracks = Arguments.createArray();
+ if (captionTrackList != null) {
+ for(int i = 0; i < captionTrackList.size(); i++) {
+ WritableMap captionTrack = Arguments.createMap();
+ Caption track = captionTrackList.get(i);
+ captionTrack.putString("file", track.getFile());
+ captionTrack.putString("label", track.getLabel());
+ captionTrack.putBoolean("default", track.isDefault());
+ captionTracks.pushMap(captionTrack);
+ }
+ }
+ event.putString("message", "onCaptionsList");
+ event.putInt("index", captionsListEvent.getCurrentCaptionIndex());
+ event.putArray("tracks", captionTracks);
+ getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCaptionsList", event);
+
+ }
+
// Player Events
@Override
@@ -1652,16 +1686,6 @@ public void onTime(TimeEvent timeEvent) {
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topTime", event);
}
- @Override
- public void onCaptionsChanged(CaptionsChangedEvent captionsChangedEvent) {
-
- }
-
- @Override
- public void onCaptionsList(CaptionsListEvent captionsListEvent) {
-
- }
-
@Override
public void onMeta(MetaEvent metaEvent) {
diff --git a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java
index e0b9e06..11f14d8 100644
--- a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java
+++ b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java
@@ -155,6 +155,14 @@ public Map getExportedCustomBubblingEventTypeConstants() {
MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of("bubbled", "onAudioTracks")))
+ .put("topCaptionsChanged",
+ MapBuilder.of(
+ "phasedRegistrationNames",
+ MapBuilder.of("bubbled", "onCaptionsChanged")))
+ .put("topCaptionsList",
+ MapBuilder.of(
+ "phasedRegistrationNames",
+ MapBuilder.of("bubbled", "onCaptionsList")))
.put("topCasting",
MapBuilder.of(
"phasedRegistrationNames",
diff --git a/index.d.ts b/index.d.ts
index 9a8a866..63f11ee 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -523,6 +523,15 @@ declare module "@jwplayer/jwplayer-react-native" {
type: number;
}
// Overloaded type to be used in multiple error events
+ interface CaptionsChangedEventProps {
+ index?: number;
+ }
+ interface CaptionsListEventProps {
+ index: number;
+ file?: string;
+ label: string;
+ default: string;
+ }
type NativeError = (event: BaseEvent | BaseEvent | BaseEvent) => void;
type NativeWarning = (event: BaseEvent) => void;
interface PropsType {
@@ -557,6 +566,8 @@ declare module "@jwplayer/jwplayer-react-native" {
onControlBarVisible?: (event: BaseEvent) => void;
onPlaylistComplete?: () => void;
onPlaylistItem?: (event: BaseEvent) => void;
+ onCaptionsChanged?: (event: BaseEvent) => void;
+ onCaptionsList?: (event: BaseEvent) => void;
onAudioTracks?: () => void;
shouldComponentUpdate?: (nextProps: any, nextState: any) => boolean;
}
@@ -588,6 +599,7 @@ declare module "@jwplayer/jwplayer-react-native" {
getCurrentAudioTrack(): Promise;
setCurrentAudioTrack(index: number): void;
setCurrentCaptions(index: number): void;
+ getCurrentCaptions(): Promise;
setVisibility(visibility: boolean, controls: JWControlType[]): void;
}
}
diff --git a/index.js b/index.js
index 12852af..7a8fc96 100644
--- a/index.js
+++ b/index.js
@@ -370,6 +370,9 @@ export default class JWPlayer extends Component {
getCurrentAudioTrack: PropTypes.func,
setCurrentAudioTrack: PropTypes.func,
setCurrentCaptions: PropTypes.func,
+ getCurrentCaptions: PropTypes.func,
+ onCaptionsChanged: PropTypes.func,
+ onCaptionsList: PropTypes.func,
onAudioTracks: PropTypes.func,
};
@@ -688,6 +691,21 @@ export default class JWPlayer extends Component {
);
}
}
+
+ async getCurrentCaptions() {
+ if (RNJWPlayerManager) {
+ try {
+ var currentCaptionTrack =
+ await RNJWPlayerManager.getCurrentCaptions(
+ this.getRNJWPlayerBridgeHandle()
+ );
+ return currentCaptionTrack;
+ } catch (e) {
+ console.error(e);
+ return null;
+ }
+ }
+ }
getRNJWPlayerBridgeHandle() {
return findNodeHandle(this[this.ref_key]);
diff --git a/ios/RNJWPlayer/RNJWPlayerView.swift b/ios/RNJWPlayer/RNJWPlayerView.swift
index 8beae89..87e106d 100644
--- a/ios/RNJWPlayer/RNJWPlayerView.swift
+++ b/ios/RNJWPlayer/RNJWPlayerView.swift
@@ -85,6 +85,8 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
@objc var onCasting: RCTDirectEventBlock?
@objc var onCastingEnded: RCTDirectEventBlock?
@objc var onCastingFailed: RCTDirectEventBlock?
+ @objc var onCaptionsChanged: RCTDirectEventBlock?
+ @objc var onCaptionsList: RCTDirectEventBlock?
init() {
super.init(frame: CGRect(x: 20, y: 0, width: UIScreen.main.bounds.width - 40, height: 300))
@@ -1400,7 +1402,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
}
func jwplayer(_ player:JWPlayer, captionTrackChanged index:Int) {
-
+ self.onCaptionsChanged?(["index": index])
}
func jwplayer(_ player: JWPlayer, visualQualityChanged currentVisualQuality: JWVisualQuality) {
@@ -1416,7 +1418,15 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
}
func jwplayer(_ player:JWPlayer, updatedCaptionList options:[JWMediaSelectionOption]) {
-
+ var tracks: [[String: Any]] = []
+ for track in player.captionsTracks {
+ var dict: [String: Any] = [:]
+ dict["label"] = track.name
+ dict["default"] = track.defaultOption
+ tracks.append(dict)
+ }
+ let currentIndex = player.currentCaptionsTrack
+ self.onCaptionsList?(["index": currentIndex, "tracks": tracks])
}
// MARK: - JWPlayer audio session && interruption handling
diff --git a/ios/RNJWPlayer/RNJWPlayerViewController.swift b/ios/RNJWPlayer/RNJWPlayerViewController.swift
index a9f257f..b4587e8 100644
--- a/ios/RNJWPlayer/RNJWPlayerViewController.swift
+++ b/ios/RNJWPlayer/RNJWPlayerViewController.swift
@@ -588,6 +588,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
override func jwplayer(_ player:JWPlayer, captionTrackChanged index:Int) {
super.jwplayer(player, captionTrackChanged:index)
+ parentView.onCaptionsChanged?(["index": index])
}
override func jwplayer(_ player:JWPlayer, qualityLevelChanged currentLevel:Int) {
@@ -600,6 +601,16 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
override func jwplayer(_ player:JWPlayer, updatedCaptionList options:[JWMediaSelectionOption]) {
super.jwplayer(player, updatedCaptionList:options)
+
+ var tracks: [[String: Any]] = []
+ for track in player.captionsTracks {
+ var dict: [String: Any] = [:]
+ dict["label"] = track.name
+ dict["default"] = track.defaultOption
+ tracks.append(dict)
+ }
+ let currentIndex = player.currentCaptionsTrack
+ parentView.onCaptionsList?(["index": currentIndex, "tracks": tracks])
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
diff --git a/ios/RNJWPlayer/RNJWPlayerViewManager.m b/ios/RNJWPlayer/RNJWPlayerViewManager.m
index c47d6d5..887b63c 100644
--- a/ios/RNJWPlayer/RNJWPlayerViewManager.m
+++ b/ios/RNJWPlayer/RNJWPlayerViewManager.m
@@ -32,6 +32,8 @@ @interface RCT_EXTERN_MODULE(RNJWPlayerViewManager, RCTViewManager)
/* av events */
RCT_EXPORT_VIEW_PROPERTY(onAudioTracks, RCTDirectEventBlock);
+RCT_EXPORT_VIEW_PROPERTY(onCaptionsChanged, RCTDirectEventBlock);
+RCT_EXPORT_VIEW_PROPERTY(onCaptionsList, RCTDirectEventBlock);
/* player events */
RCT_EXPORT_VIEW_PROPERTY(onPlayerReady, RCTDirectEventBlock);
@@ -121,7 +123,7 @@ @interface RCT_EXTERN_MODULE(RNJWPlayerViewManager, RCTViewManager)
RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
-RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
+RCT_EXTERN_METHOD(getCurrentCaptions: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(setLicenseKey: (nonnull NSNumber *)reactTag: (nonnull NSString *)license)
diff --git a/ios/RNJWPlayer/RNJWPlayerViewManager.swift b/ios/RNJWPlayer/RNJWPlayerViewManager.swift
index a8f6107..024efe9 100644
--- a/ios/RNJWPlayer/RNJWPlayerViewManager.swift
+++ b/ios/RNJWPlayer/RNJWPlayerViewManager.swift
@@ -420,6 +420,25 @@ class RNJWPlayerViewManager: RCTViewManager {
}
}
+ @objc func getCurrentCaptions(_ reactTag: NSNumber, _ resolve: @escaping RCTPromiseResolveBlock, _ reject: @escaping RCTPromiseRejectBlock) {
+ self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
+ guard let view = viewRegistry?[reactTag] as? RNJWPlayerView else {
+ let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "There is no player"])
+ reject("no_player", "Invalid view returned from registry, expecting RNJWPlayerView", error)
+ return
+ }
+
+ if let playerView = view.playerView {
+ resolve(NSNumber(value: playerView.player.currentCaptionsTrack))
+ } else if let playerViewController = view.playerViewController {
+ resolve(NSNumber(value: playerViewController.player.currentCaptionsTrack))
+ } else {
+ let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "There is no player"])
+ reject("no_player", "There is no player", error)
+ }
+ }
+ }
+
@objc func setLicenseKey(_ reactTag: NSNumber, _ license: String) {
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
guard let view = viewRegistry?[reactTag] as? RNJWPlayerView else {