Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPLEMENT] Expose more captions callbacks and events #99

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
44 changes: 34 additions & 10 deletions android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<Caption> 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
Expand Down Expand Up @@ -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) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PlayerErrorEventProps> | BaseEvent<PlayerSetupErrorProps> | BaseEvent<PlayerErrorProps>) => void;
type NativeWarning = (event: BaseEvent<PlayerWarningEventProps>) => void;
interface PropsType {
Expand Down Expand Up @@ -557,6 +566,8 @@ declare module "@jwplayer/jwplayer-react-native" {
onControlBarVisible?: (event: BaseEvent<ControlBarVisibleEventProps>) => void;
onPlaylistComplete?: () => void;
onPlaylistItem?: (event: BaseEvent<PlaylistItemEventProps>) => void;
onCaptionsChanged?: (event: BaseEvent<CaptionsChangedEventProps>) => void;
onCaptionsList?: (event: BaseEvent<CaptionsListEventProps>) => void;
onAudioTracks?: () => void;
shouldComponentUpdate?: (nextProps: any, nextState: any) => boolean;
}
Expand Down Expand Up @@ -588,6 +599,7 @@ declare module "@jwplayer/jwplayer-react-native" {
getCurrentAudioTrack(): Promise<number | null>;
setCurrentAudioTrack(index: number): void;
setCurrentCaptions(index: number): void;
getCurrentCaptions(): Promise<number | null>;
setVisibility(visibility: boolean, controls: JWControlType[]): void;
}
}
18 changes: 18 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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]);
Expand Down
14 changes: 12 additions & 2 deletions ios/RNJWPlayer/RNJWPlayerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions ios/RNJWPlayer/RNJWPlayerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion ios/RNJWPlayer/RNJWPlayerViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)

Expand Down
19 changes: 19 additions & 0 deletions ios/RNJWPlayer/RNJWPlayerViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down