@24i/smartapps-bigscreen-rnv-build-engine 5.0.0-alpha.25 → 5.0.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.
Files changed (86) hide show
  1. package/build-freshness.js +158 -0
  2. package/dist/.build-freshness.json +1010 -0
  3. package/dist/templates/platforms/firetv/project/README.md +196 -0
  4. package/dist/templates/platforms/firetv/project/amazoniap/build.gradle +41 -0
  5. package/dist/templates/platforms/firetv/project/amazoniap/consumer-rules.pro +0 -0
  6. package/dist/templates/platforms/firetv/project/amazoniap/proguard-rules.pro +21 -0
  7. package/dist/templates/platforms/firetv/project/amazoniap/src/main/kotlin/com/twentyfouri/bridgetv/amazoniap/AmazonIapService.kt +91 -0
  8. package/dist/templates/platforms/firetv/project/amazoniap/src/main/kotlin/com/twentyfouri/bridgetv/amazoniap/AmazonIapServiceFactory.kt +13 -0
  9. package/dist/templates/platforms/firetv/project/app/build.gradle +138 -0
  10. package/dist/templates/platforms/firetv/project/app/proguard-rules.pro +47 -0
  11. package/dist/templates/platforms/firetv/project/app/src/bigscreenAppstage/kotlin/com/twentyfouri/tvbridge/Flavor.kt +9 -0
  12. package/dist/templates/platforms/firetv/project/app/src/bigscreenAppstage/res/raw/configuration.json +8 -0
  13. package/dist/templates/platforms/firetv/project/app/src/main/AndroidManifest.xml +52 -0
  14. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/FlavorBase.kt +9 -0
  15. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/bridge/BridgeFromJavaScript.kt +421 -0
  16. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/bridge/BridgeToJavaScript.kt +121 -0
  17. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/BridgeConfig.kt +19 -0
  18. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/ConfigCallback.kt +6 -0
  19. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/ConfigProvider.kt +27 -0
  20. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/Drm.kt +10 -0
  21. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/UserAgent.kt +6 -0
  22. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/BaseApplication.kt +73 -0
  23. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ActivityComponent.kt +13 -0
  24. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ActivityModule.kt +14 -0
  25. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ApplicationComponent.kt +23 -0
  26. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ApplicationModule.kt +51 -0
  27. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/PerActivity.kt +26 -0
  28. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/LocalStorage.kt +30 -0
  29. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/LoggingDrmCallback.kt +38 -0
  30. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/OverriddenHttpMediaDrmCallback.kt +134 -0
  31. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryBitmovinCustomized.kt +19 -0
  32. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryConfigured.kt +37 -0
  33. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerCustomized.kt +106 -0
  34. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerNpaw.kt +581 -0
  35. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryVersion.kt +27 -0
  36. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/SimpleActivityLifecycleCallback.kt +32 -0
  37. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/SourceDrmOverridden.kt +91 -0
  38. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/sound/TouchPlayer.kt +86 -0
  39. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/client/BridgeWebView.kt +82 -0
  40. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/client/BridgeWebViewClient.kt +114 -0
  41. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/AnalyticsPlaybackInfo.kt +10 -0
  42. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/ContentModel.kt +14 -0
  43. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/DrmModel.kt +20 -0
  44. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/MediaEventType.kt +31 -0
  45. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/parser/PlaybackInfoParser.kt +10 -0
  46. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/parser/SourceParser.kt +89 -0
  47. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/parser/TrackJsonModelParser.kt +58 -0
  48. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/view/Mainview.kt +18 -0
  49. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/view/PlayerCache.kt +15 -0
  50. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/view/WebViewActivity.kt +252 -0
  51. package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/viewmodel/WebViewModel.kt +748 -0
  52. package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_background_focus.xml +10 -0
  53. package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_background_normal.xml +7 -0
  54. package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_background_pressed.xml +7 -0
  55. package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_focus.xml +8 -0
  56. package/dist/templates/platforms/firetv/project/app/src/main/res/layout/activity_web_view.xml +28 -0
  57. package/dist/templates/platforms/firetv/project/app/src/main/res/raw/click.ogg +0 -0
  58. package/dist/templates/platforms/firetv/project/app/src/main/res/values/styles.xml +12 -0
  59. package/dist/templates/platforms/firetv/project/app/src/main/res/xml/network_security_config.xml +4 -0
  60. package/dist/templates/platforms/firetv/project/build.gradle +45 -0
  61. package/dist/templates/platforms/firetv/project/common/build.gradle +41 -0
  62. package/dist/templates/platforms/firetv/project/common/consumer-rules.pro +0 -0
  63. package/dist/templates/platforms/firetv/project/common/proguard-rules.pro +21 -0
  64. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/Util.kt +11 -0
  65. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapCallback.kt +11 -0
  66. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapException.kt +14 -0
  67. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapProduct.kt +16 -0
  68. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapPurchase.kt +9 -0
  69. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapService.kt +14 -0
  70. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapServiceFactory.kt +8 -0
  71. package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/NullIapService.kt +32 -0
  72. package/dist/templates/platforms/firetv/project/googleiap/build.gradle +41 -0
  73. package/dist/templates/platforms/firetv/project/googleiap/consumer-rules.pro +0 -0
  74. package/dist/templates/platforms/firetv/project/googleiap/proguard-rules.pro +21 -0
  75. package/dist/templates/platforms/firetv/project/googleiap/src/main/kotlin/com/twentyfouri/tvbridge/googleiap/GoogleIapService.kt +6 -0
  76. package/dist/templates/platforms/firetv/project/googleiap/src/main/kotlin/com/twentyfouri/tvbridge/googleiap/GoogleIapServiceFactory.kt +13 -0
  77. package/dist/templates/platforms/firetv/project/gradle/wrapper/gradle-wrapper.jar +0 -0
  78. package/dist/templates/platforms/firetv/project/gradle/wrapper/gradle-wrapper.properties +7 -0
  79. package/dist/templates/platforms/firetv/project/gradle.properties +4 -0
  80. package/dist/templates/platforms/firetv/project/gradlew +251 -0
  81. package/dist/templates/platforms/firetv/project/gradlew.bat +94 -0
  82. package/dist/templates/platforms/firetv/project/proguard-rules.pro +42 -0
  83. package/dist/templates/platforms/firetv/project/settings.gradle +4 -0
  84. package/index.js +2 -31
  85. package/package.json +4 -3
  86. package/project.json +57 -11
@@ -0,0 +1,581 @@
1
+ package com.twentyfouri.tvbridge.global.utils
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.content.ContextWrapper
6
+ import android.os.Bundle
7
+ import android.os.Parcelable
8
+ import android.util.AttributeSet
9
+ import android.view.Gravity
10
+ import android.view.MotionEvent
11
+ import android.view.SurfaceView
12
+ import android.view.View
13
+ import android.view.ViewGroup
14
+ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
15
+ import android.widget.FrameLayout
16
+ import android.widget.ImageView
17
+ import android.widget.Space
18
+ import androidx.appcompat.widget.AppCompatImageView
19
+ import androidx.lifecycle.DefaultLifecycleObserver
20
+ import androidx.lifecycle.Lifecycle
21
+ import androidx.lifecycle.LifecycleOwner
22
+ import com.google.android.exoplayer2.C
23
+ import com.google.android.exoplayer2.ExoPlayerLibraryInfo
24
+ import com.google.android.exoplayer2.Player
25
+ import com.google.android.exoplayer2.drm.DefaultDrmSessionManager
26
+ import com.google.android.exoplayer2.drm.DrmSessionManager
27
+ import com.google.android.exoplayer2.drm.FrameworkMediaDrm
28
+ import com.google.android.exoplayer2.drm.MediaDrmCallback
29
+ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
30
+ import com.google.android.exoplayer2.text.Cue
31
+ import com.google.android.exoplayer2.text.CueGroup
32
+ import com.google.android.exoplayer2.text.TextOutput
33
+ import com.google.android.exoplayer2.ui.AdOverlayInfo
34
+ import com.google.android.exoplayer2.ui.AdViewProvider
35
+ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
36
+ import com.google.android.exoplayer2.ui.SubtitleView
37
+ import com.google.android.exoplayer2.upstream.HttpDataSource
38
+ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy
39
+ import com.google.android.exoplayer2.upstream.cache.CacheDataSource
40
+ import com.google.android.exoplayer2.video.VideoSize
41
+ import com.npaw.NpawPlugin
42
+ import com.npaw.analytics.video.VideoAdapter
43
+ import com.npaw.analytics.video.VideoOptions
44
+ import com.npaw.core.options.AnalyticsOptions
45
+ import com.npaw.exoplayer.ExoPlayerAdapter
46
+ import com.npaw.exoplayer.ExoPlayerBalancer
47
+ import com.twentyfouri.player.base.BackgroundMode
48
+ import com.twentyfouri.player.base.CastingEnded
49
+ import com.twentyfouri.player.base.CastingStarted
50
+ import com.twentyfouri.player.base.CastingType
51
+ import com.twentyfouri.player.base.DrmPlayready
52
+ import com.twentyfouri.player.base.DrmWidevine
53
+ import com.twentyfouri.player.base.EventListenerDispose
54
+ import com.twentyfouri.player.base.PlayerBase
55
+ import com.twentyfouri.player.base.PlayerGesturesTarget
56
+ import com.twentyfouri.player.base.PlayerProxy
57
+ import com.twentyfouri.player.base.PlayerSurface
58
+ import com.twentyfouri.player.base.ResizeEvent
59
+ import com.twentyfouri.player.base.ScaleType
60
+ import com.twentyfouri.player.base.Source
61
+ import com.twentyfouri.player.base.SourceAuthorization
62
+ import com.twentyfouri.player.base.SourceDrm
63
+ import com.twentyfouri.player.base.SourceMetadata
64
+ import com.twentyfouri.player.base.addEventListener
65
+ import com.twentyfouri.player.base.buildUpon
66
+ import com.twentyfouri.player.base.getBoolean
67
+ import com.twentyfouri.player.base.getString
68
+ import com.twentyfouri.player.exoplayer.AuthorizedDataSourceProvider
69
+ import com.twentyfouri.player.exoplayer.DefaultDrmSessionManagerProvider
70
+ import com.twentyfouri.player.exoplayer.DefaultPlayerComponents
71
+ import com.twentyfouri.player.exoplayer.DefaultSharedComponents
72
+ import com.twentyfouri.player.exoplayer.DrmSessionManagerProvider
73
+ import com.twentyfouri.player.exoplayer.PlayerComponents
74
+ import com.twentyfouri.player.exoplayer.PlayerExoPlayer
75
+ import com.twentyfouri.player.exoplayer.PlayerFactoryExoPlayer
76
+ import com.twentyfouri.player.exoplayer.PlayerViewAdvertisement
77
+ import com.twentyfouri.player.exoplayer.SharedComponents
78
+ import com.twentyfouri.tvbridge.BuildConfig
79
+ import com.twentyfouri.tvbridge.configuration.BridgeConfig
80
+ import java.util.WeakHashMap
81
+ import com.npaw.core.util.extensions.Log as NpawLog
82
+
83
+ class PlayerFactoryExoPlayerNpaw(
84
+ private val config: BridgeConfig
85
+ ) : PlayerFactoryExoPlayer(), PlayerFactoryVersion {
86
+
87
+ override val engineName: String = "Exoplayer"
88
+
89
+ override val engineVersion: String = ExoPlayerLibraryInfo.VERSION
90
+
91
+ private val activePlayers = WeakHashMap<PlayerBase, Boolean>()
92
+
93
+ override fun createSharedComponents(context: Context): SharedComponents =
94
+ CustomizedSharedComponents(context)
95
+
96
+ override fun createView(parent: ViewGroup): View {
97
+ return NpawPlayerView(parent.context).also {
98
+ val basePlayer = createPlayer(parent.context)
99
+ val npawPlugin = (
100
+ (basePlayer.components as CustomizedPlayerComponents)
101
+ .sharedComponents as CustomizedSharedComponents
102
+ ).npawPlugin
103
+ val npawPlayer = NpawPlayer(basePlayer, npawPlugin)
104
+ activePlayers[npawPlayer] = true
105
+ it.player = npawPlayer
106
+ }
107
+ }
108
+
109
+ override fun getPlayerSurface(view: View): PlayerSurface {
110
+ return view as NpawPlayerView
111
+ }
112
+
113
+ override fun getPlayer(view: View): PlayerBase {
114
+ return (view as NpawPlayerView).player
115
+ ?: throw IllegalStateException("Player was not initialized by PlayerFactory")
116
+ }
117
+
118
+ override fun release() {
119
+ activePlayers.keys.forEach {
120
+ it.destroy()
121
+ }
122
+ activePlayers.clear()
123
+ super.release()
124
+ }
125
+
126
+ private inner class CustomizedSharedComponents(context: Context) : DefaultSharedComponents(context) {
127
+
128
+ val npawPlugin: NpawPlugin = NpawPlugin
129
+ .Builder(application, config.npawAccount)
130
+ .setLogLevel(if (BuildConfig.DEBUG) NpawLog.Level.VERBOSE else NpawLog.Level.ERROR)
131
+ .build()
132
+
133
+ val npawBalancer = ExoPlayerBalancer(npawPlugin)
134
+
135
+ override fun destroy() {
136
+ super.destroy()
137
+ npawPlugin.destroy()
138
+ }
139
+
140
+ override val drmProvider: DrmSessionManagerProvider by lazy {
141
+ CustomizedDrmSessionManagerProvider(loadErrorHandlingPolicy)
142
+ }
143
+
144
+ override val applicationName: String
145
+ get() = config.playerAppName ?: super.applicationName
146
+
147
+ override fun buildIsolatedComponents(): PlayerComponents {
148
+ return CustomizedPlayerComponents(this)
149
+ }
150
+
151
+ override val baseHttpFactory: HttpDataSource.Factory by lazy {
152
+ npawBalancer.dataSourceFactory as OkHttpDataSource.Factory
153
+ }
154
+
155
+ override val dataSourceFactory: AuthorizedDataSourceProvider by lazy {
156
+ object : AuthorizedDataSourceProvider {
157
+ override fun buildDataSourceFactory(authorization: SourceAuthorization?): CacheDataSource.Factory {
158
+ return buildReadOnlyDataSourceFactory(baseHttpFactory)
159
+ }
160
+
161
+ override fun buildHttpDataSourceFactory(authorization: SourceAuthorization?): HttpDataSource.Factory {
162
+ return baseHttpFactory
163
+ }
164
+ }
165
+ }
166
+
167
+ override val cacheDataSourceFactory: AuthorizedDataSourceProvider by lazy {
168
+ dataSourceFactory
169
+ }
170
+ }
171
+
172
+ private inner class CustomizedPlayerComponents(shared: SharedComponents) : DefaultPlayerComponents(shared) {
173
+ override val handleAudioFocus: Boolean
174
+ get() = false
175
+ }
176
+
177
+ private class CustomizedDrmSessionManagerProvider(
178
+ loadErrorHandlingPolicy: LoadErrorHandlingPolicy
179
+ ) : DefaultDrmSessionManagerProvider(loadErrorHandlingPolicy) {
180
+ override fun buildDrmSessionManager(
181
+ dataSourceFactory: AuthorizedDataSourceProvider,
182
+ source: Source,
183
+ sourceDrm: SourceDrm
184
+ ): DrmSessionManager? {
185
+ if (sourceDrm is SourceDrmOverridden) {
186
+ return buildDrmOverridden(dataSourceFactory, sourceDrm)
187
+ } else {
188
+ return super.buildDrmSessionManager(dataSourceFactory, source, sourceDrm)
189
+ }
190
+ }
191
+
192
+ private fun buildDrmOverridden(dataSourceFactory: AuthorizedDataSourceProvider, sourceDrm: SourceDrmOverridden): DrmSessionManager? {
193
+ val drmUuid = when (sourceDrm.packageName) {
194
+ DrmWidevine.packageName -> C.WIDEVINE_UUID
195
+ DrmPlayready.packageName -> C.PLAYREADY_UUID
196
+ else -> C.UUID_NIL
197
+ }
198
+ val licenseUrl = sourceDrm.licenseServer?.toString() ?: ""
199
+ val forceLicenseUrl = sourceDrm.forceLicenseServer
200
+ val httpDataSourceFactory = dataSourceFactory.buildHttpDataSourceFactory(sourceDrm.authorization)
201
+
202
+ val drmCallback = LoggingDrmCallback(
203
+ OverriddenHttpMediaDrmCallback(
204
+ licenseUrl,
205
+ forceLicenseUrl,
206
+ httpDataSourceFactory
207
+ ),
208
+ licenseUrl
209
+ )
210
+
211
+ return DefaultDrmSessionManager.Builder()
212
+ .setUuidAndExoMediaDrmProvider(drmUuid, FrameworkMediaDrm.DEFAULT_PROVIDER)
213
+ .setMultiSession(false)
214
+ .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
215
+ .build(drmCallback)
216
+ }
217
+
218
+ override fun buildDrmCallback(licenseUrl: String, forceLicenseUrl: Boolean, httpDataSourceFactory: HttpDataSource.Factory): MediaDrmCallback {
219
+ return LoggingDrmCallback(super.buildDrmCallback(licenseUrl, forceLicenseUrl, httpDataSourceFactory), licenseUrl)
220
+ }
221
+ }
222
+
223
+ private class NpawPlayer(
224
+ val basePlayer: PlayerExoPlayer,
225
+ val npawPlugin: NpawPlugin
226
+ ) : PlayerBase by basePlayer {
227
+ var exoPlayerAdapter: ExoPlayerAdapter? = null
228
+
229
+ var videoAdapter: VideoAdapter? = null
230
+ set(value) {
231
+ field?.destroy()
232
+ field = value
233
+ }
234
+
235
+ fun extractYouboraAnalyticsOptions(source: Source?, target: AnalyticsOptions) {
236
+ if (source == null) return
237
+ val jsonAnalyticsArray = source.extras?.optJSONArray("analytics") ?: return
238
+ for (index in 0 until jsonAnalyticsArray.length()) {
239
+ val jsonAnalytics = jsonAnalyticsArray.getJSONObject(index)
240
+ if (jsonAnalytics.getString("integration", "") != "youbora") continue
241
+ jsonAnalytics.keys().forEach { key ->
242
+ when (key) {
243
+ "app.name" -> target.appName = jsonAnalytics.getString(key, null)
244
+ "app.releaseVersion" -> target.appReleaseVersion = jsonAnalytics.getString(key, null)
245
+ "device.id" -> target.deviceId = jsonAnalytics.getString(key, null)
246
+ "username" -> target.username = jsonAnalytics.getString(key, null)
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ fun extractYouboraVideoOptions(source: Source?, target: VideoOptions) {
253
+ if (source == null) return
254
+ val jsonAnalyticsArray = source.extras?.optJSONArray("analytics") ?: return
255
+ for (index in 0 until jsonAnalyticsArray.length()) {
256
+ val jsonAnalytics = jsonAnalyticsArray.getJSONObject(index)
257
+ if (jsonAnalytics.getString("integration", "") != "youbora") continue
258
+ jsonAnalytics.keys().forEach { key ->
259
+ when (key) {
260
+ "content.id" -> target.contentId = jsonAnalytics.getString(key, null)
261
+ "content.title" -> target.contentTitle = jsonAnalytics.getString(key, null)
262
+ "content.isLive" -> target.live = jsonAnalytics.getBoolean(key, false)
263
+ "content.type" -> target.contentType = jsonAnalytics.getString(key, null)
264
+ "content.program" -> target.program = jsonAnalytics.getString(key, null)
265
+ "content.channel" -> target.contentChannel = jsonAnalytics.getString(key, null)
266
+ "content.episodeTitle" -> target.contentEpisodeTitle = jsonAnalytics.getString(key, null)
267
+ "content.season" -> target.contentSeason = jsonAnalytics.getString(key, null)
268
+ "content.tvShow" -> target.contentTvShow = jsonAnalytics.getString(key, null)
269
+ "content.customDimension.1" -> target.contentCustomDimension1 = jsonAnalytics.getString(key, null)
270
+ "content.customDimension.2" -> target.contentCustomDimension2 = jsonAnalytics.getString(key, null)
271
+ }
272
+ }
273
+ }
274
+ }
275
+
276
+ override var source: Source?
277
+ get() = basePlayer.source
278
+ set(value) {
279
+ exoPlayerAdapter = null
280
+ videoAdapter = null
281
+ if (value != null) {
282
+ val videoOptions = VideoOptions()
283
+ extractYouboraAnalyticsOptions(value, npawPlugin.analyticsOptions)
284
+ extractYouboraVideoOptions(value, videoOptions)
285
+
286
+ var adjustedSource = value
287
+ if (value.metadata?.title.isNullOrEmpty() && !videoOptions.contentTitle.isNullOrEmpty()) {
288
+ adjustedSource = value.buildUpon {
289
+ metadata = (metadata?.buildUpon() ?: SourceMetadata.Builder()).apply {
290
+ title = videoOptions.contentTitle
291
+ }.build()
292
+ }
293
+ }
294
+
295
+ basePlayer.source = adjustedSource
296
+ exoPlayerAdapter = ExoPlayerAdapter(basePlayer.components.context, basePlayer.exoPlayer)
297
+ videoAdapter = npawPlugin.videoBuilder()
298
+ .setPlayerAdapter(exoPlayerAdapter)
299
+ .setOptions(videoOptions)
300
+ .build()
301
+ } else {
302
+ basePlayer.source = null
303
+ }
304
+ }
305
+
306
+ override fun unload() {
307
+ exoPlayerAdapter = null
308
+ videoAdapter = null
309
+ basePlayer.unload()
310
+ }
311
+
312
+ override fun destroy() {
313
+ exoPlayerAdapter = null
314
+ videoAdapter = null
315
+ basePlayer.destroy()
316
+ }
317
+ }
318
+
319
+ class NpawPlayerView @JvmOverloads constructor(
320
+ context: Context,
321
+ attrs: AttributeSet? = null,
322
+ defStyleAttr: Int = 0
323
+ ) : FrameLayout(context, attrs, defStyleAttr), AdViewProvider, PlayerSurface {
324
+
325
+ private val componentListener = ComponentListener()
326
+ private val onDetachPlayer = EventListenerDispose()
327
+
328
+ val videoFrame: AspectRatioFrameLayout = AspectRatioFrameLayout(context)
329
+ val surfaceView: View = when {
330
+ isInEditMode -> Space(context)
331
+ else -> SurfaceView(context)
332
+ }
333
+ val posterView: ImageView = AppCompatImageView(context).also {
334
+ it.scaleType = ImageView.ScaleType.FIT_CENTER
335
+ }
336
+ val subtitleView: SubtitleView = SubtitleView(context)
337
+ val adOverlay: PlayerViewAdvertisement = PlayerViewAdvertisement(context).also {
338
+ it.fitsSystemWindows = true
339
+ }
340
+ override var adObstructions: List<View> = emptyList()
341
+ override var gesturesTarget: PlayerGesturesTarget?
342
+ get() = adOverlay.gesturesTarget
343
+ set(value) {
344
+ adOverlay.gesturesTarget = value
345
+ }
346
+
347
+ override var supportsPictureInPicture: Boolean
348
+ get() = false
349
+ set(value) {}
350
+
351
+ var player: PlayerBase? = null
352
+ set(value) {
353
+ if (field == value) return
354
+ field?.also { detachPlayer(it) }
355
+ field = value
356
+ field?.also { attachPlayer(it) }
357
+ }
358
+
359
+ var pausedByBackground: Boolean = false
360
+ get() = player?.asPlayerExoPlayer()?.pausedByBackground ?: field
361
+ set(value) {
362
+ field = value
363
+ player?.asPlayerExoPlayer()?.pausedByBackground = value
364
+ }
365
+ var surfaceAttached: Boolean = false
366
+
367
+ init {
368
+ if (!isInEditMode) {
369
+ subtitleView.setUserDefaultTextSize()
370
+ subtitleView.setUserDefaultStyle()
371
+ subtitleView.setApplyEmbeddedStyles(false)
372
+ }
373
+
374
+ addView(videoFrame, LayoutParams(MATCH_PARENT, MATCH_PARENT, Gravity.CENTER))
375
+ videoFrame.addView(surfaceView, MATCH_PARENT, MATCH_PARENT)
376
+ videoFrame.addView(subtitleView, MATCH_PARENT, MATCH_PARENT)
377
+ addView(posterView, MATCH_PARENT, MATCH_PARENT)
378
+ addView(adOverlay, MATCH_PARENT, MATCH_PARENT)
379
+
380
+ context.lifecycle?.addObserver(componentListener)
381
+ }
382
+
383
+ private val Context.lifecycle: Lifecycle?
384
+ get() = when (this) {
385
+ is LifecycleOwner -> lifecycle
386
+ is ContextWrapper -> baseContext.lifecycle
387
+ else -> null
388
+ }
389
+
390
+ private fun detachPlayer(player: PlayerBase) {
391
+ onDetachPlayer()
392
+ player.asPlayerExoPlayer()?.also { detachSurface(it) }
393
+ }
394
+
395
+ private fun detachSurface(player: PlayerExoPlayer) {
396
+ if (!surfaceAttached) return
397
+ surfaceAttached = false
398
+ player.exoPlayer.removeListener(componentListener)
399
+ player.adViewProvider = null
400
+ player.posterSurface = null
401
+ player.videoSurface = null
402
+ subtitleView.setCues(null)
403
+ }
404
+
405
+ private fun attachPlayer(player: PlayerBase) {
406
+ player.asPlayerExoPlayer()?.also { attachSurface(it) }
407
+ player.fullscreen = isFullscreen
408
+ onDetachPlayer += player.addEventListener<ResizeEvent> {
409
+ videoFrame.resizeMode = player.scaleType.toResizeMode()
410
+ }
411
+ onDetachPlayer += player.addEventListener<CastingStarted> {
412
+ player.asPlayerExoPlayer()?.also { detachSurface(it) }
413
+ }
414
+ onDetachPlayer += player.addEventListener<CastingEnded> {
415
+ player.asPlayerExoPlayer()?.also { attachSurface(it) }
416
+ }
417
+ videoFrame.resizeMode = player.scaleType.toResizeMode()
418
+ }
419
+
420
+ private fun attachSurface(player: PlayerExoPlayer) {
421
+ if (surfaceAttached) return
422
+ surfaceAttached = true
423
+ player.exoPlayer.addListener(componentListener)
424
+ player.adViewProvider = this
425
+ player.posterSurface = posterView
426
+ player.videoSurface = surfaceView as? SurfaceView
427
+ }
428
+
429
+ private fun PlayerBase.asPlayerExoPlayer(): PlayerExoPlayer? = when {
430
+ this is PlayerExoPlayer -> this
431
+ this is PlayerProxy -> this.base?.asPlayerExoPlayer()
432
+ this is NpawPlayer -> this.basePlayer
433
+ else -> null
434
+ }
435
+
436
+ protected fun ScaleType?.toResizeMode(): Int = when (this) {
437
+ ScaleType.STRETCH -> AspectRatioFrameLayout.RESIZE_MODE_FILL
438
+ ScaleType.ZOOM -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
439
+ else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
440
+ }
441
+
442
+ private var isFullscreen = false
443
+ set(value) {
444
+ if (field == value) return
445
+ field = value
446
+ player?.fullscreen = isFullscreen
447
+ }
448
+
449
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
450
+ super.onLayout(changed, left, top, right, bottom)
451
+
452
+ val displayMetrics = context.resources.displayMetrics
453
+ val screenWidth = displayMetrics.widthPixels
454
+ val minFullWidth = screenWidth * 9 / 10
455
+ val screenHeight = displayMetrics.heightPixels
456
+ val minFullHeight = screenHeight * 9 / 10
457
+ val width = right - left
458
+ val height = bottom - top
459
+ isFullscreen = width >= minFullWidth && height >= minFullHeight
460
+ }
461
+
462
+ @SuppressLint("ClickableViewAccessibility")
463
+ override fun onTouchEvent(event: MotionEvent): Boolean {
464
+ return gesturesTarget?.onDelegatedTouchEvent(event) ?: super.onTouchEvent(event)
465
+ }
466
+
467
+ override fun getAdViewGroup(): ViewGroup =
468
+ adOverlay
469
+
470
+ override fun getAdOverlayInfos(): List<AdOverlayInfo> =
471
+ adObstructions.map { AdOverlayInfo.Builder(it, AdOverlayInfo.PURPOSE_CONTROLS).build() }
472
+
473
+ override fun onSaveInstanceState(): Parcelable {
474
+ val state = Bundle()
475
+ state.putParcelable("base", super.onSaveInstanceState())
476
+ state.putBoolean("pausedByBackground", pausedByBackground)
477
+ return state
478
+ }
479
+
480
+ override fun onRestoreInstanceState(state: Parcelable?) {
481
+ val bundle = state as? Bundle
482
+ if (bundle == null) {
483
+ super.onRestoreInstanceState(state)
484
+ } else {
485
+ super.onRestoreInstanceState(bundle.getParcelable("base"))
486
+ pausedByBackground = bundle.getBoolean("pausedByBackground")
487
+ }
488
+ }
489
+
490
+ override fun onUserLeaveHint() {
491
+ (player as? PlayerProxy)?.onUserLeaveHint()
492
+ }
493
+
494
+ override fun onPictureInPictureModeChanged(inPictureInPictureMode: Boolean) {
495
+ }
496
+
497
+ fun autoPauseByBackground() {
498
+ val player = this.player ?: return
499
+ if (player.live) {
500
+ player.stop()
501
+ } else {
502
+ player.pause()
503
+ }
504
+ pausedByBackground = true
505
+ }
506
+
507
+ private inner class ComponentListener : TextOutput, Player.Listener,
508
+ DefaultLifecycleObserver {
509
+ override fun onCues(cueGroup: CueGroup) {
510
+ subtitleView.setCues(cueGroup.cues)
511
+ }
512
+
513
+ @Deprecated("No longer used")
514
+ override fun onCues(cues: MutableList<Cue>) {
515
+ // only overridden because compiler is confused about multiple default implementations
516
+ }
517
+
518
+ override fun onVideoSizeChanged(videoSize: VideoSize) {
519
+ videoFrame.resizeMode = player?.scaleType.toResizeMode()
520
+ videoFrame.setAspectRatio(
521
+ when {
522
+ videoSize.height <= 0 -> 1f
523
+ else -> videoSize.width * videoSize.pixelWidthHeightRatio / videoSize.height
524
+ }
525
+ )
526
+ }
527
+
528
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
529
+ this@NpawPlayerView.keepScreenOn = isPlaying
530
+ }
531
+
532
+ override fun onStop(owner: LifecycleOwner) {
533
+ val player = this@NpawPlayerView.player ?: return
534
+ player.asPlayerExoPlayer()?.also {
535
+ detachSurface(it)
536
+ }
537
+ if (
538
+ !player.paused && player.state.source != null &&
539
+ player.castingType == CastingType.NONE
540
+ ) {
541
+ val shouldPause = when (player.continueInBackgroundMode) {
542
+ BackgroundMode.PAUSE -> true
543
+ BackgroundMode.PICTURE_IN_PICTURE -> when {
544
+ player.isCastingHost -> false
545
+ player.pictureInPicture -> false
546
+ else -> true // PiP disabled in system settings?
547
+ }
548
+ else -> false
549
+ }
550
+ if (shouldPause) {
551
+ autoPauseByBackground()
552
+ }
553
+ }
554
+ if (player is PlayerProxy) {
555
+ player.onLifecycleStop()
556
+ }
557
+ }
558
+
559
+ override fun onStart(owner: LifecycleOwner) {
560
+ val player = this@NpawPlayerView.player ?: return
561
+ if (pausedByBackground) {
562
+ pausedByBackground = false
563
+ player.play()
564
+ }
565
+ if (player is PlayerProxy) {
566
+ player.onLifecycleStart()
567
+ }
568
+ player.asPlayerExoPlayer()?.also {
569
+ if (player.castingType == CastingType.NONE) {
570
+ attachSurface(it)
571
+ }
572
+ }
573
+ }
574
+
575
+ override fun onDestroy(owner: LifecycleOwner) {
576
+ context.lifecycle?.removeObserver(this)
577
+ }
578
+ }
579
+ }
580
+ }
581
+
@@ -0,0 +1,27 @@
1
+ package com.twentyfouri.tvbridge.global.utils
2
+
3
+ import com.twentyfouri.player.base.PlayerFactoryBase
4
+
5
+ interface PlayerFactoryVersion {
6
+ val engineName: String
7
+ val engineVersion: String
8
+
9
+ private data class Fixed(
10
+ override val engineName: String,
11
+ override val engineVersion: String
12
+ ): PlayerFactoryVersion
13
+
14
+ companion object {
15
+ fun from(factory: PlayerFactoryBase): PlayerFactoryVersion {
16
+ (factory as? PlayerFactoryVersion)?.also { return it }
17
+
18
+ // fallback to just engine name
19
+ val engineName = factory.javaClass.simpleName
20
+ .replace("PlayerFactory", "")
21
+ .replace("Customized", "")
22
+ .lowercase()
23
+ val engineVersion = "0.0.0"
24
+ return Fixed(engineName, engineVersion)
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,32 @@
1
+ package com.twentyfouri.tvbridge.global.utils
2
+
3
+ import android.app.Activity
4
+ import android.app.Application.ActivityLifecycleCallbacks
5
+ import android.os.Bundle
6
+
7
+ abstract class SimpleActivityLifecycleCallback : ActivityLifecycleCallbacks {
8
+
9
+ override fun onActivityStarted(activity: Activity) {
10
+ //Do nothing
11
+ }
12
+
13
+ override fun onActivityResumed(activity: Activity) {
14
+ //Do nothing
15
+ }
16
+
17
+ override fun onActivityPaused(activity: Activity) {
18
+ //Do nothing
19
+ }
20
+
21
+ override fun onActivityStopped(activity: Activity) {
22
+ //Do nothing
23
+ }
24
+
25
+ override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {
26
+ //Do nothing
27
+ }
28
+
29
+ override fun onActivityDestroyed(activity: Activity) {
30
+ //Do nothing
31
+ }
32
+ }