react-native-unified-player 0.2.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.
@@ -0,0 +1,265 @@
1
+ package com.unifiedplayer
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.graphics.Color
6
+ import android.util.Log
7
+ import android.view.Gravity
8
+ import android.widget.FrameLayout
9
+ import com.facebook.react.bridge.Arguments
10
+ import com.google.android.exoplayer2.ExoPlayer
11
+ import com.google.android.exoplayer2.MediaItem
12
+ import com.google.android.exoplayer2.Player
13
+ import com.google.android.exoplayer2.PlaybackException
14
+ import com.google.android.exoplayer2.Timeline
15
+ import com.google.android.exoplayer2.Tracks
16
+ import com.google.android.exoplayer2.ui.PlayerView
17
+ import com.google.android.exoplayer2.video.VideoSize
18
+ import com.facebook.react.bridge.WritableMap
19
+
20
+ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
21
+ companion object {
22
+ private const val TAG = "UnifiedPlayerView"
23
+ }
24
+
25
+ private var videoUrl: String? = null
26
+ private var authToken: String? = null
27
+ private var autoplay: Boolean = true
28
+ private var loop: Boolean = false
29
+ private var playerView: PlayerView
30
+ private var player: ExoPlayer? = null
31
+ private var isPlaying = false
32
+ private var currentProgress = 0
33
+
34
+ init {
35
+ setBackgroundColor(Color.BLACK)
36
+
37
+ // Create ExoPlayer
38
+ player = ExoPlayer.Builder(context).build()
39
+
40
+ // Create PlayerView
41
+ playerView = PlayerView(context).apply {
42
+ layoutParams = LayoutParams(
43
+ LayoutParams.MATCH_PARENT,
44
+ LayoutParams.MATCH_PARENT
45
+ )
46
+ setPlayer(player)
47
+ // playerView.surfaceView?.surfaceType = android.view.SurfaceView.SURFACE_TYPE_SOFTWARE // Reverted: Let's remove surfaceType setting
48
+ }
49
+
50
+ addView(playerView)
51
+ // Add logging for playerView dimensions and post play call
52
+ playerView.post {
53
+ Log.d(TAG, "PlayerView dimensions after addView: width=${playerView.width}, height=${playerView.height}")
54
+ // Post play call to ensure PlayerView is laid out
55
+ if (autoplay) {
56
+ player?.play()
57
+ isPlaying = true
58
+ Log.d(TAG, "Autoplay: player?.play() called after layout")
59
+ }
60
+ }
61
+
62
+ player?.addListener(object : Player.Listener {
63
+ override fun onPlaybackStateChanged(playbackState: Int) {
64
+ Log.d(TAG, "onPlaybackStateChanged: $playbackState") // Added log
65
+ when (playbackState) {
66
+ Player.STATE_READY -> {
67
+ Log.d(TAG, "ExoPlayer STATE_READY")
68
+ sendEvent("onReadyToPlay", Arguments.createMap())
69
+ // Removed custom autoplay logic, relying on player?.playWhenReady
70
+ }
71
+ Player.STATE_ENDED -> {
72
+ Log.d(TAG, "ExoPlayer STATE_ENDED")
73
+ sendEvent("onPlaybackComplete", Arguments.createMap())
74
+ }
75
+ Player.STATE_BUFFERING -> {
76
+ Log.d(TAG, "ExoPlayer STATE_BUFFERING")
77
+ }
78
+ Player.STATE_IDLE -> {
79
+ Log.d(TAG, "ExoPlayer STATE_IDLE")
80
+ }
81
+ Player.STATE_BUFFERING -> {
82
+ Log.d(TAG, "ExoPlayer STATE_BUFFERING")
83
+ }
84
+ }
85
+ }
86
+
87
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
88
+ if (isPlaying) {
89
+ Log.d(TAG, "ExoPlayer isPlaying")
90
+ sendEvent("onPlay", Arguments.createMap())
91
+ } else {
92
+ Log.d(TAG, "ExoPlayer isPaused")
93
+ sendEvent("onPause", Arguments.createMap())
94
+ }
95
+ }
96
+
97
+ override fun onPlayerError(error: PlaybackException) {
98
+ Log.e(TAG, "ExoPlayer Error: ${error.message}, errorCode: ${error.errorCodeName}, errorCode বিস্তারিত: ${error.errorCode}")
99
+ val event = Arguments.createMap().apply {
100
+ putInt("code", error.errorCode)
101
+ putString("message", error.message)
102
+ }
103
+ sendEvent("onError", event)
104
+ }
105
+
106
+ override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
107
+ Log.d(TAG, "ExoPlayer onMediaItemTransition")
108
+ sendEvent("onLoadStart", Arguments.createMap())
109
+ }
110
+
111
+ override fun onPlaybackSuppressionReasonChanged(playbackSuppressionReason: Int) {
112
+ // Called when playback is suppressed temporarily due to content restrictions.
113
+ Log.d(TAG, "ExoPlayer onPlaybackSuppressionReasonChanged: $playbackSuppressionReason")
114
+ }
115
+
116
+ override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
117
+ // Called when playWhenReady state changes.
118
+ Log.d(TAG, "ExoPlayer onPlayWhenReadyChanged: playWhenReady=$playWhenReady, reason=$reason")
119
+ }
120
+
121
+ override fun onPositionDiscontinuity(reason: Int) {
122
+ // Called when there's a discontinuity in playback, such as seeking or ad insertion.
123
+ Log.d(TAG, "ExoPlayer onPositionDiscontinuity: reason=$reason")
124
+ }
125
+
126
+ override fun onRepeatModeChanged(repeatMode: Int) {
127
+ // Called when the repeat mode changes.
128
+ Log.d(TAG, "ExoPlayer onRepeatModeChanged: repeatMode=$repeatMode")
129
+ }
130
+
131
+ override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
132
+ // Called when shuffle mode is enabled or disabled.
133
+ Log.d(TAG, "ExoPlayer onShuffleModeEnabledChanged: shuffleModeEnabled=$shuffleModeEnabled")
134
+ }
135
+
136
+ override fun onTracksChanged(tracks: Tracks) {
137
+ // Called when available tracks change.
138
+ Log.d(TAG, "ExoPlayer onTracksChanged: tracks=$tracks")
139
+ }
140
+
141
+ override fun onVolumeChanged(volume: Float) {
142
+ // Called when volume changes.
143
+ Log.d(TAG, "ExoPlayer onVolumeChanged: volume=$volume")
144
+ }
145
+
146
+ override fun onVideoSizeChanged(videoSize: VideoSize) {
147
+ // Called when video size changes.
148
+ Log.d(TAG, "ExoPlayer onVideoSizeChanged: videoSize=$videoSize")
149
+ Log.d(TAG, "Video size changed: width=${videoSize.width}, height=${videoSize.height}")
150
+ }
151
+
152
+ override fun onSurfaceSizeChanged(width: Int, height: Int) {
153
+ // Called when the size of the surface changes.
154
+ Log.d(TAG, "ExoPlayer onSurfaceSizeChanged: width=$width, height=$height")
155
+ }
156
+
157
+ override fun onRenderedFirstFrame() {
158
+ // Called when the first frame is rendered.
159
+ Log.d(TAG, "ExoPlayer onRenderedFirstFrame")
160
+ }
161
+
162
+ override fun onSkipSilenceEnabledChanged(skipSilenceEnabled: Boolean) {
163
+ // Called when skip silence is enabled or disabled.
164
+ Log.d(TAG, "ExoPlayer onSkipSilenceEnabledChanged: skipSilenceEnabled=$skipSilenceEnabled")
165
+ }
166
+
167
+ override fun onTimelineChanged(timeline: Timeline, reason: Int) {
168
+ // Called when the timeline changes, like when a new media source is loaded or an ad break starts or ends.
169
+ Log.d(TAG, "ExoPlayer onTimelineChanged: timeline=$timeline, reason=$reason")
170
+ }
171
+ })
172
+ }
173
+
174
+ fun setVideoUrl(url: String?) {
175
+ Log.d(TAG, "Setting video URL: $url")
176
+ if (url.isNullOrEmpty()) {
177
+ Log.d(TAG, "Video URL is null or empty, skipping load")
178
+ return
179
+ }
180
+
181
+ videoUrl = url
182
+
183
+
184
+ // Load video with ExoPlayer
185
+ Log.d(TAG, "Creating MediaItem from URI: $videoUrl")
186
+ val mediaItem = MediaItem.fromUri(videoUrl!!)
187
+ player?.setMediaItem(mediaItem)
188
+ Log.d(TAG, "Preparing ExoPlayer")
189
+ player?.prepare()
190
+ Log.d(TAG, "ExoPlayer prepared")
191
+ }
192
+
193
+ fun setAuthToken(token: String?) {
194
+ authToken = token
195
+ // If URL already set, reload with new token
196
+ if (!videoUrl.isNullOrEmpty()) {
197
+ setVideoUrl(videoUrl) // reload video
198
+ }
199
+ }
200
+
201
+ fun setAutoplay(value: Boolean) {
202
+ autoplay = value
203
+ player?.playWhenReady = value // Set ExoPlayer's playWhenReady property
204
+ }
205
+
206
+ fun setLoop(value: Boolean) {
207
+ loop = value
208
+ player?.repeatMode = if (loop) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF
209
+ }
210
+
211
+ fun play() {
212
+ Log.d(TAG, "Play called")
213
+ player?.play()
214
+ isPlaying = true
215
+ }
216
+
217
+ fun pause() {
218
+ Log.d(TAG, "Pause called")
219
+ player?.pause()
220
+ isPlaying = false
221
+ }
222
+
223
+ fun seekTo(time: Float) {
224
+ Log.d(TAG, "Seek called: $time")
225
+ player?.seekTo((time * 1000).toLong()) // time in seconds, ExoPlayer in milliseconds
226
+ }
227
+
228
+ fun getCurrentTime(): Float {
229
+ return (player?.currentPosition?.toFloat() ?: 0f) / 1000f // to seconds
230
+ }
231
+
232
+ fun getDuration(): Float {
233
+ return (player?.duration?.toFloat() ?: 0f) / 1000f // to seconds
234
+ }
235
+
236
+ private fun sendEvent(eventName: String, params: WritableMap) {
237
+ UnifiedPlayerEventEmitter.getInstance()?.sendEvent(eventName, params)
238
+ }
239
+
240
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
241
+ super.onLayout(changed, left, top, right, bottom)
242
+ val width = right - left
243
+ val height = bottom - top
244
+ Log.d(TAG, "UnifiedPlayerView onLayout: width=$width, height=$height")
245
+ // Ensure playerView also gets laid out
246
+ playerView.layout(0, 0, width, height)
247
+ }
248
+
249
+ override fun onAttachedToWindow() {
250
+ super.onAttachedToWindow()
251
+ Log.d(TAG, "UnifiedPlayerView onAttachedToWindow")
252
+ playerView.setPlayer(player)
253
+ // Autoplay logic can remain in post block or be moved here
254
+ if (autoplay) {
255
+ player?.play()
256
+ isPlaying = true
257
+ Log.d(TAG, "Autoplay: player?.play() called in onAttachedToWindow")
258
+ }
259
+ }
260
+
261
+ override fun onDetachedFromWindow() {
262
+ super.onDetachedFromWindow()
263
+ player?.release()
264
+ }
265
+ }
@@ -0,0 +1,33 @@
1
+ package com.unifiedplayer
2
+
3
+ import com.facebook.react.uimanager.SimpleViewManager
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.uimanager.annotations.ReactProp
6
+
7
+ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
8
+ override fun getName() = "UnifiedPlayerView"
9
+
10
+ override fun createViewInstance(reactContext: ThemedReactContext): UnifiedPlayerView {
11
+ return UnifiedPlayerView(reactContext)
12
+ }
13
+
14
+ @ReactProp(name = "videoUrl")
15
+ fun setVideoUrl(view: UnifiedPlayerView, url: String?) {
16
+ view.setVideoUrl(url)
17
+ }
18
+
19
+ @ReactProp(name = "authToken")
20
+ fun setAuthToken(view: UnifiedPlayerView, token: String?) {
21
+ view.setAuthToken(token)
22
+ }
23
+
24
+ @ReactProp(name = "autoplay")
25
+ fun setAutoplay(view: UnifiedPlayerView, autoplay: Boolean) {
26
+ view.setAutoplay(autoplay)
27
+ }
28
+
29
+ @ReactProp(name = "loop")
30
+ fun setLoop(view: UnifiedPlayerView, loop: Boolean) {
31
+ view.setLoop(loop)
32
+ }
33
+ }
@@ -0,0 +1,5 @@
1
+ #import <React/RCTEventEmitter.h>
2
+ #import <React/RCTBridgeModule.h>
3
+
4
+ @interface UnifiedPlayerModule : RCTEventEmitter <RCTBridgeModule>
5
+ @end