@24i/smartapps-bigscreen-rnv-build-engine 5.0.0-alpha.16 → 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.
- package/build-freshness.js +158 -0
- package/dist/.build-freshness.json +1010 -0
- package/dist/templates/platforms/firetv/project/README.md +196 -0
- package/dist/templates/platforms/firetv/project/amazoniap/build.gradle +41 -0
- package/dist/templates/platforms/firetv/project/amazoniap/consumer-rules.pro +0 -0
- package/dist/templates/platforms/firetv/project/amazoniap/proguard-rules.pro +21 -0
- package/dist/templates/platforms/firetv/project/amazoniap/src/main/kotlin/com/twentyfouri/bridgetv/amazoniap/AmazonIapService.kt +91 -0
- package/dist/templates/platforms/firetv/project/amazoniap/src/main/kotlin/com/twentyfouri/bridgetv/amazoniap/AmazonIapServiceFactory.kt +13 -0
- package/dist/templates/platforms/firetv/project/app/build.gradle +138 -0
- package/dist/templates/platforms/firetv/project/app/proguard-rules.pro +47 -0
- package/dist/templates/platforms/firetv/project/app/src/bigscreenAppstage/kotlin/com/twentyfouri/tvbridge/Flavor.kt +9 -0
- package/dist/templates/platforms/firetv/project/app/src/bigscreenAppstage/res/raw/configuration.json +8 -0
- package/dist/templates/platforms/firetv/project/app/src/main/AndroidManifest.xml +52 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/FlavorBase.kt +9 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/bridge/BridgeFromJavaScript.kt +421 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/bridge/BridgeToJavaScript.kt +121 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/BridgeConfig.kt +19 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/ConfigCallback.kt +6 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/ConfigProvider.kt +27 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/Drm.kt +10 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/configuration/UserAgent.kt +6 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/BaseApplication.kt +73 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ActivityComponent.kt +13 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ActivityModule.kt +14 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ApplicationComponent.kt +23 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/ApplicationModule.kt +51 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/di/PerActivity.kt +26 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/LocalStorage.kt +30 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/LoggingDrmCallback.kt +38 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/OverriddenHttpMediaDrmCallback.kt +134 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryBitmovinCustomized.kt +19 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryConfigured.kt +37 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerCustomized.kt +106 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerNpaw.kt +581 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/PlayerFactoryVersion.kt +27 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/SimpleActivityLifecycleCallback.kt +32 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/global/utils/SourceDrmOverridden.kt +91 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/sound/TouchPlayer.kt +86 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/client/BridgeWebView.kt +82 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/client/BridgeWebViewClient.kt +114 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/AnalyticsPlaybackInfo.kt +10 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/ContentModel.kt +14 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/DrmModel.kt +20 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/model/MediaEventType.kt +31 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/parser/PlaybackInfoParser.kt +10 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/parser/SourceParser.kt +89 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/parser/TrackJsonModelParser.kt +58 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/view/Mainview.kt +18 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/view/PlayerCache.kt +15 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/view/WebViewActivity.kt +252 -0
- package/dist/templates/platforms/firetv/project/app/src/main/kotlin/com/twentyfouri/tvbridge/webview/viewmodel/WebViewModel.kt +748 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_background_focus.xml +10 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_background_normal.xml +7 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_background_pressed.xml +7 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/drawable/button_focus.xml +8 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/layout/activity_web_view.xml +28 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/raw/click.ogg +0 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/values/styles.xml +12 -0
- package/dist/templates/platforms/firetv/project/app/src/main/res/xml/network_security_config.xml +4 -0
- package/dist/templates/platforms/firetv/project/build.gradle +45 -0
- package/dist/templates/platforms/firetv/project/common/build.gradle +41 -0
- package/dist/templates/platforms/firetv/project/common/consumer-rules.pro +0 -0
- package/dist/templates/platforms/firetv/project/common/proguard-rules.pro +21 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/Util.kt +11 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapCallback.kt +11 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapException.kt +14 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapProduct.kt +16 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapPurchase.kt +9 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapService.kt +14 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/IapServiceFactory.kt +8 -0
- package/dist/templates/platforms/firetv/project/common/src/main/kotlin/com/twentyfouri/tvbridge/common/iap/NullIapService.kt +32 -0
- package/dist/templates/platforms/firetv/project/googleiap/build.gradle +41 -0
- package/dist/templates/platforms/firetv/project/googleiap/consumer-rules.pro +0 -0
- package/dist/templates/platforms/firetv/project/googleiap/proguard-rules.pro +21 -0
- package/dist/templates/platforms/firetv/project/googleiap/src/main/kotlin/com/twentyfouri/tvbridge/googleiap/GoogleIapService.kt +6 -0
- package/dist/templates/platforms/firetv/project/googleiap/src/main/kotlin/com/twentyfouri/tvbridge/googleiap/GoogleIapServiceFactory.kt +13 -0
- package/dist/templates/platforms/firetv/project/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/dist/templates/platforms/firetv/project/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/dist/templates/platforms/firetv/project/gradle.properties +4 -0
- package/dist/templates/platforms/firetv/project/gradlew +251 -0
- package/dist/templates/platforms/firetv/project/gradlew.bat +94 -0
- package/dist/templates/platforms/firetv/project/proguard-rules.pro +42 -0
- package/dist/templates/platforms/firetv/project/settings.gradle +4 -0
- package/index.js +2 -31
- package/package.json +7 -3
- package/project.json +96 -0
|
@@ -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
|
+
}
|