diff --git a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java deleted file mode 100644 index be6166b4..00000000 --- a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java +++ /dev/null @@ -1,489 +0,0 @@ -package com.brentvatne.common.react; - -import androidx.annotation.StringDef; - -import android.view.View; - -import com.brentvatne.common.api.TimedMetadata; -import com.brentvatne.common.api.Track; -import com.brentvatne.common.api.VideoTrack; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.UIManager; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.UIManagerHelper; -import com.facebook.react.uimanager.common.ViewUtil; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Map; - -public class VideoEventEmitter { - - private final ReactContext mReactContext; - - private int viewId = View.NO_ID; - - public VideoEventEmitter(ReactContext reactContext) { - this.mReactContext = reactContext; - } - - private static final String EVENT_LOAD_START = "onVideoLoadStart"; - private static final String EVENT_LOAD = "onVideoLoad"; - private static final String EVENT_ERROR = "onVideoError"; - private static final String EVENT_PROGRESS = "onVideoProgress"; - private static final String EVENT_BANDWIDTH = "onVideoBandwidthUpdate"; - private static final String EVENT_CONTROLS_VISIBILITY_CHANGE = "onControlsVisibilityChange"; - private static final String EVENT_SEEK = "onVideoSeek"; - private static final String EVENT_END = "onVideoEnd"; - private static final String EVENT_FULLSCREEN_WILL_PRESENT = "onVideoFullscreenPlayerWillPresent"; - private static final String EVENT_FULLSCREEN_DID_PRESENT = "onVideoFullscreenPlayerDidPresent"; - private static final String EVENT_FULLSCREEN_WILL_DISMISS = "onVideoFullscreenPlayerWillDismiss"; - private static final String EVENT_FULLSCREEN_DID_DISMISS = "onVideoFullscreenPlayerDidDismiss"; - - private static final String EVENT_STALLED = "onPlaybackStalled"; - private static final String EVENT_RESUME = "onPlaybackResume"; - private static final String EVENT_READY = "onReadyForDisplay"; - private static final String EVENT_BUFFER = "onVideoBuffer"; - private static final String EVENT_PLAYBACK_STATE_CHANGED = "onVideoPlaybackStateChanged"; - private static final String EVENT_IDLE = "onVideoIdle"; - private static final String EVENT_TIMED_METADATA = "onTimedMetadata"; - private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy"; - private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged"; - private static final String EVENT_PLAYBACK_RATE_CHANGE = "onPlaybackRateChange"; - private static final String EVENT_VOLUME_CHANGE = "onVolumeChange"; - private static final String EVENT_AUDIO_TRACKS = "onAudioTracks"; - private static final String EVENT_TEXT_TRACKS = "onTextTracks"; - - private static final String EVENT_TEXT_TRACK_DATA_CHANGED = "onTextTrackDataChanged"; - private static final String EVENT_VIDEO_TRACKS = "onVideoTracks"; - private static final String EVENT_ON_RECEIVE_AD_EVENT = "onReceiveAdEvent"; - - static public final String[] Events = { - EVENT_LOAD_START, - EVENT_LOAD, - EVENT_ERROR, - EVENT_PROGRESS, - EVENT_SEEK, - EVENT_END, - EVENT_FULLSCREEN_WILL_PRESENT, - EVENT_FULLSCREEN_DID_PRESENT, - EVENT_FULLSCREEN_WILL_DISMISS, - EVENT_FULLSCREEN_DID_DISMISS, - EVENT_STALLED, - EVENT_RESUME, - EVENT_READY, - EVENT_BUFFER, - EVENT_PLAYBACK_STATE_CHANGED, - EVENT_IDLE, - EVENT_TIMED_METADATA, - EVENT_AUDIO_BECOMING_NOISY, - EVENT_AUDIO_FOCUS_CHANGE, - EVENT_PLAYBACK_RATE_CHANGE, - EVENT_VOLUME_CHANGE, - EVENT_AUDIO_TRACKS, - EVENT_TEXT_TRACKS, - EVENT_TEXT_TRACK_DATA_CHANGED, - EVENT_VIDEO_TRACKS, - EVENT_BANDWIDTH, - EVENT_CONTROLS_VISIBILITY_CHANGE, - EVENT_ON_RECEIVE_AD_EVENT - }; - - @Retention(RetentionPolicy.SOURCE) - @StringDef({ - EVENT_LOAD_START, - EVENT_LOAD, - EVENT_ERROR, - EVENT_PROGRESS, - EVENT_SEEK, - EVENT_END, - EVENT_FULLSCREEN_WILL_PRESENT, - EVENT_FULLSCREEN_DID_PRESENT, - EVENT_FULLSCREEN_WILL_DISMISS, - EVENT_FULLSCREEN_DID_DISMISS, - EVENT_STALLED, - EVENT_RESUME, - EVENT_READY, - EVENT_BUFFER, - EVENT_PLAYBACK_STATE_CHANGED, - EVENT_IDLE, - EVENT_TIMED_METADATA, - EVENT_AUDIO_BECOMING_NOISY, - EVENT_AUDIO_FOCUS_CHANGE, - EVENT_PLAYBACK_RATE_CHANGE, - EVENT_VOLUME_CHANGE, - EVENT_AUDIO_TRACKS, - EVENT_TEXT_TRACKS, - EVENT_TEXT_TRACK_DATA_CHANGED, - EVENT_VIDEO_TRACKS, - EVENT_BANDWIDTH, - EVENT_CONTROLS_VISIBILITY_CHANGE, - EVENT_ON_RECEIVE_AD_EVENT - }) - @interface VideoEvents { - } - - private static final String EVENT_PROP_FAST_FORWARD = "canPlayFastForward"; - private static final String EVENT_PROP_SLOW_FORWARD = "canPlaySlowForward"; - private static final String EVENT_PROP_SLOW_REVERSE = "canPlaySlowReverse"; - private static final String EVENT_PROP_REVERSE = "canPlayReverse"; - private static final String EVENT_PROP_STEP_FORWARD = "canStepForward"; - private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward"; - - private static final String EVENT_PROP_DURATION = "duration"; - private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration"; - private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration"; - private static final String EVENT_PROP_CURRENT_TIME = "currentTime"; - private static final String EVENT_PROP_CURRENT_PLAYBACK_TIME = "currentPlaybackTime"; - private static final String EVENT_PROP_SEEK_TIME = "seekTime"; - private static final String EVENT_PROP_NATURAL_SIZE = "naturalSize"; - private static final String EVENT_PROP_TRACK_ID = "trackId"; - private static final String EVENT_PROP_WIDTH = "width"; - private static final String EVENT_PROP_HEIGHT = "height"; - private static final String EVENT_PROP_ORIENTATION = "orientation"; - private static final String EVENT_PROP_VIDEO_TRACKS = "videoTracks"; - private static final String EVENT_PROP_AUDIO_TRACKS = "audioTracks"; - private static final String EVENT_PROP_TEXT_TRACKS = "textTracks"; - private static final String EVENT_PROP_TEXT_TRACK_DATA = "subtitleTracks"; - private static final String EVENT_PROP_HAS_AUDIO_FOCUS = "hasAudioFocus"; - private static final String EVENT_PROP_IS_BUFFERING = "isBuffering"; - private static final String EVENT_PROP_PLAYBACK_RATE = "playbackRate"; - private static final String EVENT_PROP_VOLUME = "volume"; - - private static final String EVENT_PROP_ERROR = "error"; - private static final String EVENT_PROP_ERROR_STRING = "errorString"; - private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException"; - private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace"; - private static final String EVENT_PROP_ERROR_CODE = "errorCode"; - - private static final String EVENT_PROP_TIMED_METADATA = "metadata"; - - private static final String EVENT_PROP_BITRATE = "bitrate"; - - private static final String EVENT_PROP_IS_PLAYING = "isPlaying"; - - private static final String EVENT_CONTROLS_VISIBLE = "isVisible"; - - public void setViewId(int viewId) { - this.viewId = viewId; - } - - public void loadStart() { - receiveEvent(EVENT_LOAD_START, null); - } - - WritableMap aspectRatioToNaturalSize(int videoWidth, int videoHeight) { - WritableMap naturalSize = Arguments.createMap(); - naturalSize.putInt(EVENT_PROP_WIDTH, videoWidth); - naturalSize.putInt(EVENT_PROP_HEIGHT, videoHeight); - if (videoWidth > videoHeight) { - naturalSize.putString(EVENT_PROP_ORIENTATION, "landscape"); - } else if (videoWidth < videoHeight) { - naturalSize.putString(EVENT_PROP_ORIENTATION, "portrait"); - } else { - naturalSize.putString(EVENT_PROP_ORIENTATION, "square"); - } - return naturalSize; - } - - WritableArray audioTracksToArray(ArrayList audioTracks) { - WritableArray waAudioTracks = Arguments.createArray(); - if( audioTracks != null ){ - for (int i = 0; i < audioTracks.size(); ++i) { - Track format = audioTracks.get(i); - WritableMap audioTrack = Arguments.createMap(); - audioTrack.putInt("index", i); - audioTrack.putString("title", format.getTitle()); - if (format.getMimeType() != null) { - audioTrack.putString("type", format.getMimeType()); - } - if (format.getLanguage() != null) { - audioTrack.putString("language", format.getLanguage()); - } - if (format.getBitrate() > 0) { - audioTrack.putInt("bitrate", format.getBitrate()); - } - audioTrack.putBoolean("selected", format.isSelected()); - waAudioTracks.pushMap(audioTrack); - } - } - return waAudioTracks; - } - - WritableArray videoTracksToArray(ArrayList videoTracks) { - WritableArray waVideoTracks = Arguments.createArray(); - if( videoTracks != null ){ - for (int i = 0; i < videoTracks.size(); ++i) { - VideoTrack vTrack = videoTracks.get(i); - WritableMap videoTrack = Arguments.createMap(); - videoTrack.putInt("width", vTrack.getWidth()); - videoTrack.putInt("height",vTrack.getHeight()); - videoTrack.putInt("bitrate", vTrack.getBitrate()); - videoTrack.putString("codecs", vTrack.getCodecs()); - videoTrack.putString("trackId", vTrack.getTrackId()); - videoTrack.putInt("index", vTrack.getIndex()); - videoTrack.putBoolean("selected", vTrack.isSelected()); - videoTrack.putInt("rotation", vTrack.getRotation()); - waVideoTracks.pushMap(videoTrack); - } - } - return waVideoTracks; - } - - WritableArray textTracksToArray(ArrayList textTracks) { - WritableArray waTextTracks = Arguments.createArray(); - if (textTracks != null) { - for (int i = 0; i < textTracks.size(); ++i) { - Track format = textTracks.get(i); - WritableMap textTrack = Arguments.createMap(); - textTrack.putInt("index", i); - textTrack.putString("title", format.getTitle()); - textTrack.putString("type", format.getMimeType()); - textTrack.putString("language", format.getLanguage()); - textTrack.putBoolean("selected", format.isSelected()); - waTextTracks.pushMap(textTrack); - } - } - return waTextTracks; - } - - public void load(double duration, double currentPosition, int videoWidth, int videoHeight, - ArrayList audioTracks, ArrayList textTracks, ArrayList videoTracks, String trackId){ - WritableArray waAudioTracks = audioTracksToArray(audioTracks); - WritableArray waVideoTracks = videoTracksToArray(videoTracks); - WritableArray waTextTracks = textTracksToArray(textTracks); - - load( duration, currentPosition, videoWidth, videoHeight, waAudioTracks, waTextTracks, waVideoTracks, trackId); - } - - void load(double duration, double currentPosition, int videoWidth, int videoHeight, - WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) { - WritableMap event = Arguments.createMap(); - event.putDouble(EVENT_PROP_DURATION, duration / 1000D); - event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D); - - WritableMap naturalSize = aspectRatioToNaturalSize(videoWidth, videoHeight); - event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize); - event.putString(EVENT_PROP_TRACK_ID, trackId); - event.putArray(EVENT_PROP_VIDEO_TRACKS, videoTracks); - event.putArray(EVENT_PROP_AUDIO_TRACKS, audioTracks); - event.putArray(EVENT_PROP_TEXT_TRACKS, textTracks); - - // TODO: Actually check if you can. - event.putBoolean(EVENT_PROP_FAST_FORWARD, true); - event.putBoolean(EVENT_PROP_SLOW_FORWARD, true); - event.putBoolean(EVENT_PROP_SLOW_REVERSE, true); - event.putBoolean(EVENT_PROP_REVERSE, true); - event.putBoolean(EVENT_PROP_FAST_FORWARD, true); - event.putBoolean(EVENT_PROP_STEP_BACKWARD, true); - event.putBoolean(EVENT_PROP_STEP_FORWARD, true); - - receiveEvent(EVENT_LOAD, event); - } - - WritableMap arrayToObject(String field, WritableArray array) { - WritableMap event = Arguments.createMap(); - event.putArray(field, array); - return event; - } - - public void audioTracks(ArrayList audioTracks){ - receiveEvent(EVENT_AUDIO_TRACKS, arrayToObject(EVENT_PROP_AUDIO_TRACKS, audioTracksToArray(audioTracks))); - } - - public void textTracks(ArrayList textTracks){ - receiveEvent(EVENT_TEXT_TRACKS, arrayToObject(EVENT_PROP_TEXT_TRACKS, textTracksToArray(textTracks))); - } - - public void textTrackDataChanged(String textTrackData){ - WritableMap event = Arguments.createMap(); - event.putString(EVENT_PROP_TEXT_TRACK_DATA, textTrackData); - receiveEvent(EVENT_TEXT_TRACK_DATA_CHANGED, event); - } - - public void videoTracks(ArrayList videoTracks){ - receiveEvent(EVENT_VIDEO_TRACKS, arrayToObject(EVENT_PROP_VIDEO_TRACKS, videoTracksToArray(videoTracks))); - } - - public void progressChanged(double currentPosition, double bufferedDuration, double seekableDuration, double currentPlaybackTime) { - WritableMap event = Arguments.createMap(); - event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D); - event.putDouble(EVENT_PROP_PLAYABLE_DURATION, bufferedDuration / 1000D); - event.putDouble(EVENT_PROP_SEEKABLE_DURATION, seekableDuration / 1000D); - event.putDouble(EVENT_PROP_CURRENT_PLAYBACK_TIME, currentPlaybackTime); - receiveEvent(EVENT_PROGRESS, event); - } - - public void bandwidthReport(double bitRateEstimate, int height, int width, String id) { - WritableMap event = Arguments.createMap(); - event.putDouble(EVENT_PROP_BITRATE, bitRateEstimate); - event.putInt(EVENT_PROP_WIDTH, width); - event.putInt(EVENT_PROP_HEIGHT, height); - event.putString(EVENT_PROP_TRACK_ID, id); - receiveEvent(EVENT_BANDWIDTH, event); - } - - public void seek(long currentPosition, long seekTime) { - WritableMap event = Arguments.createMap(); - event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D); - event.putDouble(EVENT_PROP_SEEK_TIME, seekTime / 1000D); - receiveEvent(EVENT_SEEK, event); - } - - public void ready() { - receiveEvent(EVENT_READY, null); - } - - public void buffering(boolean isBuffering) { - WritableMap map = Arguments.createMap(); - map.putBoolean(EVENT_PROP_IS_BUFFERING, isBuffering); - receiveEvent(EVENT_BUFFER, map); - } - - public void playbackStateChanged(boolean isPlaying) { - WritableMap map = Arguments.createMap(); - map.putBoolean(EVENT_PROP_IS_PLAYING, isPlaying); - receiveEvent(EVENT_PLAYBACK_STATE_CHANGED, map); - } - - public void idle() { - receiveEvent(EVENT_IDLE, null); - } - - public void end() { - receiveEvent(EVENT_END, null); - } - - public void controlsVisibilityChanged(boolean isVisible) { - WritableMap map = Arguments.createMap(); - map.putBoolean(EVENT_CONTROLS_VISIBLE, isVisible); - receiveEvent(EVENT_CONTROLS_VISIBILITY_CHANGE, map); - } - - public void fullscreenWillPresent() { - receiveEvent(EVENT_FULLSCREEN_WILL_PRESENT, null); - } - - public void fullscreenDidPresent() { - receiveEvent(EVENT_FULLSCREEN_DID_PRESENT, null); - } - - public void fullscreenWillDismiss() { - receiveEvent(EVENT_FULLSCREEN_WILL_DISMISS, null); - } - - public void fullscreenDidDismiss() { - receiveEvent(EVENT_FULLSCREEN_DID_DISMISS, null); - } - - public void error(String errorString, Exception exception) { - _error(errorString, exception, "0001"); - } - - public void error(String errorString, Exception exception, String errorCode) { - _error(errorString, exception, errorCode); - } - - void _error(String errorString, Exception exception, String errorCode) { - // Prepare stack trace - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - exception.printStackTrace(pw); - String stackTrace = sw.toString(); - - WritableMap error = Arguments.createMap(); - error.putString(EVENT_PROP_ERROR_STRING, errorString); - error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString()); - error.putString(EVENT_PROP_ERROR_CODE, errorCode); - error.putString(EVENT_PROP_ERROR_TRACE, stackTrace); - WritableMap event = Arguments.createMap(); - event.putMap(EVENT_PROP_ERROR, error); - receiveEvent(EVENT_ERROR, event); - } - - public void playbackRateChange(float rate) { - WritableMap map = Arguments.createMap(); - map.putDouble(EVENT_PROP_PLAYBACK_RATE, (double)rate); - receiveEvent(EVENT_PLAYBACK_RATE_CHANGE, map); - } - - public void volumeChange(float volume) { - WritableMap map = Arguments.createMap(); - map.putDouble(EVENT_PROP_VOLUME, volume); - receiveEvent(EVENT_VOLUME_CHANGE, map); - } - - public void timedMetadata(ArrayList _metadataArrayList) { - if (_metadataArrayList.size() == 0) { - return; - } - WritableArray metadataArray = Arguments.createArray(); - - for (int i = 0; i < _metadataArrayList.size(); i++) { - WritableMap map = Arguments.createMap(); - map.putString("identifier", _metadataArrayList.get(i).getIdentifier()); - map.putString("value", _metadataArrayList.get(i).getValue()); - metadataArray.pushMap(map); - } - - WritableMap event = Arguments.createMap(); - event.putArray(EVENT_PROP_TIMED_METADATA, metadataArray); - receiveEvent(EVENT_TIMED_METADATA, event); - } - - public void audioFocusChanged(boolean hasFocus) { - WritableMap map = Arguments.createMap(); - map.putBoolean(EVENT_PROP_HAS_AUDIO_FOCUS, hasFocus); - receiveEvent(EVENT_AUDIO_FOCUS_CHANGE, map); - } - - public void audioBecomingNoisy() { - receiveEvent(EVENT_AUDIO_BECOMING_NOISY, null); - } - - public void receiveAdEvent(String event, Map data) { - WritableMap map = Arguments.createMap(); - map.putString("event", event); - - WritableMap dataMap = Arguments.createMap(); - for (Map.Entry entry : data.entrySet()) { - dataMap.putString(entry.getKey(), entry.getValue()); - } - map.putMap("data", dataMap); - - receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map); - } - - public void receiveAdEvent(String event) { - WritableMap map = Arguments.createMap(); - map.putString("event", event); - - receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map); - } - - public void receiveAdErrorEvent(String message, String code, String type) { - WritableMap map = Arguments.createMap(); - map.putString("event", "ERROR"); - - WritableMap dataMap = Arguments.createMap(); - dataMap.putString("message", message); - dataMap.putString("code", code); - dataMap.putString("type", type); - map.putMap("data", dataMap); - - receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map); - } - - private void receiveEvent(@VideoEvents String type, WritableMap event) { - UIManager uiManager = UIManagerHelper.getUIManager(mReactContext, ViewUtil.getUIManagerType(viewId)); - - if(uiManager != null) { - uiManager.receiveEvent(UIManagerHelper.getSurfaceId(mReactContext), viewId, type, event); - } - } -} diff --git a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.kt b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.kt new file mode 100644 index 00000000..0dee8716 --- /dev/null +++ b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.kt @@ -0,0 +1,349 @@ +package com.brentvatne.common.react + +import com.brentvatne.common.api.TimedMetadata +import com.brentvatne.common.api.Track +import com.brentvatne.common.api.VideoTrack +import com.brentvatne.exoplayer.ReactExoplayerView +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableArray +import com.facebook.react.bridge.WritableMap +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.events.Event +import com.facebook.react.uimanager.events.EventDispatcher +import java.io.PrintWriter +import java.io.StringWriter + +enum class EventTypes(val eventName: String) { + EVENT_LOAD_START("onVideoLoadStart"), + EVENT_LOAD("onVideoLoad"), + EVENT_ERROR("onVideoError"), + EVENT_PROGRESS("onVideoProgress"), + EVENT_BANDWIDTH("onVideoBandwidthUpdate"), + EVENT_CONTROLS_VISIBILITY_CHANGE("onControlsVisibilityChange"), + EVENT_SEEK("onVideoSeek"), + EVENT_END("onVideoEnd"), + EVENT_FULLSCREEN_WILL_PRESENT("onVideoFullscreenPlayerWillPresent"), + EVENT_FULLSCREEN_DID_PRESENT("onVideoFullscreenPlayerDidPresent"), + EVENT_FULLSCREEN_WILL_DISMISS("onVideoFullscreenPlayerWillDismiss"), + EVENT_FULLSCREEN_DID_DISMISS("onVideoFullscreenPlayerDidDismiss"), + + EVENT_READY("onReadyForDisplay"), + EVENT_BUFFER("onVideoBuffer"), + EVENT_PLAYBACK_STATE_CHANGED("onVideoPlaybackStateChanged"), + EVENT_IDLE("onVideoIdle"), + EVENT_TIMED_METADATA("onTimedMetadata"), + EVENT_AUDIO_BECOMING_NOISY("onVideoAudioBecomingNoisy"), + EVENT_AUDIO_FOCUS_CHANGE("onAudioFocusChanged"), + EVENT_PLAYBACK_RATE_CHANGE("onPlaybackRateChange"), + EVENT_VOLUME_CHANGE("onVolumeChange"), + EVENT_AUDIO_TRACKS("onAudioTracks"), + EVENT_TEXT_TRACKS("onTextTracks"), + + EVENT_TEXT_TRACK_DATA_CHANGED("onTextTrackDataChanged"), + EVENT_VIDEO_TRACKS("onVideoTracks"), + EVENT_ON_RECEIVE_AD_EVENT("onReceiveAdEvent"); + + companion object { + fun toMap() = + mutableMapOf().apply { + EventTypes.entries.forEach { eventType -> + put("top${eventType.eventName.removePrefix("on")}", mapOf("registrationName" to eventType.eventName)) + } + } + } +} + +class VideoEventEmitter { + lateinit var onVideoLoadStart: () -> Unit + lateinit var onVideoLoad: ( + duration: Long, + currentPosition: Long, + videoWidth: Int, + videoHeight: Int, + audioTracks: ArrayList, + textTracks: ArrayList, + videoTracks: ArrayList, + trackId: String + ) -> Unit + lateinit var onVideoError: (errorString: String, exception: Exception, errorCode: String) -> Unit + lateinit var onVideoProgress: (currentPosition: Long, bufferedDuration: Long, seekableDuration: Long, currentPlaybackTime: Double) -> Unit + lateinit var onVideoBandwidthUpdate: (bitRateEstimate: Long, height: Int, width: Int, trackId: String) -> Unit + lateinit var onVideoPlaybackStateChanged: (isPlaying: Boolean) -> Unit + lateinit var onVideoSeek: (currentPosition: Long, seekTime: Long) -> Unit + lateinit var onVideoEnd: () -> Unit + lateinit var onVideoFullscreenPlayerWillPresent: () -> Unit + lateinit var onVideoFullscreenPlayerDidPresent: () -> Unit + lateinit var onVideoFullscreenPlayerWillDismiss: () -> Unit + lateinit var onVideoFullscreenPlayerDidDismiss: () -> Unit + lateinit var onReadyForDisplay: () -> Unit + lateinit var onVideoBuffer: (isBuffering: Boolean) -> Unit + lateinit var onControlsVisibilityChange: (isVisible: Boolean) -> Unit + lateinit var onVideoIdle: () -> Unit + lateinit var onTimedMetadata: (metadataArrayList: ArrayList) -> Unit + lateinit var onVideoAudioBecomingNoisy: () -> Unit + lateinit var onAudioFocusChanged: (hasFocus: Boolean) -> Unit + lateinit var onPlaybackRateChange: (rate: Float) -> Unit + lateinit var onVolumeChange: (volume: Float) -> Unit + lateinit var onAudioTracks: (audioTracks: ArrayList?) -> Unit + lateinit var onTextTracks: (textTracks: ArrayList?) -> Unit + lateinit var onVideoTracks: (videoTracks: ArrayList?) -> Unit + lateinit var onTextTrackDataChanged: (textTrackData: String) -> Unit + lateinit var onReceiveAdEvent: (adEvent: String, adData: Map?) -> Unit + + fun addEventEmitters(reactContext: ThemedReactContext, view: ReactExoplayerView) { + val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) + val surfaceId = UIManagerHelper.getSurfaceId(reactContext) + + if (dispatcher != null) { + val event = EventBuilder(surfaceId, view.id, dispatcher) + + onVideoLoadStart = { + event.dispatch(EventTypes.EVENT_LOAD_START) + } + onVideoLoad = { duration, currentPosition, videoWidth, videoHeight, audioTracks, textTracks, videoTracks, trackId -> + event.dispatch(EventTypes.EVENT_LOAD) { + putDouble("duration", duration / 1000.0) + putDouble("playableDuration", currentPosition / 1000.0) + + val naturalSize: WritableMap = aspectRatioToNaturalSize(videoWidth, videoHeight) + putMap("seekableDuration", naturalSize) + putString("trackId", trackId) + putArray("videoTracks", videoTracksToArray(videoTracks)) + putArray("audioTracks", audioTracksToArray(audioTracks)) + putArray("textTracks", textTracksToArray(textTracks)) + + // TODO: Actually check if you can. + putBoolean("canPlayFastForward", true) + putBoolean("canPlaySlowForward", true) + putBoolean("canPlaySlowReverse", true) + putBoolean("canPlayReverse", true) + putBoolean("canPlayFastForward", true) + putBoolean("canStepBackward", true) + putBoolean("canStepForward", true) + } + } + onVideoError = { errorString, exception, errorCode -> + event.dispatch(EventTypes.EVENT_ERROR) { + putMap( + "error", + Arguments.createMap().apply { + // Prepare stack trace + val sw = StringWriter() + val pw = PrintWriter(sw) + exception.printStackTrace(pw) + val stackTrace = sw.toString() + + putString("errorString", errorString) + putString("errorException", exception.toString()) + putString("errorCode", errorCode) + putString("errorStackTrace", stackTrace) + } + ) + } + } + onVideoProgress = { currentPosition, bufferedDuration, seekableDuration, currentPlaybackTime -> + event.dispatch(EventTypes.EVENT_PROGRESS) { + putDouble("currentTime", currentPosition / 1000.0) + putDouble("playableDuration", bufferedDuration / 1000.0) + putDouble("seekableDuration", seekableDuration / 1000.0) + putDouble("currentPlaybackTime", currentPlaybackTime) + } + } + onVideoBandwidthUpdate = { bitRateEstimate, height, width, trackId -> + event.dispatch(EventTypes.EVENT_BANDWIDTH) { + putDouble("bitrate", bitRateEstimate.toDouble()) + putInt("width", width) + putInt("height", height) + putString("trackId", trackId) + } + } + onVideoPlaybackStateChanged = { isPlaying -> + event.dispatch(EventTypes.EVENT_PLAYBACK_STATE_CHANGED) { + putBoolean("isPlaying", isPlaying) + } + } + onVideoSeek = { currentPosition, seekTime -> + event.dispatch(EventTypes.EVENT_SEEK) { + putDouble("currentTime", currentPosition / 1000.0) + putDouble("seekTime", seekTime / 1000.0) + } + } + onVideoEnd = { + event.dispatch(EventTypes.EVENT_END) + } + onVideoFullscreenPlayerWillPresent = { + event.dispatch(EventTypes.EVENT_FULLSCREEN_WILL_PRESENT) + } + onVideoFullscreenPlayerDidPresent = { + event.dispatch(EventTypes.EVENT_FULLSCREEN_DID_PRESENT) + } + onVideoFullscreenPlayerWillDismiss = { + event.dispatch(EventTypes.EVENT_FULLSCREEN_WILL_DISMISS) + } + onVideoFullscreenPlayerDidDismiss = { + event.dispatch(EventTypes.EVENT_FULLSCREEN_DID_DISMISS) + } + onReadyForDisplay = { + event.dispatch(EventTypes.EVENT_READY) + } + onVideoBuffer = { isBuffering -> + event.dispatch(EventTypes.EVENT_BUFFER) { + putBoolean("isBuffering", isBuffering) + } + } + onControlsVisibilityChange = { isVisible -> + event.dispatch(EventTypes.EVENT_CONTROLS_VISIBILITY_CHANGE) { + putBoolean("isVisible", isVisible) + } + } + onVideoIdle = { + event.dispatch(EventTypes.EVENT_IDLE) + } + onTimedMetadata = fn@{ metadataArrayList -> + if (metadataArrayList.size == 0) { + return@fn + } + event.dispatch(EventTypes.EVENT_TIMED_METADATA) { + putArray( + "metadata", + Arguments.createArray().apply { + metadataArrayList.forEachIndexed { i, metadata -> + pushMap( + Arguments.createMap().apply { + putString("identifier", metadata.identifier) + putString("value", metadata.value) + } + ) + } + } + ) + } + } + onVideoAudioBecomingNoisy = { + event.dispatch(EventTypes.EVENT_AUDIO_BECOMING_NOISY) + } + onAudioFocusChanged = { hasFocus -> + event.dispatch(EventTypes.EVENT_AUDIO_FOCUS_CHANGE) { + putBoolean("hasAudioFocus", hasFocus) + } + } + onPlaybackRateChange = { rate -> + event.dispatch(EventTypes.EVENT_PLAYBACK_RATE_CHANGE) { + putDouble("playbackRate", rate.toDouble()) + } + } + onVolumeChange = { volume -> + event.dispatch(EventTypes.EVENT_VOLUME_CHANGE) { + putDouble("volume", volume.toDouble()) + } + } + onAudioTracks = { audioTracks -> + event.dispatch(EventTypes.EVENT_AUDIO_TRACKS) { + putArray("audioTracks", audioTracksToArray(audioTracks)) + } + } + onTextTracks = { textTracks -> + event.dispatch(EventTypes.EVENT_TEXT_TRACKS) { + putArray("textTracks", textTracksToArray(textTracks)) + } + } + onVideoTracks = { videoTracks -> + event.dispatch(EventTypes.EVENT_VIDEO_TRACKS) { + putArray("videoTracks", videoTracksToArray(videoTracks)) + } + } + onTextTrackDataChanged = { textTrackData -> + event.dispatch(EventTypes.EVENT_TEXT_TRACK_DATA_CHANGED) { + putString("subtitleTracks", textTrackData) + } + } + onReceiveAdEvent = { adEvent, adData -> + event.dispatch(EventTypes.EVENT_ON_RECEIVE_AD_EVENT) { + putString("event", adEvent) + putMap( + "data", + Arguments.createMap().apply { + adData?.let { data -> + for ((key, value) in data) { + putString(key!!, value) + } + } + } + ) + } + } + } + } + + private class EventBuilder(private val surfaceId: Int, private val viewId: Int, private val dispatcher: EventDispatcher) { + fun dispatch(event: EventTypes, paramsSetter: (WritableMap.() -> Unit)? = null) = + dispatcher.dispatchEvent(object : Event>(surfaceId, viewId) { + override fun getEventName() = "top${event.eventName.removePrefix("on")}" + override fun getEventData() = Arguments.createMap().apply(paramsSetter ?: {}) + }) + } + + private fun audioTracksToArray(audioTracks: java.util.ArrayList?): WritableArray = + Arguments.createArray().apply { + audioTracks?.forEachIndexed { i, format -> + pushMap( + Arguments.createMap().apply { + putInt("index", i) + putString("title", format.title) + format.mimeType?.let { putString("type", it) } + format.language?.let { putString("language", it) } + if (format.bitrate > 0) putInt("bitrate", format.bitrate) + putBoolean("selected", format.isSelected) + } + ) + } + } + + private fun videoTracksToArray(videoTracks: java.util.ArrayList?): WritableArray = + Arguments.createArray().apply { + videoTracks?.forEachIndexed { i, vTrack -> + pushMap( + Arguments.createMap().apply { + putInt("width", vTrack.width) + putInt("height", vTrack.height) + putInt("bitrate", vTrack.bitrate) + putString("codecs", vTrack.codecs) + putString("trackId", vTrack.trackId) + putInt("index", vTrack.index) + putBoolean("selected", vTrack.isSelected) + putInt("rotation", vTrack.rotation) + } + ) + } + } + + private fun textTracksToArray(textTracks: ArrayList?): WritableArray = + Arguments.createArray().apply { + textTracks?.forEachIndexed { i, format -> + pushMap( + Arguments.createMap().apply { + putInt("index", i) + putString("title", format.title) + putString("type", format.mimeType) + putString("language", format.language) + putBoolean("selected", format.isSelected) + } + ) + } + } + + private fun aspectRatioToNaturalSize(videoWidth: Int, videoHeight: Int): WritableMap = + Arguments.createMap().apply { + putInt("width", videoWidth) + putInt("height", videoHeight) + val orientation = if (videoWidth > videoHeight) { + "landscape" + } else if (videoWidth < videoHeight) { + "portrait" + } else { + "square" + } + putString("orientation", orientation) + } +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 98c02ae9..c3e85be8 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -142,6 +142,7 @@ import java.lang.Math; import java.util.List; import java.util.ArrayList; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.Callable; @@ -173,7 +174,7 @@ public class ReactExoplayerView extends FrameLayout implements DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); } - private final VideoEventEmitter eventEmitter; + protected final VideoEventEmitter eventEmitter; private final ReactExoplayerConfig config; private final DefaultBandwidthMeter bandwidthMeter; private LegacyPlayerControlView playerControlView; @@ -280,7 +281,7 @@ public class ReactExoplayerView extends FrameLayout implements lastPos = pos; lastBufferDuration = bufferedDuration; lastDuration = duration; - eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos)); + eventEmitter.onVideoProgress.invoke(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos)); } } } @@ -307,7 +308,7 @@ public class ReactExoplayerView extends FrameLayout implements public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) { super(context); this.themedReactContext = context; - this.eventEmitter = new VideoEventEmitter(context); + this.eventEmitter = new VideoEventEmitter(); this.config = config; this.bandwidthMeter = config.getBandwidthMeter(); @@ -323,12 +324,6 @@ public class ReactExoplayerView extends FrameLayout implements return player != null && player.isPlayingAd(); } - @Override - public void setId(int id) { - super.setId(id); - eventEmitter.setViewId(id); - } - private void createViews() { if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER); @@ -388,13 +383,13 @@ public class ReactExoplayerView extends FrameLayout implements public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) { if (mReportBandwidth) { if (player == null) { - eventEmitter.bandwidthReport(bitrate, 0, 0, "-1"); + eventEmitter.onVideoBandwidthUpdate.invoke(bitrate, 0, 0, "-1"); } else { Format videoFormat = player.getVideoFormat(); int width = videoFormat != null ? videoFormat.width : 0; int height = videoFormat != null ? videoFormat.height : 0; String trackId = videoFormat != null ? videoFormat.id : "-1"; - eventEmitter.bandwidthReport(bitrate, height, width, trackId); + eventEmitter.onVideoBandwidthUpdate.invoke(bitrate, height, width, trackId); } } } @@ -423,7 +418,7 @@ public class ReactExoplayerView extends FrameLayout implements playerControlView.addVisibilityListener(new LegacyPlayerControlView.VisibilityListener() { @Override public void onVisibilityChange(int visibility) { - eventEmitter.controlsVisibilityChanged(visibility == View.VISIBLE); + eventEmitter.onControlsVisibilityChange.invoke(visibility == View.VISIBLE); } }); } @@ -471,7 +466,7 @@ public class ReactExoplayerView extends FrameLayout implements //Handling the pauseButton click event ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause); pauseButton.setOnClickListener((View v) -> - setPausedModifier(true) + setPausedModifier(true) ); //Handling the fullScreenButton click event @@ -682,7 +677,7 @@ public class ReactExoplayerView extends FrameLayout implements } if (activity == null) { DebugLog.e(TAG, "Failed to initialize Player!, null activity"); - eventEmitter.error("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001"); + eventEmitter.onVideoError.invoke("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001"); return; } @@ -699,7 +694,7 @@ public class ReactExoplayerView extends FrameLayout implements DebugLog.e(TAG, "Failed to initialize Player! 1"); DebugLog.e(TAG, ex.toString()); ex.printStackTrace(); - self.eventEmitter.error(ex.toString(), ex, "1001"); + eventEmitter.onVideoError.invoke(ex.toString(), ex, "1001"); } }); }); @@ -711,7 +706,7 @@ public class ReactExoplayerView extends FrameLayout implements DebugLog.e(TAG, "Failed to initialize Player! 2"); DebugLog.e(TAG, ex.toString()); ex.printStackTrace(); - eventEmitter.error(ex.toString(), ex, "1001"); + eventEmitter.onVideoError.invoke(ex.toString(), ex, "1001"); } }; mainHandler.postDelayed(mainRunnable, 1); @@ -797,7 +792,7 @@ public class ReactExoplayerView extends FrameLayout implements int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - eventEmitter.error(getResources().getString(errorStringId), e, "3003"); + eventEmitter.onVideoError.invoke(getResources().getString(errorStringId), e, "3003"); return null; } } @@ -812,7 +807,7 @@ public class ReactExoplayerView extends FrameLayout implements if (drmSessionManager == null && drmProps != null && drmProps.getDrmUUID() != null) { // Failed to intialize DRM session manager - cannot continue DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!"); - eventEmitter.error("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003"); + eventEmitter.onVideoError.invoke("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003"); return; } @@ -873,7 +868,7 @@ public class ReactExoplayerView extends FrameLayout implements reLayoutControls(); - eventEmitter.loadStart(); + eventEmitter.onVideoLoadStart.invoke(); loadVideoStarted = true; finishPlayerInitialization(); @@ -985,7 +980,7 @@ public class ReactExoplayerView extends FrameLayout implements return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, ++retryCount); } // Handle the unknow exception and emit to JS - eventEmitter.error(ex.toString(), ex, "3006"); + eventEmitter.onVideoError.invoke(ex.toString(), ex, "3006"); return null; } } @@ -1193,7 +1188,7 @@ public class ReactExoplayerView extends FrameLayout implements switch (focusChange) { case AudioManager.AUDIOFOCUS_LOSS: view.hasAudioFocus = false; - view.eventEmitter.audioFocusChanged(false); + view.eventEmitter.onAudioFocusChanged.invoke(false); // FIXME this pause can cause issue if content doesn't have pause capability (can happen on live channel) if (activity != null) { activity.runOnUiThread(view::pausePlayback); @@ -1201,11 +1196,11 @@ public class ReactExoplayerView extends FrameLayout implements view.audioManager.abandonAudioFocus(this); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - view.eventEmitter.audioFocusChanged(false); + view.eventEmitter.onAudioFocusChanged.invoke(false); break; case AudioManager.AUDIOFOCUS_GAIN: view.hasAudioFocus = true; - view.eventEmitter.audioFocusChanged(true); + view.eventEmitter.onAudioFocusChanged.invoke(true); break; default: break; @@ -1216,14 +1211,14 @@ public class ReactExoplayerView extends FrameLayout implements // Lower the volume if (!view.muted) { activity.runOnUiThread(() -> - view.player.setVolume(view.audioVolume * 0.8f) + view.player.setVolume(view.audioVolume * 0.8f) ); } } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // Raise it back to normal if (!view.muted) { activity.runOnUiThread(() -> - view.player.setVolume(view.audioVolume * 1) + view.player.setVolume(view.audioVolume * 1) ); } } @@ -1326,7 +1321,7 @@ public class ReactExoplayerView extends FrameLayout implements // AudioBecomingNoisyListener implementation @Override public void onAudioBecomingNoisy() { - eventEmitter.audioBecomingNoisy(); + eventEmitter.onVideoAudioBecomingNoisy.invoke(); } // Player.Listener implementation @@ -1341,11 +1336,11 @@ public class ReactExoplayerView extends FrameLayout implements int playbackState = player.getPlaybackState(); boolean playWhenReady = player.getPlayWhenReady(); String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState="; - eventEmitter.playbackRateChange(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f); + eventEmitter.onPlaybackRateChange.invoke(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f); switch (playbackState) { case Player.STATE_IDLE: text += "idle"; - eventEmitter.idle(); + eventEmitter.onVideoIdle.invoke(); clearProgressMessageHandler(); if (!player.getPlayWhenReady()) { setKeepScreenOn(false); @@ -1359,7 +1354,7 @@ public class ReactExoplayerView extends FrameLayout implements break; case Player.STATE_READY: text += "ready"; - eventEmitter.ready(); + eventEmitter.onReadyForDisplay.invoke(); onBuffering(false); clearProgressMessageHandler(); // ensure there is no other message startProgressHandler(); @@ -1377,7 +1372,7 @@ public class ReactExoplayerView extends FrameLayout implements case Player.STATE_ENDED: text += "ended"; updateProgress(); - eventEmitter.end(); + eventEmitter.onVideoEnd.invoke(); onStopPlayback(); setKeepScreenOn(false); break; @@ -1434,7 +1429,7 @@ public class ReactExoplayerView extends FrameLayout implements if (videoTracks != null) { isUsingContentResolution = true; } - eventEmitter.load(duration, currentPosition, width, height, + eventEmitter.onVideoLoad.invoke(duration, currentPosition, width, height, audioTracks, textTracks, videoTracks, trackId ); }); @@ -1443,7 +1438,7 @@ public class ReactExoplayerView extends FrameLayout implements ArrayList videoTracks = getVideoTrackInfo(); - eventEmitter.load(duration, currentPosition, width, height, + eventEmitter.onVideoLoad.invoke(duration, currentPosition, width, height, audioTracks, textTracks, videoTracks, trackId); } } @@ -1626,13 +1621,13 @@ public class ReactExoplayerView extends FrameLayout implements } isBuffering = buffering; - eventEmitter.buffering(buffering); + eventEmitter.onVideoBuffer.invoke(buffering); } @Override public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, @Player.DiscontinuityReason int reason) { if (reason == Player.DISCONTINUITY_REASON_SEEK) { - eventEmitter.seek(player.getCurrentPosition(), newPosition.positionMs % 1000); // time are in seconds /°\ + eventEmitter.onVideoSeek.invoke(player.getCurrentPosition(), newPosition.positionMs % 1000); // time are in seconds /°\ if (isUsingContentResolution) { // We need to update the selected track to make sure that it still matches user selection if track list has changed in this period setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); @@ -1655,7 +1650,7 @@ public class ReactExoplayerView extends FrameLayout implements if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION && player.getRepeatMode() == Player.REPEAT_MODE_ONE) { updateProgress(); - eventEmitter.end(); + eventEmitter.onVideoEnd.invoke(); } } @@ -1666,24 +1661,24 @@ public class ReactExoplayerView extends FrameLayout implements @Override public void onTracksChanged(@NonNull Tracks tracks) { - eventEmitter.textTracks(getTextTrackInfo()); - eventEmitter.audioTracks(getAudioTrackInfo()); - eventEmitter.videoTracks(getVideoTrackInfo()); + eventEmitter.onTextTracks.invoke(getTextTrackInfo()); + eventEmitter.onAudioTracks.invoke(getAudioTrackInfo()); + eventEmitter.onVideoTracks.invoke(getVideoTrackInfo()); } @Override public void onPlaybackParametersChanged(PlaybackParameters params) { - eventEmitter.playbackRateChange(params.speed); + eventEmitter.onPlaybackRateChange.invoke(params.speed); } @Override public void onVolumeChanged(float volume) { - eventEmitter.volumeChange(volume); + eventEmitter.onVolumeChange.invoke(volume); } @Override public void onIsPlayingChanged(boolean isPlaying) { - eventEmitter.playbackStateChanged(isPlaying); + eventEmitter.onVideoPlaybackStateChanged.invoke(isPlaying); } @Override @@ -1709,7 +1704,7 @@ public class ReactExoplayerView extends FrameLayout implements default: break; } - eventEmitter.error(errorString, e, errorCode); + eventEmitter.onVideoError.invoke(errorString, e, errorCode); playerNeedsSource = true; if (isBehindLiveWindow(e)) { clearResumePosition(); @@ -1760,13 +1755,13 @@ public class ReactExoplayerView extends FrameLayout implements DebugLog.d(TAG, "unhandled metadata " + entry); } } - eventEmitter.timedMetadata(metadataArray); + eventEmitter.onTimedMetadata.invoke(metadataArray); } public void onCues(CueGroup cueGroup) { if (!cueGroup.cues.isEmpty() && cueGroup.cues.get(0).text != null) { String subtitleText = cueGroup.cues.get(0).text.toString(); - eventEmitter.textTrackDataChanged(subtitleText); + eventEmitter.onTextTrackDataChanged.invoke(subtitleText); } } @@ -2220,7 +2215,7 @@ public class ReactExoplayerView extends FrameLayout implements Window window = activity.getWindow(); WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(window, window.getDecorView()); if (isFullscreen) { - eventEmitter.fullscreenWillPresent(); + eventEmitter.onVideoFullscreenPlayerWillPresent.invoke(); if (fullScreenPlayerView != null) { fullScreenPlayerView.show(); } @@ -2228,10 +2223,10 @@ public class ReactExoplayerView extends FrameLayout implements WindowCompat.setDecorFitsSystemWindows(window, false); controller.hide(WindowInsetsCompat.Type.systemBars()); controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); - eventEmitter.fullscreenDidPresent(); + eventEmitter.onVideoFullscreenPlayerDidPresent.invoke(); }); } else { - eventEmitter.fullscreenWillDismiss(); + eventEmitter.onVideoFullscreenPlayerWillDismiss.invoke(); if (fullScreenPlayerView != null) { fullScreenPlayerView.dismiss(); reLayoutControls(); @@ -2240,7 +2235,7 @@ public class ReactExoplayerView extends FrameLayout implements UiThreadUtil.runOnUiThread(() -> { WindowCompat.setDecorFitsSystemWindows(window, true); controller.show(WindowInsetsCompat.Type.systemBars()); - eventEmitter.fullscreenDidDismiss(); + eventEmitter.onVideoFullscreenPlayerDidDismiss.invoke(); }); } // need to be done at the end to avoid hiding fullscreen control button when fullScreenPlayerView is shown @@ -2291,7 +2286,7 @@ public class ReactExoplayerView extends FrameLayout implements @Override public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, @NonNull Exception e) { DebugLog.d("DRM Info", "onDrmSessionManagerError"); - eventEmitter.error("onDrmSessionManagerError", e, "3002"); + eventEmitter.onVideoError.invoke("onDrmSessionManagerError", e, "3002"); } @Override @@ -2333,16 +2328,21 @@ public class ReactExoplayerView extends FrameLayout implements @Override public void onAdEvent(AdEvent adEvent) { if (adEvent.getAdData() != null) { - eventEmitter.receiveAdEvent(adEvent.getType().name(), adEvent.getAdData()); + eventEmitter.onReceiveAdEvent.invoke(adEvent.getType().name(), adEvent.getAdData()); } else { - eventEmitter.receiveAdEvent(adEvent.getType().name()); + eventEmitter.onReceiveAdEvent.invoke(adEvent.getType().name(), null); } } @Override public void onAdError(AdErrorEvent adErrorEvent) { AdError error = adErrorEvent.getError(); - eventEmitter.receiveAdErrorEvent(error.getMessage(), String.valueOf(error.getErrorCode()), String.valueOf(error.getErrorType())); + Map errMap = Map.of( + "message", error.getMessage(), + "code", String.valueOf(error.getErrorCode()), + "type", String.valueOf(error.getErrorType()) + ); + eventEmitter.onReceiveAdEvent.invoke("ERROR", errMap); } public void setControlsStyles(ControlsConfig controlsStyles) { diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index af8e4a51..ee257ddf 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -18,13 +18,12 @@ import com.brentvatne.common.api.SideLoadedTextTrackList; import com.brentvatne.common.api.Source; import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.ViewType; -import com.brentvatne.common.react.VideoEventEmitter; +import com.brentvatne.common.react.EventTypes; import com.brentvatne.common.toolbox.DebugLog; import com.brentvatne.common.toolbox.ReactBridgeUtils; import com.brentvatne.react.ReactNativeVideoManager; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.annotations.ReactProp; @@ -110,11 +109,13 @@ public class ReactExoplayerViewManager extends ViewGroupManager getExportedCustomDirectEventTypeConstants() { - MapBuilder.Builder builder = MapBuilder.builder(); - for (String event : VideoEventEmitter.Events) { - builder.put(event, MapBuilder.of("registrationName", event)); - } - return builder.build(); + return EventTypes.Companion.toMap(); + } + + @Override + public void addEventEmitters(@NonNull ThemedReactContext reactContext, @NonNull ReactExoplayerView view) { + super.addEventEmitters(reactContext, view); + view.eventEmitter.addEventEmitters(reactContext, view); } @ReactProp(name = PROP_DRM)