Skip to content

Commit e3a349e

Browse files
edougherErin DougherJmilham21
authored
[IMPLEMENT] Expose more captions callbacks and events (#99)
* added changes for 'onCaptionsChanged' and 'getCurrentCaptions' * removed license keys * added onCaptionsList for Android side * added changes to PlayerView.Swift fro onCaptionsList * addec changes for onCaptionList * final changes before implementing 'onCaptionsList' for iOS side * finale changes for all three captions events * clean up code for onCaptionsList iOS * addec onCaptionsList to SingleExample.js * removed changes in SinglePlayer.js * removed captions events from SinglePlayer.js Co-authored-by: Joe Milham <[email protected]> --------- Co-authored-by: Erin Dougher <[email protected]> Co-authored-by: Joe Milham <[email protected]>
1 parent 80059bc commit e3a349e

9 files changed

+139
-13
lines changed

android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java

+22
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,28 @@ public void execute (NativeViewHierarchyManager nvhm) {
505505
}
506506
}
507507

508+
509+
@ReactMethod
510+
public void getCurrentCaptions(final int reactTag, final Promise promise) {
511+
try {
512+
UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class);
513+
uiManager.addUIBlock(new UIBlock() {
514+
public void execute (NativeViewHierarchyManager nvhm) {
515+
RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag);
516+
517+
if (playerView != null && playerView.mPlayer != null) {
518+
promise.resolve(playerView.mPlayer.getCurrentCaptions());
519+
} else {
520+
promise.reject("RNJW Error", "Player is null");
521+
}
522+
}
523+
});
524+
} catch (IllegalViewOperationException e) {
525+
throw e;
526+
}
527+
}
528+
529+
508530
private int stateToInt(PlayerState playerState) {
509531
switch (playerState) {
510532
case IDLE:

android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java

+34-10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.facebook.react.bridge.ReactApplicationContext;
3434
import com.facebook.react.bridge.ReadableArray;
3535
import com.facebook.react.bridge.ReadableMap;
36+
import com.facebook.react.bridge.WritableArray;
3637
import com.facebook.react.bridge.WritableMap;
3738
import com.facebook.react.common.MapBuilder;
3839
import com.facebook.react.uimanager.ThemedReactContext;
@@ -108,6 +109,7 @@
108109
import com.jwplayer.pub.api.fullscreen.delegates.DialogLayoutDelegate;
109110
import com.jwplayer.pub.api.fullscreen.delegates.SystemUiDelegate;
110111
import com.jwplayer.pub.api.license.LicenseUtil;
112+
import com.jwplayer.pub.api.media.captions.Caption;
111113
import com.jwplayer.pub.api.media.playlists.PlaylistItem;
112114
import com.jwplayer.ui.views.CueMarkerSeekbar;
113115

@@ -1443,6 +1445,38 @@ public void onAudioTrackChanged(AudioTrackChangedEvent audioTrackChangedEvent) {
14431445

14441446
}
14451447

1448+
// Captions Events
1449+
1450+
@Override
1451+
public void onCaptionsChanged(CaptionsChangedEvent captionsChangedEvent) {
1452+
WritableMap event = Arguments.createMap();
1453+
event.putString("message", "onCaptionsChanged");
1454+
event.putInt("index", captionsChangedEvent.getCurrentTrack());
1455+
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCaptionsChanged", event);
1456+
}
1457+
1458+
@Override
1459+
public void onCaptionsList(CaptionsListEvent captionsListEvent) {
1460+
WritableMap event = Arguments.createMap();
1461+
List<Caption> captionTrackList = captionsListEvent.getCaptions();
1462+
WritableArray captionTracks = Arguments.createArray();
1463+
if (captionTrackList != null) {
1464+
for(int i = 0; i < captionTrackList.size(); i++) {
1465+
WritableMap captionTrack = Arguments.createMap();
1466+
Caption track = captionTrackList.get(i);
1467+
captionTrack.putString("file", track.getFile());
1468+
captionTrack.putString("label", track.getLabel());
1469+
captionTrack.putBoolean("default", track.isDefault());
1470+
captionTracks.pushMap(captionTrack);
1471+
}
1472+
}
1473+
event.putString("message", "onCaptionsList");
1474+
event.putInt("index", captionsListEvent.getCurrentCaptionIndex());
1475+
event.putArray("tracks", captionTracks);
1476+
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCaptionsList", event);
1477+
1478+
}
1479+
14461480
// Player Events
14471481

14481482
@Override
@@ -1658,16 +1692,6 @@ public void onTime(TimeEvent timeEvent) {
16581692
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topTime", event);
16591693
}
16601694

1661-
@Override
1662-
public void onCaptionsChanged(CaptionsChangedEvent captionsChangedEvent) {
1663-
1664-
}
1665-
1666-
@Override
1667-
public void onCaptionsList(CaptionsListEvent captionsListEvent) {
1668-
1669-
}
1670-
16711695
@Override
16721696
public void onMeta(MetaEvent metaEvent) {
16731697

android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java

+8
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ public Map getExportedCustomBubblingEventTypeConstants() {
155155
MapBuilder.of(
156156
"phasedRegistrationNames",
157157
MapBuilder.of("bubbled", "onAudioTracks")))
158+
.put("topCaptionsChanged",
159+
MapBuilder.of(
160+
"phasedRegistrationNames",
161+
MapBuilder.of("bubbled", "onCaptionsChanged")))
162+
.put("topCaptionsList",
163+
MapBuilder.of(
164+
"phasedRegistrationNames",
165+
MapBuilder.of("bubbled", "onCaptionsList")))
158166
.put("topCasting",
159167
MapBuilder.of(
160168
"phasedRegistrationNames",

index.d.ts

+12
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,15 @@ declare module "@jwplayer/jwplayer-react-native" {
523523
type: number;
524524
}
525525
// Overloaded type to be used in multiple error events
526+
interface CaptionsChangedEventProps {
527+
index?: number;
528+
}
529+
interface CaptionsListEventProps {
530+
index: number;
531+
file?: string;
532+
label: string;
533+
default: string;
534+
}
526535
type NativeError = (event: BaseEvent<PlayerErrorEventProps> | BaseEvent<PlayerSetupErrorProps> | BaseEvent<PlayerErrorProps>) => void;
527536
type NativeWarning = (event: BaseEvent<PlayerWarningEventProps>) => void;
528537
interface PropsType {
@@ -557,6 +566,8 @@ declare module "@jwplayer/jwplayer-react-native" {
557566
onControlBarVisible?: (event: BaseEvent<ControlBarVisibleEventProps>) => void;
558567
onPlaylistComplete?: () => void;
559568
onPlaylistItem?: (event: BaseEvent<PlaylistItemEventProps>) => void;
569+
onCaptionsChanged?: (event: BaseEvent<CaptionsChangedEventProps>) => void;
570+
onCaptionsList?: (event: BaseEvent<CaptionsListEventProps>) => void;
560571
onAudioTracks?: () => void;
561572
shouldComponentUpdate?: (nextProps: any, nextState: any) => boolean;
562573
}
@@ -588,6 +599,7 @@ declare module "@jwplayer/jwplayer-react-native" {
588599
getCurrentAudioTrack(): Promise<number | null>;
589600
setCurrentAudioTrack(index: number): void;
590601
setCurrentCaptions(index: number): void;
602+
getCurrentCaptions(): Promise<number | null>;
591603
setVisibility(visibility: boolean, controls: JWControlType[]): void;
592604
}
593605
}

index.js

+18
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ export default class JWPlayer extends Component {
370370
getCurrentAudioTrack: PropTypes.func,
371371
setCurrentAudioTrack: PropTypes.func,
372372
setCurrentCaptions: PropTypes.func,
373+
getCurrentCaptions: PropTypes.func,
374+
onCaptionsChanged: PropTypes.func,
375+
onCaptionsList: PropTypes.func,
373376
onAudioTracks: PropTypes.func,
374377
};
375378

@@ -688,6 +691,21 @@ export default class JWPlayer extends Component {
688691
);
689692
}
690693
}
694+
695+
async getCurrentCaptions() {
696+
if (RNJWPlayerManager) {
697+
try {
698+
var currentCaptionTrack =
699+
await RNJWPlayerManager.getCurrentCaptions(
700+
this.getRNJWPlayerBridgeHandle()
701+
);
702+
return currentCaptionTrack;
703+
} catch (e) {
704+
console.error(e);
705+
return null;
706+
}
707+
}
708+
}
691709

692710
getRNJWPlayerBridgeHandle() {
693711
return findNodeHandle(this[this.ref_key]);

ios/RNJWPlayer/RNJWPlayerView.swift

+12-2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
8585
@objc var onCasting: RCTDirectEventBlock?
8686
@objc var onCastingEnded: RCTDirectEventBlock?
8787
@objc var onCastingFailed: RCTDirectEventBlock?
88+
@objc var onCaptionsChanged: RCTDirectEventBlock?
89+
@objc var onCaptionsList: RCTDirectEventBlock?
8890

8991
init() {
9092
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
14001402
}
14011403

14021404
func jwplayer(_ player:JWPlayer, captionTrackChanged index:Int) {
1403-
1405+
self.onCaptionsChanged?(["index": index])
14041406
}
14051407

14061408
func jwplayer(_ player: JWPlayer, visualQualityChanged currentVisualQuality: JWVisualQuality) {
@@ -1416,7 +1418,15 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
14161418
}
14171419

14181420
func jwplayer(_ player:JWPlayer, updatedCaptionList options:[JWMediaSelectionOption]) {
1419-
1421+
var tracks: [[String: Any]] = []
1422+
for track in player.captionsTracks {
1423+
var dict: [String: Any] = [:]
1424+
dict["label"] = track.name
1425+
dict["default"] = track.defaultOption
1426+
tracks.append(dict)
1427+
}
1428+
let currentIndex = player.currentCaptionsTrack
1429+
self.onCaptionsList?(["index": currentIndex, "tracks": tracks])
14201430
}
14211431

14221432
// MARK: - JWPlayer audio session && interruption handling

ios/RNJWPlayer/RNJWPlayerViewController.swift

+11
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
588588

589589
override func jwplayer(_ player:JWPlayer, captionTrackChanged index:Int) {
590590
super.jwplayer(player, captionTrackChanged:index)
591+
parentView.onCaptionsChanged?(["index": index])
591592
}
592593

593594
override func jwplayer(_ player:JWPlayer, qualityLevelChanged currentLevel:Int) {
@@ -600,6 +601,16 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
600601

601602
override func jwplayer(_ player:JWPlayer, updatedCaptionList options:[JWMediaSelectionOption]) {
602603
super.jwplayer(player, updatedCaptionList:options)
604+
605+
var tracks: [[String: Any]] = []
606+
for track in player.captionsTracks {
607+
var dict: [String: Any] = [:]
608+
dict["label"] = track.name
609+
dict["default"] = track.defaultOption
610+
tracks.append(dict)
611+
}
612+
let currentIndex = player.currentCaptionsTrack
613+
parentView.onCaptionsList?(["index": currentIndex, "tracks": tracks])
603614
}
604615

605616
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

ios/RNJWPlayer/RNJWPlayerViewManager.m

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ @interface RCT_EXTERN_MODULE(RNJWPlayerViewManager, RCTViewManager)
3232

3333
/* av events */
3434
RCT_EXPORT_VIEW_PROPERTY(onAudioTracks, RCTDirectEventBlock);
35+
RCT_EXPORT_VIEW_PROPERTY(onCaptionsChanged, RCTDirectEventBlock);
36+
RCT_EXPORT_VIEW_PROPERTY(onCaptionsList, RCTDirectEventBlock);
3537

3638
/* player events */
3739
RCT_EXPORT_VIEW_PROPERTY(onPlayerReady, RCTDirectEventBlock);
@@ -121,7 +123,7 @@ @interface RCT_EXTERN_MODULE(RNJWPlayerViewManager, RCTViewManager)
121123

122124
RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
123125

124-
RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
126+
RCT_EXTERN_METHOD(getCurrentCaptions: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
125127

126128
RCT_EXTERN_METHOD(setLicenseKey: (nonnull NSNumber *)reactTag: (nonnull NSString *)license)
127129

ios/RNJWPlayer/RNJWPlayerViewManager.swift

+19
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,25 @@ class RNJWPlayerViewManager: RCTViewManager {
420420
}
421421
}
422422

423+
@objc func getCurrentCaptions(_ reactTag: NSNumber, _ resolve: @escaping RCTPromiseResolveBlock, _ reject: @escaping RCTPromiseRejectBlock) {
424+
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
425+
guard let view = viewRegistry?[reactTag] as? RNJWPlayerView else {
426+
let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "There is no player"])
427+
reject("no_player", "Invalid view returned from registry, expecting RNJWPlayerView", error)
428+
return
429+
}
430+
431+
if let playerView = view.playerView {
432+
resolve(NSNumber(value: playerView.player.currentCaptionsTrack))
433+
} else if let playerViewController = view.playerViewController {
434+
resolve(NSNumber(value: playerViewController.player.currentCaptionsTrack))
435+
} else {
436+
let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "There is no player"])
437+
reject("no_player", "There is no player", error)
438+
}
439+
}
440+
}
441+
423442
@objc func setLicenseKey(_ reactTag: NSNumber, _ license: String) {
424443
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
425444
guard let view = viewRegistry?[reactTag] as? RNJWPlayerView else {

0 commit comments

Comments
 (0)