react-native-unified-player 0.3.2 → 0.3.4

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.
package/README.md CHANGED
@@ -65,7 +65,6 @@ const MyPlayerComponent = () => {
65
65
  videoUrl="YOUR_VIDEO_URL_HERE" // Replace with your video URL
66
66
  autoplay={false} // Optional: set to true to autoplay
67
67
  loop={false} // Optional: set to true to loop
68
- // authToken="YOUR_AUTH_TOKEN" // Optional: for protected streams
69
68
  // You can also use direct view props instead of or in addition to event listeners:
70
69
  // onReadyToPlay={() => console.log('View prop: Ready to play')}
71
70
  // onError={(e) => console.log('View prop: Error', e)}
@@ -87,7 +86,6 @@ export default MyPlayerComponent;
87
86
  | `style` | `ViewStyle` | Yes | Apply custom styling |
88
87
  | `autoplay` | `boolean` | No | Autoplay video when loaded |
89
88
  | `loop` | `boolean` | No | Should video loop when finished |
90
- | `authToken` | `string` | No | Optional auth token for protected streams |
91
89
  | `onReadyToPlay` | `() => void` | No | Callback when video is ready to play |
92
90
  | `onError` | `(error: any) => void` | No | Callback when an error occurs |
93
91
  | `onPlaybackComplete` | `() => void` | No | Callback when video playback finishes |
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package["license"]
11
11
  s.authors = package["author"]
12
12
 
13
- s.platforms = { :ios => min_ios_version_supported }
13
+ s.platforms = { :ios => "13.0" }
14
14
  s.source = { :git => "https://github.com/blueromans/react-native-unified-player.git", :tag => "#{s.version}" }
15
15
 
16
16
  s.source_files = "ios/**/*.{h,m,mm}"
@@ -12,12 +12,15 @@ class UnifiedPlayerEventEmitter(private val reactContext: ReactApplicationContex
12
12
  private const val TAG = "UnifiedPlayerEventEmitter"
13
13
 
14
14
  // Define all possible event types
15
+ const val EVENT_LOAD_START = "onLoadStart"
15
16
  const val EVENT_READY = "onReadyToPlay"
16
17
  const val EVENT_ERROR = "onError"
17
18
  const val EVENT_PROGRESS = "onProgress"
18
19
  const val EVENT_COMPLETE = "onPlaybackComplete"
19
20
  const val EVENT_STALLED = "onPlaybackStalled"
20
21
  const val EVENT_RESUMED = "onPlaybackResumed"
22
+ const val EVENT_PLAYING = "onPlaying"
23
+ const val EVENT_PAUSED = "onPaused"
21
24
 
22
25
  // Singleton instance for access from other classes
23
26
  private var instance: UnifiedPlayerEventEmitter? = null
@@ -59,4 +62,4 @@ class UnifiedPlayerEventEmitter(private val reactContext: ReactApplicationContex
59
62
  super.onCatalystInstanceDestroy()
60
63
  instance = null
61
64
  }
62
- }
65
+ }
@@ -1,176 +1,187 @@
1
1
  package com.unifiedplayer
2
2
 
3
- import com.facebook.react.bridge.ReactApplicationContext
4
- import com.facebook.react.bridge.ReactContextBaseJavaModule
5
- import com.facebook.react.bridge.ReactMethod
6
- import com.facebook.react.bridge.Promise
7
- import com.facebook.react.uimanager.UIManagerModule
3
+ import android.graphics.Bitmap
4
+ import android.util.Base64
8
5
  import android.util.Log
9
- import com.facebook.react.bridge.UiThreadUtil
10
- import android.view.View
6
+ import com.facebook.react.bridge.*
7
+ import com.facebook.react.uimanager.UIManagerModule
8
+ import com.facebook.react.uimanager.UIBlock
9
+ import com.facebook.react.uimanager.NativeViewHierarchyManager
10
+ import java.io.ByteArrayOutputStream
11
11
 
12
12
  class UnifiedPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
13
- companion object {
14
- private const val TAG = "UnifiedPlayerModule"
15
- }
16
-
17
- init {
18
- Log.d(TAG, "UnifiedPlayerModule initialized")
19
- }
20
-
21
- override fun getName(): String {
22
- Log.d(TAG, "getName() called, returning 'UnifiedPlayer'")
23
- return "UnifiedPlayer"
24
- }
25
-
26
- private fun getPlayerViewByTag(viewTag: Int): UnifiedPlayerView? {
13
+ private var isModuleReady = false
14
+
15
+ override fun initialize() {
16
+ super.initialize()
27
17
  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}")
18
+ reactContext.getNativeModule(UIManagerModule::class.java)?.let {
19
+ isModuleReady = true
20
+ Log.d(TAG, "Module successfully initialized")
35
21
  }
36
22
  } catch (e: Exception) {
37
- Log.e(TAG, "Error finding view with tag $viewTag: ${e.message}", e)
23
+ Log.e(TAG, "Initialization failed", e)
38
24
  }
39
- return null
40
25
  }
41
-
26
+ companion object {
27
+ private const val TAG = "UnifiedPlayerModule"
28
+ }
29
+
30
+ override fun getName(): String = "UnifiedPlayer"
31
+
42
32
  @ReactMethod
43
33
  fun play(viewTag: Int, promise: Promise) {
44
- Log.d(TAG, "Native play method called with viewTag: $viewTag")
45
34
  try {
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")
35
+ val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)
36
+ ?: throw IllegalStateException("UIManagerModule not available")
37
+
38
+ uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
39
+ try {
40
+ val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
41
+ if (view != null) {
42
+ view.play()
52
43
  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)
44
+ } else {
45
+ promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
56
46
  }
47
+ } catch (e: Exception) {
48
+ Log.e(TAG, "Error in play method", e)
49
+ promise.reject("PLAY_ERROR", "Error in play method", e)
57
50
  }
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")
61
- }
51
+ })
62
52
  } catch (e: Exception) {
63
- Log.e(TAG, "Error in play method: ${e.message}", e)
64
- promise.reject("PLAY_ERROR", "Error in play method: ${e.message}", e)
53
+ Log.e(TAG, "Error in play method", e)
54
+ promise.reject("UIMANAGER_ERROR", "UIManagerModule not available", e)
65
55
  }
66
56
  }
67
57
 
68
58
  @ReactMethod
69
59
  fun pause(viewTag: Int, promise: Promise) {
70
- Log.d(TAG, "Native pause method called with viewTag: $viewTag")
71
60
  try {
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")
61
+ val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)
62
+ ?: throw IllegalStateException("UIManagerModule not available")
63
+
64
+ uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
65
+ try {
66
+ val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
67
+ if (view != null) {
68
+ view.pause()
78
69
  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)
70
+ } else {
71
+ promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
82
72
  }
73
+ } catch (e: Exception) {
74
+ Log.e(TAG, "Error in pause method", e)
75
+ promise.reject("PAUSE_ERROR", "Error in pause method", e)
83
76
  }
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")
87
- }
77
+ })
88
78
  } catch (e: Exception) {
89
- Log.e(TAG, "Error in pause method: ${e.message}", e)
90
- promise.reject("PAUSE_ERROR", "Error in pause method: ${e.message}", e)
79
+ Log.e(TAG, "Error in pause method", e)
80
+ promise.reject("UIMANAGER_ERROR", "UIManagerModule not available", e)
91
81
  }
92
82
  }
93
83
 
94
84
  @ReactMethod
95
85
  fun seekTo(viewTag: Int, seconds: Float, promise: Promise) {
96
- Log.d(TAG, "Native seekTo method called with viewTag: $viewTag, seconds: $seconds")
97
86
  try {
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")
87
+ val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)
88
+ ?: throw IllegalStateException("UIManagerModule not available")
89
+
90
+ uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
91
+ try {
92
+ val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
93
+ if (view != null) {
94
+ view.seekTo(seconds)
104
95
  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)
96
+ } else {
97
+ promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
108
98
  }
99
+ } catch (e: Exception) {
100
+ Log.e(TAG, "Error in seekTo method", e)
101
+ promise.reject("SEEK_ERROR", "Error in seekTo method", e)
109
102
  }
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")
113
- }
103
+ })
114
104
  } catch (e: Exception) {
115
- Log.e(TAG, "Error in seekTo method: ${e.message}", e)
116
- promise.reject("SEEK_ERROR", "Error in seekTo method: ${e.message}", e)
105
+ Log.e(TAG, "Error in seekTo method", e)
106
+ promise.reject("UIMANAGER_ERROR", "UIManagerModule not available", e)
117
107
  }
118
108
  }
119
109
 
120
110
  @ReactMethod
121
111
  fun getCurrentTime(viewTag: Int, promise: Promise) {
122
- Log.d(TAG, "Native getCurrentTime method called with viewTag: $viewTag")
123
- try {
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)
112
+ reactApplicationContext.getNativeModule(UIManagerModule::class.java)?.let { uiManager ->
113
+ uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
114
+ try {
115
+ val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
116
+ if (view != null) {
117
+ promise.resolve(view.getCurrentTime())
118
+ } else {
119
+ promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
134
120
  }
121
+ } catch (e: Exception) {
122
+ Log.e(TAG, "Error in getCurrentTime method", e)
123
+ promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method", e)
135
124
  }
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")
139
- }
140
- } catch (e: Exception) {
141
- Log.e(TAG, "Error in getCurrentTime method: ${e.message}", e)
142
- promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method: ${e.message}", e)
125
+ })
126
+ } ?: run {
127
+ promise.reject("ERROR", "UIManagerModule not available")
143
128
  }
144
129
  }
145
130
 
146
131
  @ReactMethod
147
132
  fun getDuration(viewTag: Int, promise: Promise) {
148
- Log.d(TAG, "Native getDuration method called with viewTag: $viewTag")
149
- try {
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)
133
+ reactApplicationContext.getNativeModule(UIManagerModule::class.java)?.let { uiManager ->
134
+ uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
135
+ try {
136
+ val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
137
+ if (view != null) {
138
+ promise.resolve(view.getDuration())
139
+ } else {
140
+ promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
160
141
  }
142
+ } catch (e: Exception) {
143
+ Log.e(TAG, "Error in getDuration method", e)
144
+ promise.reject("GET_DURATION_ERROR", "Error in getDuration method", e)
161
145
  }
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")
165
- }
166
- } catch (e: Exception) {
167
- Log.e(TAG, "Error in getDuration method: ${e.message}", e)
168
- promise.reject("GET_DURATION_ERROR", "Error in getDuration method: ${e.message}", e)
146
+ })
147
+ } ?: run {
148
+ promise.reject("ERROR", "UIManagerModule not available")
169
149
  }
170
150
  }
171
151
 
172
152
  @ReactMethod
173
- fun testMethod(message: String) {
174
- Log.d(TAG, "Test method called with message: $message")
153
+ fun capture(viewTag: Int, promise: Promise) {
154
+ try {
155
+ if (!isModuleReady) {
156
+ throw IllegalStateException("Module not ready. Ensure React Native bridge is initialized.")
157
+ }
158
+ val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)
159
+ ?: throw IllegalStateException("UIManagerModule not available. Is the bridge active?")
160
+
161
+ uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
162
+ try {
163
+ val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
164
+ if (view != null) {
165
+ val bitmap = view.captureFrame()
166
+ if (bitmap != null) {
167
+ val outputStream = ByteArrayOutputStream()
168
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
169
+ val base64 = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT)
170
+ promise.resolve(base64)
171
+ } else {
172
+ promise.reject("CAPTURE_ERROR", "Failed to capture frame")
173
+ }
174
+ } else {
175
+ promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
176
+ }
177
+ } catch (e: Exception) {
178
+ Log.e(TAG, "Error in capture method", e)
179
+ promise.reject("CAPTURE_ERROR", "Error in capture method", e)
180
+ }
181
+ })
182
+ } catch (e: Exception) {
183
+ Log.e(TAG, "Error in capture method", e)
184
+ promise.reject("UIMANAGER_ERROR", "UIManagerModule not available", e)
185
+ }
175
186
  }
176
187
  }
@@ -12,7 +12,12 @@ class UnifiedPlayerPackage : ReactPackage {
12
12
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
13
13
  Log.d(TAG, "Creating native modules")
14
14
  return listOf(
15
- UnifiedPlayerModule(reactContext)
15
+ UnifiedPlayerModule(reactContext).apply {
16
+ reactContext.runOnUiQueueThread {
17
+ // Initialize module on UI thread
18
+ Log.d(TAG, "Module initialized on UI thread")
19
+ }
20
+ }
16
21
  )
17
22
  }
18
23
 
@@ -4,9 +4,13 @@ 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.graphics.Bitmap
8
+ import android.graphics.Canvas
7
9
  import android.os.Handler
8
10
  import android.os.Looper
9
11
  import android.view.Gravity
12
+ import android.view.TextureView
13
+ import android.view.View
10
14
  import android.widget.FrameLayout
11
15
  import com.facebook.react.bridge.Arguments
12
16
  import com.google.android.exoplayer2.ExoPlayer
@@ -19,6 +23,15 @@ import com.google.android.exoplayer2.video.VideoSize
19
23
  import com.facebook.react.bridge.WritableMap
20
24
  import com.facebook.react.bridge.ReactContext
21
25
  import com.facebook.react.uimanager.events.RCTEventEmitter
26
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_COMPLETE
27
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_ERROR
28
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_LOAD_START
29
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_PAUSED
30
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_PLAYING
31
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_PROGRESS
32
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_READY
33
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_RESUMED
34
+ import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_STALLED
22
35
 
23
36
  class UnifiedPlayerView(context: Context) : FrameLayout(context) {
24
37
  companion object {
@@ -50,7 +63,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
50
63
  event.putDouble("duration", duration.toDouble())
51
64
 
52
65
  Log.d(TAG, "Sending progress event: currentTime=$currentTime, duration=$duration")
53
- sendEvent("topProgress", event)
66
+ sendEvent(EVENT_PROGRESS, event)
54
67
  } else {
55
68
  Log.d(TAG, "Not sending progress event because duration is $duration (raw: ${it.duration})")
56
69
  }
@@ -89,14 +102,15 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
89
102
  when (playbackState) {
90
103
  Player.STATE_READY -> {
91
104
  Log.d(TAG, "ExoPlayer STATE_READY")
92
- sendEvent("topReadyToPlay", Arguments.createMap())
105
+ sendEvent(EVENT_READY, Arguments.createMap())
93
106
  }
94
107
  Player.STATE_ENDED -> {
95
108
  Log.d(TAG, "ExoPlayer STATE_ENDED")
96
- sendEvent("topPlaybackComplete", Arguments.createMap())
109
+ sendEvent(EVENT_COMPLETE, Arguments.createMap())
97
110
  }
98
111
  Player.STATE_BUFFERING -> {
99
112
  Log.d(TAG, "ExoPlayer STATE_BUFFERING")
113
+ sendEvent(EVENT_STALLED, Arguments.createMap())
100
114
  }
101
115
  Player.STATE_IDLE -> {
102
116
  Log.d(TAG, "ExoPlayer STATE_IDLE")
@@ -105,29 +119,28 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
105
119
  }
106
120
 
107
121
  override fun onIsPlayingChanged(isPlaying: Boolean) {
108
- Log.d(TAG, "onIsPlayingChanged: $isPlaying")
122
+ Log.d(TAG, "onIsPlayingChanged: $isPlaying") // Added log
109
123
  if (isPlaying) {
110
- Log.d(TAG, "ExoPlayer isPlaying")
111
- // Use the defined event constant for playback resumed
112
- sendEvent("topPlaybackResumed", Arguments.createMap())
124
+ Log.d(TAG, "ExoPlayer is now playing")
125
+ sendEvent(EVENT_RESUMED, Arguments.createMap())
126
+ sendEvent(EVENT_PLAYING, Arguments.createMap())
113
127
  } else {
114
- Log.d(TAG, "ExoPlayer isPaused")
115
- // Add event emission for pause state
116
- sendEvent("topPlaybackPaused", Arguments.createMap())
128
+ Log.d(TAG, "ExoPlayer is now paused")
129
+ sendEvent(EVENT_PAUSED, Arguments.createMap())
117
130
  }
118
131
  }
119
132
 
120
133
  override fun onPlayerError(error: PlaybackException) {
121
- Log.d(TAG, "ExoPlayer onPlayerError: ${error.message}")
122
- val event = Arguments.createMap().apply {
123
- putString("error", error.message)
124
- }
125
- sendEvent("topError", event)
134
+ Log.e(TAG, "ExoPlayer error: $error")
135
+ val event = Arguments.createMap()
136
+ event.putString("code", "PLAYBACK_ERROR")
137
+ event.putString("message", error.message ?: "Unknown playback error")
138
+ sendEvent(EVENT_ERROR, event)
126
139
  }
127
140
 
128
141
  override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
129
- Log.d(TAG, "ExoPlayer onMediaItemTransition")
130
- sendEvent("topLoadStart", Arguments.createMap())
142
+ Log.d(TAG, "onMediaItemTransition with reason: $reason")
143
+ sendEvent(EVENT_LOAD_START, Arguments.createMap())
131
144
  }
132
145
 
133
146
  override fun onPlaybackSuppressionReasonChanged(playbackSuppressionReason: Int) {
@@ -238,8 +251,9 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
238
251
 
239
252
  // Send error event
240
253
  val event = Arguments.createMap()
241
- event.putString("error", "Failed to load video: ${e.message}")
242
- sendEvent("topError", event)
254
+ event.putString("code", "SOURCE_ERROR")
255
+ event.putString("message", "Failed to load video source: $url")
256
+ sendEvent(EVENT_ERROR, event)
243
257
  }
244
258
  }
245
259
 
@@ -302,17 +316,41 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
302
316
  }
303
317
  }
304
318
 
305
- fun getDuration(): Float {
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
- }
319
+ fun getDuration(): Float {
320
+ Log.d(TAG, "GetDuration method called")
321
+ return player?.let {
322
+ val duration = it.duration.toFloat() / 1000f
323
+ Log.d(TAG, "Duration: $duration seconds (raw: ${it.duration})")
324
+ if (it.duration > 0) duration else 0f
325
+ } ?: run {
326
+ Log.e(TAG, "Cannot get duration: player is null")
327
+ 0f
328
+ }
329
+ }
330
+
331
+ fun captureFrame(): Bitmap? {
332
+ Log.d(TAG, "CaptureFrame method called")
333
+ try {
334
+ // Create a bitmap with the same dimensions as the player view
335
+ val bitmap = Bitmap.createBitmap(
336
+ playerView.width,
337
+ playerView.height,
338
+ Bitmap.Config.ARGB_8888
339
+ )
340
+
341
+ // Create a canvas with the bitmap
342
+ val canvas = Canvas(bitmap)
343
+
344
+ // Draw the player view onto the canvas
345
+ playerView.draw(canvas)
346
+
347
+ Log.d(TAG, "Successfully captured frame from PlayerView")
348
+ return bitmap
349
+ } catch (e: Exception) {
350
+ Log.e(TAG, "Error capturing frame: ${e.message}", e)
351
+ return null
315
352
  }
353
+ }
316
354
 
317
355
  // Add a getter for the ExoPlayer instance
318
356
  val exoPlayer: ExoPlayer?
@@ -323,10 +361,24 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
323
361
  // Log the event for debugging
324
362
  Log.d(TAG, "Sending direct event: $eventName with params: $params")
325
363
 
364
+ // Map event names to their corresponding top event names
365
+ val topEventName = when (eventName) {
366
+ EVENT_READY -> "topReadyToPlay"
367
+ EVENT_ERROR -> "topError"
368
+ EVENT_PROGRESS -> "topProgress"
369
+ EVENT_COMPLETE -> "topPlaybackComplete"
370
+ EVENT_STALLED -> "topPlaybackStalled"
371
+ EVENT_RESUMED -> "topPlaybackResumed"
372
+ EVENT_PLAYING -> "topPlaying"
373
+ EVENT_PAUSED -> "topPlaybackPaused"
374
+ EVENT_LOAD_START -> "topLoadStart"
375
+ else -> "top${eventName.substring(2)}" // Fallback for any other events
376
+ }
377
+
326
378
  // Use the ReactContext to dispatch the event directly to the view
327
379
  val reactContext = context as ReactContext
328
380
  reactContext.getJSModule(RCTEventEmitter::class.java)
329
- .receiveEvent(id, eventName, params)
381
+ .receiveEvent(id, topEventName, params)
330
382
  } catch (e: Exception) {
331
383
  Log.e(TAG, "Error sending event $eventName: ${e.message}", e)
332
384
  }
@@ -37,6 +37,8 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
37
37
  fun setIsPaused(view: UnifiedPlayerView, isPaused: Boolean) {
38
38
  view.setIsPaused(isPaused)
39
39
  }
40
+
41
+
40
42
 
41
43
  // Register direct events
42
44
  override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
@@ -49,7 +51,9 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
49
51
  .put("topProgress", MapBuilder.of("registrationName", "onProgress"))
50
52
  .put("topPlaybackComplete", MapBuilder.of("registrationName", "onPlaybackComplete"))
51
53
  .put("topPlaybackResumed", MapBuilder.of("registrationName", "onPlaybackResumed"))
52
- .put("topPlaybackPaused", MapBuilder.of("registrationName", "onPlaybackPaused"))
54
+ .put("topPlaybackStalled", MapBuilder.of("registrationName", "onPlaybackStalled"))
55
+ .put("topPlaybackPaused", MapBuilder.of("registrationName", "onPaused"))
56
+ .put("topPlaying", MapBuilder.of("registrationName", "onPlaying"))
53
57
  .put("topLoadStart", MapBuilder.of("registrationName", "onLoadStart"))
54
58
  .build()
55
59
  }
@@ -1,5 +1,9 @@
1
1
  #import <React/RCTEventEmitter.h>
2
2
  #import <React/RCTBridgeModule.h>
3
+ #import <MobileVLCKit/MobileVLCKit.h> // Import MobileVLCKit
4
+
5
+ // Forward declaration for UnifiedPlayerUIView
6
+ @class UnifiedPlayerUIView;
3
7
 
4
8
  @interface UnifiedPlayerModule : RCTEventEmitter <RCTBridgeModule>
5
- @end
9
+ @end