@24i/smartapps-bigscreen-rnv-build-engine 0.17.2 → 0.17.3-alpha.1
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/dist/src/__tests__/resolvers/resolveBuildFileBaseName.spec.js +80 -0
- package/dist/src/resolvers/index.js +3 -1
- package/dist/src/resolvers/resolveBuildFileBaseName.js +34 -0
- package/dist/src/resolvers/resolveBuildFileName.js +3 -27
- package/dist/src/scripts/releaseAllWithScheme.js +1 -1
- package/dist/src/sdks/index.js +5 -1
- package/dist/src/sdks/sdk-androidtv/index.js +17 -0
- package/dist/src/sdks/sdk-androidtv/sdkAndroidtv.js +264 -0
- package/dist/src/sdks/sdk-utils/sdk-installer/services/unzip.js +3 -2
- package/dist/src/tasks/task.rnv.build.js +3 -1
- package/dist/src/tasks/task.rnv.configure.js +3 -1
- package/dist/src/tasks/task.rnv.platform.configure.js +4 -1
- package/dist/templates/platforms/_shared/template.js +3 -3
- package/dist/templates/platforms/androidtv/project/README.md +196 -0
- package/dist/templates/platforms/androidtv/project/amazoniap/build.gradle +37 -0
- package/dist/templates/platforms/androidtv/project/amazoniap/consumer-rules.pro +0 -0
- package/dist/templates/platforms/androidtv/project/amazoniap/libs/in-app-purchasing-2.0.76.jar +0 -0
- package/dist/templates/platforms/androidtv/project/amazoniap/proguard-rules.pro +21 -0
- package/dist/templates/platforms/androidtv/project/amazoniap/src/main/AndroidManifest.xml +4 -0
- package/dist/templates/platforms/androidtv/project/amazoniap/src/main/java/com/twentyfouri/bridgetv/amazoniap/AmazonIapSevice.kt +106 -0
- package/dist/templates/platforms/androidtv/project/app/build.gradle +112 -0
- package/dist/templates/platforms/androidtv/project/app/proguard-rules.pro +32 -0
- package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/java/com/twentyfouri/tvbridge/Flavor.kt +5 -0
- package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/res/raw/configuration.json +9 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/AndroidManifest.xml +53 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/FlavorBase.kt +12 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeFromJavaScript.kt +392 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeToJavaScript.kt +89 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/BridgeConfig.kt +16 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/ConfigCallback.kt +6 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/ConfigProvider.kt +27 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/Drm.kt +10 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/UserAgent.kt +6 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/BaseApplication.kt +73 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ActivityComponent.kt +13 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ActivityModule.kt +17 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ApplicationComponent.kt +26 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ApplicationModule.kt +51 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/PerActivity.kt +26 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/LocalStorage.kt +30 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/LoggingDrmCallback.kt +38 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/OverriddenHttpMediaDrmCallback.kt +134 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerCustomized.kt +84 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/SimpleActivityLifecycleCallback.kt +32 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/SourceDrmOverridden.kt +49 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/jump/JumpAnalytics.kt +211 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/sound/TouchPlayer.kt +86 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/client/BridgeWebView.kt +82 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/client/BridgeWebViewClient.kt +114 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/AnalyticsPlaybackInfo.kt +10 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/ContentModel.kt +14 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/DrmModel.kt +20 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/MediaEventType.kt +20 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/ContentModelParser.kt +16 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/DeviceInfoParser.kt +22 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/PlaybackInfoParser.kt +10 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/TrackJsonModelParser.kt +58 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/Mainview.kt +20 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/PlayerCache.kt +15 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/WebViewActivity.kt +306 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/viewmodel/WebViewModel.kt +609 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_background_focus.xml +10 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_background_normal.xml +7 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_background_pressed.xml +7 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_focus.xml +8 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/layout/activity_web_view.xml +76 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/raw/click.ogg +0 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/values/styles.xml +12 -0
- package/dist/templates/platforms/androidtv/project/app/src/main/res/xml/network_security_config.xml +4 -0
- package/dist/templates/platforms/androidtv/project/build.gradle +48 -0
- package/dist/templates/platforms/androidtv/project/common/build.gradle +36 -0
- package/dist/templates/platforms/androidtv/project/common/consumer-rules.pro +0 -0
- package/dist/templates/platforms/androidtv/project/common/proguard-rules.pro +21 -0
- package/dist/templates/platforms/androidtv/project/common/src/main/AndroidManifest.xml +4 -0
- package/dist/templates/platforms/androidtv/project/common/src/main/java/com/twentyfouri/tvbridge/common/IapService.kt +114 -0
- package/dist/templates/platforms/androidtv/project/googleiap/build.gradle +38 -0
- package/dist/templates/platforms/androidtv/project/googleiap/consumer-rules.pro +0 -0
- package/dist/templates/platforms/androidtv/project/googleiap/proguard-rules.pro +21 -0
- package/dist/templates/platforms/androidtv/project/googleiap/src/main/AndroidManifest.xml +6 -0
- package/dist/templates/platforms/androidtv/project/googleiap/src/main/java/com/twentyfouri/tvbridge/googleiap/GoogleIapService.kt +29 -0
- package/dist/templates/platforms/androidtv/project/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/dist/templates/platforms/androidtv/project/gradle/wrapper/gradle-wrapper.properties +6 -0
- package/dist/templates/platforms/androidtv/project/gradle.properties +4 -0
- package/dist/templates/platforms/androidtv/project/gradlew +185 -0
- package/dist/templates/platforms/androidtv/project/gradlew.bat +104 -0
- package/dist/templates/platforms/androidtv/project/proguard-rules.pro +42 -0
- package/dist/templates/platforms/androidtv/project/settings.gradle +4 -0
- package/package.json +3 -2
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.di
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.twentyfouri.player.base.PlayerFactoryBase
|
|
5
|
+
import com.twentyfouri.tvbridge.Flavor
|
|
6
|
+
import com.twentyfouri.tvbridge.common.NullIapService
|
|
7
|
+
import com.twentyfouri.tvbridge.configuration.BridgeConfig
|
|
8
|
+
import com.twentyfouri.tvbridge.global.BaseApplication
|
|
9
|
+
import com.twentyfouri.tvbridge.global.utils.LocalStorage
|
|
10
|
+
import com.twentyfouri.tvbridge.global.utils.PlayerFactoryExoPlayerCustomized
|
|
11
|
+
import com.twentyfouri.tvbridge.sound.TouchPlayer
|
|
12
|
+
import dagger.Module
|
|
13
|
+
import dagger.Provides
|
|
14
|
+
import javax.inject.Singleton
|
|
15
|
+
|
|
16
|
+
@Module
|
|
17
|
+
class ApplicationModule(
|
|
18
|
+
private val baseApplication: BaseApplication,
|
|
19
|
+
private val configuration: BridgeConfig
|
|
20
|
+
) {
|
|
21
|
+
@Provides
|
|
22
|
+
fun getApplication() = baseApplication
|
|
23
|
+
|
|
24
|
+
@Provides
|
|
25
|
+
fun getConfiguration() = configuration
|
|
26
|
+
|
|
27
|
+
@Provides
|
|
28
|
+
fun provideApplicationContext(): Context = baseApplication
|
|
29
|
+
|
|
30
|
+
@Singleton
|
|
31
|
+
@Provides
|
|
32
|
+
fun provideLocalStorage() = LocalStorage(baseApplication.applicationContext)
|
|
33
|
+
|
|
34
|
+
@Singleton
|
|
35
|
+
@Provides
|
|
36
|
+
fun provideTouchPlayer() = TouchPlayer(baseApplication)
|
|
37
|
+
|
|
38
|
+
@Singleton
|
|
39
|
+
@Provides
|
|
40
|
+
fun provideIapService() = Flavor()
|
|
41
|
+
.getIapServiceFactories()
|
|
42
|
+
.firstOrNull { it.detectSupport(baseApplication) }
|
|
43
|
+
?.build(baseApplication)
|
|
44
|
+
?: NullIapService()
|
|
45
|
+
|
|
46
|
+
@Provides
|
|
47
|
+
@Singleton
|
|
48
|
+
fun providePlayerFactory(config: BridgeConfig): PlayerFactoryBase =
|
|
49
|
+
PlayerFactoryExoPlayerCustomized(config)
|
|
50
|
+
|
|
51
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.di
|
|
2
|
+
|
|
3
|
+
import javax.inject.Scope
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Copyright (C) 2013 Square, Inc.
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
* you may not use this file except in compliance with the License.
|
|
10
|
+
* You may obtain a copy of the License at
|
|
11
|
+
*
|
|
12
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
* See the License for the specific language governing permissions and
|
|
18
|
+
* limitations under the License.
|
|
19
|
+
*/ /**
|
|
20
|
+
* A scoping annotation to permit objects whose lifetime should
|
|
21
|
+
* conform to the life of the activity to be memoized in the
|
|
22
|
+
* correct component.
|
|
23
|
+
*/
|
|
24
|
+
@Scope
|
|
25
|
+
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
|
|
26
|
+
annotation class PerActivity
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.utils
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.SharedPreferences
|
|
5
|
+
|
|
6
|
+
class LocalStorage(context: Context) {
|
|
7
|
+
|
|
8
|
+
private val preferences: SharedPreferences =
|
|
9
|
+
context.applicationContext.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE)
|
|
10
|
+
|
|
11
|
+
fun saveCookieJson(key: String?, value: String?) {
|
|
12
|
+
val editor = preferences.edit()
|
|
13
|
+
editor.putString(key, value)
|
|
14
|
+
editor.apply()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fun removeFromLocalStorage(key: String?) {
|
|
18
|
+
val editor = preferences.edit()
|
|
19
|
+
editor.remove(key)
|
|
20
|
+
editor.apply()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fun getFromLocalStorage(key: String?): String? {
|
|
24
|
+
return preferences.getString(key, null)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
companion object {
|
|
28
|
+
private const val SHARED_PREFERENCES = "com.twentyfouri.tvbridge"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.utils
|
|
2
|
+
|
|
3
|
+
import android.util.Base64
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.google.android.exoplayer2.drm.ExoMediaDrm
|
|
6
|
+
import com.google.android.exoplayer2.drm.MediaDrmCallback
|
|
7
|
+
import java.util.*
|
|
8
|
+
|
|
9
|
+
class LoggingDrmCallback(
|
|
10
|
+
private val base: MediaDrmCallback,
|
|
11
|
+
private val licenseUrl: String? = null
|
|
12
|
+
) : MediaDrmCallback {
|
|
13
|
+
override fun executeProvisionRequest(
|
|
14
|
+
uuid: UUID,
|
|
15
|
+
request: ExoMediaDrm.ProvisionRequest
|
|
16
|
+
): ByteArray {
|
|
17
|
+
return base.executeProvisionRequest(uuid, request)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override fun executeKeyRequest(uuid: UUID, request: ExoMediaDrm.KeyRequest): ByteArray {
|
|
21
|
+
if (com.twentyfouri.player.exoplayer.BuildConfig.DEBUG) {
|
|
22
|
+
Log.v(TAG, "DRM Embedded URL: ${request.licenseServerUrl}")
|
|
23
|
+
Log.v(TAG, "DRM Default URL: $licenseUrl")
|
|
24
|
+
Log.v(TAG, "DRM request:\n${request.data.toBase64()}")
|
|
25
|
+
}
|
|
26
|
+
val response = base.executeKeyRequest(uuid, request)
|
|
27
|
+
if (com.twentyfouri.player.exoplayer.BuildConfig.DEBUG) {
|
|
28
|
+
Log.v(TAG, "DRM response:\n" + response.toBase64())
|
|
29
|
+
}
|
|
30
|
+
return response
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun ByteArray.toBase64(): String = Base64.encodeToString(this, Base64.DEFAULT)
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
const val TAG = "DrmCallback"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.utils
|
|
2
|
+
|
|
3
|
+
import android.net.Uri
|
|
4
|
+
import android.util.Base64
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.google.android.exoplayer2.C
|
|
7
|
+
import com.google.android.exoplayer2.drm.ExoMediaDrm
|
|
8
|
+
import com.google.android.exoplayer2.drm.MediaDrmCallback
|
|
9
|
+
import com.google.android.exoplayer2.upstream.DataSourceInputStream
|
|
10
|
+
import com.google.android.exoplayer2.upstream.DataSpec
|
|
11
|
+
import com.google.android.exoplayer2.upstream.HttpDataSource
|
|
12
|
+
import com.google.android.exoplayer2.util.Util
|
|
13
|
+
import com.twentyfouri.tvbridge.BuildConfig
|
|
14
|
+
import org.json.JSONException
|
|
15
|
+
import org.json.JSONObject
|
|
16
|
+
import java.io.BufferedReader
|
|
17
|
+
import java.io.IOException
|
|
18
|
+
import java.io.InputStreamReader
|
|
19
|
+
import java.net.MalformedURLException
|
|
20
|
+
import java.net.URL
|
|
21
|
+
import java.net.URLEncoder
|
|
22
|
+
import java.util.HashMap
|
|
23
|
+
import java.util.UUID
|
|
24
|
+
|
|
25
|
+
class OverriddenHttpMediaDrmCallback(
|
|
26
|
+
private val defaultLicenseUrl: String?,
|
|
27
|
+
private val forceDefaultLicenseUrl: Boolean,
|
|
28
|
+
private val dataSourceFactory: HttpDataSource.Factory
|
|
29
|
+
) : MediaDrmCallback {
|
|
30
|
+
|
|
31
|
+
@Throws(IOException::class)
|
|
32
|
+
override fun executeProvisionRequest(uuid: UUID, request: ExoMediaDrm.ProvisionRequest): ByteArray {
|
|
33
|
+
Log.d(TAG, "executeProvisionRequest")
|
|
34
|
+
val url = request.defaultUrl + "&signedRequest=" + String(request.data)
|
|
35
|
+
return executePost(dataSourceFactory, url, ByteArray(0), null)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Throws(Exception::class)
|
|
39
|
+
override fun executeKeyRequest(uuid: UUID, request: ExoMediaDrm.KeyRequest): ByteArray {
|
|
40
|
+
Log.d(TAG, "executeKeyRequest")
|
|
41
|
+
var url: String? = request.licenseServerUrl
|
|
42
|
+
if (forceDefaultLicenseUrl || url.isNullOrEmpty()) {
|
|
43
|
+
url = defaultLicenseUrl
|
|
44
|
+
}
|
|
45
|
+
val requestProperties: MutableMap<String, String> = HashMap()
|
|
46
|
+
// Add standard request properties for supported schemes.
|
|
47
|
+
val contentType = "text/plain"
|
|
48
|
+
requestProperties["Content-Type"] = contentType
|
|
49
|
+
if (C.PLAYREADY_UUID == uuid) {
|
|
50
|
+
requestProperties["SOAPAction"] = "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//adding data to url
|
|
54
|
+
val challenge = request.data
|
|
55
|
+
val encoded = URLEncoder.encode(Base64.encodeToString(challenge, Base64.NO_WRAP), "UTF-8")
|
|
56
|
+
url = appendQuery(url, "_widevineChallenge=$encoded")
|
|
57
|
+
if (BuildConfig.DEBUG) {
|
|
58
|
+
Log.d(TAG, "Executing post on url $url with content type $contentType")
|
|
59
|
+
}
|
|
60
|
+
return executeGet(url)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
companion object {
|
|
64
|
+
private const val TAG = "DrmCallback"
|
|
65
|
+
|
|
66
|
+
private fun executeGet(urlString: String): ByteArray {
|
|
67
|
+
try {
|
|
68
|
+
val licenserRequest = URL(urlString)
|
|
69
|
+
val yc = licenserRequest.openConnection()
|
|
70
|
+
val input = BufferedReader(InputStreamReader(yc.getInputStream()))
|
|
71
|
+
val builder = StringBuilder()
|
|
72
|
+
var inputLine: String?
|
|
73
|
+
while (input.readLine().also { inputLine = it } != null) {
|
|
74
|
+
builder.append(inputLine)
|
|
75
|
+
}
|
|
76
|
+
input.close()
|
|
77
|
+
val jsonObject = JSONObject(builder.toString())
|
|
78
|
+
val container = jsonObject.getJSONObject("getWidevineLicenseResponse")
|
|
79
|
+
val license = container.getString("license")
|
|
80
|
+
if (BuildConfig.DEBUG) {
|
|
81
|
+
Log.d(TAG, "Finished post: $license")
|
|
82
|
+
}
|
|
83
|
+
return Base64.decode(license, Base64.NO_WRAP)
|
|
84
|
+
} catch (e: MalformedURLException) {
|
|
85
|
+
e.printStackTrace()
|
|
86
|
+
} catch (e: IOException) {
|
|
87
|
+
e.printStackTrace()
|
|
88
|
+
} catch (e: JSONException) {
|
|
89
|
+
e.printStackTrace()
|
|
90
|
+
}
|
|
91
|
+
return byteArrayOf()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@Throws(IOException::class)
|
|
95
|
+
private fun executePost(
|
|
96
|
+
dataSourceFactory: HttpDataSource.Factory,
|
|
97
|
+
url: String,
|
|
98
|
+
data: ByteArray,
|
|
99
|
+
requestProperties: Map<String, String>?
|
|
100
|
+
): ByteArray {
|
|
101
|
+
Log.d(TAG, "executePost")
|
|
102
|
+
val dataSource = dataSourceFactory.createDataSource()
|
|
103
|
+
if (requestProperties != null) {
|
|
104
|
+
for ((key, value) in requestProperties) {
|
|
105
|
+
dataSource.setRequestProperty(key, value)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
val dataSpec = DataSpec.Builder()
|
|
109
|
+
.setUri(Uri.parse(url))
|
|
110
|
+
.setFlags(DataSpec.FLAG_ALLOW_GZIP)
|
|
111
|
+
.setHttpMethod(DataSpec.HTTP_METHOD_POST)
|
|
112
|
+
.setHttpBody(data)
|
|
113
|
+
.setHttpRequestHeaders(requestProperties ?: emptyMap())
|
|
114
|
+
.build()
|
|
115
|
+
val inputStream = DataSourceInputStream(dataSource, dataSpec)
|
|
116
|
+
return try {
|
|
117
|
+
Util.toByteArray(inputStream)
|
|
118
|
+
} finally {
|
|
119
|
+
Util.closeQuietly(inputStream)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
fun appendQuery(url: String?, appendQuery: String): String {
|
|
124
|
+
val oldUri = Uri.parse(url)
|
|
125
|
+
var newQuery = oldUri.query
|
|
126
|
+
if (newQuery == null) {
|
|
127
|
+
newQuery = appendQuery
|
|
128
|
+
} else {
|
|
129
|
+
newQuery += "&$appendQuery"
|
|
130
|
+
}
|
|
131
|
+
return oldUri.scheme + "://" + oldUri.authority + oldUri.path + "?" + newQuery
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.utils
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.google.android.exoplayer2.C
|
|
5
|
+
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager
|
|
6
|
+
import com.google.android.exoplayer2.drm.DrmSessionManager
|
|
7
|
+
import com.google.android.exoplayer2.drm.FrameworkMediaDrm
|
|
8
|
+
import com.google.android.exoplayer2.drm.MediaDrmCallback
|
|
9
|
+
import com.google.android.exoplayer2.upstream.HttpDataSource
|
|
10
|
+
import com.google.android.exoplayer2.util.Util
|
|
11
|
+
import com.twentyfouri.player.base.DrmPlayready
|
|
12
|
+
import com.twentyfouri.player.base.DrmWidevine
|
|
13
|
+
import com.twentyfouri.player.base.Source
|
|
14
|
+
import com.twentyfouri.player.base.SourceDrm
|
|
15
|
+
import com.twentyfouri.player.exoplayer.*
|
|
16
|
+
import com.twentyfouri.tvbridge.configuration.BridgeConfig
|
|
17
|
+
|
|
18
|
+
class PlayerFactoryExoPlayerCustomized(
|
|
19
|
+
private val config: BridgeConfig
|
|
20
|
+
) : PlayerFactoryExoPlayer() {
|
|
21
|
+
override fun createSharedComponents(context: Context): SharedComponents =
|
|
22
|
+
CustomizedSharedComponents(context)
|
|
23
|
+
|
|
24
|
+
private inner class CustomizedSharedComponents(context: Context) : DefaultSharedComponents(context) {
|
|
25
|
+
|
|
26
|
+
override val drmProvider: DrmSessionManagerProvider by lazy { CustomizedDrmSessionManagerProvider() }
|
|
27
|
+
|
|
28
|
+
override val applicationName: String
|
|
29
|
+
get() = config.playerAppName ?: super.applicationName
|
|
30
|
+
|
|
31
|
+
override fun buildIsolatedComponents(): PlayerComponents {
|
|
32
|
+
return CustomizedPlayerComponents(this)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private inner class CustomizedPlayerComponents(shared: SharedComponents) : DefaultPlayerComponents(shared) {
|
|
37
|
+
override val handleAudioFocus: Boolean
|
|
38
|
+
get() = false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private class CustomizedDrmSessionManagerProvider : DefaultDrmSessionManagerProvider() {
|
|
42
|
+
override fun buildDrmSessionManager(
|
|
43
|
+
dataSourceFactory: AuthorizedDataSourceProvider,
|
|
44
|
+
source: Source,
|
|
45
|
+
sourceDrm: SourceDrm
|
|
46
|
+
): DrmSessionManager? {
|
|
47
|
+
if (sourceDrm is SourceDrmOverridden) {
|
|
48
|
+
return buildDrmOverridden(dataSourceFactory, sourceDrm)
|
|
49
|
+
} else {
|
|
50
|
+
return super.buildDrmSessionManager(dataSourceFactory, source, sourceDrm)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private fun buildDrmOverridden(dataSourceFactory: AuthorizedDataSourceProvider, sourceDrm: SourceDrmOverridden): DrmSessionManager? {
|
|
55
|
+
val drmUuid = when (sourceDrm.packageName) {
|
|
56
|
+
DrmWidevine.packageName -> C.WIDEVINE_UUID
|
|
57
|
+
DrmPlayready.packageName -> C.PLAYREADY_UUID
|
|
58
|
+
else -> C.UUID_NIL
|
|
59
|
+
}
|
|
60
|
+
val licenseUrl = sourceDrm.licenseServer?.toString() ?: ""
|
|
61
|
+
val forceLicenseUrl = sourceDrm.forceLicenseServer
|
|
62
|
+
val httpDataSourceFactory = dataSourceFactory.buildHttpDataSourceFactory(sourceDrm.authorization)
|
|
63
|
+
|
|
64
|
+
val drmCallback = LoggingDrmCallback(
|
|
65
|
+
OverriddenHttpMediaDrmCallback(
|
|
66
|
+
licenseUrl,
|
|
67
|
+
forceLicenseUrl,
|
|
68
|
+
httpDataSourceFactory
|
|
69
|
+
),
|
|
70
|
+
licenseUrl
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return DefaultDrmSessionManager.Builder()
|
|
74
|
+
.setUuidAndExoMediaDrmProvider(drmUuid, FrameworkMediaDrm.DEFAULT_PROVIDER)
|
|
75
|
+
.setMultiSession(false)
|
|
76
|
+
.build(drmCallback)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override fun buildDrmCallback(licenseUrl: String, forceLicenseUrl: Boolean, httpDataSourceFactory: HttpDataSource.Factory): MediaDrmCallback {
|
|
80
|
+
return LoggingDrmCallback(super.buildDrmCallback(licenseUrl, forceLicenseUrl, httpDataSourceFactory), licenseUrl)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.global.utils
|
|
2
|
+
|
|
3
|
+
import com.twentyfouri.player.base.SourceAuthorization
|
|
4
|
+
import com.twentyfouri.player.base.SourceDrm
|
|
5
|
+
import java.net.URI
|
|
6
|
+
|
|
7
|
+
open class SourceDrmOverridden protected constructor(
|
|
8
|
+
builder: Builder
|
|
9
|
+
) : SourceDrm() {
|
|
10
|
+
|
|
11
|
+
override val packageName: String = builder.packageName
|
|
12
|
+
?: throw IllegalStateException("Missing SourceDrmOverridden.packageName")
|
|
13
|
+
val licenseServer: URI? = builder.licenseServer
|
|
14
|
+
val forceLicenseServer: Boolean = builder.forceLicenseServer
|
|
15
|
+
val authorization: SourceAuthorization? = builder.authorization
|
|
16
|
+
|
|
17
|
+
override fun hashCode(): Int = licenseServer.hashCode()
|
|
18
|
+
|
|
19
|
+
override fun toString(): String = "SourceDrmOverridden($licenseServer)"
|
|
20
|
+
|
|
21
|
+
override fun equals(other: Any?): Boolean = other is SourceDrmOverridden &&
|
|
22
|
+
packageName == other.packageName &&
|
|
23
|
+
licenseServer == other.licenseServer &&
|
|
24
|
+
forceLicenseServer == other.forceLicenseServer &&
|
|
25
|
+
authorization == other.authorization
|
|
26
|
+
|
|
27
|
+
override fun buildUpon() = Builder()
|
|
28
|
+
.also { it.applyFrom(this) }
|
|
29
|
+
|
|
30
|
+
open class Builder : SourceDrm.Builder() {
|
|
31
|
+
var packageName: String? = null
|
|
32
|
+
var licenseServer: URI? = null
|
|
33
|
+
var forceLicenseServer: Boolean = false
|
|
34
|
+
var authorization: SourceAuthorization? = null
|
|
35
|
+
|
|
36
|
+
open fun applyFrom(source: SourceDrmOverridden) {
|
|
37
|
+
packageName = source.packageName
|
|
38
|
+
licenseServer = source.licenseServer
|
|
39
|
+
forceLicenseServer = source.forceLicenseServer
|
|
40
|
+
authorization = source.authorization
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override fun build() =
|
|
44
|
+
SourceDrmOverridden(this)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
inline fun SourceDrmOverridden(block: SourceDrmOverridden.Builder.() -> Unit) =
|
|
49
|
+
SourceDrmOverridden.Builder().apply(block).build()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.jump
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.google.android.exoplayer2.ExoPlayer
|
|
5
|
+
import com.google.android.exoplayer2.SimpleExoPlayer
|
|
6
|
+
import com.google.gson.JsonObject
|
|
7
|
+
import com.twentyfouri.tvbridge.BuildConfig
|
|
8
|
+
import com.twentyfouri.tvbridge.webview.model.AnalyticsPlaybackInfo
|
|
9
|
+
import jump.JumpKit
|
|
10
|
+
import jump.insights.models.contextinformation.*
|
|
11
|
+
import jump.insights.models.track.events.JKContentType
|
|
12
|
+
import jump.insights.models.track.events.JKTransactionType
|
|
13
|
+
import jump.libs.gson.annotations.SerializedName
|
|
14
|
+
import java.util.*
|
|
15
|
+
|
|
16
|
+
class JumpAnalytics {
|
|
17
|
+
|
|
18
|
+
private var started = false
|
|
19
|
+
private var hasContent = false
|
|
20
|
+
|
|
21
|
+
fun setPlaybackInfo(playbackInfo: AnalyticsPlaybackInfo) {
|
|
22
|
+
setApplicationId(playbackInfo.id ?: return)
|
|
23
|
+
setUserInfo(playbackInfo.userInfo)
|
|
24
|
+
setContentInfo(playbackInfo.contentInfo)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private fun setApplicationId(applicationId: String) {
|
|
28
|
+
if (started) return
|
|
29
|
+
started = true
|
|
30
|
+
JumpKit.setDebugMode(BuildConfig.DEBUG)
|
|
31
|
+
JumpKit.start(applicationId)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private fun setUserInfo(params: JsonObject?) {
|
|
35
|
+
JumpKit.insights.setUserInfo(parseUserInfo(params))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private fun setContentInfo(params: JsonObject?) {
|
|
39
|
+
val contentInfo = parseContentInfo(params)
|
|
40
|
+
jump.exoplayerextension.JumpKit.insights.setContentInfo(contentInfo)
|
|
41
|
+
hasContent = true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun setVideoPlayer(player: ExoPlayer?) {
|
|
45
|
+
@Suppress("DEPRECATION")
|
|
46
|
+
if (player !is SimpleExoPlayer) {
|
|
47
|
+
/* NOTICE: passing null also causes insights.setContentInfo(null) */
|
|
48
|
+
jump.exoplayerextension.JumpKit.insights.setVideoPlayer(null)
|
|
49
|
+
hasContent = false
|
|
50
|
+
} else if (started && hasContent) {
|
|
51
|
+
jump.exoplayerextension.JumpKit.insights.setVideoPlayer(player)
|
|
52
|
+
} else if (started) {
|
|
53
|
+
Log.w("JumpAnalytics", "No content data available. Did you call SetPlaybackInfo before SetVideoPath?")
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private fun parseUserInfo(params: JsonObject?): JKUserInfo {
|
|
58
|
+
return try {
|
|
59
|
+
var userType = parseEnum(params, "userType", JKUserType::class.java)
|
|
60
|
+
if (userType == null) userType = JKUserType.ANONYMOUS
|
|
61
|
+
val userId = parseString(params, "userId")
|
|
62
|
+
val userCountry = parseString(params, "userCountry")
|
|
63
|
+
val userBirthDate = parseString(params, "userBirthDate")
|
|
64
|
+
var userSex = parseEnum(params, "userSex", JKUserSex::class.java)
|
|
65
|
+
if (userSex == null) userSex = JKUserSex.UNDEFINED
|
|
66
|
+
val userProfileId = parseString(params, "userProfileId")
|
|
67
|
+
val userOperator = parseString(params, "userOperator")
|
|
68
|
+
if (userType == JKUserType.ANONYMOUS) JKUserInfo.anonymousInstance() else JKUserInfo(userType, userId, userCountry, userBirthDate, userSex, userProfileId, userOperator)
|
|
69
|
+
} catch (e: Exception) {
|
|
70
|
+
Log.w("JumpAnalytics", "Parsing userInfo failed. Falling back to ANONYMOUS", e)
|
|
71
|
+
JKUserInfo.anonymousInstance()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private fun parseCategory(params: JsonObject?, property: String): JKContextCategory? {
|
|
76
|
+
val jsonValue = params?.get(property) ?: return null
|
|
77
|
+
return if (!jsonValue.isJsonObject) null else parseCategory(jsonValue.asJsonObject)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun parseCategory(params: JsonObject): JKContextCategory {
|
|
81
|
+
return JKContextCategory(
|
|
82
|
+
parseString(params, "categoryId"),
|
|
83
|
+
parseString(params, "categoryName")
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private fun parseSubCategory(params: JsonObject?, property: String): JKContextSubcategory? {
|
|
88
|
+
val jsonValue = params?.get(property) ?: return null
|
|
89
|
+
return if (!jsonValue.isJsonObject) null else parseSubCategory(jsonValue.asJsonObject)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private fun parseSubCategory(params: JsonObject): JKContextSubcategory {
|
|
93
|
+
return JKContextSubcategory(
|
|
94
|
+
parseString(params, "subcategoryId"),
|
|
95
|
+
parseString(params, "categoryId"),
|
|
96
|
+
parseString(params, "subcategoryName")
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private fun parseGenre(params: JsonObject): JKContextGenre {
|
|
101
|
+
return JKContextGenre(
|
|
102
|
+
parseString(params, "genreId"),
|
|
103
|
+
parseString(params, "genreName"),
|
|
104
|
+
parseCategory(params, "categoryId"),
|
|
105
|
+
parseSubCategory(params, "subCategoryId")
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private fun parseContentInfo(params: JsonObject?): JKContextContent {
|
|
110
|
+
return JKContextContent(
|
|
111
|
+
parseString(params, "contentId"),
|
|
112
|
+
parseString(params, "contentTitle"),
|
|
113
|
+
parseString(params, "contentSerieTitle"),
|
|
114
|
+
parseInteger(params, "contentEpisodeNumber"),
|
|
115
|
+
parseInteger(params, "contentSeasonNumber"),
|
|
116
|
+
parseString(params, "contentDescription"),
|
|
117
|
+
parseCategory(params, "contentCategory"),
|
|
118
|
+
parseSubCategory(params, "contentSubCategory"),
|
|
119
|
+
parseGenres(params, "contentGenres"),
|
|
120
|
+
parseString(params, "contentProvider"),
|
|
121
|
+
parseEnum(params, "contentTransactionType", JKTransactionType::class.java),
|
|
122
|
+
parseEnum(params, "contentType", JKContentType::class.java),
|
|
123
|
+
parseString(params, "contentLanguage")
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun parseGenres(container: JsonObject?, property: String): List<JKContextGenre>? {
|
|
128
|
+
if (container == null) return null
|
|
129
|
+
val jsonValue = container[property] ?: return null
|
|
130
|
+
if (!jsonValue.isJsonArray) return null
|
|
131
|
+
val list = ArrayList<JKContextGenre>()
|
|
132
|
+
for (jsonElement in jsonValue.asJsonArray) {
|
|
133
|
+
if (!jsonElement.isJsonObject) continue
|
|
134
|
+
list.add(parseGenre(jsonElement.asJsonObject))
|
|
135
|
+
}
|
|
136
|
+
return list
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private fun <T : Enum<T>> parseEnum(container: JsonObject?, property: String, type: Class<T>): T? {
|
|
140
|
+
if (container == null) return null
|
|
141
|
+
val jsonValue = container[property] ?: return null
|
|
142
|
+
if (!jsonValue.isJsonPrimitive) return null
|
|
143
|
+
val primitive = jsonValue.asJsonPrimitive
|
|
144
|
+
return when {
|
|
145
|
+
primitive.isString ->
|
|
146
|
+
parseEnum(type, jsonValue.asString)
|
|
147
|
+
primitive.isNumber ->
|
|
148
|
+
parseEnum(type, jsonValue.asInt)
|
|
149
|
+
else ->
|
|
150
|
+
null
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private fun <T : Enum<T>> parseEnum(type: Class<T>, value: String): T? {
|
|
155
|
+
try {
|
|
156
|
+
for (constant in type.enumConstants ?: return null) {
|
|
157
|
+
val name = constant.name
|
|
158
|
+
if (name == value) {
|
|
159
|
+
return constant
|
|
160
|
+
}
|
|
161
|
+
val annotation = type.getField(name).getAnnotation(SerializedName::class.java)
|
|
162
|
+
if (annotation != null) {
|
|
163
|
+
if (annotation.value == value) {
|
|
164
|
+
return constant
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null
|
|
169
|
+
} catch (e: NoSuchFieldException) {
|
|
170
|
+
return null
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@Suppress("UNCHECKED_CAST")
|
|
175
|
+
private fun <T : Enum<T>> parseEnum(type: Class<T>, value: Int): T? {
|
|
176
|
+
return try {
|
|
177
|
+
val map = type.getDeclaredField("mapper")[null] as Map<T, Int>
|
|
178
|
+
for ((key, value1) in map) {
|
|
179
|
+
if (value1 == value) {
|
|
180
|
+
return key
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
null
|
|
184
|
+
} catch (e: ClassCastException) {
|
|
185
|
+
null
|
|
186
|
+
} catch (e: IllegalAccessException) {
|
|
187
|
+
null
|
|
188
|
+
} catch (e: NoSuchFieldException) {
|
|
189
|
+
null
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private fun parseString(container: JsonObject?, property: String): String? {
|
|
194
|
+
if (container == null) return null
|
|
195
|
+
var jsonValue = container[property]
|
|
196
|
+
if (jsonValue == null && property.endsWith("Id")) {
|
|
197
|
+
// inconsistencies in SDK cause confusion, try uppercased xxxxID just in case
|
|
198
|
+
jsonValue = container[property.replace("Id", "ID")]
|
|
199
|
+
}
|
|
200
|
+
if (jsonValue == null) return null
|
|
201
|
+
if (!jsonValue.isJsonPrimitive) return null
|
|
202
|
+
return if (!jsonValue.asJsonPrimitive.isString) null else jsonValue.asString
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private fun parseInteger(container: JsonObject?, property: String): Int? {
|
|
206
|
+
if (container == null) return null
|
|
207
|
+
val jsonValue = container[property] ?: return null
|
|
208
|
+
if (!jsonValue.isJsonPrimitive) return null
|
|
209
|
+
return if (!jsonValue.asJsonPrimitive.isNumber) null else jsonValue.asInt
|
|
210
|
+
}
|
|
211
|
+
}
|