@24i/smartapps-bigscreen-rnv-build-engine 0.17.6-alpha.9 → 0.17.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -12
- package/dist/src/constants/buildProductionPackages.js +1 -0
- package/dist/src/constants/delopyLivePlatforms.js +2 -1
- package/dist/src/constants/platforms.js +2 -0
- package/dist/src/engine.js +10 -0
- package/dist/src/resolvers/index.js +3 -1
- package/dist/src/resolvers/resolveBaseUrl.js +5 -24
- package/dist/src/resolvers/resolveProductionZipFileRelativePath.js +1 -2
- package/dist/src/resolvers/resolveS3BucketAppPath.js +15 -0
- package/dist/src/resolvers/shared/constructFullUrl.js +25 -0
- package/dist/src/scripts/buildAllWithSchemes.js +23 -4
- package/dist/src/scripts/release/appCenter/deployProductionPackagesToAppCenter.js +20 -10
- package/dist/src/scripts/release/urlsMarkdown.js +2 -0
- package/dist/src/sdks/index.js +1 -0
- package/dist/src/sdks/sdk-androidtv/sdkAndroidtv.js +25 -9
- package/dist/src/sdks/sdk-tizen/sdkTizen.js +2 -0
- package/dist/src/sdks/sdk-webos/sdkWebos.js +2 -0
- package/dist/src/sdks/sdk-webpack/sdkWebpack.js +2 -0
- package/dist/src/tasks/deploy/aswS3/deployS3.js +3 -10
- package/dist/src/tasks/deploy/aswS3/distToLiveS3.js +4 -5
- package/dist/src/tasks/deploy/hbbtv/deployHbbtv.js +1 -1
- package/dist/src/tasks/task.rnv.build.js +16 -6
- package/dist/src/tasks/task.rnv.configure.js +2 -0
- package/dist/src/tasks/task.rnv.deploy.js +2 -0
- package/dist/src/tasks/task.rnv.platform.configure.js +33 -1
- package/dist/src/tasks/task.rnv.start.js +2 -0
- package/dist/src/utils/copyTheoPlayerLibs.js +48 -0
- package/dist/src/utils/zipStagingProdBuildsByPlatforms.js +3 -3
- package/dist/templates/platforms/androidtv/project/amazoniap/build.gradle +2 -2
- package/dist/templates/platforms/androidtv/project/app/build.gradle +17 -21
- package/dist/templates/platforms/androidtv/project/app/proguard-rules.pro +15 -0
- package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/res/raw/configuration.json +1 -2
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeFromJavaScript.kt +4 -58
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeToJavaScript.kt +15 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/BridgeConfig.kt +2 -1
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ActivityModule.kt +0 -3
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ApplicationModule.kt +2 -2
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/PerActivity.kt +0 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryConfigured.kt +33 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerCustomized.kt +7 -1
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryTheoPlayerCustomized.kt +14 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryVersion.kt +27 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/SourceDrmOverridden.kt +42 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/MediaEventType.kt +3 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/SourceParser.kt +89 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/Mainview.kt +0 -3
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/PlayerCache.kt +2 -2
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/WebViewActivity.kt +24 -83
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/viewmodel/WebViewModel.kt +48 -111
- package/dist/templates/platforms/androidtv/project/app/src/main/res/layout/activity_web_view.xml +23 -71
- package/dist/templates/platforms/androidtv/project/common/build.gradle +2 -2
- package/dist/templates/platforms/androidtv/project/googleiap/build.gradle +2 -2
- package/package.json +1 -1
- package/renative.engine.json +2 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/jump/JumpAnalytics.kt +0 -211
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/ContentModelParser.kt +0 -16
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/DeviceInfoParser.kt +0 -22
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.webview.parser
|
|
2
|
+
|
|
3
|
+
import com.twentyfouri.player.base.*
|
|
4
|
+
import com.twentyfouri.tvbridge.global.utils.SourceDrmOverridden
|
|
5
|
+
import org.json.JSONException
|
|
6
|
+
import org.json.JSONObject
|
|
7
|
+
import java.net.URI
|
|
8
|
+
import java.util.UUID
|
|
9
|
+
|
|
10
|
+
object SourceParser {
|
|
11
|
+
private val sourceSerializer = DefaultSourceSerializer()
|
|
12
|
+
|
|
13
|
+
fun stringToJson(jsonString: String?): Source? {
|
|
14
|
+
if (jsonString.isNullOrEmpty()) return null
|
|
15
|
+
if (jsonString.startsWith("http://") || jsonString.startsWith("https://")) {
|
|
16
|
+
return Source {
|
|
17
|
+
url = URI(jsonString)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
val jsonSource = JSONObject(jsonString)
|
|
23
|
+
if (jsonSource.has("streamUrl")) {
|
|
24
|
+
return parseContentModel(jsonSource)
|
|
25
|
+
}
|
|
26
|
+
return sourceSerializer.toSource(jsonSource)
|
|
27
|
+
|
|
28
|
+
} catch (e: JSONException) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun parseContentModel(jsonSource: JSONObject): Source? {
|
|
34
|
+
return Source {
|
|
35
|
+
val streamUrl = jsonSource.getString("streamUrl", null)
|
|
36
|
+
if (streamUrl.isNullOrEmpty()) return null
|
|
37
|
+
url = URI(streamUrl)
|
|
38
|
+
|
|
39
|
+
mimeType = jsonSource.getString("streamType", null)
|
|
40
|
+
?.let { MimeType.parse(it) }
|
|
41
|
+
|
|
42
|
+
jsonSource.getObject("drmConfig") { parseDrmModel(it) }
|
|
43
|
+
?.also { drm.add(it) }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun parseDrmModel(jsonDrm: JSONObject): SourceDrm? {
|
|
48
|
+
val drmSchemeUuid = jsonDrm.getString("drmSchemeUuid", null)
|
|
49
|
+
val drmLicenseUri = jsonDrm.getString("drmLicenseUrl", null)
|
|
50
|
+
?.let { URI(it) }
|
|
51
|
+
val drmCustomData = jsonDrm.getObject("drmCustomData") { parseSourceAuthorization (it) }
|
|
52
|
+
val overrideDrmCallback = jsonDrm.getBoolean("overrideDrmCallback")
|
|
53
|
+
|
|
54
|
+
return if (overrideDrmCallback) {
|
|
55
|
+
SourceDrmOverridden {
|
|
56
|
+
packageName = when (drmSchemeUuid) {
|
|
57
|
+
"widevine", WIDEVINE_UUID -> DrmWidevine.packageName
|
|
58
|
+
"playready", PLAYREADY_UUID -> DrmPlayready.packageName
|
|
59
|
+
else -> null
|
|
60
|
+
}
|
|
61
|
+
licenseServer = drmLicenseUri
|
|
62
|
+
authorization = drmCustomData
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
when (drmSchemeUuid) {
|
|
66
|
+
"widevine", WIDEVINE_UUID -> SourceDrmWidevine {
|
|
67
|
+
licenseServer = drmLicenseUri
|
|
68
|
+
authorization = drmCustomData
|
|
69
|
+
}
|
|
70
|
+
"playready", PLAYREADY_UUID -> SourceDrmPlayready {
|
|
71
|
+
licenseServer = drmLicenseUri
|
|
72
|
+
authorization = drmCustomData
|
|
73
|
+
}
|
|
74
|
+
else -> null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private fun parseSourceAuthorization(jsonMap: JSONObject): SourceAuthorization? {
|
|
80
|
+
return SourceAuthorizationHeaders {
|
|
81
|
+
jsonMap.keys().forEach { key ->
|
|
82
|
+
headers[key] = jsonMap.getString(key)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
val WIDEVINE_UUID = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
|
|
88
|
+
val PLAYREADY_UUID = "9a04f079-9840-4286-ab92-e65be0885f95"
|
|
89
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
package com.twentyfouri.tvbridge.webview.view
|
|
2
2
|
|
|
3
|
-
import com.twentyfouri.player.base.PlayerBase
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Created by destivar on 15/02/16.
|
|
7
5
|
*/
|
|
@@ -9,7 +7,6 @@ interface Mainview {
|
|
|
9
7
|
fun loadUrl(url: String?)
|
|
10
8
|
fun goFullscreen()
|
|
11
9
|
fun exitFullscreen(width: Int, height: Int, left: Int, top: Int)
|
|
12
|
-
fun setUrlToView(urlString: String?)
|
|
13
10
|
fun setBlackCurtainVisibility(visibility: Int)
|
|
14
11
|
fun showMessage(message: String?)
|
|
15
12
|
fun requestAudioFocus(): Int
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
package com.twentyfouri.tvbridge.webview.view
|
|
2
2
|
|
|
3
|
-
import com.twentyfouri.
|
|
3
|
+
import com.twentyfouri.player.base.Source
|
|
4
4
|
|
|
5
5
|
class PlayerCache {
|
|
6
6
|
enum class PlayerState {
|
|
@@ -11,5 +11,5 @@ class PlayerCache {
|
|
|
11
11
|
var savedPlayerPosition: Double = 0.0
|
|
12
12
|
var savedPlayerDuration: Double = 0.0
|
|
13
13
|
var savedPlayerState: PlayerState? = null
|
|
14
|
-
var
|
|
14
|
+
var savedSource: Source? = null
|
|
15
15
|
}
|
|
@@ -2,9 +2,6 @@ package com.twentyfouri.tvbridge.webview.view
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.app.Activity
|
|
5
|
-
import android.content.Intent
|
|
6
|
-
import android.graphics.Bitmap
|
|
7
|
-
import android.graphics.Canvas
|
|
8
5
|
import android.graphics.Point
|
|
9
6
|
import android.media.AudioManager
|
|
10
7
|
import android.media.AudioManager.OnAudioFocusChangeListener
|
|
@@ -14,10 +11,9 @@ import android.text.TextWatcher
|
|
|
14
11
|
import android.util.Log
|
|
15
12
|
import android.view.KeyEvent
|
|
16
13
|
import android.view.View
|
|
14
|
+
import android.view.View.MeasureSpec
|
|
17
15
|
import android.view.WindowManager
|
|
18
16
|
import android.view.inputmethod.InputMethodManager
|
|
19
|
-
import android.webkit.WebChromeClient
|
|
20
|
-
import android.webkit.WebView
|
|
21
17
|
import android.widget.RelativeLayout
|
|
22
18
|
import android.widget.Toast
|
|
23
19
|
import androidx.appcompat.app.AppCompatActivity
|
|
@@ -25,7 +21,6 @@ import androidx.databinding.DataBindingUtil
|
|
|
25
21
|
import androidx.media.AudioAttributesCompat
|
|
26
22
|
import androidx.media.AudioFocusRequestCompat
|
|
27
23
|
import androidx.media.AudioManagerCompat
|
|
28
|
-
import com.twentyfouri.tvbridge.BuildConfig
|
|
29
24
|
import com.twentyfouri.tvbridge.R
|
|
30
25
|
import com.twentyfouri.tvbridge.configuration.BridgeConfig
|
|
31
26
|
import com.twentyfouri.tvbridge.databinding.ActivityWebViewBinding
|
|
@@ -76,36 +71,14 @@ class WebViewActivity : AppCompatActivity(), Mainview {
|
|
|
76
71
|
CookieHandler.setDefault(defaultCookieManager)
|
|
77
72
|
}
|
|
78
73
|
|
|
79
|
-
binding =
|
|
80
|
-
binding.
|
|
81
|
-
binding.
|
|
74
|
+
binding = ActivityWebViewBinding.inflate(layoutInflater)
|
|
75
|
+
setContentView(binding.root)
|
|
76
|
+
binding.playerView.playerFactory = viewModel.playerFactory
|
|
82
77
|
viewModel.attachView(this)
|
|
83
78
|
viewModel.configureWebView(binding.webview)
|
|
84
|
-
viewModel.player = binding.
|
|
79
|
+
viewModel.player = binding.playerView.player
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
|
88
|
-
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
|
89
|
-
viewModel.appUrl = s.toString()
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
override fun afterTextChanged(s: Editable) {}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
val appUrl = config.baseUrl
|
|
96
|
-
if (config.customizableUrl) {
|
|
97
|
-
viewModel.urlSetterVisible = true
|
|
98
|
-
viewModel.appUrl = appUrl
|
|
99
|
-
setUrlToView(appUrl)
|
|
100
|
-
} else {
|
|
101
|
-
viewModel.urlSetterVisible = false
|
|
102
|
-
loadUrl(appUrl)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
binding.root.viewTreeObserver.addOnGlobalFocusChangeListener { oldFocus: View, newFocus: View ->
|
|
106
|
-
Log.v("FOCUS", "global focus changed old = $oldFocus")
|
|
107
|
-
Log.v("FOCUS", "global focus changed new = $newFocus")
|
|
108
|
-
}
|
|
81
|
+
loadUrl(config.baseUrl)
|
|
109
82
|
|
|
110
83
|
initialized = true
|
|
111
84
|
}
|
|
@@ -170,13 +143,12 @@ class WebViewActivity : AppCompatActivity(), Mainview {
|
|
|
170
143
|
public override fun onDestroy() {
|
|
171
144
|
if (initialized) {
|
|
172
145
|
viewModel.releasePlayer()
|
|
173
|
-
binding.unbind()
|
|
174
146
|
}
|
|
175
147
|
super.onDestroy()
|
|
176
148
|
}
|
|
177
149
|
|
|
178
150
|
override fun goFullscreen() {
|
|
179
|
-
val layoutParams = binding.
|
|
151
|
+
val layoutParams = binding.playerView.layoutParams as RelativeLayout.LayoutParams
|
|
180
152
|
layoutParams.width = -1
|
|
181
153
|
layoutParams.height = -1
|
|
182
154
|
layoutParams.leftMargin = 0
|
|
@@ -187,8 +159,12 @@ class WebViewActivity : AppCompatActivity(), Mainview {
|
|
|
187
159
|
display.getSize(size)
|
|
188
160
|
val width = size.x
|
|
189
161
|
val height = size.y
|
|
190
|
-
binding.
|
|
191
|
-
binding.
|
|
162
|
+
binding.playerView.layoutParams = layoutParams
|
|
163
|
+
binding.playerView.measure(
|
|
164
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
165
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
166
|
+
)
|
|
167
|
+
binding.playerView.layout(width, height, 0, 0)
|
|
192
168
|
}
|
|
193
169
|
|
|
194
170
|
override fun launchIapPurchase(sku: String) {
|
|
@@ -196,37 +172,22 @@ class WebViewActivity : AppCompatActivity(), Mainview {
|
|
|
196
172
|
}
|
|
197
173
|
|
|
198
174
|
override fun exitFullscreen(width: Int, height: Int, left: Int, top: Int) {
|
|
199
|
-
val layoutParams = binding.
|
|
175
|
+
val layoutParams = binding.playerView.layoutParams as RelativeLayout.LayoutParams
|
|
200
176
|
layoutParams.width = width
|
|
201
177
|
layoutParams.height = height
|
|
202
178
|
layoutParams.leftMargin = left
|
|
203
179
|
layoutParams.topMargin = top
|
|
204
180
|
|
|
205
181
|
//set the layout inner and outer sizes
|
|
206
|
-
binding.
|
|
207
|
-
binding.
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
override fun setUrlToView(urlString: String?) {
|
|
211
|
-
binding.url.requestFocus()
|
|
212
|
-
binding.url.setText(urlString)
|
|
213
|
-
binding.url.setSelection(urlString?.length ?: 0)
|
|
182
|
+
binding.playerView.layoutParams = layoutParams
|
|
183
|
+
binding.playerView.layout(width, height, left, top)
|
|
214
184
|
}
|
|
215
185
|
|
|
216
186
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
|
217
187
|
when (event.keyCode) {
|
|
218
|
-
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT -> changeFocus()
|
|
219
188
|
KeyEvent.KEYCODE_DPAD_CENTER -> goToUrl()
|
|
220
|
-
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BUTTON_B, KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_MENU, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_MEDIA_PAUSE, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, KeyEvent.KEYCODE_MEDIA_REWIND -> {
|
|
221
|
-
if (!binding.url.hasFocus() && !binding.buttonGo.hasFocus()) {
|
|
222
|
-
// Send these key presses to the WebView, through a JavaScript bridge.
|
|
223
|
-
viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_DOWN)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
189
|
else -> {
|
|
227
|
-
|
|
228
|
-
viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_DOWN)
|
|
229
|
-
}
|
|
190
|
+
viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_DOWN)
|
|
230
191
|
}
|
|
231
192
|
}
|
|
232
193
|
return super.onKeyDown(keyCode, event)
|
|
@@ -254,35 +215,19 @@ class WebViewActivity : AppCompatActivity(), Mainview {
|
|
|
254
215
|
}
|
|
255
216
|
|
|
256
217
|
private fun goToUrl() {
|
|
257
|
-
|
|
258
|
-
viewModel.onClickGo()
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
private fun changeFocus() {
|
|
263
|
-
if (binding.url.hasFocus()) {
|
|
264
|
-
binding.buttonGo.requestFocus()
|
|
265
|
-
} else if (binding.buttonGo.hasFocus()) {
|
|
266
|
-
binding.url.requestFocus()
|
|
267
|
-
}
|
|
218
|
+
viewModel.onClickGo()
|
|
268
219
|
}
|
|
269
220
|
|
|
270
221
|
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return true
|
|
275
|
-
}
|
|
276
|
-
return super.onKeyUp(keyCode, event)
|
|
222
|
+
// Send these key presses to the WebView, through a JavaScript bridge.
|
|
223
|
+
viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_UP)
|
|
224
|
+
return true
|
|
277
225
|
}
|
|
278
226
|
|
|
279
227
|
override fun onKeyLongPress(keyCode: Int, event: KeyEvent): Boolean {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return true
|
|
284
|
-
}
|
|
285
|
-
return super.onKeyLongPress(keyCode, event)
|
|
228
|
+
// Send these key presses to the WebView, through a JavaScript bridge.
|
|
229
|
+
viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_PRESS)
|
|
230
|
+
return true
|
|
286
231
|
}
|
|
287
232
|
|
|
288
233
|
override fun loadUrl(url: String?) {
|
|
@@ -294,10 +239,6 @@ class WebViewActivity : AppCompatActivity(), Mainview {
|
|
|
294
239
|
}
|
|
295
240
|
}
|
|
296
241
|
|
|
297
|
-
override fun onBackPressed() {
|
|
298
|
-
super.onBackPressed()
|
|
299
|
-
}
|
|
300
|
-
|
|
301
242
|
companion object {
|
|
302
243
|
private val defaultCookieManager: CookieManager = CookieManager().apply {
|
|
303
244
|
setCookiePolicy(CookiePolicy.ACCEPT_ALL)
|
|
@@ -5,7 +5,6 @@ import android.content.Context
|
|
|
5
5
|
import android.content.Intent
|
|
6
6
|
import android.graphics.Bitmap
|
|
7
7
|
import android.graphics.Canvas
|
|
8
|
-
import android.net.Uri
|
|
9
8
|
import android.os.Build
|
|
10
9
|
import android.util.Log
|
|
11
10
|
import android.view.KeyEvent
|
|
@@ -13,15 +12,9 @@ import android.view.View
|
|
|
13
12
|
import android.webkit.CookieManager
|
|
14
13
|
import android.webkit.WebChromeClient
|
|
15
14
|
import android.webkit.WebView
|
|
16
|
-
import androidx.databinding.BaseObservable
|
|
17
|
-
import androidx.databinding.Bindable
|
|
18
|
-
import com.google.android.exoplayer2.C
|
|
19
|
-
import com.google.android.exoplayer2.SeekParameters
|
|
20
15
|
import com.google.gson.JsonArray
|
|
21
16
|
import com.google.gson.JsonObject
|
|
22
17
|
import com.twentyfouri.player.base.*
|
|
23
|
-
import com.twentyfouri.player.exoplayer.PlayerExoPlayer
|
|
24
|
-
import com.twentyfouri.tvbridge.BR
|
|
25
18
|
import com.twentyfouri.tvbridge.BuildConfig
|
|
26
19
|
import com.twentyfouri.tvbridge.Flavor
|
|
27
20
|
import com.twentyfouri.tvbridge.bridge.BridgeFromJavaScript
|
|
@@ -29,14 +22,12 @@ import com.twentyfouri.tvbridge.bridge.BridgeToJavaScript
|
|
|
29
22
|
import com.twentyfouri.tvbridge.common.*
|
|
30
23
|
import com.twentyfouri.tvbridge.configuration.BridgeConfig
|
|
31
24
|
import com.twentyfouri.tvbridge.global.utils.LocalStorage
|
|
25
|
+
import com.twentyfouri.tvbridge.global.utils.PlayerFactoryVersion
|
|
32
26
|
import com.twentyfouri.tvbridge.global.utils.SourceDrmOverridden
|
|
33
|
-
import com.twentyfouri.tvbridge.jump.JumpAnalytics
|
|
34
27
|
import com.twentyfouri.tvbridge.webview.client.BridgeWebView
|
|
35
28
|
import com.twentyfouri.tvbridge.webview.client.BridgeWebViewClient
|
|
36
29
|
import com.twentyfouri.tvbridge.webview.client.BridgeWebViewClient.BridgeWebViewClientCallback
|
|
37
30
|
import com.twentyfouri.tvbridge.webview.model.AnalyticsPlaybackInfo
|
|
38
|
-
import com.twentyfouri.tvbridge.webview.model.ContentModel
|
|
39
|
-
import com.twentyfouri.tvbridge.webview.model.DrmModel
|
|
40
31
|
import com.twentyfouri.tvbridge.webview.model.MediaEventType
|
|
41
32
|
import com.twentyfouri.tvbridge.webview.view.Mainview
|
|
42
33
|
import com.twentyfouri.tvbridge.webview.view.PlayerCache
|
|
@@ -44,7 +35,6 @@ import org.json.JSONArray
|
|
|
44
35
|
import org.json.JSONException
|
|
45
36
|
import org.json.JSONObject
|
|
46
37
|
import java.net.NetworkInterface
|
|
47
|
-
import java.net.URI
|
|
48
38
|
import javax.inject.Inject
|
|
49
39
|
|
|
50
40
|
/**
|
|
@@ -56,7 +46,7 @@ class WebViewModel @Inject constructor(
|
|
|
56
46
|
private val config: BridgeConfig,
|
|
57
47
|
private val iapService: IapService,
|
|
58
48
|
val playerFactory: PlayerFactoryBase
|
|
59
|
-
) :
|
|
49
|
+
) : EventListener {
|
|
60
50
|
var player: PlayerBase? = null
|
|
61
51
|
private var view: Mainview? = null
|
|
62
52
|
private val jsBridge = BridgeToJavaScript(config) { s: String? ->
|
|
@@ -64,12 +54,9 @@ class WebViewModel @Inject constructor(
|
|
|
64
54
|
}
|
|
65
55
|
private val iapCallback = InnerIapCallback()
|
|
66
56
|
private val playerCache = PlayerCache()
|
|
67
|
-
private val jumpAnalytics = JumpAnalytics()
|
|
68
57
|
|
|
69
|
-
private var
|
|
58
|
+
private var source: Source? = null
|
|
70
59
|
|
|
71
|
-
val videoTrack: VideoTrack?
|
|
72
|
-
get() = player?.selectedVideoTrack
|
|
73
60
|
val audioTrack: AudioTrack?
|
|
74
61
|
get() = player?.selectedAudioTrack
|
|
75
62
|
val subtitleTrack: TextTrack?
|
|
@@ -77,17 +64,6 @@ class WebViewModel @Inject constructor(
|
|
|
77
64
|
|
|
78
65
|
var appUrl: String? = null
|
|
79
66
|
|
|
80
|
-
var urlSetterVisible: Boolean = false
|
|
81
|
-
set(value) {
|
|
82
|
-
if (field == value) return
|
|
83
|
-
field = value
|
|
84
|
-
notifyPropertyChanged(BR.urlSetterVisibility)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@get:Bindable
|
|
88
|
-
val urlSetterVisibility: Int
|
|
89
|
-
get() = if (urlSetterVisible) View.VISIBLE else View.GONE
|
|
90
|
-
|
|
91
67
|
fun attachView(view: Mainview?) {
|
|
92
68
|
this.view = view
|
|
93
69
|
if (view != null) {
|
|
@@ -142,52 +118,42 @@ class WebViewModel @Inject constructor(
|
|
|
142
118
|
|
|
143
119
|
fun onClickGo() {
|
|
144
120
|
view?.loadUrl(appUrl)
|
|
145
|
-
urlSetterVisible = false
|
|
146
121
|
if (BuildConfig.DEBUG) {
|
|
147
122
|
view?.hideKeyboard()
|
|
148
123
|
}
|
|
149
124
|
}
|
|
150
125
|
|
|
151
|
-
fun setVideo(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
private fun logContentModel(contentModel: ContentModel) {
|
|
157
|
-
Log.i("BridgeVideo", "Url: " + contentModel.streamUrl)
|
|
158
|
-
val drmModel = contentModel.drmModel
|
|
159
|
-
if (drmModel != null) {
|
|
160
|
-
Log.i("BridgeVideo", "DRM: " + drmModel.drmSchemeUuid)
|
|
161
|
-
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.drmLicenseUrl)
|
|
126
|
+
fun setVideo(source: Source) {
|
|
127
|
+
var adaptedSource = source
|
|
128
|
+
if (config.drm.shouldOverrideDrmCallback()) {
|
|
129
|
+
adaptedSource = SourceDrmOverridden.replaceSource(adaptedSource)
|
|
162
130
|
}
|
|
131
|
+
logSource(adaptedSource)
|
|
132
|
+
playDirectly(adaptedSource)
|
|
163
133
|
}
|
|
164
134
|
|
|
165
|
-
private fun
|
|
166
|
-
|
|
167
|
-
val drmModel =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
135
|
+
private fun logSource(source: Source) {
|
|
136
|
+
Log.i("BridgeVideo", "Url: ${source.url}")
|
|
137
|
+
when (val drmModel = source.drm.firstOrNull()) {
|
|
138
|
+
is SourceDrmWidevine -> {
|
|
139
|
+
Log.i("BridgeVideo", "DRM: " + drmModel.packageName)
|
|
140
|
+
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.licenseServer)
|
|
141
|
+
}
|
|
142
|
+
is SourceDrmPlayready -> {
|
|
143
|
+
Log.i("BridgeVideo", "DRM: " + drmModel.packageName)
|
|
144
|
+
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.licenseServer)
|
|
145
|
+
}
|
|
146
|
+
is SourceDrmOverridden -> {
|
|
147
|
+
Log.i("BridgeVideo", "DRM: " + drmModel.packageName)
|
|
148
|
+
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.licenseServer)
|
|
149
|
+
}
|
|
176
150
|
}
|
|
177
151
|
}
|
|
178
152
|
|
|
179
|
-
private fun playDirectly(
|
|
180
|
-
this.
|
|
181
|
-
(player as? PlayerExoPlayer)?.exoPlayer?.also { exoPlayer ->
|
|
182
|
-
jumpAnalytics.setVideoPlayer(exoPlayer)
|
|
183
|
-
exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC)
|
|
184
|
-
}
|
|
153
|
+
private fun playDirectly(source: Source) {
|
|
154
|
+
this.source = source
|
|
185
155
|
player?.also { player ->
|
|
186
|
-
player.source =
|
|
187
|
-
url = contentModel.streamUrl?.let { URI(it) }
|
|
188
|
-
mimeType = detectMimeType(contentModel)
|
|
189
|
-
contentModel.drmModel?.let { buildDrm(it) }?.also { drm.add(it) }
|
|
190
|
-
}
|
|
156
|
+
player.source = source
|
|
191
157
|
|
|
192
158
|
if (isLowEndDevice) {
|
|
193
159
|
player.trackRestrictions = player.trackRestrictions.buildUpon {
|
|
@@ -207,43 +173,6 @@ class WebViewModel @Inject constructor(
|
|
|
207
173
|
}
|
|
208
174
|
}
|
|
209
175
|
|
|
210
|
-
private fun detectMimeType(contentModel: ContentModel) =
|
|
211
|
-
when (contentModel.streamType) {
|
|
212
|
-
null, "" -> null
|
|
213
|
-
".mpd", "mpd", "dash" -> ManifestDash.mimeType
|
|
214
|
-
".ism", "ism", "ss" -> ManifestSmoothStreaming.mimeType
|
|
215
|
-
".m3u8", "m3u8", "hls" -> ManifestHls.mimeType
|
|
216
|
-
else -> null
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private fun buildDrm(drmModel: DrmModel): SourceDrm? {
|
|
220
|
-
val drmScheme = drmModel.drmSchemeUuid
|
|
221
|
-
val useOverride = drmModel.overrideDrmCallback ?: config.drm.shouldOverrideDrmCallback()
|
|
222
|
-
val useWidevine = drmScheme == "widevine" || drmScheme == C.WIDEVINE_UUID.toString()
|
|
223
|
-
val usePlayReady = drmScheme == "playready" || drmScheme == C.PLAYREADY_UUID.toString()
|
|
224
|
-
return when {
|
|
225
|
-
drmScheme == null -> null
|
|
226
|
-
useOverride -> SourceDrmOverridden {
|
|
227
|
-
packageName = when {
|
|
228
|
-
useWidevine -> DrmWidevine.packageName
|
|
229
|
-
usePlayReady -> DrmPlayready.packageName
|
|
230
|
-
else -> ""
|
|
231
|
-
}
|
|
232
|
-
licenseServer = drmModel.drmLicenseUrl?.let { URI(it) }
|
|
233
|
-
forceLicenseServer = true
|
|
234
|
-
}
|
|
235
|
-
useWidevine -> SourceDrmWidevine {
|
|
236
|
-
licenseServer = drmModel.drmLicenseUrl?.let { URI(it) }
|
|
237
|
-
forceLicenseServer = true
|
|
238
|
-
}
|
|
239
|
-
usePlayReady -> SourceDrmWidevine {
|
|
240
|
-
licenseServer = drmModel.drmLicenseUrl?.let { URI(it) }
|
|
241
|
-
forceLicenseServer = true
|
|
242
|
-
}
|
|
243
|
-
else -> SourceDrmUnrecognized(drmScheme)
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
176
|
private val isLowEndDevice: Boolean
|
|
248
177
|
get() = when (Build.MODEL) {
|
|
249
178
|
"AFTB" -> true
|
|
@@ -256,9 +185,9 @@ class WebViewModel @Inject constructor(
|
|
|
256
185
|
}
|
|
257
186
|
|
|
258
187
|
fun resumePlayer() {
|
|
259
|
-
val
|
|
260
|
-
if (playerCache.loadFromCache &&
|
|
261
|
-
setVideo(
|
|
188
|
+
val savedSource = playerCache.savedSource
|
|
189
|
+
if (playerCache.loadFromCache && savedSource != null && playerCache.savedPlayerState !== PlayerCache.PlayerState.PAUSED) {
|
|
190
|
+
setVideo(savedSource)
|
|
262
191
|
var position = playerCache.savedPlayerPosition
|
|
263
192
|
val player = this.player ?: return
|
|
264
193
|
if (player.live) {
|
|
@@ -283,26 +212,24 @@ class WebViewModel @Inject constructor(
|
|
|
283
212
|
|
|
284
213
|
fun stopPlayer() {
|
|
285
214
|
view?.setBlackCurtainVisibility(View.VISIBLE)
|
|
286
|
-
|
|
215
|
+
source = null
|
|
287
216
|
|
|
288
217
|
player?.also { player ->
|
|
289
218
|
player.removeEventListener(Event.ALL, this)
|
|
290
219
|
player.unload()
|
|
291
|
-
jumpAnalytics.setVideoPlayer(null)
|
|
292
220
|
}
|
|
293
221
|
}
|
|
294
222
|
|
|
295
223
|
fun releasePlayer() {
|
|
296
224
|
val player = this.player ?: return
|
|
297
|
-
if (
|
|
225
|
+
if (source != null) {
|
|
298
226
|
playerCache.loadFromCache = true
|
|
299
|
-
playerCache.
|
|
227
|
+
playerCache.savedSource = source
|
|
300
228
|
playerCache.savedPlayerDuration = player.duration
|
|
301
229
|
playerCache.savedPlayerPosition = player.currentTime
|
|
302
230
|
playerCache.savedPlayerState = if (player.playing) PlayerCache.PlayerState.PLAYING else PlayerCache.PlayerState.PAUSED
|
|
303
231
|
}
|
|
304
232
|
player.unload()
|
|
305
|
-
jumpAnalytics.setVideoPlayer(null)
|
|
306
233
|
}
|
|
307
234
|
|
|
308
235
|
val currentPosition: Double
|
|
@@ -317,7 +244,7 @@ class WebViewModel @Inject constructor(
|
|
|
317
244
|
val isPlayerPlaying: Boolean
|
|
318
245
|
get() = player?.playing == true
|
|
319
246
|
val isInPlayer: Boolean
|
|
320
|
-
get() = player != null &&
|
|
247
|
+
get() = player != null && source != null
|
|
321
248
|
|
|
322
249
|
fun loadUrl(url: String?) {
|
|
323
250
|
view?.loadUrl(url)
|
|
@@ -359,6 +286,7 @@ class WebViewModel @Inject constructor(
|
|
|
359
286
|
}
|
|
360
287
|
is LoadedMetadataEvent -> {
|
|
361
288
|
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.LOADEDMETADATA)
|
|
289
|
+
jsBridge.bridgeAndroidToJavaScript_MediaTrackEvent(MediaEventType.TRACKS_CHANGED, player.audioTracks, player.textTracks)
|
|
362
290
|
}
|
|
363
291
|
is CanPlayEvent -> {
|
|
364
292
|
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.CANPLAY)
|
|
@@ -389,13 +317,21 @@ class WebViewModel @Inject constructor(
|
|
|
389
317
|
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.PAUSE)
|
|
390
318
|
}
|
|
391
319
|
}
|
|
392
|
-
is LoadedMetadataEvent,
|
|
393
320
|
is AddAudioTrackEvent, is AddTextTrackEvent,
|
|
394
321
|
is RemoveAudioTrackEvent, is RemoveTextTrackEvent,
|
|
395
322
|
is ChangeAudioTrackEvent, is ChangeTextTrackEvent -> {
|
|
396
323
|
Log.d("WebViewModel", "onTracksChanged: audio=$audioTrack subtitle=$subtitleTrack")
|
|
397
324
|
jsBridge.bridgeAndroidToJavaScript_MediaTrackEvent(MediaEventType.TRACKS_CHANGED, player.audioTracks, player.textTracks)
|
|
398
325
|
}
|
|
326
|
+
is AdBreakStartedEvent -> {
|
|
327
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.AD_BREAK_START)
|
|
328
|
+
}
|
|
329
|
+
is AdBreakEndedEvent -> {
|
|
330
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.AD_BREAK_END)
|
|
331
|
+
}
|
|
332
|
+
is AdBreaksRescheduledEvent -> {
|
|
333
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdBreaksEvent(MediaEventType.AD_BREAKS_RESCHEDULED, player.timeline)
|
|
334
|
+
}
|
|
399
335
|
}
|
|
400
336
|
}
|
|
401
337
|
|
|
@@ -424,9 +360,7 @@ class WebViewModel @Inject constructor(
|
|
|
424
360
|
}
|
|
425
361
|
|
|
426
362
|
fun setPlaybackInfo(playbackInfo: AnalyticsPlaybackInfo) {
|
|
427
|
-
|
|
428
|
-
jumpAnalytics.setPlaybackInfo(playbackInfo)
|
|
429
|
-
}
|
|
363
|
+
// previously used for Jump analytics, not supported with THEO
|
|
430
364
|
}
|
|
431
365
|
|
|
432
366
|
fun showMessage(message: String?) {
|
|
@@ -524,11 +458,14 @@ class WebViewModel @Inject constructor(
|
|
|
524
458
|
fun detectDeviceInfo(): JSONObject {
|
|
525
459
|
val deviceInfo = JSONObject()
|
|
526
460
|
try {
|
|
461
|
+
val engineVersion = PlayerFactoryVersion.from(playerFactory)
|
|
527
462
|
deviceInfo.put("deviceBrand", Build.MANUFACTURER)
|
|
528
463
|
deviceInfo.put("deviceModel", Build.MODEL)
|
|
529
464
|
deviceInfo.put("osVersion", Build.VERSION.RELEASE)
|
|
530
465
|
deviceInfo.put("appFlavor", BuildConfig.FLAVOR)
|
|
531
466
|
deviceInfo.put("appVersion", BuildConfig.VERSION_NAME)
|
|
467
|
+
deviceInfo.put("engineName", engineVersion.engineName)
|
|
468
|
+
deviceInfo.put("engineVersion", engineVersion.engineVersion)
|
|
532
469
|
|
|
533
470
|
val drmKeySystems = mutableSetOf<String>()
|
|
534
471
|
playerFactory.getSupportedFormats(context).manifest.forEach { manifest ->
|