@bluebillywig/react-native-bb-player 8.44.0 → 8.45.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.
- package/README.md +80 -59
- package/android/build.gradle +2 -1
- package/android/src/main/java/com/bluebillywig/bbplayer/BBPlayerModule.kt +146 -28
- package/android/src/main/java/com/bluebillywig/bbplayer/BBPlayerView.kt +74 -165
- package/android/src/main/java/com/bluebillywig/bbplayer/BBPlayerViewManager.kt +0 -6
- package/android/src/paper/java/com/bluebillywig/bbplayer/NativeBBPlayerModuleSpec.java +19 -8
- package/ios/BBPlayerModule.mm +17 -8
- package/ios/BBPlayerModule.swift +138 -26
- package/ios/BBPlayerView.swift +41 -140
- package/ios/BBPlayerViewManager.m +0 -2
- package/lib/commonjs/BBModalPlayer.js +21 -0
- package/lib/commonjs/BBModalPlayer.js.map +1 -0
- package/lib/commonjs/BBOutstreamView.js +0 -1
- package/lib/commonjs/BBOutstreamView.js.map +1 -1
- package/lib/commonjs/BBPlayerView.js +0 -1
- package/lib/commonjs/BBPlayerView.js.map +1 -1
- package/lib/commonjs/NativeCommands.js +24 -24
- package/lib/commonjs/NativeCommands.js.map +1 -1
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/NativeBBPlayerModule.js.map +1 -1
- package/lib/module/BBModalPlayer.js +17 -0
- package/lib/module/BBModalPlayer.js.map +1 -0
- package/lib/module/BBOutstreamView.js +0 -1
- package/lib/module/BBOutstreamView.js.map +1 -1
- package/lib/module/BBPlayerView.js +0 -1
- package/lib/module/BBPlayerView.js.map +1 -1
- package/lib/module/NativeCommands.js +24 -24
- package/lib/module/NativeCommands.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/NativeBBPlayerModule.js.map +1 -1
- package/lib/typescript/src/BBModalPlayer.d.ts +13 -0
- package/lib/typescript/src/BBModalPlayer.d.ts.map +1 -0
- package/lib/typescript/src/BBOutstreamView.d.ts.map +1 -1
- package/lib/typescript/src/BBPlayer.types.d.ts +24 -20
- package/lib/typescript/src/BBPlayer.types.d.ts.map +1 -1
- package/lib/typescript/src/BBPlayerView.d.ts +0 -2
- package/lib/typescript/src/BBPlayerView.d.ts.map +1 -1
- package/lib/typescript/src/NativeCommands.d.ts +8 -9
- package/lib/typescript/src/NativeCommands.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeBBPlayerModule.d.ts +13 -8
- package/lib/typescript/src/specs/NativeBBPlayerModule.d.ts.map +1 -1
- package/package.json +8 -11
- package/src/BBModalPlayer.ts +32 -0
- package/src/BBOutstreamView.tsx +0 -1
- package/src/BBPlayer.types.ts +31 -17
- package/src/BBPlayerView.tsx +0 -12
- package/src/NativeCommands.ts +37 -26
- package/src/index.ts +2 -0
- package/src/specs/NativeBBPlayerModule.ts +25 -8
- package/android/proguard-rules.pro +0 -59
|
@@ -12,6 +12,7 @@ import android.widget.FrameLayout
|
|
|
12
12
|
import com.facebook.react.modules.core.ReactChoreographer
|
|
13
13
|
import androidx.collection.ArrayMap
|
|
14
14
|
import androidx.mediarouter.app.MediaRouteButton
|
|
15
|
+
import org.json.JSONObject
|
|
15
16
|
import com.bluebillywig.bbnativeplayersdk.BBNativePlayer
|
|
16
17
|
import com.bluebillywig.bbnativeplayersdk.BBNativePlayerView
|
|
17
18
|
import com.bluebillywig.bbnativeplayersdk.BBNativePlayerViewDelegate
|
|
@@ -176,14 +177,8 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
176
177
|
// END NATIVE LAYOUT INTEGRATION
|
|
177
178
|
// ==================================================================================
|
|
178
179
|
|
|
179
|
-
// Timer for periodic time updates (opt-in for performance)
|
|
180
|
-
private val timeUpdateHandler = Handler(Looper.getMainLooper())
|
|
181
|
-
private var timeUpdateRunnable: Runnable? = null
|
|
182
180
|
private var isPlaying = false
|
|
183
181
|
private var currentDuration: Double = 0.0
|
|
184
|
-
private var lastKnownTime: Double = 0.0
|
|
185
|
-
private var playbackStartTimestamp: Long = 0
|
|
186
|
-
private var enableTimeUpdates: Boolean = false
|
|
187
182
|
|
|
188
183
|
// Event emission helper using modern EventDispatcher
|
|
189
184
|
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
@@ -222,17 +217,6 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
222
217
|
this.options["autoPlay"] = autoPlay
|
|
223
218
|
}
|
|
224
219
|
|
|
225
|
-
fun setEnableTimeUpdates(enabled: Boolean) {
|
|
226
|
-
enableTimeUpdates = enabled
|
|
227
|
-
debugLog("BBPlayerView") { "Time updates ${if (enabled) "enabled" else "disabled"}" }
|
|
228
|
-
|
|
229
|
-
if (!enabled && timeUpdateRunnable != null) {
|
|
230
|
-
stopTimeUpdates()
|
|
231
|
-
} else if (enabled && isPlaying && timeUpdateRunnable == null) {
|
|
232
|
-
startTimeUpdates()
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
220
|
fun setOptions(optionsMap: ReadableMap?) {
|
|
237
221
|
optionsMap?.let { map ->
|
|
238
222
|
val iterator = map.keySetIterator()
|
|
@@ -275,91 +259,36 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
275
259
|
|
|
276
260
|
addView(playerView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
277
261
|
|
|
278
|
-
//
|
|
279
|
-
//
|
|
262
|
+
// RESIZE_MODE_FILL prevents SurfaceView bleed-through at container edges
|
|
263
|
+
// when embedding inline above a WebView. The container aspect ratio is
|
|
264
|
+
// adapted dynamically via onDidTriggerMediaClipLoaded to avoid stretching.
|
|
280
265
|
postDelayed({
|
|
281
|
-
setExoPlayerResizeMode(playerView
|
|
282
|
-
},
|
|
266
|
+
setExoPlayerResizeMode(playerView)
|
|
267
|
+
}, 500)
|
|
283
268
|
|
|
284
269
|
playerSetup = true
|
|
285
270
|
debugLog("BBPlayerView") { "Player setup complete with URL: $jsonUrl" }
|
|
286
271
|
}
|
|
287
272
|
|
|
288
|
-
companion object {
|
|
289
|
-
// AspectRatioFrameLayout resize mode constants
|
|
290
|
-
private const val RESIZE_MODE_FILL = 3
|
|
291
|
-
}
|
|
292
|
-
|
|
293
273
|
/**
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* This fixes top/bottom margin issues caused by AspectRatioFrameLayout letterboxing.
|
|
274
|
+
* Set ExoPlayer's resize mode to FILL via reflection.
|
|
275
|
+
* FILL prevents SurfaceView bleed-through artifacts at container edges.
|
|
297
276
|
*/
|
|
298
|
-
private fun setExoPlayerResizeMode(view: View
|
|
299
|
-
// Try Media3 AspectRatioFrameLayout via reflection
|
|
300
|
-
try {
|
|
301
|
-
val aspectRatioClass = Class.forName("androidx.media3.ui.AspectRatioFrameLayout")
|
|
302
|
-
if (aspectRatioClass.isInstance(view)) {
|
|
303
|
-
val method = aspectRatioClass.getMethod("setResizeMode", Int::class.javaPrimitiveType)
|
|
304
|
-
method.invoke(view, resizeMode)
|
|
305
|
-
return true
|
|
306
|
-
}
|
|
307
|
-
} catch (_: ClassNotFoundException) {
|
|
308
|
-
// Media3 not available
|
|
309
|
-
} catch (_: Exception) {
|
|
310
|
-
// Failed to set resize mode
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Try Media3 PlayerView via reflection
|
|
277
|
+
private fun setExoPlayerResizeMode(view: View): Boolean {
|
|
314
278
|
try {
|
|
315
279
|
val playerViewClass = Class.forName("androidx.media3.ui.PlayerView")
|
|
316
280
|
if (playerViewClass.isInstance(view)) {
|
|
317
281
|
val method = playerViewClass.getMethod("setResizeMode", Int::class.javaPrimitiveType)
|
|
318
|
-
method.invoke(view,
|
|
319
|
-
// Continue searching - there might be an AspectRatioFrameLayout inside
|
|
320
|
-
}
|
|
321
|
-
} catch (_: ClassNotFoundException) {
|
|
322
|
-
// Media3 PlayerView not available
|
|
323
|
-
} catch (_: Exception) {
|
|
324
|
-
// Failed to set resize mode
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Try legacy ExoPlayer2 AspectRatioFrameLayout via reflection
|
|
328
|
-
try {
|
|
329
|
-
val legacyAspectRatioClass = Class.forName("com.google.android.exoplayer2.ui.AspectRatioFrameLayout")
|
|
330
|
-
if (legacyAspectRatioClass.isInstance(view)) {
|
|
331
|
-
val method = legacyAspectRatioClass.getMethod("setResizeMode", Int::class.javaPrimitiveType)
|
|
332
|
-
method.invoke(view, resizeMode)
|
|
282
|
+
method.invoke(view, 3) // RESIZE_MODE_FILL = 3
|
|
333
283
|
return true
|
|
334
284
|
}
|
|
335
|
-
} catch (_:
|
|
336
|
-
// ExoPlayer2 not available
|
|
337
|
-
} catch (_: Exception) {
|
|
338
|
-
// Failed to set resize mode
|
|
339
|
-
}
|
|
285
|
+
} catch (_: Exception) {}
|
|
340
286
|
|
|
341
|
-
// Try legacy ExoPlayer2 StyledPlayerView via reflection
|
|
342
|
-
try {
|
|
343
|
-
val styledPlayerViewClass = Class.forName("com.google.android.exoplayer2.ui.StyledPlayerView")
|
|
344
|
-
if (styledPlayerViewClass.isInstance(view)) {
|
|
345
|
-
val method = styledPlayerViewClass.getMethod("setResizeMode", Int::class.javaPrimitiveType)
|
|
346
|
-
method.invoke(view, resizeMode)
|
|
347
|
-
}
|
|
348
|
-
} catch (_: ClassNotFoundException) {
|
|
349
|
-
// StyledPlayerView not available
|
|
350
|
-
} catch (_: Exception) {
|
|
351
|
-
// Failed to set resize mode
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Recursively search children
|
|
355
287
|
if (view is ViewGroup) {
|
|
356
288
|
for (i in 0 until view.childCount) {
|
|
357
|
-
if (setExoPlayerResizeMode(view.getChildAt(i)
|
|
358
|
-
return true
|
|
359
|
-
}
|
|
289
|
+
if (setExoPlayerResizeMode(view.getChildAt(i))) return true
|
|
360
290
|
}
|
|
361
291
|
}
|
|
362
|
-
|
|
363
292
|
return false
|
|
364
293
|
}
|
|
365
294
|
|
|
@@ -370,13 +299,13 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
370
299
|
*
|
|
371
300
|
* Note: Shorts URLs (/sh/{id}.json) are NOT supported here - use BBShortsView instead.
|
|
372
301
|
*/
|
|
373
|
-
fun loadWithJsonUrl(url: String, autoPlay: Boolean = true) {
|
|
302
|
+
fun loadWithJsonUrl(url: String, autoPlay: Boolean = true, contextJson: String? = null) {
|
|
374
303
|
if (!::playerView.isInitialized) {
|
|
375
304
|
Log.w("BBPlayerView", "Cannot load content - playerView not initialized")
|
|
376
305
|
return
|
|
377
306
|
}
|
|
378
307
|
|
|
379
|
-
Log.d("BBPlayerView", "loadWithJsonUrl called with URL: $url")
|
|
308
|
+
Log.d("BBPlayerView", "loadWithJsonUrl called with URL: $url, context: $contextJson")
|
|
380
309
|
|
|
381
310
|
// Extract ID from URL patterns like:
|
|
382
311
|
// /c/{id}.json or /mediaclip/{id}.json -> clip ID
|
|
@@ -388,6 +317,8 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
388
317
|
val projectIdRegex = Regex("/pj/([0-9]+)\\.json|/project/([0-9]+)")
|
|
389
318
|
val shortsIdRegex = Regex("/sh/([0-9]+)\\.json")
|
|
390
319
|
|
|
320
|
+
val context = parseContext(contextJson)
|
|
321
|
+
|
|
391
322
|
when {
|
|
392
323
|
shortsIdRegex.containsMatchIn(url) -> {
|
|
393
324
|
// Shorts require a separate BBShortsView component
|
|
@@ -401,7 +332,7 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
401
332
|
val clipListId = match?.groupValues?.drop(1)?.firstOrNull { it.isNotEmpty() }
|
|
402
333
|
if (clipListId != null) {
|
|
403
334
|
Log.d("BBPlayerView", "Loading ClipList by ID: $clipListId")
|
|
404
|
-
playerView.player?.loadWithClipListId(clipListId, "external", autoPlay, null)
|
|
335
|
+
playerView.player?.loadWithClipListId(clipListId, "external", autoPlay, null, context)
|
|
405
336
|
} else {
|
|
406
337
|
Log.e("BBPlayerView", "Failed to extract cliplist ID from URL: $url")
|
|
407
338
|
}
|
|
@@ -411,7 +342,7 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
411
342
|
val projectId = match?.groupValues?.drop(1)?.firstOrNull { it.isNotEmpty() }
|
|
412
343
|
if (projectId != null) {
|
|
413
344
|
Log.d("BBPlayerView", "Loading Project by ID: $projectId")
|
|
414
|
-
playerView.player?.loadWithProjectId(projectId, "external", autoPlay, null)
|
|
345
|
+
playerView.player?.loadWithProjectId(projectId, "external", autoPlay, null, context)
|
|
415
346
|
} else {
|
|
416
347
|
Log.e("BBPlayerView", "Failed to extract project ID from URL: $url")
|
|
417
348
|
}
|
|
@@ -421,7 +352,7 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
421
352
|
val clipId = match?.groupValues?.drop(1)?.firstOrNull { it.isNotEmpty() }
|
|
422
353
|
if (clipId != null) {
|
|
423
354
|
Log.d("BBPlayerView", "Loading Clip by ID: $clipId")
|
|
424
|
-
playerView.player?.loadWithClipId(clipId, "external", autoPlay, null)
|
|
355
|
+
playerView.player?.loadWithClipId(clipId, "external", autoPlay, null, context)
|
|
425
356
|
} else {
|
|
426
357
|
Log.e("BBPlayerView", "Failed to extract clip ID from URL: $url")
|
|
427
358
|
}
|
|
@@ -439,7 +370,6 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
439
370
|
debugLog("BBPlayerView") { "removePlayer called" }
|
|
440
371
|
playerSetup = false
|
|
441
372
|
isPlaying = false
|
|
442
|
-
stopTimeUpdates()
|
|
443
373
|
removeAllViews()
|
|
444
374
|
|
|
445
375
|
if (::playerView.isInitialized) {
|
|
@@ -469,9 +399,7 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
469
399
|
|
|
470
400
|
fun seekRelative(offsetInSeconds: Double) {
|
|
471
401
|
if (::playerView.isInitialized) {
|
|
472
|
-
|
|
473
|
-
val newPosition = kotlin.math.max(0.0, kotlin.math.min(currentDuration, currentTime + offsetInSeconds))
|
|
474
|
-
playerView.player?.seek(newPosition)
|
|
402
|
+
playerView.player?.seekRelative(offsetInSeconds)
|
|
475
403
|
}
|
|
476
404
|
}
|
|
477
405
|
|
|
@@ -566,6 +494,20 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
566
494
|
return null
|
|
567
495
|
}
|
|
568
496
|
|
|
497
|
+
fun presentModal() {
|
|
498
|
+
Log.d("BBPlayerView", "presentModal() called")
|
|
499
|
+
if (::playerView.isInitialized) {
|
|
500
|
+
playerView.player?.enterFullScreen()
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
fun closeModal() {
|
|
505
|
+
Log.d("BBPlayerView", "closeModal() called")
|
|
506
|
+
if (::playerView.isInitialized) {
|
|
507
|
+
playerView.player?.exitFullScreen()
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
569
511
|
fun destroy() {
|
|
570
512
|
Log.d("BBPlayerView", "destroy() called")
|
|
571
513
|
if (::playerView.isInitialized) {
|
|
@@ -610,22 +552,6 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
610
552
|
} else null
|
|
611
553
|
}
|
|
612
554
|
|
|
613
|
-
private fun calculateEstimatedCurrentTime(): Double {
|
|
614
|
-
return if (isPlaying && playbackStartTimestamp > 0) {
|
|
615
|
-
val elapsedSeconds = (System.currentTimeMillis() - playbackStartTimestamp) / 1000.0
|
|
616
|
-
val estimatedTime = lastKnownTime + elapsedSeconds
|
|
617
|
-
kotlin.math.min(estimatedTime, currentDuration)
|
|
618
|
-
} else {
|
|
619
|
-
lastKnownTime
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
fun getCurrentTime(): Double? {
|
|
624
|
-
return if (::playerView.isInitialized) {
|
|
625
|
-
calculateEstimatedCurrentTime()
|
|
626
|
-
} else null
|
|
627
|
-
}
|
|
628
|
-
|
|
629
555
|
fun getDuration(): Double? {
|
|
630
556
|
return if (::playerView.isInitialized) {
|
|
631
557
|
playerView.getApiProperty(com.bluebillywig.bbnativeshared.enums.ApiProperty.duration) as? Double
|
|
@@ -672,73 +598,63 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
672
598
|
return null
|
|
673
599
|
}
|
|
674
600
|
|
|
675
|
-
//
|
|
676
|
-
fun
|
|
677
|
-
if (
|
|
678
|
-
|
|
601
|
+
// Helper to parse context JSON into a Map for the native SDK
|
|
602
|
+
private fun parseContext(contextJson: String?): Map<String, Any?>? {
|
|
603
|
+
if (contextJson.isNullOrBlank()) return null
|
|
604
|
+
return try {
|
|
605
|
+
val json = JSONObject(contextJson)
|
|
606
|
+
mapOf(
|
|
607
|
+
"contextEntityType" to json.optString("contextEntityType", null),
|
|
608
|
+
"contextEntityId" to json.optString("contextEntityId", null),
|
|
609
|
+
"contextCollectionType" to json.optString("contextCollectionType", null),
|
|
610
|
+
"contextCollectionId" to json.optString("contextCollectionId", null)
|
|
611
|
+
).filterValues { it != null }
|
|
612
|
+
} catch (e: Exception) {
|
|
613
|
+
Log.w("BBPlayerView", "Failed to parse context JSON: $contextJson", e)
|
|
614
|
+
null
|
|
679
615
|
}
|
|
680
616
|
}
|
|
681
617
|
|
|
682
|
-
|
|
618
|
+
// Load methods
|
|
619
|
+
fun loadWithClipId(clipId: String, initiator: String? = "external", autoPlay: Boolean? = true, seekTo: Double? = null, contextJson: String? = null) {
|
|
683
620
|
if (::playerView.isInitialized) {
|
|
684
|
-
|
|
621
|
+
val context = parseContext(contextJson)
|
|
622
|
+
playerView.player?.loadWithClipId(clipId, initiator, autoPlay, seekTo, context)
|
|
685
623
|
}
|
|
686
624
|
}
|
|
687
625
|
|
|
688
|
-
fun
|
|
626
|
+
fun loadWithClipListId(clipListId: String, initiator: String? = "external", autoPlay: Boolean? = true, seekTo: Double? = null, contextJson: String? = null) {
|
|
689
627
|
if (::playerView.isInitialized) {
|
|
690
|
-
|
|
628
|
+
val context = parseContext(contextJson)
|
|
629
|
+
playerView.player?.loadWithClipListId(clipListId, initiator, autoPlay, seekTo, context)
|
|
691
630
|
}
|
|
692
631
|
}
|
|
693
632
|
|
|
694
|
-
fun
|
|
633
|
+
fun loadWithProjectId(projectId: String, initiator: String? = "external", autoPlay: Boolean? = true, seekTo: Double? = null, contextJson: String? = null) {
|
|
695
634
|
if (::playerView.isInitialized) {
|
|
696
|
-
|
|
635
|
+
val context = parseContext(contextJson)
|
|
636
|
+
playerView.player?.loadWithProjectId(projectId, initiator, autoPlay, seekTo, context)
|
|
697
637
|
}
|
|
698
638
|
}
|
|
699
639
|
|
|
700
|
-
fun
|
|
640
|
+
fun loadWithClipJson(clipJson: String, initiator: String? = "external", autoPlay: Boolean? = true, seekTo: Double? = null, contextJson: String? = null) {
|
|
701
641
|
if (::playerView.isInitialized) {
|
|
702
|
-
|
|
642
|
+
val context = parseContext(contextJson)
|
|
643
|
+
playerView.player?.loadWithClipJson(clipJson, initiator, autoPlay, seekTo, context)
|
|
703
644
|
}
|
|
704
645
|
}
|
|
705
646
|
|
|
706
|
-
fun
|
|
647
|
+
fun loadWithClipListJson(clipListJson: String, initiator: String? = "external", autoPlay: Boolean? = true, seekTo: Double? = null, contextJson: String? = null) {
|
|
707
648
|
if (::playerView.isInitialized) {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
private fun startTimeUpdates() {
|
|
713
|
-
if (!enableTimeUpdates || timeUpdateRunnable != null) {
|
|
714
|
-
return
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
timeUpdateRunnable = object : Runnable {
|
|
718
|
-
override fun run() {
|
|
719
|
-
if (::playerView.isInitialized && isPlaying) {
|
|
720
|
-
val currentTime = calculateEstimatedCurrentTime()
|
|
721
|
-
|
|
722
|
-
if (currentDuration > 0) {
|
|
723
|
-
val params = Arguments.createMap().apply {
|
|
724
|
-
putDouble("currentTime", currentTime)
|
|
725
|
-
putDouble("duration", currentDuration)
|
|
726
|
-
}
|
|
727
|
-
sendEvent("onDidTriggerTimeUpdate", params)
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
timeUpdateHandler.postDelayed(this, 1000)
|
|
731
|
-
}
|
|
732
|
-
}
|
|
649
|
+
val context = parseContext(contextJson)
|
|
650
|
+
playerView.player?.loadWithClipListJson(clipListJson, initiator, autoPlay, seekTo, context)
|
|
733
651
|
}
|
|
734
|
-
|
|
735
|
-
timeUpdateHandler.postDelayed(timeUpdateRunnable!!, 1000)
|
|
736
652
|
}
|
|
737
653
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
654
|
+
fun loadWithProjectJson(projectJson: String, initiator: String? = "external", autoPlay: Boolean? = true, seekTo: Double? = null, contextJson: String? = null) {
|
|
655
|
+
if (::playerView.isInitialized) {
|
|
656
|
+
val context = parseContext(contextJson)
|
|
657
|
+
playerView.player?.loadWithProjectJson(projectJson, initiator, autoPlay, seekTo, context)
|
|
742
658
|
}
|
|
743
659
|
}
|
|
744
660
|
|
|
@@ -756,10 +672,12 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
756
672
|
}
|
|
757
673
|
|
|
758
674
|
override fun didTriggerMediaClipLoaded(view: BBNativePlayerView, clipData: MediaClip?) {
|
|
759
|
-
debugLog("BBPlayerView") { "didTriggerMediaClipLoaded: ${clipData?.title}" }
|
|
675
|
+
debugLog("BBPlayerView") { "didTriggerMediaClipLoaded: ${clipData?.title} (${clipData?.width}x${clipData?.height})" }
|
|
760
676
|
val params = Arguments.createMap().apply {
|
|
761
677
|
putString("title", clipData?.title)
|
|
762
678
|
putString("id", clipData?.id)
|
|
679
|
+
clipData?.width?.let { putDouble("width", it.toDouble()) }
|
|
680
|
+
clipData?.height?.let { putDouble("height", it.toDouble()) }
|
|
763
681
|
}
|
|
764
682
|
sendEvent("onDidTriggerMediaClipLoaded", params)
|
|
765
683
|
}
|
|
@@ -839,24 +757,20 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
839
757
|
}
|
|
840
758
|
|
|
841
759
|
override fun didTriggerPlaying(view: BBNativePlayerView) {
|
|
842
|
-
debugLog("BBPlayerView") { "didTriggerPlaying
|
|
760
|
+
debugLog("BBPlayerView") { "didTriggerPlaying" }
|
|
843
761
|
isPlaying = true
|
|
844
|
-
playbackStartTimestamp = System.currentTimeMillis()
|
|
845
|
-
startTimeUpdates()
|
|
846
762
|
sendEvent("onDidTriggerPlaying")
|
|
847
763
|
}
|
|
848
764
|
|
|
849
765
|
override fun didTriggerPause(view: BBNativePlayerView) {
|
|
850
|
-
debugLog("BBPlayerView") { "didTriggerPause
|
|
766
|
+
debugLog("BBPlayerView") { "didTriggerPause" }
|
|
851
767
|
isPlaying = false
|
|
852
|
-
stopTimeUpdates()
|
|
853
768
|
sendEvent("onDidTriggerPause")
|
|
854
769
|
}
|
|
855
770
|
|
|
856
771
|
override fun didTriggerEnded(view: BBNativePlayerView) {
|
|
857
|
-
debugLog("BBPlayerView") { "didTriggerEnded
|
|
772
|
+
debugLog("BBPlayerView") { "didTriggerEnded" }
|
|
858
773
|
isPlaying = false
|
|
859
|
-
stopTimeUpdates()
|
|
860
774
|
sendEvent("onDidTriggerEnded")
|
|
861
775
|
}
|
|
862
776
|
|
|
@@ -867,9 +781,6 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
867
781
|
|
|
868
782
|
override fun didTriggerSeeked(view: BBNativePlayerView, seekOffset: Double?) {
|
|
869
783
|
debugLog("BBPlayerView") { "didTriggerSeeked: $seekOffset" }
|
|
870
|
-
lastKnownTime = seekOffset ?: 0.0
|
|
871
|
-
playbackStartTimestamp = System.currentTimeMillis()
|
|
872
|
-
|
|
873
784
|
val params = Arguments.createMap().apply {
|
|
874
785
|
putDouble("payload", seekOffset ?: 0.0)
|
|
875
786
|
}
|
|
@@ -1062,8 +973,6 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
1062
973
|
|
|
1063
974
|
override fun onDetachedFromWindow() {
|
|
1064
975
|
debugLog("BBPlayerView") { "onDetachedFromWindow - cleaning up player" }
|
|
1065
|
-
stopTimeUpdates()
|
|
1066
|
-
timeUpdateHandler.removeCallbacksAndMessages(null)
|
|
1067
976
|
removePlayer()
|
|
1068
977
|
super.onDetachedFromWindow()
|
|
1069
978
|
}
|
|
@@ -43,11 +43,6 @@ class BBPlayerViewManager : ViewGroupManager<BBPlayerView>() {
|
|
|
43
43
|
view.setAutoPlay(autoPlay)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
@ReactProp(name = "enableTimeUpdates")
|
|
47
|
-
fun setEnableTimeUpdates(view: BBPlayerView, enabled: Boolean) {
|
|
48
|
-
view.setEnableTimeUpdates(enabled)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
46
|
@ReactProp(name = "options")
|
|
52
47
|
fun setOptions(view: BBPlayerView, options: ReadableMap?) {
|
|
53
48
|
view.setOptions(options)
|
|
@@ -186,7 +181,6 @@ class BBPlayerViewManager : ViewGroupManager<BBPlayerView>() {
|
|
|
186
181
|
"onDidTriggerEnded",
|
|
187
182
|
"onDidTriggerSeeking",
|
|
188
183
|
"onDidTriggerSeeked",
|
|
189
|
-
"onDidTriggerTimeUpdate",
|
|
190
184
|
"onDidTriggerDurationChange",
|
|
191
185
|
"onDidTriggerVolumeChange",
|
|
192
186
|
"onDidTriggerCanPlay",
|
|
@@ -31,23 +31,34 @@ public abstract class NativeBBPlayerModuleSpec extends ReactContextBaseJavaModul
|
|
|
31
31
|
public abstract void collapse(int viewTag);
|
|
32
32
|
public abstract void expand(int viewTag);
|
|
33
33
|
|
|
34
|
+
// Void methods - modal control
|
|
35
|
+
public abstract void presentModal(int viewTag);
|
|
36
|
+
public abstract void closeModal(int viewTag);
|
|
37
|
+
|
|
34
38
|
// Void methods - other commands
|
|
35
39
|
public abstract void autoPlayNextCancel(int viewTag);
|
|
36
40
|
public abstract void destroy(int viewTag);
|
|
37
41
|
public abstract void showCastPicker(int viewTag);
|
|
38
42
|
|
|
43
|
+
// Modal player (module-level)
|
|
44
|
+
public abstract void presentModalPlayer(String jsonUrl, String optionsJson);
|
|
45
|
+
public abstract void dismissModalPlayer();
|
|
46
|
+
|
|
47
|
+
// Event emitter support
|
|
48
|
+
public abstract void addListener(String eventName);
|
|
49
|
+
public abstract void removeListeners(double count);
|
|
50
|
+
|
|
39
51
|
// Load methods
|
|
40
|
-
public abstract void loadWithClipId(int viewTag, String clipId, String initiator, boolean autoPlay, double seekTo);
|
|
41
|
-
public abstract void loadWithClipListId(int viewTag, String clipListId, String initiator, boolean autoPlay, double seekTo);
|
|
42
|
-
public abstract void loadWithProjectId(int viewTag, String projectId, String initiator, boolean autoPlay, double seekTo);
|
|
43
|
-
public abstract void loadWithClipJson(int viewTag, String clipJson, String initiator, boolean autoPlay, double seekTo);
|
|
44
|
-
public abstract void loadWithClipListJson(int viewTag, String clipListJson, String initiator, boolean autoPlay, double seekTo);
|
|
45
|
-
public abstract void loadWithProjectJson(int viewTag, String projectJson, String initiator, boolean autoPlay, double seekTo);
|
|
46
|
-
public abstract void loadWithJsonUrl(int viewTag, String jsonUrl, boolean autoPlay);
|
|
52
|
+
public abstract void loadWithClipId(int viewTag, String clipId, String initiator, boolean autoPlay, double seekTo, String contextJson);
|
|
53
|
+
public abstract void loadWithClipListId(int viewTag, String clipListId, String initiator, boolean autoPlay, double seekTo, String contextJson);
|
|
54
|
+
public abstract void loadWithProjectId(int viewTag, String projectId, String initiator, boolean autoPlay, double seekTo, String contextJson);
|
|
55
|
+
public abstract void loadWithClipJson(int viewTag, String clipJson, String initiator, boolean autoPlay, double seekTo, String contextJson);
|
|
56
|
+
public abstract void loadWithClipListJson(int viewTag, String clipListJson, String initiator, boolean autoPlay, double seekTo, String contextJson);
|
|
57
|
+
public abstract void loadWithProjectJson(int viewTag, String projectJson, String initiator, boolean autoPlay, double seekTo, String contextJson);
|
|
58
|
+
public abstract void loadWithJsonUrl(int viewTag, String jsonUrl, boolean autoPlay, String contextJson);
|
|
47
59
|
|
|
48
60
|
// Promise getters
|
|
49
61
|
public abstract void getDuration(int viewTag, Promise promise);
|
|
50
|
-
public abstract void getCurrentTime(int viewTag, Promise promise);
|
|
51
62
|
public abstract void getMuted(int viewTag, Promise promise);
|
|
52
63
|
public abstract void getVolume(int viewTag, Promise promise);
|
|
53
64
|
public abstract void getPhase(int viewTag, Promise promise);
|
package/ios/BBPlayerModule.mm
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTEventEmitter.h>
|
|
2
3
|
|
|
3
4
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
4
5
|
#import <BBPlayerModuleSpec/BBPlayerModuleSpec.h>
|
|
5
6
|
#endif
|
|
6
7
|
|
|
7
|
-
@interface RCT_EXTERN_MODULE(BBPlayerModule,
|
|
8
|
+
@interface RCT_EXTERN_MODULE(BBPlayerModule, RCTEventEmitter)
|
|
8
9
|
|
|
9
10
|
// Playback control
|
|
10
11
|
RCT_EXTERN_METHOD(play:(nonnull NSNumber *)viewTag)
|
|
@@ -29,13 +30,13 @@ RCT_EXTERN_METHOD(destroy:(nonnull NSNumber *)viewTag)
|
|
|
29
30
|
RCT_EXTERN_METHOD(showCastPicker:(nonnull NSNumber *)viewTag)
|
|
30
31
|
|
|
31
32
|
// Load methods
|
|
32
|
-
RCT_EXTERN_METHOD(loadWithClipId:(nonnull NSNumber *)viewTag clipId:(NSString *)clipId initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo)
|
|
33
|
-
RCT_EXTERN_METHOD(loadWithClipListId:(nonnull NSNumber *)viewTag clipListId:(NSString *)clipListId initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo)
|
|
34
|
-
RCT_EXTERN_METHOD(loadWithProjectId:(nonnull NSNumber *)viewTag projectId:(NSString *)projectId initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo)
|
|
35
|
-
RCT_EXTERN_METHOD(loadWithClipJson:(nonnull NSNumber *)viewTag clipJson:(NSString *)clipJson initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo)
|
|
36
|
-
RCT_EXTERN_METHOD(loadWithClipListJson:(nonnull NSNumber *)viewTag clipListJson:(NSString *)clipListJson initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo)
|
|
37
|
-
RCT_EXTERN_METHOD(loadWithProjectJson:(nonnull NSNumber *)viewTag projectJson:(NSString *)projectJson initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo)
|
|
38
|
-
RCT_EXTERN_METHOD(loadWithJsonUrl:(nonnull NSNumber *)viewTag jsonUrl:(NSString *)jsonUrl autoPlay:(BOOL)autoPlay)
|
|
33
|
+
RCT_EXTERN_METHOD(loadWithClipId:(nonnull NSNumber *)viewTag clipId:(NSString *)clipId initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo contextJson:(NSString *)contextJson)
|
|
34
|
+
RCT_EXTERN_METHOD(loadWithClipListId:(nonnull NSNumber *)viewTag clipListId:(NSString *)clipListId initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo contextJson:(NSString *)contextJson)
|
|
35
|
+
RCT_EXTERN_METHOD(loadWithProjectId:(nonnull NSNumber *)viewTag projectId:(NSString *)projectId initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo contextJson:(NSString *)contextJson)
|
|
36
|
+
RCT_EXTERN_METHOD(loadWithClipJson:(nonnull NSNumber *)viewTag clipJson:(NSString *)clipJson initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo contextJson:(NSString *)contextJson)
|
|
37
|
+
RCT_EXTERN_METHOD(loadWithClipListJson:(nonnull NSNumber *)viewTag clipListJson:(NSString *)clipListJson initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo contextJson:(NSString *)contextJson)
|
|
38
|
+
RCT_EXTERN_METHOD(loadWithProjectJson:(nonnull NSNumber *)viewTag projectJson:(NSString *)projectJson initiator:(NSString *)initiator autoPlay:(BOOL)autoPlay seekTo:(NSNumber *)seekTo contextJson:(NSString *)contextJson)
|
|
39
|
+
RCT_EXTERN_METHOD(loadWithJsonUrl:(nonnull NSNumber *)viewTag jsonUrl:(NSString *)jsonUrl autoPlay:(BOOL)autoPlay contextJson:(NSString *)contextJson)
|
|
39
40
|
|
|
40
41
|
// Getter methods with Promise
|
|
41
42
|
RCT_EXTERN_METHOD(getDuration:(nonnull NSNumber *)viewTag resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
@@ -49,6 +50,14 @@ RCT_EXTERN_METHOD(getClipData:(nonnull NSNumber *)viewTag resolver:(RCTPromiseRe
|
|
|
49
50
|
RCT_EXTERN_METHOD(getProjectData:(nonnull NSNumber *)viewTag resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
50
51
|
RCT_EXTERN_METHOD(getPlayoutData:(nonnull NSNumber *)viewTag resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
51
52
|
|
|
53
|
+
// Modal player (module-level)
|
|
54
|
+
RCT_EXTERN_METHOD(presentModalPlayer:(NSString *)jsonUrl optionsJson:(NSString *)optionsJson)
|
|
55
|
+
RCT_EXTERN_METHOD(dismissModalPlayer)
|
|
56
|
+
|
|
57
|
+
// Event emitter support
|
|
58
|
+
RCT_EXTERN_METHOD(addListener:(NSString *)eventName)
|
|
59
|
+
RCT_EXTERN_METHOD(removeListeners:(double)count)
|
|
60
|
+
|
|
52
61
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
53
62
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
54
63
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|