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

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
@@ -245,11 +287,241 @@ class PlayerFactoryExoPlayerNpaw(
245
287
  }
246
288
  }
247
289
 
290
+ override fun unload() {
291
+ exoPlayerAdapter = null
292
+ videoAdapter = null
293
+ basePlayer.unload()
294
+ }
295
+
248
296
  override fun destroy() {
249
297
  exoPlayerAdapter = null
250
298
  videoAdapter = null
251
299
  basePlayer.destroy()
252
300
  }
253
301
  }
302
+
303
+ class NpawPlayerView @JvmOverloads constructor(
304
+ context: Context,
305
+ attrs: AttributeSet? = null,
306
+ defStyleAttr: Int = 0
307
+ ) : FrameLayout(context, attrs, defStyleAttr), AdViewProvider, PlayerSurface {
308
+
309
+ private val componentListener = ComponentListener()
310
+ private val onDetachPlayer = EventListenerDispose()
311
+
312
+ val videoFrame: AspectRatioFrameLayout = AspectRatioFrameLayout(context)
313
+ val surfaceView: View = when {
314
+ isInEditMode -> Space(context)
315
+ else -> SurfaceView(context)
316
+ }
317
+ val posterView: ImageView = AppCompatImageView(context).also {
318
+ it.scaleType = ImageView.ScaleType.FIT_CENTER
319
+ }
320
+ val subtitleView: SubtitleView = SubtitleView(context)
321
+ val adOverlay: PlayerViewAdvertisement = PlayerViewAdvertisement(context).also {
322
+ it.fitsSystemWindows = true
323
+ }
324
+ override var adObstructions: List<View> = emptyList()
325
+ override var gesturesTarget: PlayerGesturesTarget?
326
+ get() = adOverlay.gesturesTarget
327
+ set(value) {
328
+ adOverlay.gesturesTarget = value
329
+ }
330
+
331
+ var player: PlayerBase? = null
332
+ set(value) {
333
+ if (field == value) return
334
+ field?.also { detachPlayer(it) }
335
+ field = value
336
+ field?.also { attachPlayer(it) }
337
+ }
338
+
339
+ var pausedByBackground: Boolean = false
340
+ get() = player?.asPlayerExoPlayer()?.pausedByBackground ?: field
341
+ set(value) {
342
+ field = value
343
+ player?.asPlayerExoPlayer()?.pausedByBackground = value
344
+ }
345
+ var surfaceAttached: Boolean = false
346
+
347
+ init {
348
+ if (!isInEditMode) {
349
+ subtitleView.setUserDefaultTextSize()
350
+ subtitleView.setUserDefaultStyle()
351
+ subtitleView.setApplyEmbeddedStyles(false)
352
+ }
353
+
354
+ addView(videoFrame, LayoutParams(MATCH_PARENT, MATCH_PARENT, Gravity.CENTER))
355
+ videoFrame.addView(surfaceView, MATCH_PARENT, MATCH_PARENT)
356
+ videoFrame.addView(subtitleView, MATCH_PARENT, MATCH_PARENT)
357
+ addView(posterView, MATCH_PARENT, MATCH_PARENT)
358
+ addView(adOverlay, MATCH_PARENT, MATCH_PARENT)
359
+
360
+ context.lifecycle?.addObserver(componentListener)
361
+ }
362
+
363
+ private val Context.lifecycle: Lifecycle?
364
+ get() = when (this) {
365
+ is LifecycleOwner -> lifecycle
366
+ is ContextWrapper -> baseContext.lifecycle
367
+ else -> null
368
+ }
369
+
370
+ private fun detachPlayer(player: PlayerBase) {
371
+ onDetachPlayer()
372
+ player.asPlayerExoPlayer()?.also { detachSurface(it) }
373
+ }
374
+
375
+ private fun detachSurface(player: PlayerExoPlayer) {
376
+ if (!surfaceAttached) return
377
+ surfaceAttached = false
378
+ player.exoPlayer.removeListener(componentListener)
379
+ player.adViewProvider = null
380
+ player.posterSurface = null
381
+ player.videoSurface = null
382
+ subtitleView.setCues(null)
383
+ }
384
+
385
+ private fun attachPlayer(player: PlayerBase) {
386
+ player.asPlayerExoPlayer()?.also { attachSurface(it) }
387
+ player.fullscreen = isFullscreen
388
+ onDetachPlayer += player.addEventListener<ResizeEvent> {
389
+ videoFrame.resizeMode = player.scaleType.toResizeMode()
390
+ }
391
+ onDetachPlayer += player.addEventListener<CastingStarted> {
392
+ player.asPlayerExoPlayer()?.also { detachSurface(it) }
393
+ }
394
+ onDetachPlayer += player.addEventListener<CastingEnded> {
395
+ player.asPlayerExoPlayer()?.also { attachSurface(it) }
396
+ }
397
+ videoFrame.resizeMode = player.scaleType.toResizeMode()
398
+ }
399
+
400
+ private fun attachSurface(player: PlayerExoPlayer) {
401
+ if (surfaceAttached) return
402
+ surfaceAttached = true
403
+ player.exoPlayer.addListener(componentListener)
404
+ player.adViewProvider = this
405
+ player.posterSurface = posterView
406
+ player.videoSurface = surfaceView as? SurfaceView
407
+ }
408
+
409
+ private fun PlayerBase.asPlayerExoPlayer(): PlayerExoPlayer? = when {
410
+ this is PlayerExoPlayer -> this
411
+ this is PlayerProxy -> this.base?.asPlayerExoPlayer()
412
+ this is NpawPlayer -> this.basePlayer
413
+ else -> null
414
+ }
415
+
416
+ protected fun ScaleType?.toResizeMode(): Int = when (this) {
417
+ ScaleType.STRETCH -> AspectRatioFrameLayout.RESIZE_MODE_FILL
418
+ ScaleType.ZOOM -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
419
+ else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
420
+ }
421
+
422
+ private var isFullscreen = false
423
+ set(value) {
424
+ if (field == value) return
425
+ field = value
426
+ player?.fullscreen = isFullscreen
427
+ }
428
+
429
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
430
+ super.onLayout(changed, left, top, right, bottom)
431
+
432
+ val displayMetrics = context.resources.displayMetrics
433
+ val screenWidth = displayMetrics.widthPixels
434
+ val minFullWidth = screenWidth * 9 / 10
435
+ val screenHeight = displayMetrics.heightPixels
436
+ val minFullHeight = screenHeight * 9 / 10
437
+ val width = right - left
438
+ val height = bottom - top
439
+ isFullscreen = width >= minFullWidth && height >= minFullHeight
440
+ }
441
+
442
+ @SuppressLint("ClickableViewAccessibility")
443
+ override fun onTouchEvent(event: MotionEvent): Boolean {
444
+ return gesturesTarget?.onDelegatedTouchEvent(event) ?: super.onTouchEvent(event)
445
+ }
446
+
447
+ override fun getAdViewGroup(): ViewGroup =
448
+ adOverlay
449
+
450
+ override fun getAdOverlayInfos(): List<AdOverlayInfo> =
451
+ adObstructions.map { AdOverlayInfo.Builder(it, AdOverlayInfo.PURPOSE_CONTROLS).build() }
452
+
453
+ override fun onSaveInstanceState(): Parcelable {
454
+ val state = Bundle()
455
+ state.putParcelable("base", super.onSaveInstanceState())
456
+ state.putBoolean("pausedByBackground", pausedByBackground)
457
+ return state
458
+ }
459
+
460
+ override fun onRestoreInstanceState(state: Parcelable?) {
461
+ val bundle = state as? Bundle
462
+ if (bundle == null) {
463
+ super.onRestoreInstanceState(state)
464
+ } else {
465
+ super.onRestoreInstanceState(bundle.getParcelable("base"))
466
+ pausedByBackground = bundle.getBoolean("pausedByBackground")
467
+ }
468
+ }
469
+
470
+ private inner class ComponentListener : TextOutput, Player.Listener,
471
+ DefaultLifecycleObserver {
472
+ override fun onCues(cueGroup: CueGroup) {
473
+ subtitleView.setCues(cueGroup.cues)
474
+ }
475
+
476
+ @Deprecated("No longer used")
477
+ override fun onCues(cues: MutableList<Cue>) {
478
+ // only overridden because compiler is confused about multiple default implementations
479
+ }
480
+
481
+ override fun onVideoSizeChanged(videoSize: VideoSize) {
482
+ videoFrame.resizeMode = player?.scaleType.toResizeMode()
483
+ videoFrame.setAspectRatio(
484
+ when {
485
+ videoSize.height <= 0 -> 1f
486
+ else -> videoSize.width * videoSize.pixelWidthHeightRatio / videoSize.height
487
+ }
488
+ )
489
+ }
490
+
491
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
492
+ this@NpawPlayerView.keepScreenOn = isPlaying
493
+ }
494
+
495
+ override fun onPause(owner: LifecycleOwner) {
496
+ val player = this@NpawPlayerView.player ?: return
497
+ player.asPlayerExoPlayer()?.also {
498
+ detachSurface(it)
499
+ }
500
+ if (!player.continueInBackground && !player.paused && player.state.source != null) {
501
+ if (player.live) {
502
+ player.stop()
503
+ } else {
504
+ player.pause()
505
+ }
506
+ pausedByBackground = true
507
+ }
508
+ }
509
+
510
+ override fun onResume(owner: LifecycleOwner) {
511
+ val player = this@NpawPlayerView.player ?: return
512
+ if (pausedByBackground) {
513
+ pausedByBackground = false
514
+ player.play()
515
+ }
516
+ player.asPlayerExoPlayer()?.also {
517
+ attachSurface(it)
518
+ }
519
+ }
520
+
521
+ override fun onDestroy(owner: LifecycleOwner) {
522
+ context.lifecycle?.removeObserver(this)
523
+ }
524
+ }
525
+ }
254
526
  }
255
527
 
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.23",
4
4
  "description": "Package with build engine for SmartApps BIGscreen projects...",
5
5
  "main": "dist/src/index.js",
6
6
  "scripts": {