@24i/smartapps-bigscreen-rnv-build-engine 0.17.21 → 0.17.22

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.
@@ -1,31 +1,64 @@
1
1
  package com.twentyfouri.tvbridge.global.utils
2
2
 
3
+ import android.annotation.SuppressLint
3
4
  import android.app.Application
4
5
  import android.content.Context
6
+ import android.content.ContextWrapper
7
+ import android.os.Bundle
8
+ import android.os.Parcelable
9
+ import android.util.AttributeSet
10
+ import android.view.Gravity
11
+ import android.view.MotionEvent
12
+ import android.view.SurfaceView
5
13
  import android.view.View
6
14
  import android.view.ViewGroup
15
+ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
16
+ import android.widget.FrameLayout
17
+ import android.widget.ImageView
18
+ import android.widget.Space
19
+ import androidx.appcompat.widget.AppCompatImageView
20
+ import androidx.lifecycle.DefaultLifecycleObserver
21
+ import androidx.lifecycle.Lifecycle
22
+ import androidx.lifecycle.LifecycleOwner
7
23
  import com.google.android.exoplayer2.C
8
24
  import com.google.android.exoplayer2.ExoPlayerLibraryInfo
25
+ import com.google.android.exoplayer2.Player
9
26
  import com.google.android.exoplayer2.drm.DefaultDrmSessionManager
10
27
  import com.google.android.exoplayer2.drm.DrmSessionManager
11
28
  import com.google.android.exoplayer2.drm.FrameworkMediaDrm
12
29
  import com.google.android.exoplayer2.drm.MediaDrmCallback
13
30
  import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
31
+ import com.google.android.exoplayer2.text.Cue
32
+ import com.google.android.exoplayer2.text.CueGroup
33
+ import com.google.android.exoplayer2.text.TextOutput
34
+ import com.google.android.exoplayer2.ui.AdOverlayInfo
35
+ import com.google.android.exoplayer2.ui.AdViewProvider
36
+ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
37
+ import com.google.android.exoplayer2.ui.SubtitleView
14
38
  import com.google.android.exoplayer2.upstream.HttpDataSource
15
39
  import com.google.android.exoplayer2.upstream.cache.CacheDataSource
40
+ import com.google.android.exoplayer2.video.VideoSize
16
41
  import com.npaw.NpawPlugin
17
42
  import com.npaw.analytics.video.VideoAdapter
18
43
  import com.npaw.analytics.video.VideoOptions
19
44
  import com.npaw.core.options.AnalyticsOptions
20
45
  import com.npaw.exoplayer.ExoPlayerAdapter
21
46
  import com.npaw.exoplayer.ExoPlayerBalancer
47
+ import com.twentyfouri.player.base.CastingEnded
48
+ import com.twentyfouri.player.base.CastingStarted
22
49
  import com.twentyfouri.player.base.DrmPlayready
23
50
  import com.twentyfouri.player.base.DrmWidevine
51
+ import com.twentyfouri.player.base.EventListenerDispose
24
52
  import com.twentyfouri.player.base.PlayerBase
53
+ import com.twentyfouri.player.base.PlayerGesturesTarget
54
+ import com.twentyfouri.player.base.PlayerSurface
55
+ import com.twentyfouri.player.base.ResizeEvent
56
+ import com.twentyfouri.player.base.ScaleType
25
57
  import com.twentyfouri.player.base.Source
26
58
  import com.twentyfouri.player.base.SourceAuthorization
27
59
  import com.twentyfouri.player.base.SourceDrm
28
60
  import com.twentyfouri.player.base.SourceMetadata
61
+ import com.twentyfouri.player.base.addEventListener
29
62
  import com.twentyfouri.player.base.buildUpon
30
63
  import com.twentyfouri.player.base.getBoolean
31
64
  import com.twentyfouri.player.base.getString
@@ -37,7 +70,8 @@ import com.twentyfouri.player.exoplayer.DrmSessionManagerProvider
37
70
  import com.twentyfouri.player.exoplayer.PlayerComponents
38
71
  import com.twentyfouri.player.exoplayer.PlayerExoPlayer
39
72
  import com.twentyfouri.player.exoplayer.PlayerFactoryExoPlayer
40
- import com.twentyfouri.player.exoplayer.PlayerViewExoPlayer
73
+ import com.twentyfouri.player.exoplayer.PlayerProxy
74
+ import com.twentyfouri.player.exoplayer.PlayerViewAdvertisement
41
75
  import com.twentyfouri.player.exoplayer.SharedComponents
42
76
  import com.twentyfouri.tvbridge.BuildConfig
43
77
  import com.twentyfouri.tvbridge.configuration.BridgeConfig
@@ -55,7 +89,7 @@ class PlayerFactoryExoPlayerNpaw(
55
89
  CustomizedSharedComponents(context)
56
90
 
57
91
  override fun createView(parent: ViewGroup): View {
58
- return PlayerViewExoPlayer(parent.context).also {
92
+ return NpawPlayerView(parent.context).also {
59
93
  val basePlayer = createPlayer(parent.context)
60
94
  val npawPlugin = (
61
95
  (basePlayer.components as CustomizedPlayerComponents)
@@ -67,6 +101,15 @@ class PlayerFactoryExoPlayerNpaw(
67
101
  }
68
102
  }
69
103
 
104
+ override fun getPlayerSurface(view: View): PlayerSurface {
105
+ return view as NpawPlayerView
106
+ }
107
+
108
+ override fun getPlayer(view: View): PlayerBase {
109
+ return (view as NpawPlayerView).player
110
+ ?: throw IllegalStateException("Player was not initialized by PlayerFactory")
111
+ }
112
+
70
113
  private inner class CustomizedSharedComponents(context: Context) : DefaultSharedComponents(context) {
71
114
 
72
115
  val application = context.applicationContext as? Application
@@ -166,7 +209,6 @@ class PlayerFactoryExoPlayerNpaw(
166
209
  val basePlayer: PlayerExoPlayer,
167
210
  val npawPlugin: NpawPlugin
168
211
  ) : PlayerBase by basePlayer {
169
-
170
212
  var exoPlayerAdapter: ExoPlayerAdapter? = null
171
213
 
172
214
  var videoAdapter: VideoAdapter? = null
@@ -251,5 +293,229 @@ class PlayerFactoryExoPlayerNpaw(
251
293
  basePlayer.destroy()
252
294
  }
253
295
  }
296
+
297
+ class NpawPlayerView @JvmOverloads constructor(
298
+ context: Context,
299
+ attrs: AttributeSet? = null,
300
+ defStyleAttr: Int = 0
301
+ ) : FrameLayout(context, attrs, defStyleAttr), AdViewProvider, PlayerSurface {
302
+
303
+ private val componentListener = ComponentListener()
304
+ private val onDetachPlayer = EventListenerDispose()
305
+
306
+ val videoFrame: AspectRatioFrameLayout = AspectRatioFrameLayout(context)
307
+ val surfaceView: View = when {
308
+ isInEditMode -> Space(context)
309
+ else -> SurfaceView(context)
310
+ }
311
+ val posterView: ImageView = AppCompatImageView(context).also {
312
+ it.scaleType = ImageView.ScaleType.FIT_CENTER
313
+ }
314
+ val subtitleView: SubtitleView = SubtitleView(context)
315
+ val adOverlay: PlayerViewAdvertisement = PlayerViewAdvertisement(context).also {
316
+ it.fitsSystemWindows = true
317
+ }
318
+ override var adObstructions: List<View> = emptyList()
319
+ override var gesturesTarget: PlayerGesturesTarget?
320
+ get() = adOverlay.gesturesTarget
321
+ set(value) {
322
+ adOverlay.gesturesTarget = value
323
+ }
324
+
325
+ var player: PlayerBase? = null
326
+ set(value) {
327
+ if (field == value) return
328
+ field?.also { detachPlayer(it) }
329
+ field = value
330
+ field?.also { attachPlayer(it) }
331
+ }
332
+
333
+ var pausedByBackground: Boolean = false
334
+ get() = player?.asPlayerExoPlayer()?.pausedByBackground ?: field
335
+ set(value) {
336
+ field = value
337
+ player?.asPlayerExoPlayer()?.pausedByBackground = value
338
+ }
339
+ var surfaceAttached: Boolean = false
340
+
341
+ init {
342
+ if (!isInEditMode) {
343
+ subtitleView.setUserDefaultTextSize()
344
+ subtitleView.setUserDefaultStyle()
345
+ subtitleView.setApplyEmbeddedStyles(false)
346
+ }
347
+
348
+ addView(videoFrame, LayoutParams(MATCH_PARENT, MATCH_PARENT, Gravity.CENTER))
349
+ videoFrame.addView(surfaceView, MATCH_PARENT, MATCH_PARENT)
350
+ videoFrame.addView(subtitleView, MATCH_PARENT, MATCH_PARENT)
351
+ addView(posterView, MATCH_PARENT, MATCH_PARENT)
352
+ addView(adOverlay, MATCH_PARENT, MATCH_PARENT)
353
+
354
+ context.lifecycle?.addObserver(componentListener)
355
+ }
356
+
357
+ private val Context.lifecycle: Lifecycle?
358
+ get() = when (this) {
359
+ is LifecycleOwner -> lifecycle
360
+ is ContextWrapper -> baseContext.lifecycle
361
+ else -> null
362
+ }
363
+
364
+ private fun detachPlayer(player: PlayerBase) {
365
+ onDetachPlayer()
366
+ player.asPlayerExoPlayer()?.also { detachSurface(it) }
367
+ }
368
+
369
+ private fun detachSurface(player: PlayerExoPlayer) {
370
+ if (!surfaceAttached) return
371
+ surfaceAttached = false
372
+ player.exoPlayer.removeListener(componentListener)
373
+ player.adViewProvider = null
374
+ player.posterSurface = null
375
+ player.videoSurface = null
376
+ subtitleView.setCues(null)
377
+ }
378
+
379
+ private fun attachPlayer(player: PlayerBase) {
380
+ player.asPlayerExoPlayer()?.also { attachSurface(it) }
381
+ player.fullscreen = isFullscreen
382
+ onDetachPlayer += player.addEventListener<ResizeEvent> {
383
+ videoFrame.resizeMode = player.scaleType.toResizeMode()
384
+ }
385
+ onDetachPlayer += player.addEventListener<CastingStarted> {
386
+ player.asPlayerExoPlayer()?.also { detachSurface(it) }
387
+ }
388
+ onDetachPlayer += player.addEventListener<CastingEnded> {
389
+ player.asPlayerExoPlayer()?.also { attachSurface(it) }
390
+ }
391
+ videoFrame.resizeMode = player.scaleType.toResizeMode()
392
+ }
393
+
394
+ private fun attachSurface(player: PlayerExoPlayer) {
395
+ if (surfaceAttached) return
396
+ surfaceAttached = true
397
+ player.exoPlayer.addListener(componentListener)
398
+ player.adViewProvider = this
399
+ player.posterSurface = posterView
400
+ player.videoSurface = surfaceView as? SurfaceView
401
+ }
402
+
403
+ private fun PlayerBase.asPlayerExoPlayer(): PlayerExoPlayer? = when {
404
+ this is PlayerExoPlayer -> this
405
+ this is PlayerProxy -> this.base?.asPlayerExoPlayer()
406
+ this is NpawPlayer -> this.basePlayer
407
+ else -> null
408
+ }
409
+
410
+ protected fun ScaleType?.toResizeMode(): Int = when (this) {
411
+ ScaleType.STRETCH -> AspectRatioFrameLayout.RESIZE_MODE_FILL
412
+ ScaleType.ZOOM -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
413
+ else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
414
+ }
415
+
416
+ private var isFullscreen = false
417
+ set(value) {
418
+ if (field == value) return
419
+ field = value
420
+ player?.fullscreen = isFullscreen
421
+ }
422
+
423
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
424
+ super.onLayout(changed, left, top, right, bottom)
425
+
426
+ val displayMetrics = context.resources.displayMetrics
427
+ val screenWidth = displayMetrics.widthPixels
428
+ val minFullWidth = screenWidth * 9 / 10
429
+ val screenHeight = displayMetrics.heightPixels
430
+ val minFullHeight = screenHeight * 9 / 10
431
+ val width = right - left
432
+ val height = bottom - top
433
+ isFullscreen = width >= minFullWidth && height >= minFullHeight
434
+ }
435
+
436
+ @SuppressLint("ClickableViewAccessibility")
437
+ override fun onTouchEvent(event: MotionEvent): Boolean {
438
+ return gesturesTarget?.onDelegatedTouchEvent(event) ?: super.onTouchEvent(event)
439
+ }
440
+
441
+ override fun getAdViewGroup(): ViewGroup =
442
+ adOverlay
443
+
444
+ override fun getAdOverlayInfos(): List<AdOverlayInfo> =
445
+ adObstructions.map { AdOverlayInfo.Builder(it, AdOverlayInfo.PURPOSE_CONTROLS).build() }
446
+
447
+ override fun onSaveInstanceState(): Parcelable {
448
+ val state = Bundle()
449
+ state.putParcelable("base", super.onSaveInstanceState())
450
+ state.putBoolean("pausedByBackground", pausedByBackground)
451
+ return state
452
+ }
453
+
454
+ override fun onRestoreInstanceState(state: Parcelable?) {
455
+ val bundle = state as? Bundle
456
+ if (bundle == null) {
457
+ super.onRestoreInstanceState(state)
458
+ } else {
459
+ super.onRestoreInstanceState(bundle.getParcelable("base"))
460
+ pausedByBackground = bundle.getBoolean("pausedByBackground")
461
+ }
462
+ }
463
+
464
+ private inner class ComponentListener : TextOutput, Player.Listener,
465
+ DefaultLifecycleObserver {
466
+ override fun onCues(cueGroup: CueGroup) {
467
+ subtitleView.setCues(cueGroup.cues)
468
+ }
469
+
470
+ @Deprecated("No longer used")
471
+ override fun onCues(cues: MutableList<Cue>) {
472
+ // only overridden because compiler is confused about multiple default implementations
473
+ }
474
+
475
+ override fun onVideoSizeChanged(videoSize: VideoSize) {
476
+ videoFrame.resizeMode = player?.scaleType.toResizeMode()
477
+ videoFrame.setAspectRatio(
478
+ when {
479
+ videoSize.height <= 0 -> 1f
480
+ else -> videoSize.width * videoSize.pixelWidthHeightRatio / videoSize.height
481
+ }
482
+ )
483
+ }
484
+
485
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
486
+ this@NpawPlayerView.keepScreenOn = isPlaying
487
+ }
488
+
489
+ override fun onPause(owner: LifecycleOwner) {
490
+ val player = this@NpawPlayerView.player ?: return
491
+ player.asPlayerExoPlayer()?.also {
492
+ detachSurface(it)
493
+ }
494
+ if (!player.continueInBackground && !player.paused && player.state.source != null) {
495
+ if (player.live) {
496
+ player.stop()
497
+ } else {
498
+ player.pause()
499
+ }
500
+ pausedByBackground = true
501
+ }
502
+ }
503
+
504
+ override fun onResume(owner: LifecycleOwner) {
505
+ val player = this@NpawPlayerView.player ?: return
506
+ if (pausedByBackground) {
507
+ pausedByBackground = false
508
+ player.play()
509
+ }
510
+ player.asPlayerExoPlayer()?.also {
511
+ attachSurface(it)
512
+ }
513
+ }
514
+
515
+ override fun onDestroy(owner: LifecycleOwner) {
516
+ context.lifecycle?.removeObserver(this)
517
+ }
518
+ }
519
+ }
254
520
  }
255
521
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@24i/smartapps-bigscreen-rnv-build-engine",
3
- "version": "0.17.21",
3
+ "version": "0.17.22",
4
4
  "description": "Package with build engine for SmartApps BIGscreen projects...",
5
5
  "main": "dist/src/index.js",
6
6
  "scripts": {