react-native-unified-player 0.2.4 → 0.3.1

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.
@@ -9,7 +9,7 @@ buildscript {
9
9
  }
10
10
 
11
11
  dependencies {
12
- classpath "com.android.tools.build:gradle:7.3.1"
12
+ classpath "com.android.tools.build:gradle:8.1.0"
13
13
  // noinspection DifferentKotlinGradleVersion
14
14
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
15
15
  }
@@ -68,6 +68,7 @@ android {
68
68
  repositories {
69
69
  mavenCentral()
70
70
  google()
71
+ maven { url "$rootDir/../node_modules/react-native/android" }
71
72
  }
72
73
 
73
74
  def kotlin_version = getExtOrDefault("kotlinVersion")
@@ -3,3 +3,4 @@ UnifiedPlayer_minSdkVersion=24
3
3
  UnifiedPlayer_targetSdkVersion=34
4
4
  UnifiedPlayer_compileSdkVersion=35
5
5
  UnifiedPlayer_ndkVersion=27.1.12297006
6
+ android.useAndroidX=true
@@ -6,88 +6,171 @@ import com.facebook.react.bridge.ReactMethod
6
6
  import com.facebook.react.bridge.Promise
7
7
  import com.facebook.react.uimanager.UIManagerModule
8
8
  import android.util.Log
9
+ import com.facebook.react.bridge.UiThreadUtil
10
+ import android.view.View
9
11
 
10
12
  class UnifiedPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
11
13
  companion object {
12
14
  private const val TAG = "UnifiedPlayerModule"
13
15
  }
14
16
 
17
+ init {
18
+ Log.d(TAG, "UnifiedPlayerModule initialized")
19
+ }
20
+
15
21
  override fun getName(): String {
22
+ Log.d(TAG, "getName() called, returning 'UnifiedPlayer'")
16
23
  return "UnifiedPlayer"
17
24
  }
18
25
 
19
- private fun getPlayerView(viewId: Int): UnifiedPlayerView? {
20
- val uiManager = reactContext.getNativeModule(UIManagerModule::class.java)
21
- val view = uiManager?.resolveView(viewId) as? UnifiedPlayerView
22
-
23
- if (view == null) {
24
- Log.e(TAG, "Unable to find UnifiedPlayerView with id $viewId")
26
+ private fun getPlayerViewByTag(viewTag: Int): UnifiedPlayerView? {
27
+ try {
28
+ val view = reactApplicationContext.currentActivity?.findViewById<View>(viewTag)
29
+ Log.d(TAG, "Looking for view with tag: $viewTag, found: ${view != null}")
30
+
31
+ if (view is UnifiedPlayerView) {
32
+ return view
33
+ } else if (view != null) {
34
+ Log.e(TAG, "View with tag $viewTag is not a UnifiedPlayerView, it's a ${view.javaClass.simpleName}")
35
+ }
36
+ } catch (e: Exception) {
37
+ Log.e(TAG, "Error finding view with tag $viewTag: ${e.message}", e)
25
38
  }
26
-
27
- return view
39
+ return null
28
40
  }
29
41
 
30
42
  @ReactMethod
31
- fun play(viewId: Int) {
43
+ fun play(viewTag: Int, promise: Promise) {
44
+ Log.d(TAG, "Native play method called with viewTag: $viewTag")
32
45
  try {
33
- Log.d(TAG, "Play command received for view $viewId")
34
- reactContext.runOnUiQueueThread {
35
- getPlayerView(viewId)?.play()
46
+ val playerView = getPlayerViewByTag(viewTag)
47
+ if (playerView != null) {
48
+ UiThreadUtil.runOnUiThread {
49
+ try {
50
+ playerView.play()
51
+ Log.d(TAG, "Play command executed successfully")
52
+ promise.resolve(true)
53
+ } catch (e: Exception) {
54
+ Log.e(TAG, "Error during play: ${e.message}", e)
55
+ promise.reject("PLAY_ERROR", "Error during play: ${e.message}", e)
56
+ }
57
+ }
58
+ } else {
59
+ Log.e(TAG, "Player view not found for tag: $viewTag")
60
+ promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
36
61
  }
37
62
  } catch (e: Exception) {
38
- Log.e(TAG, "Error playing video: ${e.message}")
63
+ Log.e(TAG, "Error in play method: ${e.message}", e)
64
+ promise.reject("PLAY_ERROR", "Error in play method: ${e.message}", e)
39
65
  }
40
66
  }
41
-
67
+
42
68
  @ReactMethod
43
- fun pause(viewId: Int) {
69
+ fun pause(viewTag: Int, promise: Promise) {
70
+ Log.d(TAG, "Native pause method called with viewTag: $viewTag")
44
71
  try {
45
- Log.d(TAG, "Pause command received for view $viewId")
46
- reactContext.runOnUiQueueThread {
47
- getPlayerView(viewId)?.pause()
72
+ val playerView = getPlayerViewByTag(viewTag)
73
+ if (playerView != null) {
74
+ UiThreadUtil.runOnUiThread {
75
+ try {
76
+ playerView.pause()
77
+ Log.d(TAG, "Pause command executed successfully")
78
+ promise.resolve(true)
79
+ } catch (e: Exception) {
80
+ Log.e(TAG, "Error during pause: ${e.message}", e)
81
+ promise.reject("PAUSE_ERROR", "Error during pause: ${e.message}", e)
82
+ }
83
+ }
84
+ } else {
85
+ Log.e(TAG, "Player view not found for tag: $viewTag")
86
+ promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
48
87
  }
49
88
  } catch (e: Exception) {
50
- Log.e(TAG, "Error pausing video: ${e.message}")
89
+ Log.e(TAG, "Error in pause method: ${e.message}", e)
90
+ promise.reject("PAUSE_ERROR", "Error in pause method: ${e.message}", e)
51
91
  }
52
92
  }
53
-
93
+
54
94
  @ReactMethod
55
- fun seekTo(viewId: Int, time: Float) {
95
+ fun seekTo(viewTag: Int, seconds: Float, promise: Promise) {
96
+ Log.d(TAG, "Native seekTo method called with viewTag: $viewTag, seconds: $seconds")
56
97
  try {
57
- Log.d(TAG, "Seek command received for view $viewId to time $time")
58
- reactContext.runOnUiQueueThread {
59
- getPlayerView(viewId)?.seekTo(time)
98
+ val playerView = getPlayerViewByTag(viewTag)
99
+ if (playerView != null) {
100
+ UiThreadUtil.runOnUiThread {
101
+ try {
102
+ playerView.seekTo(seconds)
103
+ Log.d(TAG, "SeekTo command executed successfully to $seconds seconds")
104
+ promise.resolve(true)
105
+ } catch (e: Exception) {
106
+ Log.e(TAG, "Error during seekTo: ${e.message}", e)
107
+ promise.reject("SEEK_ERROR", "Error during seekTo: ${e.message}", e)
108
+ }
109
+ }
110
+ } else {
111
+ Log.e(TAG, "Player view not found for tag: $viewTag")
112
+ promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
60
113
  }
61
114
  } catch (e: Exception) {
62
- Log.e(TAG, "Error seeking: ${e.message}")
115
+ Log.e(TAG, "Error in seekTo method: ${e.message}", e)
116
+ promise.reject("SEEK_ERROR", "Error in seekTo method: ${e.message}", e)
63
117
  }
64
118
  }
65
-
119
+
66
120
  @ReactMethod
67
- fun getCurrentTime(viewId: Int, promise: Promise) {
121
+ fun getCurrentTime(viewTag: Int, promise: Promise) {
122
+ Log.d(TAG, "Native getCurrentTime method called with viewTag: $viewTag")
68
123
  try {
69
- Log.d(TAG, "Get current time for view $viewId")
70
- reactContext.runOnUiQueueThread {
71
- val time = getPlayerView(viewId)?.getCurrentTime() ?: 0f
72
- promise.resolve(time)
124
+ val playerView = getPlayerViewByTag(viewTag)
125
+ if (playerView != null) {
126
+ UiThreadUtil.runOnUiThread {
127
+ try {
128
+ val currentTime = playerView.getCurrentTime()
129
+ Log.d(TAG, "getCurrentTime executed successfully, current time: $currentTime")
130
+ promise.resolve(currentTime)
131
+ } catch (e: Exception) {
132
+ Log.e(TAG, "Error getting current time: ${e.message}", e)
133
+ promise.reject("GET_TIME_ERROR", "Error getting current time: ${e.message}", e)
134
+ }
135
+ }
136
+ } else {
137
+ Log.e(TAG, "Player view not found for tag: $viewTag")
138
+ promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
73
139
  }
74
140
  } catch (e: Exception) {
75
- Log.e(TAG, "Error getting current time: ${e.message}")
76
- promise.reject("ERROR", "Failed to get current time: ${e.message}")
141
+ Log.e(TAG, "Error in getCurrentTime method: ${e.message}", e)
142
+ promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method: ${e.message}", e)
77
143
  }
78
144
  }
79
-
145
+
80
146
  @ReactMethod
81
- fun getDuration(viewId: Int, promise: Promise) {
147
+ fun getDuration(viewTag: Int, promise: Promise) {
148
+ Log.d(TAG, "Native getDuration method called with viewTag: $viewTag")
82
149
  try {
83
- Log.d(TAG, "Get duration for view $viewId")
84
- reactContext.runOnUiQueueThread {
85
- val duration = getPlayerView(viewId)?.getDuration() ?: 0f
86
- promise.resolve(duration)
150
+ val playerView = getPlayerViewByTag(viewTag)
151
+ if (playerView != null) {
152
+ UiThreadUtil.runOnUiThread {
153
+ try {
154
+ val duration = playerView.getDuration()
155
+ Log.d(TAG, "getDuration executed successfully, duration: $duration")
156
+ promise.resolve(duration)
157
+ } catch (e: Exception) {
158
+ Log.e(TAG, "Error getting duration: ${e.message}", e)
159
+ promise.reject("GET_DURATION_ERROR", "Error getting duration: ${e.message}", e)
160
+ }
161
+ }
162
+ } else {
163
+ Log.e(TAG, "Player view not found for tag: $viewTag")
164
+ promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
87
165
  }
88
166
  } catch (e: Exception) {
89
- Log.e(TAG, "Error getting duration: ${e.message}")
90
- promise.reject("ERROR", "Failed to get duration: ${e.message}")
167
+ Log.e(TAG, "Error in getDuration method: ${e.message}", e)
168
+ promise.reject("GET_DURATION_ERROR", "Error in getDuration method: ${e.message}", e)
91
169
  }
92
170
  }
93
- }
171
+
172
+ @ReactMethod
173
+ fun testMethod(message: String) {
174
+ Log.d(TAG, "Test method called with message: $message")
175
+ }
176
+ }
@@ -4,17 +4,20 @@ import com.facebook.react.ReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.uimanager.ViewManager
7
-
7
+ import android.util.Log
8
8
 
9
9
  class UnifiedPlayerPackage : ReactPackage {
10
+ private val TAG = "UnifiedPlayerPackage"
11
+
10
12
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
13
+ Log.d(TAG, "Creating native modules")
11
14
  return listOf(
12
- UnifiedPlayerModule(reactContext),
13
- UnifiedPlayerEventEmitter(reactContext)
15
+ UnifiedPlayerModule(reactContext)
14
16
  )
15
17
  }
16
18
 
17
19
  override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
20
+ Log.d(TAG, "Creating view managers")
18
21
  return listOf(UnifiedPlayerViewManager())
19
22
  }
20
23
  }
@@ -4,6 +4,8 @@ import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.graphics.Color
6
6
  import android.util.Log
7
+ import android.os.Handler
8
+ import android.os.Looper
7
9
  import android.view.Gravity
8
10
  import android.widget.FrameLayout
9
11
  import com.facebook.react.bridge.Arguments
@@ -11,11 +13,12 @@ import com.google.android.exoplayer2.ExoPlayer
11
13
  import com.google.android.exoplayer2.MediaItem
12
14
  import com.google.android.exoplayer2.Player
13
15
  import com.google.android.exoplayer2.PlaybackException
14
- import com.google.android.exoplayer2.Timeline
15
16
  import com.google.android.exoplayer2.Tracks
16
17
  import com.google.android.exoplayer2.ui.PlayerView
17
18
  import com.google.android.exoplayer2.video.VideoSize
18
19
  import com.facebook.react.bridge.WritableMap
20
+ import com.facebook.react.bridge.ReactContext
21
+ import com.facebook.react.uimanager.events.RCTEventEmitter
19
22
 
20
23
  class UnifiedPlayerView(context: Context) : FrameLayout(context) {
21
24
  companion object {
@@ -23,13 +26,40 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
23
26
  }
24
27
 
25
28
  private var videoUrl: String? = null
26
- private var authToken: String? = null
27
29
  private var autoplay: Boolean = true
28
30
  private var loop: Boolean = false
29
31
  private var playerView: PlayerView
30
32
  private var player: ExoPlayer? = null
31
- private var isPlaying = false
32
33
  private var currentProgress = 0
34
+ private var isPaused = false
35
+
36
+ private val progressHandler = Handler(Looper.getMainLooper())
37
+ private val progressRunnable: Runnable = object : Runnable {
38
+ override fun run() {
39
+ player?.let {
40
+ val currentTime = it.currentPosition.toFloat() / 1000f
41
+ val duration = it.duration.toFloat() / 1000f
42
+
43
+ // Log the actual values for debugging
44
+ Log.d(TAG, "Progress values - currentTime: $currentTime, duration: $duration, raw duration: ${it.duration}")
45
+
46
+ // Only send valid duration values
47
+ if (it.duration > 0) {
48
+ val event = Arguments.createMap()
49
+ event.putDouble("currentTime", currentTime.toDouble())
50
+ event.putDouble("duration", duration.toDouble())
51
+
52
+ Log.d(TAG, "Sending progress event: currentTime=$currentTime, duration=$duration")
53
+ sendEvent("topProgress", event)
54
+ } else {
55
+ Log.d(TAG, "Not sending progress event because duration is $duration (raw: ${it.duration})")
56
+ }
57
+ } ?: Log.e(TAG, "Cannot send progress event: player is null")
58
+
59
+ // Schedule the next update
60
+ progressHandler.postDelayed(this, 250) // Update every 250ms
61
+ }
62
+ }
33
63
 
34
64
  init {
35
65
  setBackgroundColor(Color.BLACK)
@@ -51,12 +81,6 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
51
81
  // Add logging for playerView dimensions and post play call
52
82
  playerView.post {
53
83
  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
84
  }
61
85
 
62
86
  player?.addListener(object : Player.Listener {
@@ -65,12 +89,11 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
65
89
  when (playbackState) {
66
90
  Player.STATE_READY -> {
67
91
  Log.d(TAG, "ExoPlayer STATE_READY")
68
- sendEvent("onReadyToPlay", Arguments.createMap())
69
- // Removed custom autoplay logic, relying on player?.playWhenReady
92
+ sendEvent("topReadyToPlay", Arguments.createMap())
70
93
  }
71
94
  Player.STATE_ENDED -> {
72
95
  Log.d(TAG, "ExoPlayer STATE_ENDED")
73
- sendEvent("onPlaybackComplete", Arguments.createMap())
96
+ sendEvent("topPlaybackComplete", Arguments.createMap())
74
97
  }
75
98
  Player.STATE_BUFFERING -> {
76
99
  Log.d(TAG, "ExoPlayer STATE_BUFFERING")
@@ -78,34 +101,33 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
78
101
  Player.STATE_IDLE -> {
79
102
  Log.d(TAG, "ExoPlayer STATE_IDLE")
80
103
  }
81
- Player.STATE_BUFFERING -> {
82
- Log.d(TAG, "ExoPlayer STATE_BUFFERING")
83
- }
84
104
  }
85
105
  }
86
106
 
87
107
  override fun onIsPlayingChanged(isPlaying: Boolean) {
108
+ Log.d(TAG, "onIsPlayingChanged: $isPlaying")
88
109
  if (isPlaying) {
89
110
  Log.d(TAG, "ExoPlayer isPlaying")
90
- sendEvent("onPlay", Arguments.createMap())
111
+ // Use the defined event constant for playback resumed
112
+ sendEvent("topPlaybackResumed", Arguments.createMap())
91
113
  } else {
92
114
  Log.d(TAG, "ExoPlayer isPaused")
93
- sendEvent("onPause", Arguments.createMap())
115
+ // Add event emission for pause state
116
+ sendEvent("topPlaybackPaused", Arguments.createMap())
94
117
  }
95
118
  }
96
119
 
97
120
  override fun onPlayerError(error: PlaybackException) {
98
- Log.e(TAG, "ExoPlayer Error: ${error.message}, errorCode: ${error.errorCodeName}, errorCode বিস্তারিত: ${error.errorCode}")
121
+ Log.d(TAG, "ExoPlayer onPlayerError: ${error.message}")
99
122
  val event = Arguments.createMap().apply {
100
- putInt("code", error.errorCode)
101
- putString("message", error.message)
123
+ putString("error", error.message)
102
124
  }
103
- sendEvent("onError", event)
125
+ sendEvent("topError", event)
104
126
  }
105
127
 
106
128
  override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
107
129
  Log.d(TAG, "ExoPlayer onMediaItemTransition")
108
- sendEvent("onLoadStart", Arguments.createMap())
130
+ sendEvent("topLoadStart", Arguments.createMap())
109
131
  }
110
132
 
111
133
  override fun onPlaybackSuppressionReasonChanged(playbackSuppressionReason: Int) {
@@ -163,39 +185,67 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
163
185
  // Called when skip silence is enabled or disabled.
164
186
  Log.d(TAG, "ExoPlayer onSkipSilenceEnabledChanged: skipSilenceEnabled=$skipSilenceEnabled")
165
187
  }
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
188
  })
172
189
  }
173
190
 
174
191
  fun setVideoUrl(url: String?) {
175
192
  Log.d(TAG, "Setting video URL: $url")
176
- if (url.isNullOrEmpty()) {
177
- Log.d(TAG, "Video URL is null or empty, skipping load")
193
+
194
+ if (url == null || url.isEmpty()) {
195
+ Log.e(TAG, "Empty or null URL provided")
178
196
  return
179
197
  }
180
-
198
+
181
199
  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")
200
+
201
+ try {
202
+ // Create a MediaItem
203
+ val mediaItem = MediaItem.fromUri(url)
204
+
205
+ // Reset the player to ensure clean state
206
+ player?.stop()
207
+ player?.clearMediaItems()
208
+
209
+ // Set the media item
210
+ player?.setMediaItem(mediaItem)
211
+
212
+ // Prepare the player (this will start loading the media)
213
+ player?.prepare()
214
+
215
+ // Set playWhenReady based on autoplay setting
216
+ player?.playWhenReady = autoplay && !isPaused
217
+
218
+ // Set repeat mode based on loop setting
219
+ player?.repeatMode = if (loop) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF
220
+
221
+ // Log that we've set up the player
222
+ Log.d(TAG, "ExoPlayer configured with URL: $url, autoplay: $autoplay, loop: $loop")
223
+
224
+ // Add a listener to check when the player is ready and has duration
225
+ player?.addListener(object : Player.Listener {
226
+ override fun onPlaybackStateChanged(state: Int) {
227
+ if (state == Player.STATE_READY) {
228
+ val duration = player?.duration ?: 0
229
+ Log.d(TAG, "Player ready with duration: ${duration / 1000f} seconds")
230
+
231
+ // Force a progress update immediately
232
+ progressRunnable.run()
233
+ }
234
+ }
235
+ })
236
+ } catch (e: Exception) {
237
+ Log.e(TAG, "Error setting video URL: ${e.message}", e)
238
+
239
+ // Send error event
240
+ val event = Arguments.createMap()
241
+ event.putString("error", "Failed to load video: ${e.message}")
242
+ sendEvent("topError", event)
243
+ }
191
244
  }
192
245
 
193
246
  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
- }
247
+ // Removed as per request
248
+ Log.d(TAG, "Auth token handling removed")
199
249
  }
200
250
 
201
251
  fun setAutoplay(value: Boolean) {
@@ -208,33 +258,87 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
208
258
  player?.repeatMode = if (loop) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF
209
259
  }
210
260
 
261
+ fun setIsPaused(isPaused: Boolean) {
262
+ Log.d(TAG, "setIsPaused called with value: $isPaused")
263
+ this.isPaused = isPaused
264
+ if (isPaused) {
265
+ player?.pause()
266
+ } else {
267
+ player?.play()
268
+ }
269
+ }
270
+
211
271
  fun play() {
212
- Log.d(TAG, "Play called")
213
- player?.play()
214
- isPlaying = true
272
+ Log.d(TAG, "Play method called")
273
+ player?.playWhenReady = true
215
274
  }
216
275
 
217
276
  fun pause() {
218
- Log.d(TAG, "Pause called")
219
- player?.pause()
220
- isPlaying = false
277
+ Log.d(TAG, "Pause method called")
278
+ player?.playWhenReady = false
221
279
  }
222
280
 
223
- fun seekTo(time: Float) {
224
- Log.d(TAG, "Seek called: $time")
225
- player?.seekTo((time * 1000).toLong()) // time in seconds, ExoPlayer in milliseconds
281
+ fun seekTo(seconds: Float) {
282
+ Log.d(TAG, "SeekTo method called with seconds: $seconds")
283
+ player?.let {
284
+ val milliseconds = (seconds * 1000).toLong()
285
+ Log.d(TAG, "Seeking to $milliseconds ms")
286
+ it.seekTo(milliseconds)
287
+
288
+ // Force a progress update after seeking
289
+ progressRunnable.run()
290
+ } ?: Log.e(TAG, "Cannot seek: player is null")
226
291
  }
227
292
 
228
293
  fun getCurrentTime(): Float {
229
- return (player?.currentPosition?.toFloat() ?: 0f) / 1000f // to seconds
294
+ Log.d(TAG, "GetCurrentTime method called")
295
+ return player?.let {
296
+ val currentTime = it.currentPosition.toFloat() / 1000f
297
+ Log.d(TAG, "Current time: $currentTime seconds")
298
+ currentTime
299
+ } ?: run {
300
+ Log.e(TAG, "Cannot get current time: player is null")
301
+ 0f
302
+ }
230
303
  }
231
304
 
232
305
  fun getDuration(): Float {
233
- return (player?.duration?.toFloat() ?: 0f) / 1000f // to seconds
306
+ Log.d(TAG, "GetDuration method called")
307
+ return player?.let {
308
+ val duration = it.duration.toFloat() / 1000f
309
+ Log.d(TAG, "Duration: $duration seconds (raw: ${it.duration})")
310
+ if (it.duration > 0) duration else 0f
311
+ } ?: run {
312
+ Log.e(TAG, "Cannot get duration: player is null")
313
+ 0f
314
+ }
234
315
  }
235
316
 
317
+ // Add a getter for the ExoPlayer instance
318
+ val exoPlayer: ExoPlayer?
319
+ get() = this.player
320
+
236
321
  private fun sendEvent(eventName: String, params: WritableMap) {
237
- UnifiedPlayerEventEmitter.getInstance()?.sendEvent(eventName, params)
322
+ try {
323
+ // Log the event for debugging
324
+ Log.d(TAG, "Sending direct event: $eventName with params: $params")
325
+
326
+ // Use the ReactContext to dispatch the event directly to the view
327
+ val reactContext = context as ReactContext
328
+ reactContext.getJSModule(RCTEventEmitter::class.java)
329
+ .receiveEvent(id, eventName, params)
330
+ } catch (e: Exception) {
331
+ Log.e(TAG, "Error sending event $eventName: ${e.message}", e)
332
+ }
333
+ }
334
+
335
+ // Add a method to explicitly start progress updates
336
+ private fun startProgressUpdates() {
337
+ Log.d(TAG, "Starting progress updates")
338
+ // Remove any existing callbacks to avoid duplicates
339
+ progressHandler.removeCallbacks(progressRunnable)
340
+ // Post the runnable to start updates
341
+ progressHandler.post(progressRunnable)
238
342
  }
239
343
 
240
344
  override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
@@ -250,16 +354,13 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
250
354
  super.onAttachedToWindow()
251
355
  Log.d(TAG, "UnifiedPlayerView onAttachedToWindow")
252
356
  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
- }
357
+ startProgressUpdates() // Use the new method to start progress updates
259
358
  }
260
359
 
261
360
  override fun onDetachedFromWindow() {
262
361
  super.onDetachedFromWindow()
362
+ Log.d(TAG, "UnifiedPlayerView onDetachedFromWindow")
363
+ progressHandler.removeCallbacks(progressRunnable) // Stop progress updates
263
364
  player?.release()
264
365
  }
265
366
  }
@@ -3,11 +3,18 @@ package com.unifiedplayer
3
3
  import com.facebook.react.uimanager.SimpleViewManager
4
4
  import com.facebook.react.uimanager.ThemedReactContext
5
5
  import com.facebook.react.uimanager.annotations.ReactProp
6
+ import com.facebook.react.common.MapBuilder
7
+ import android.util.Log
8
+ import com.facebook.react.bridge.ReadableArray
9
+ import com.facebook.react.uimanager.events.RCTEventEmitter
6
10
 
7
11
  class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
12
+ private val TAG = "UnifiedPlayerViewManager"
13
+
8
14
  override fun getName() = "UnifiedPlayerView"
9
15
 
10
16
  override fun createViewInstance(reactContext: ThemedReactContext): UnifiedPlayerView {
17
+ Log.d(TAG, "Creating UnifiedPlayerView instance")
11
18
  return UnifiedPlayerView(reactContext)
12
19
  }
13
20
 
@@ -16,11 +23,6 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
16
23
  view.setVideoUrl(url)
17
24
  }
18
25
 
19
- @ReactProp(name = "authToken")
20
- fun setAuthToken(view: UnifiedPlayerView, token: String?) {
21
- view.setAuthToken(token)
22
- }
23
-
24
26
  @ReactProp(name = "autoplay")
25
27
  fun setAutoplay(view: UnifiedPlayerView, autoplay: Boolean) {
26
28
  view.setAutoplay(autoplay)
@@ -30,4 +32,25 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
30
32
  fun setLoop(view: UnifiedPlayerView, loop: Boolean) {
31
33
  view.setLoop(loop)
32
34
  }
35
+
36
+ @ReactProp(name = "isPaused")
37
+ fun setIsPaused(view: UnifiedPlayerView, isPaused: Boolean) {
38
+ view.setIsPaused(isPaused)
39
+ }
40
+
41
+ // Register direct events
42
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
43
+ Log.d(TAG, "Registering direct events")
44
+
45
+ // Create a map of event names to their registration names
46
+ return MapBuilder.builder<String, Any>()
47
+ .put("topReadyToPlay", MapBuilder.of("registrationName", "onReadyToPlay"))
48
+ .put("topError", MapBuilder.of("registrationName", "onError"))
49
+ .put("topProgress", MapBuilder.of("registrationName", "onProgress"))
50
+ .put("topPlaybackComplete", MapBuilder.of("registrationName", "onPlaybackComplete"))
51
+ .put("topPlaybackResumed", MapBuilder.of("registrationName", "onPlaybackResumed"))
52
+ .put("topPlaybackPaused", MapBuilder.of("registrationName", "onPlaybackPaused"))
53
+ .put("topLoadStart", MapBuilder.of("registrationName", "onLoadStart"))
54
+ .build()
55
+ }
33
56
  }
@@ -14,6 +14,7 @@
14
14
  @property (nonatomic, copy) NSString *authToken;
15
15
  @property (nonatomic, assign) BOOL autoplay;
16
16
  @property (nonatomic, assign) BOOL loop;
17
+ @property (nonatomic, assign) BOOL isPaused; // Add isPaused property
17
18
  @property (nonatomic, strong) NSArray *mediaOptions;
18
19
  @property (nonatomic, weak) RCTBridge *bridge;
19
20
  @property (nonatomic, assign) VLCMediaPlayerState previousState;
@@ -446,6 +447,21 @@ static UnifiedPlayerModule *eventEmitter = nil;
446
447
  _loop = loop;
447
448
  }
448
449
 
450
+ - (void)setIsPaused:(BOOL)isPaused {
451
+ if (_isPaused != isPaused) {
452
+ _isPaused = isPaused;
453
+ if (_player) {
454
+ if (_isPaused && _player.isPlaying) {
455
+ [_player pause];
456
+ RCTLogInfo(@"[UnifiedPlayerViewManager] Paused via isPaused prop");
457
+ } else if (!_isPaused && !_player.isPlaying) {
458
+ [_player play];
459
+ RCTLogInfo(@"[UnifiedPlayerViewManager] Played via isPaused prop");
460
+ }
461
+ }
462
+ }
463
+ }
464
+
449
465
  - (void)setAuthToken:(NSString *)authToken {
450
466
  if (_authToken != authToken) {
451
467
  _authToken = [authToken copy];
@@ -667,4 +683,10 @@ RCT_CUSTOM_VIEW_PROPERTY(mediaOptions, NSArray, UnifiedPlayerUIView)
667
683
  view.mediaOptions = [RCTConvert NSArray:json];
668
684
  }
669
685
 
686
+ // isPaused property
687
+ RCT_CUSTOM_VIEW_PROPERTY(isPaused, BOOL, UnifiedPlayerUIView)
688
+ {
689
+ view.isPaused = [RCTConvert BOOL:json];
690
+ }
691
+
670
692
  @end
@@ -1,97 +1,121 @@
1
1
  "use strict";
2
2
 
3
- import { requireNativeComponent, UIManager, Platform, NativeModules, NativeEventEmitter } from 'react-native';
3
+ import { forwardRef } from 'react'; // Import from 'react'
4
+ import { requireNativeComponent, UIManager, NativeModules, Platform } from 'react-native';
5
+
6
+ // Check if the native module is available
4
7
  import { jsx as _jsx } from "react/jsx-runtime";
5
8
  const LINKING_ERROR = `The package 'react-native-unified-player' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
6
9
  ios: "- You have run 'pod install'\n",
7
10
  default: ''
8
11
  }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
9
12
 
10
- /**
11
- * Props for the UnifiedPlayerView component
12
- */
13
+ // Verify the native module exists
14
+ if (!UIManager.getViewManagerConfig('UnifiedPlayerView') && !NativeModules.UnifiedPlayer) {
15
+ throw new Error(LINKING_ERROR);
16
+ }
13
17
 
14
- // Name of the native component
15
- const ComponentName = 'UnifiedPlayerView';
18
+ // Define the props for the UnifiedPlayerView component
16
19
 
17
- // Native component import
18
- const NativeUnifiedPlayerView = UIManager.getViewManagerConfig(ComponentName) != null ? requireNativeComponent(ComponentName) : () => {
19
- throw new Error(LINKING_ERROR);
20
- };
20
+ // Native component registration
21
+ const NativeUnifiedPlayerView = requireNativeComponent('UnifiedPlayerView');
22
+
23
+ // Newline added here
21
24
 
22
- // Native module for additional control methods
23
- const UnifiedPlayerModule = NativeModules.UnifiedPlayerModule;
25
+ // Native module for player control methods
26
+ const UnifiedPlayerModule = NativeModules.UnifiedPlayer;
27
+
28
+ // Export event types for reference
29
+ export const UnifiedPlayerEventTypes = {
30
+ READY: 'onReadyToPlay',
31
+ ERROR: 'onError',
32
+ PROGRESS: 'onProgress',
33
+ COMPLETE: 'onPlaybackComplete',
34
+ STALLED: 'onPlaybackStalled',
35
+ RESUMED: 'onPlaybackResumed'
36
+ };
24
37
 
25
38
  /**
26
39
  * UnifiedPlayerView component for video playback
27
40
  */
28
- export const UnifiedPlayerView = props => {
41
+ export const UnifiedPlayerView = /*#__PURE__*/forwardRef((props, ref) => {
29
42
  return /*#__PURE__*/_jsx(NativeUnifiedPlayerView, {
30
- ...props
43
+ ...props,
44
+ ref: ref
31
45
  });
32
- };
46
+ });
33
47
 
34
48
  /**
35
49
  * API methods for controlling playback
36
50
  */
37
51
  export const UnifiedPlayer = {
38
- // Play the video
52
+ /**
53
+ * Start playback
54
+ * @param viewTag - The tag of the player view
55
+ */
39
56
  play: viewTag => {
40
- if (UnifiedPlayerModule?.play) {
57
+ try {
58
+ console.log('UnifiedPlayer.play called with viewTag:', viewTag);
41
59
  UnifiedPlayerModule.play(viewTag);
60
+ console.log('Native play method called successfully');
61
+ } catch (error) {
62
+ console.error('Error calling play:', error);
42
63
  }
43
64
  },
44
- // Pause the video
65
+ /**
66
+ * Pause playback
67
+ * @param viewTag - The tag of the player view
68
+ */
45
69
  pause: viewTag => {
46
- if (UnifiedPlayerModule?.pause) {
70
+ try {
71
+ console.log('UnifiedPlayer.pause called with viewTag:', viewTag);
47
72
  UnifiedPlayerModule.pause(viewTag);
73
+ console.log('Native pause method called successfully');
74
+ } catch (error) {
75
+ console.error('Error calling pause:', error);
48
76
  }
49
77
  },
50
- // Seek to a specific time
78
+ /**
79
+ * Seek to a specific time
80
+ * @param viewTag - The tag of the player view
81
+ * @param time - Time in seconds to seek to
82
+ */
51
83
  seekTo: (viewTag, time) => {
52
- if (UnifiedPlayerModule?.seekTo) {
84
+ try {
85
+ console.log('UnifiedPlayer.seekTo called with viewTag:', viewTag, 'time:', time);
53
86
  UnifiedPlayerModule.seekTo(viewTag, time);
87
+ console.log('Native seekTo method called successfully');
88
+ } catch (error) {
89
+ console.error('Error calling seekTo:', error);
54
90
  }
55
91
  },
56
- // Get the current playback time
92
+ /**
93
+ * Get current playback time
94
+ * @param viewTag - The tag of the player view
95
+ * @returns Promise resolving to current time in seconds
96
+ */
57
97
  getCurrentTime: viewTag => {
58
- if (UnifiedPlayerModule?.getCurrentTime) {
98
+ try {
99
+ console.log('UnifiedPlayer.getCurrentTime called with viewTag:', viewTag);
59
100
  return UnifiedPlayerModule.getCurrentTime(viewTag);
101
+ } catch (error) {
102
+ console.error('Error calling getCurrentTime:', error);
103
+ return Promise.reject(error);
60
104
  }
61
- return Promise.resolve(0);
62
105
  },
63
- // Get the duration of the video
106
+ /**
107
+ * Get video duration
108
+ * @param viewTag - The tag of the player view
109
+ * @returns Promise resolving to duration in seconds
110
+ */
64
111
  getDuration: viewTag => {
65
- if (UnifiedPlayerModule?.getDuration) {
112
+ try {
113
+ console.log('UnifiedPlayer.getDuration called with viewTag:', viewTag);
66
114
  return UnifiedPlayerModule.getDuration(viewTag);
115
+ } catch (error) {
116
+ console.error('Error calling getDuration:', error);
117
+ return Promise.reject(error);
67
118
  }
68
- return Promise.resolve(0);
69
- }
70
- };
71
-
72
- // Events emitter for native events
73
- let eventEmitter = null;
74
- if (UnifiedPlayerModule) {
75
- eventEmitter = new NativeEventEmitter(UnifiedPlayerModule);
76
- }
77
- export const UnifiedPlayerEvents = {
78
- addListener: (eventType, listener) => {
79
- if (eventEmitter) {
80
- return eventEmitter.addListener(eventType, listener);
81
- }
82
- return {
83
- remove: () => {}
84
- };
85
119
  }
86
120
  };
87
-
88
- // Event names
89
- export const UnifiedPlayerEventTypes = {
90
- READY: 'onReadyToPlay',
91
- ERROR: 'onError',
92
- PROGRESS: 'onProgress',
93
- COMPLETE: 'onPlaybackComplete',
94
- STALLED: 'onPlaybackStalled',
95
- RESUMED: 'onPlaybackResumed'
96
- };
97
121
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["requireNativeComponent","UIManager","Platform","NativeModules","NativeEventEmitter","jsx","_jsx","LINKING_ERROR","select","ios","default","ComponentName","NativeUnifiedPlayerView","getViewManagerConfig","Error","UnifiedPlayerModule","UnifiedPlayerView","props","UnifiedPlayer","play","viewTag","pause","seekTo","time","getCurrentTime","Promise","resolve","getDuration","eventEmitter","UnifiedPlayerEvents","addListener","eventType","listener","remove","UnifiedPlayerEventTypes","READY","ERROR","PROGRESS","COMPLETE","STALLED","RESUMED"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SACEA,sBAAsB,EACtBC,SAAS,EACTC,QAAQ,EAERC,aAAa,EACbC,kBAAkB,QACb,cAAc;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEtB,MAAMC,aAAa,GACjB,sFAAsF,GACtFL,QAAQ,CAACM,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;;AAEjC;AACA;AACA;;AA8BA;AACA,MAAMC,aAAa,GAAG,mBAAmB;;AAEzC;AACA,MAAMC,uBAAuB,GAC3BX,SAAS,CAACY,oBAAoB,CAACF,aAAa,CAAC,IAAI,IAAI,GACjDX,sBAAsB,CAAqBW,aAAa,CAAC,GACzD,MAAM;EACJ,MAAM,IAAIG,KAAK,CAACP,aAAa,CAAC;AAChC,CAAC;;AAEP;AACA,MAAMQ,mBAAmB,GAAGZ,aAAa,CAACY,mBAAmB;;AAE7D;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,GAAIC,KAAyB,IAAK;EAC9D,oBAAOX,IAAA,CAACM,uBAAuB;IAAA,GAAKK;EAAK,CAAG,CAAC;AAC/C,CAAC;;AAED;AACA;AACA;AACA,OAAO,MAAMC,aAAa,GAAG;EAC3B;EACAC,IAAI,EAAGC,OAAe,IAAK;IACzB,IAAIL,mBAAmB,EAAEI,IAAI,EAAE;MAC7BJ,mBAAmB,CAACI,IAAI,CAACC,OAAO,CAAC;IACnC;EACF,CAAC;EAED;EACAC,KAAK,EAAGD,OAAe,IAAK;IAC1B,IAAIL,mBAAmB,EAAEM,KAAK,EAAE;MAC9BN,mBAAmB,CAACM,KAAK,CAACD,OAAO,CAAC;IACpC;EACF,CAAC;EAED;EACAE,MAAM,EAAEA,CAACF,OAAe,EAAEG,IAAY,KAAK;IACzC,IAAIR,mBAAmB,EAAEO,MAAM,EAAE;MAC/BP,mBAAmB,CAACO,MAAM,CAACF,OAAO,EAAEG,IAAI,CAAC;IAC3C;EACF,CAAC;EAED;EACAC,cAAc,EAAGJ,OAAe,IAAsB;IACpD,IAAIL,mBAAmB,EAAES,cAAc,EAAE;MACvC,OAAOT,mBAAmB,CAACS,cAAc,CAACJ,OAAO,CAAC;IACpD;IACA,OAAOK,OAAO,CAACC,OAAO,CAAC,CAAC,CAAC;EAC3B,CAAC;EAED;EACAC,WAAW,EAAGP,OAAe,IAAsB;IACjD,IAAIL,mBAAmB,EAAEY,WAAW,EAAE;MACpC,OAAOZ,mBAAmB,CAACY,WAAW,CAACP,OAAO,CAAC;IACjD;IACA,OAAOK,OAAO,CAACC,OAAO,CAAC,CAAC,CAAC;EAC3B;AACF,CAAC;;AAED;AACA,IAAIE,YAAuC,GAAG,IAAI;AAElD,IAAIb,mBAAmB,EAAE;EACvBa,YAAY,GAAG,IAAIxB,kBAAkB,CAACW,mBAAmB,CAAC;AAC5D;AAEA,OAAO,MAAMc,mBAAmB,GAAG;EACjCC,WAAW,EAAEA,CAACC,SAAiB,EAAEC,QAAiC,KAAK;IACrE,IAAIJ,YAAY,EAAE;MAChB,OAAOA,YAAY,CAACE,WAAW,CAACC,SAAS,EAAEC,QAAQ,CAAC;IACtD;IACA,OAAO;MAAEC,MAAM,EAAEA,CAAA,KAAM,CAAC;IAAE,CAAC;EAC7B;AACF,CAAC;;AAED;AACA,OAAO,MAAMC,uBAAuB,GAAG;EACrCC,KAAK,EAAE,eAAe;EACtBC,KAAK,EAAE,SAAS;EAChBC,QAAQ,EAAE,YAAY;EACtBC,QAAQ,EAAE,oBAAoB;EAC9BC,OAAO,EAAE,mBAAmB;EAC5BC,OAAO,EAAE;AACX,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["forwardRef","requireNativeComponent","UIManager","NativeModules","Platform","jsx","_jsx","LINKING_ERROR","select","ios","default","getViewManagerConfig","UnifiedPlayer","Error","NativeUnifiedPlayerView","UnifiedPlayerModule","UnifiedPlayerEventTypes","READY","ERROR","PROGRESS","COMPLETE","STALLED","RESUMED","UnifiedPlayerView","props","ref","play","viewTag","console","log","error","pause","seekTo","time","getCurrentTime","Promise","reject","getDuration"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,13 +1,10 @@
1
1
  import { type ViewStyle } from 'react-native';
2
- /**
3
- * Props for the UnifiedPlayerView component
4
- */
5
2
  export type UnifiedPlayerProps = {
6
3
  videoUrl: string;
7
4
  style: ViewStyle;
8
5
  autoplay?: boolean;
9
6
  loop?: boolean;
10
- authToken?: string;
7
+ isPaused?: boolean;
11
8
  onReadyToPlay?: () => void;
12
9
  onError?: (error: any) => void;
13
10
  onPlaybackComplete?: () => void;
@@ -16,31 +13,49 @@ export type UnifiedPlayerProps = {
16
13
  duration: number;
17
14
  }) => void;
18
15
  };
16
+ export declare const UnifiedPlayerEventTypes: {
17
+ READY: string;
18
+ ERROR: string;
19
+ PROGRESS: string;
20
+ COMPLETE: string;
21
+ STALLED: string;
22
+ RESUMED: string;
23
+ };
19
24
  /**
20
25
  * UnifiedPlayerView component for video playback
21
26
  */
22
- export declare const UnifiedPlayerView: (props: UnifiedPlayerProps) => import("react/jsx-runtime").JSX.Element;
27
+ export declare const UnifiedPlayerView: import("react").ForwardRefExoticComponent<UnifiedPlayerProps & import("react").RefAttributes<import("react").Component<UnifiedPlayerProps, {}, any> & import("react-native").NativeMethods>>;
23
28
  /**
24
29
  * API methods for controlling playback
25
30
  */
26
31
  export declare const UnifiedPlayer: {
32
+ /**
33
+ * Start playback
34
+ * @param viewTag - The tag of the player view
35
+ */
27
36
  play: (viewTag: number) => void;
37
+ /**
38
+ * Pause playback
39
+ * @param viewTag - The tag of the player view
40
+ */
28
41
  pause: (viewTag: number) => void;
42
+ /**
43
+ * Seek to a specific time
44
+ * @param viewTag - The tag of the player view
45
+ * @param time - Time in seconds to seek to
46
+ */
29
47
  seekTo: (viewTag: number, time: number) => void;
48
+ /**
49
+ * Get current playback time
50
+ * @param viewTag - The tag of the player view
51
+ * @returns Promise resolving to current time in seconds
52
+ */
30
53
  getCurrentTime: (viewTag: number) => Promise<number>;
54
+ /**
55
+ * Get video duration
56
+ * @param viewTag - The tag of the player view
57
+ * @returns Promise resolving to duration in seconds
58
+ */
31
59
  getDuration: (viewTag: number) => Promise<number>;
32
60
  };
33
- export declare const UnifiedPlayerEvents: {
34
- addListener: (eventType: string, listener: (...args: any[]) => any) => import("react-native").EmitterSubscription | {
35
- remove: () => void;
36
- };
37
- };
38
- export declare const UnifiedPlayerEventTypes: {
39
- READY: string;
40
- ERROR: string;
41
- PROGRESS: string;
42
- COMPLETE: string;
43
- STALLED: string;
44
- RESUMED: string;
45
- };
46
61
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,SAAS,EAGf,MAAM,cAAc,CAAC;AAQtB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAE/B,QAAQ,EAAE,MAAM,CAAC;IAGjB,KAAK,EAAE,SAAS,CAAC;IAGjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,IAAI,CAAC,EAAE,OAAO,CAAC;IAGf,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAG3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAG/B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAGhC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACxE,CAAC;AAgBF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,kBAAkB,4CAE1D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa;oBAER,MAAM;qBAOL,MAAM;sBAOL,MAAM,QAAQ,MAAM;8BAOZ,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;2BAQ3B,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;CAMhD,CAAC;AASF,eAAO,MAAM,mBAAmB;6BACL,MAAM,YAAY,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG;;;CAMnE,CAAC;AAGF,eAAO,MAAM,uBAAuB;;;;;;;CAOnC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAkBtB,MAAM,MAAM,kBAAkB,GAAG;IAE/B,QAAQ,EAAE,MAAM,CAAC;IAGjB,KAAK,EAAE,SAAS,CAAC;IAGjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,IAAI,CAAC,EAAE,OAAO,CAAC;IAGf,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAG3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAG/B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAGhC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACxE,CAAC;AAYF,eAAO,MAAM,uBAAuB;;;;;;;CAOnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,8LAK5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;IACxB;;;OAGG;oBACa,MAAM,KAAG,IAAI;IAU7B;;;OAGG;qBACc,MAAM,KAAG,IAAI;IAU9B;;;;OAIG;sBACe,MAAM,QAAQ,MAAM,KAAG,IAAI;IAe7C;;;;OAIG;8BACuB,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;IAUlD;;;;OAIG;2BACoB,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;CAShD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-unified-player",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Unified Player",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -86,6 +86,9 @@
86
86
  "react": "*",
87
87
  "react-native": "*"
88
88
  },
89
+ "workspaces": [
90
+ "example"
91
+ ],
89
92
  "packageManager": "yarn@3.6.1",
90
93
  "jest": {
91
94
  "preset": "react-native",
package/src/index.tsx CHANGED
@@ -1,21 +1,28 @@
1
+ import { type ElementRef, forwardRef } from 'react'; // Import from 'react'
1
2
  import {
2
3
  requireNativeComponent,
3
4
  UIManager,
5
+ NativeModules,
4
6
  Platform,
5
7
  type ViewStyle,
6
- NativeModules,
7
- NativeEventEmitter,
8
8
  } from 'react-native';
9
9
 
10
+ // Check if the native module is available
10
11
  const LINKING_ERROR =
11
12
  `The package 'react-native-unified-player' doesn't seem to be linked. Make sure: \n\n` +
12
13
  Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
13
14
  '- You rebuilt the app after installing the package\n' +
14
15
  '- You are not using Expo Go\n';
15
16
 
16
- /**
17
- * Props for the UnifiedPlayerView component
18
- */
17
+ // Verify the native module exists
18
+ if (
19
+ !UIManager.getViewManagerConfig('UnifiedPlayerView') &&
20
+ !NativeModules.UnifiedPlayer
21
+ ) {
22
+ throw new Error(LINKING_ERROR);
23
+ }
24
+
25
+ // Define the props for the UnifiedPlayerView component
19
26
  export type UnifiedPlayerProps = {
20
27
  // Video source URL
21
28
  videoUrl: string;
@@ -29,8 +36,8 @@ export type UnifiedPlayerProps = {
29
36
  // Should video loop when finished
30
37
  loop?: boolean;
31
38
 
32
- // Optional auth token for protected streams
33
- authToken?: string;
39
+ // Is the player currently paused
40
+ isPaused?: boolean;
34
41
 
35
42
  // Callback when video is ready to play
36
43
  onReadyToPlay?: () => void;
@@ -45,91 +52,114 @@ export type UnifiedPlayerProps = {
45
52
  onProgress?: (data: { currentTime: number; duration: number }) => void;
46
53
  };
47
54
 
48
- // Name of the native component
49
- const ComponentName = 'UnifiedPlayerView';
50
-
51
- // Native component import
55
+ // Native component registration
52
56
  const NativeUnifiedPlayerView =
53
- UIManager.getViewManagerConfig(ComponentName) != null
54
- ? requireNativeComponent<UnifiedPlayerProps>(ComponentName)
55
- : () => {
56
- throw new Error(LINKING_ERROR);
57
- };
57
+ requireNativeComponent<UnifiedPlayerProps>('UnifiedPlayerView');
58
+
59
+ // Newline added here
60
+
61
+ // Native module for player control methods
62
+ const UnifiedPlayerModule = NativeModules.UnifiedPlayer;
58
63
 
59
- // Native module for additional control methods
60
- const UnifiedPlayerModule = NativeModules.UnifiedPlayerModule;
64
+ // Export event types for reference
65
+ export const UnifiedPlayerEventTypes = {
66
+ READY: 'onReadyToPlay',
67
+ ERROR: 'onError',
68
+ PROGRESS: 'onProgress',
69
+ COMPLETE: 'onPlaybackComplete',
70
+ STALLED: 'onPlaybackStalled',
71
+ RESUMED: 'onPlaybackResumed',
72
+ };
61
73
 
62
74
  /**
63
75
  * UnifiedPlayerView component for video playback
64
76
  */
65
- export const UnifiedPlayerView = (props: UnifiedPlayerProps) => {
66
- return <NativeUnifiedPlayerView {...props} />;
67
- };
77
+ export const UnifiedPlayerView = forwardRef<
78
+ ElementRef<typeof NativeUnifiedPlayerView>,
79
+ UnifiedPlayerProps
80
+ >((props, ref) => {
81
+ return <NativeUnifiedPlayerView {...props} ref={ref} />;
82
+ });
68
83
 
69
84
  /**
70
85
  * API methods for controlling playback
71
86
  */
72
87
  export const UnifiedPlayer = {
73
- // Play the video
74
- play: (viewTag: number) => {
75
- if (UnifiedPlayerModule?.play) {
88
+ /**
89
+ * Start playback
90
+ * @param viewTag - The tag of the player view
91
+ */
92
+ play: (viewTag: number): void => {
93
+ try {
94
+ console.log('UnifiedPlayer.play called with viewTag:', viewTag);
76
95
  UnifiedPlayerModule.play(viewTag);
96
+ console.log('Native play method called successfully');
97
+ } catch (error) {
98
+ console.error('Error calling play:', error);
77
99
  }
78
100
  },
79
101
 
80
- // Pause the video
81
- pause: (viewTag: number) => {
82
- if (UnifiedPlayerModule?.pause) {
102
+ /**
103
+ * Pause playback
104
+ * @param viewTag - The tag of the player view
105
+ */
106
+ pause: (viewTag: number): void => {
107
+ try {
108
+ console.log('UnifiedPlayer.pause called with viewTag:', viewTag);
83
109
  UnifiedPlayerModule.pause(viewTag);
110
+ console.log('Native pause method called successfully');
111
+ } catch (error) {
112
+ console.error('Error calling pause:', error);
84
113
  }
85
114
  },
86
115
 
87
- // Seek to a specific time
88
- seekTo: (viewTag: number, time: number) => {
89
- if (UnifiedPlayerModule?.seekTo) {
116
+ /**
117
+ * Seek to a specific time
118
+ * @param viewTag - The tag of the player view
119
+ * @param time - Time in seconds to seek to
120
+ */
121
+ seekTo: (viewTag: number, time: number): void => {
122
+ try {
123
+ console.log(
124
+ 'UnifiedPlayer.seekTo called with viewTag:',
125
+ viewTag,
126
+ 'time:',
127
+ time
128
+ );
90
129
  UnifiedPlayerModule.seekTo(viewTag, time);
130
+ console.log('Native seekTo method called successfully');
131
+ } catch (error) {
132
+ console.error('Error calling seekTo:', error);
91
133
  }
92
134
  },
93
135
 
94
- // Get the current playback time
136
+ /**
137
+ * Get current playback time
138
+ * @param viewTag - The tag of the player view
139
+ * @returns Promise resolving to current time in seconds
140
+ */
95
141
  getCurrentTime: (viewTag: number): Promise<number> => {
96
- if (UnifiedPlayerModule?.getCurrentTime) {
142
+ try {
143
+ console.log('UnifiedPlayer.getCurrentTime called with viewTag:', viewTag);
97
144
  return UnifiedPlayerModule.getCurrentTime(viewTag);
145
+ } catch (error) {
146
+ console.error('Error calling getCurrentTime:', error);
147
+ return Promise.reject(error);
98
148
  }
99
- return Promise.resolve(0);
100
149
  },
101
150
 
102
- // Get the duration of the video
151
+ /**
152
+ * Get video duration
153
+ * @param viewTag - The tag of the player view
154
+ * @returns Promise resolving to duration in seconds
155
+ */
103
156
  getDuration: (viewTag: number): Promise<number> => {
104
- if (UnifiedPlayerModule?.getDuration) {
157
+ try {
158
+ console.log('UnifiedPlayer.getDuration called with viewTag:', viewTag);
105
159
  return UnifiedPlayerModule.getDuration(viewTag);
160
+ } catch (error) {
161
+ console.error('Error calling getDuration:', error);
162
+ return Promise.reject(error);
106
163
  }
107
- return Promise.resolve(0);
108
164
  },
109
165
  };
110
-
111
- // Events emitter for native events
112
- let eventEmitter: NativeEventEmitter | null = null;
113
-
114
- if (UnifiedPlayerModule) {
115
- eventEmitter = new NativeEventEmitter(UnifiedPlayerModule);
116
- }
117
-
118
- export const UnifiedPlayerEvents = {
119
- addListener: (eventType: string, listener: (...args: any[]) => any) => {
120
- if (eventEmitter) {
121
- return eventEmitter.addListener(eventType, listener);
122
- }
123
- return { remove: () => {} };
124
- },
125
- };
126
-
127
- // Event names
128
- export const UnifiedPlayerEventTypes = {
129
- READY: 'onReadyToPlay',
130
- ERROR: 'onError',
131
- PROGRESS: 'onProgress',
132
- COMPLETE: 'onPlaybackComplete',
133
- STALLED: 'onPlaybackStalled',
134
- RESUMED: 'onPlaybackResumed',
135
- };