@24i/smartapps-bigscreen-rnv-build-engine 5.0.0-alpha.25 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +4 -3
- package/project.json +57 -11
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
package com.twentyfouri.tvbridge.webview.viewmodel
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.graphics.Bitmap
|
|
7
|
+
import android.graphics.Canvas
|
|
8
|
+
import android.os.Build
|
|
9
|
+
import android.provider.Settings
|
|
10
|
+
import android.util.Log
|
|
11
|
+
import android.view.KeyEvent
|
|
12
|
+
import android.view.View
|
|
13
|
+
import android.webkit.CookieManager
|
|
14
|
+
import android.webkit.WebChromeClient
|
|
15
|
+
import android.webkit.WebView
|
|
16
|
+
import com.google.android.gms.ads.identifier.AdvertisingIdClient
|
|
17
|
+
import com.google.gson.JsonArray
|
|
18
|
+
import com.google.gson.JsonObject
|
|
19
|
+
import com.twentyfouri.player.base.AbrStrategy
|
|
20
|
+
import com.twentyfouri.player.base.AdBreakEndedEvent
|
|
21
|
+
import com.twentyfouri.player.base.AdBreakStartedEvent
|
|
22
|
+
import com.twentyfouri.player.base.AdBreaksRescheduledEvent
|
|
23
|
+
import com.twentyfouri.player.base.AdErrorEvent
|
|
24
|
+
import com.twentyfouri.player.base.AdSkippedEvent
|
|
25
|
+
import com.twentyfouri.player.base.AdStartedEvent
|
|
26
|
+
import com.twentyfouri.player.base.AddAudioTrackEvent
|
|
27
|
+
import com.twentyfouri.player.base.AddTextTrackEvent
|
|
28
|
+
import com.twentyfouri.player.base.AudioTrack
|
|
29
|
+
import com.twentyfouri.player.base.CanPlayEvent
|
|
30
|
+
import com.twentyfouri.player.base.ChangeAudioTrackEvent
|
|
31
|
+
import com.twentyfouri.player.base.ChangeTextTrackEvent
|
|
32
|
+
import com.twentyfouri.player.base.DurationChangeEvent
|
|
33
|
+
import com.twentyfouri.player.base.EndedEvent
|
|
34
|
+
import com.twentyfouri.player.base.ErrorEvent
|
|
35
|
+
import com.twentyfouri.player.base.Event
|
|
36
|
+
import com.twentyfouri.player.base.EventListener
|
|
37
|
+
import com.twentyfouri.player.base.LoadedDataEvent
|
|
38
|
+
import com.twentyfouri.player.base.LoadedMetadataEvent
|
|
39
|
+
import com.twentyfouri.player.base.PauseEvent
|
|
40
|
+
import com.twentyfouri.player.base.PlayerBase
|
|
41
|
+
import com.twentyfouri.player.base.PlayerFactoryBase
|
|
42
|
+
import com.twentyfouri.player.base.PlayingEvent
|
|
43
|
+
import com.twentyfouri.player.base.RemoveAudioTrackEvent
|
|
44
|
+
import com.twentyfouri.player.base.RemoveTextTrackEvent
|
|
45
|
+
import com.twentyfouri.player.base.SeekedEvent
|
|
46
|
+
import com.twentyfouri.player.base.SeekingEvent
|
|
47
|
+
import com.twentyfouri.player.base.Source
|
|
48
|
+
import com.twentyfouri.player.base.SourceDrmPlayready
|
|
49
|
+
import com.twentyfouri.player.base.SourceDrmWidevine
|
|
50
|
+
import com.twentyfouri.player.base.SourceImaAds
|
|
51
|
+
import com.twentyfouri.player.base.TextTrack
|
|
52
|
+
import com.twentyfouri.player.base.TimeUpdateEvent
|
|
53
|
+
import com.twentyfouri.player.base.TrackKind
|
|
54
|
+
import com.twentyfouri.player.base.WaitingEvent
|
|
55
|
+
import com.twentyfouri.player.base.buildUpon
|
|
56
|
+
import com.twentyfouri.tvbridge.BuildConfig
|
|
57
|
+
import com.twentyfouri.tvbridge.Flavor
|
|
58
|
+
import com.twentyfouri.tvbridge.bridge.BridgeFromJavaScript
|
|
59
|
+
import com.twentyfouri.tvbridge.bridge.BridgeToJavaScript
|
|
60
|
+
import com.twentyfouri.tvbridge.common.iap.IapCallback
|
|
61
|
+
import com.twentyfouri.tvbridge.common.iap.IapException
|
|
62
|
+
import com.twentyfouri.tvbridge.common.iap.IapProduct
|
|
63
|
+
import com.twentyfouri.tvbridge.common.iap.IapPurchase
|
|
64
|
+
import com.twentyfouri.tvbridge.common.iap.IapService
|
|
65
|
+
import com.twentyfouri.tvbridge.configuration.BridgeConfig
|
|
66
|
+
import com.twentyfouri.tvbridge.global.utils.LocalStorage
|
|
67
|
+
import com.twentyfouri.tvbridge.global.utils.PlayerFactoryVersion
|
|
68
|
+
import com.twentyfouri.tvbridge.global.utils.SourceDrmOverridden
|
|
69
|
+
import com.twentyfouri.tvbridge.webview.client.BridgeWebView
|
|
70
|
+
import com.twentyfouri.tvbridge.webview.client.BridgeWebViewClient
|
|
71
|
+
import com.twentyfouri.tvbridge.webview.client.BridgeWebViewClient.BridgeWebViewClientCallback
|
|
72
|
+
import com.twentyfouri.tvbridge.webview.model.AnalyticsPlaybackInfo
|
|
73
|
+
import com.twentyfouri.tvbridge.webview.model.MediaEventType
|
|
74
|
+
import com.twentyfouri.tvbridge.webview.view.Mainview
|
|
75
|
+
import com.twentyfouri.tvbridge.webview.view.PlayerCache
|
|
76
|
+
import org.json.JSONArray
|
|
77
|
+
import org.json.JSONException
|
|
78
|
+
import org.json.JSONObject
|
|
79
|
+
import java.io.IOException
|
|
80
|
+
import java.net.NetworkInterface
|
|
81
|
+
import java.util.Locale
|
|
82
|
+
import javax.inject.Inject
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Created by destivar on 15/02/16.
|
|
86
|
+
*/
|
|
87
|
+
class WebViewModel @Inject constructor(
|
|
88
|
+
private val context: Context,
|
|
89
|
+
private val localStorage: LocalStorage,
|
|
90
|
+
private val config: BridgeConfig,
|
|
91
|
+
private val iapService: IapService,
|
|
92
|
+
val playerFactory: PlayerFactoryBase
|
|
93
|
+
) : EventListener {
|
|
94
|
+
var player: PlayerBase? = null
|
|
95
|
+
private var view: Mainview? = null
|
|
96
|
+
private val jsBridge = BridgeToJavaScript(config) { s: String? ->
|
|
97
|
+
view?.loadUrl(s)
|
|
98
|
+
}
|
|
99
|
+
private val iapCallback = InnerIapCallback()
|
|
100
|
+
private val playerCache = PlayerCache()
|
|
101
|
+
|
|
102
|
+
private var source: Source? = null
|
|
103
|
+
|
|
104
|
+
private var adStarted: Boolean = false
|
|
105
|
+
|
|
106
|
+
val audioTrack: AudioTrack?
|
|
107
|
+
get() = player?.selectedAudioTrack
|
|
108
|
+
val subtitleTrack: TextTrack?
|
|
109
|
+
get() = player?.selectedTextTrack
|
|
110
|
+
|
|
111
|
+
var appUrl: String? = null
|
|
112
|
+
|
|
113
|
+
fun attachView(view: Mainview?) {
|
|
114
|
+
this.view = view
|
|
115
|
+
if (view != null) {
|
|
116
|
+
iapService.initialize()
|
|
117
|
+
checkNewPurchases()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fun configureWebView(webView: BridgeWebView) {
|
|
122
|
+
|
|
123
|
+
// Enable remote debugging on devices at API level 19 (KitKat) or higher.
|
|
124
|
+
if (BuildConfig.DEBUG) {
|
|
125
|
+
WebView.setWebContentsDebuggingEnabled(true)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Add the JavaScript bridge.
|
|
129
|
+
webView.addJavascriptInterface(BridgeFromJavaScript(this), JAVA_SCRIPT_BRIDGE)
|
|
130
|
+
|
|
131
|
+
// WebView callback methods.
|
|
132
|
+
webView.webViewClient = BridgeWebViewClient(object : BridgeWebViewClientCallback {
|
|
133
|
+
override fun onUnhandledKeyEvent(view: WebView, event: KeyEvent) {
|
|
134
|
+
if (event.action == KeyEvent.ACTION_DOWN) {
|
|
135
|
+
sendKeyCode(event.keyCode, MediaEventType.KEY_DOWN)
|
|
136
|
+
} else if (event.action == KeyEvent.ACTION_UP) {
|
|
137
|
+
sendKeyCode(event.keyCode, MediaEventType.KEY_UP)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
webView.webChromeClient = object : WebChromeClient() {
|
|
143
|
+
override fun getDefaultVideoPoster(): Bitmap? {
|
|
144
|
+
val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
|
|
145
|
+
val canvas = Canvas(bitmap)
|
|
146
|
+
canvas.drawARGB(255, 0, 0, 0)
|
|
147
|
+
return bitmap
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
override fun onShowCustomView(view: View, callback: CustomViewCallback) {
|
|
151
|
+
val intent = Intent(Intent.ACTION_SEND)
|
|
152
|
+
intent.type = "video/*"
|
|
153
|
+
context.startActivity(intent)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
override fun onHideCustomView() {}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
webView.previewKeyEvent = this::dispatchKeyEvent
|
|
160
|
+
|
|
161
|
+
CookieManager.getInstance().setAcceptCookie(true)
|
|
162
|
+
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fun onClickGo() {
|
|
166
|
+
view?.loadUrl(appUrl)
|
|
167
|
+
if (BuildConfig.DEBUG) {
|
|
168
|
+
view?.hideKeyboard()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fun setVideo(source: Source) {
|
|
173
|
+
var adaptedSource = source
|
|
174
|
+
if (config.drm.shouldOverrideDrmCallback()) {
|
|
175
|
+
adaptedSource = SourceDrmOverridden.replaceSource(adaptedSource)
|
|
176
|
+
}
|
|
177
|
+
logSource(adaptedSource)
|
|
178
|
+
playDirectly(adaptedSource)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private fun logSource(source: Source) {
|
|
182
|
+
Log.i("BridgeVideo", "Url: ${source.url}")
|
|
183
|
+
when (val ads = source.ads) {
|
|
184
|
+
is SourceImaAds -> {
|
|
185
|
+
Log.i("BridgeVideo", "Ads: ${ads.url}")
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
when (val drmModel = source.drm.firstOrNull()) {
|
|
189
|
+
is SourceDrmWidevine -> {
|
|
190
|
+
Log.i("BridgeVideo", "DRM: " + drmModel.packageName)
|
|
191
|
+
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.licenseServer)
|
|
192
|
+
}
|
|
193
|
+
is SourceDrmPlayready -> {
|
|
194
|
+
Log.i("BridgeVideo", "DRM: " + drmModel.packageName)
|
|
195
|
+
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.licenseServer)
|
|
196
|
+
}
|
|
197
|
+
is SourceDrmOverridden -> {
|
|
198
|
+
Log.i("BridgeVideo", "DRM: " + drmModel.packageName)
|
|
199
|
+
Log.i("BridgeVideo", "LicenseUrl: " + drmModel.licenseServer)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private fun playDirectly(source: Source) {
|
|
205
|
+
this.source = source
|
|
206
|
+
player?.also { player ->
|
|
207
|
+
player.source = source
|
|
208
|
+
|
|
209
|
+
if (isLowEndDevice) {
|
|
210
|
+
player.trackRestrictions = player.trackRestrictions.buildUpon {
|
|
211
|
+
maxBitrate = LOW_END_DEVICE_MAX_BITRATE
|
|
212
|
+
}
|
|
213
|
+
} else if (config.startWithHighVideoQuality) {
|
|
214
|
+
player.trackRestrictions = player.trackRestrictions.buildUpon {
|
|
215
|
+
abrStrategy = AbrStrategy.QUALITY
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
player.addEventListener(Event.ALL, this)
|
|
220
|
+
|
|
221
|
+
player.autoPlay = true
|
|
222
|
+
player.load()
|
|
223
|
+
player.play()
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private val isLowEndDevice: Boolean
|
|
228
|
+
get() = when (Build.MODEL) {
|
|
229
|
+
"AFTB" -> true
|
|
230
|
+
"AFTM" -> true
|
|
231
|
+
else -> false
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
fun startPlayer() {
|
|
235
|
+
player?.play()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fun resumePlayer() {
|
|
239
|
+
val savedSource = playerCache.savedSource
|
|
240
|
+
if (playerCache.loadFromCache && savedSource != null && playerCache.savedPlayerState !== PlayerCache.PlayerState.PAUSED) {
|
|
241
|
+
setVideo(savedSource)
|
|
242
|
+
var position = playerCache.savedPlayerPosition
|
|
243
|
+
val player = this.player ?: return
|
|
244
|
+
if (player.live) {
|
|
245
|
+
position = 0.0
|
|
246
|
+
}
|
|
247
|
+
player.seek(position)
|
|
248
|
+
view?.requestAudioFocus()
|
|
249
|
+
if (playerCache.savedPlayerState === PlayerCache.PlayerState.PLAYING) {
|
|
250
|
+
player.play()
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
if (player?.live == true) {
|
|
254
|
+
player?.seek(0.0)
|
|
255
|
+
}
|
|
256
|
+
player?.play()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
fun pausePlayer() {
|
|
261
|
+
player?.pause()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fun stopPlayer() {
|
|
265
|
+
view?.setBlackCurtainVisibility(View.VISIBLE)
|
|
266
|
+
source = null
|
|
267
|
+
|
|
268
|
+
player?.also { player ->
|
|
269
|
+
player.removeEventListener(Event.ALL, this)
|
|
270
|
+
player.unload()
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
fun releasePlayer() {
|
|
275
|
+
val player = this.player ?: return
|
|
276
|
+
if (source != null) {
|
|
277
|
+
playerCache.loadFromCache = true
|
|
278
|
+
playerCache.savedSource = source
|
|
279
|
+
playerCache.savedPlayerDuration = player.duration
|
|
280
|
+
playerCache.savedPlayerPosition = player.currentTime
|
|
281
|
+
playerCache.savedPlayerState = if (player.playing) PlayerCache.PlayerState.PLAYING else PlayerCache.PlayerState.PAUSED
|
|
282
|
+
}
|
|
283
|
+
player.unload()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
fun reloadPlayer() {
|
|
287
|
+
view?.reloadPlayer()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
val currentPosition: Double
|
|
291
|
+
get() = player?.currentTime ?: playerCache.savedPlayerPosition
|
|
292
|
+
val duration: Double
|
|
293
|
+
get() = player?.duration ?: playerCache.savedPlayerDuration
|
|
294
|
+
|
|
295
|
+
fun seekToPosition(position: Double) {
|
|
296
|
+
player?.seek(position)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
val isPlayerPlaying: Boolean
|
|
300
|
+
get() = player?.playing == true
|
|
301
|
+
val isInPlayer: Boolean
|
|
302
|
+
get() = player != null && source != null
|
|
303
|
+
|
|
304
|
+
fun loadUrl(url: String?) {
|
|
305
|
+
view?.loadUrl(url)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
fun setSubtitlesVisibility(visibility: Boolean) {
|
|
309
|
+
val player = this.player ?: return
|
|
310
|
+
if (visibility) {
|
|
311
|
+
player.trackRestrictions = player.trackRestrictions.buildUpon {
|
|
312
|
+
enableCaptions = true
|
|
313
|
+
}
|
|
314
|
+
if (player.textTrack?.kind == TrackKind.EMPTY) {
|
|
315
|
+
player.textTrack = null
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
player.trackRestrictions = player.trackRestrictions.buildUpon {
|
|
319
|
+
enableCaptions = false
|
|
320
|
+
}
|
|
321
|
+
player.textTrack = player.textTracks.firstOrNull { it.kind == TrackKind.EMPTY }
|
|
322
|
+
?: DISABLED_TEXT_TRACK
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fun enableFullScreen() {
|
|
327
|
+
view?.goFullscreen()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fun disableFullScreen(width: Int, height: Int, left: Int, top: Int) {
|
|
331
|
+
view?.exitFullscreen(width, height, left, top)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
override fun onEvent(event: Event) {
|
|
335
|
+
val player = this.player ?: return
|
|
336
|
+
when (event) {
|
|
337
|
+
is ErrorEvent -> {
|
|
338
|
+
Log.e("AndroidPlayer", "Player error", event.error)
|
|
339
|
+
jsBridge.bridgeAndroidToJavaScript_ErrorEvent(
|
|
340
|
+
MediaEventType.ERROR,
|
|
341
|
+
"${event.error.message} - ${event.error.nativeCode}"
|
|
342
|
+
)
|
|
343
|
+
stopPlayer()
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
is LoadedMetadataEvent -> {
|
|
347
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.LOADEDMETADATA)
|
|
348
|
+
jsBridge.bridgeAndroidToJavaScript_MediaTrackEvent(
|
|
349
|
+
MediaEventType.TRACKS_CHANGED,
|
|
350
|
+
player.audioTracks,
|
|
351
|
+
player.textTracks
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
is LoadedDataEvent -> {
|
|
356
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.LOADEDDATA)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
is CanPlayEvent -> {
|
|
360
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.CANPLAY)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
is EndedEvent -> {
|
|
364
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.ENDED)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
is WaitingEvent -> {
|
|
368
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.WAITING)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
is PlayingEvent -> {
|
|
372
|
+
view?.setBlackCurtainVisibility(View.GONE)
|
|
373
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.PLAYING)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
is DurationChangeEvent -> {
|
|
377
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.DURATION_CHANGE)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
is SeekingEvent -> {
|
|
381
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.SEEKING)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
is SeekedEvent -> {
|
|
385
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.SEEKED)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
is TimeUpdateEvent -> {
|
|
389
|
+
if (player.playing) {
|
|
390
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.TIME_UPDATE)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
is PauseEvent -> {
|
|
395
|
+
if (!player.playing) {
|
|
396
|
+
jsBridge.bridgeAndroidToJavaScript_MediaEvent(MediaEventType.PAUSE)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
is AddAudioTrackEvent, is AddTextTrackEvent,
|
|
401
|
+
is RemoveAudioTrackEvent, is RemoveTextTrackEvent,
|
|
402
|
+
is ChangeAudioTrackEvent, is ChangeTextTrackEvent -> {
|
|
403
|
+
Log.d("WebViewModel", "onTrackschanged: audio=$audioTrack subtitle=$subtitleTrack")
|
|
404
|
+
jsBridge.bridgeAndroidToJavaScript_MediaTrackEvent(
|
|
405
|
+
MediaEventType.TRACKS_CHANGED,
|
|
406
|
+
player.audioTracks,
|
|
407
|
+
player.textTracks
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
is AdBreaksRescheduledEvent -> {
|
|
412
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdBreaksEvent(
|
|
413
|
+
MediaEventType.AD_BREAKS_RESCHEDULED,
|
|
414
|
+
player.timeline
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
is AdBreakStartedEvent -> {
|
|
419
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
420
|
+
MediaEventType.AD_BREAK_START,
|
|
421
|
+
player.currentAdBreak,
|
|
422
|
+
player.currentAd
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
is AdBreakEndedEvent -> {
|
|
427
|
+
if (adStarted) {
|
|
428
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
429
|
+
MediaEventType.AD_ENDED,
|
|
430
|
+
player.currentAdBreak,
|
|
431
|
+
null
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
adStarted = false
|
|
435
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
436
|
+
MediaEventType.AD_BREAK_END,
|
|
437
|
+
player.currentAdBreak,
|
|
438
|
+
player.currentAd
|
|
439
|
+
)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
is AdStartedEvent -> {
|
|
443
|
+
if (adStarted) {
|
|
444
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
445
|
+
MediaEventType.AD_ENDED,
|
|
446
|
+
player.currentAdBreak,
|
|
447
|
+
null
|
|
448
|
+
)
|
|
449
|
+
} else {
|
|
450
|
+
adStarted = true
|
|
451
|
+
}
|
|
452
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
453
|
+
MediaEventType.AD_STARTED,
|
|
454
|
+
player.currentAdBreak,
|
|
455
|
+
player.currentAd
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
is AdErrorEvent -> {
|
|
460
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
461
|
+
MediaEventType.AD_ERROR,
|
|
462
|
+
player.currentAdBreak,
|
|
463
|
+
player.currentAd
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
is AdSkippedEvent -> {
|
|
468
|
+
jsBridge.bridgeAndroidToJavaScript_MediaAdEvent(
|
|
469
|
+
MediaEventType.AD_SKIPPED,
|
|
470
|
+
player.currentAdBreak,
|
|
471
|
+
player.currentAd
|
|
472
|
+
)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
fun setSubtitleTrack(track: TextTrack?) {
|
|
478
|
+
player?.textTrack = track
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
fun setAudioTrack(track: AudioTrack?) {
|
|
482
|
+
player?.audioTrack = track
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
fun saveToLocalStorage(key: String?, value: String?) {
|
|
486
|
+
localStorage.saveCookieJson(key, value)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
fun getFromLocalStorage(key: String?): String? {
|
|
490
|
+
return localStorage.getFromLocalStorage(key)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
fun removeFromLocalStorage(key: String?) {
|
|
494
|
+
localStorage.removeFromLocalStorage(key)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
fun sendKeyCode(keyCode: Int, mediaEventType: String) {
|
|
498
|
+
jsBridge.bridgeAndroidToJavaScript_KeyEvent(keyCode, mediaEventType)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
fun setPlaybackInfo(playbackInfo: AnalyticsPlaybackInfo) {
|
|
502
|
+
// previously used for Jump analytics, not supported with THEO
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
fun showMessage(message: String?) {
|
|
506
|
+
view?.showMessage(message)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
fun requestAudioFocus(): Int? {
|
|
510
|
+
return view?.requestAudioFocus()
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
fun abandonAudioFocus() {
|
|
514
|
+
view?.abandonAudioFocus()
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
fun onBackPressed() {
|
|
518
|
+
stopPlayer()
|
|
519
|
+
view?.onBackPressed()
|
|
520
|
+
loadUrl(appUrl)
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
fun signData(dataJson: String?): String? {
|
|
524
|
+
return if (dataJson == null || dataJson.isEmpty()) "" else Flavor.signData(context, dataJson)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
fun launchIapPurchase(sku: String?) {
|
|
528
|
+
if (sku == null || sku.isEmpty()) return
|
|
529
|
+
view?.launchIapPurchase(sku)
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
fun checkNewPurchases() {
|
|
533
|
+
iapService.setCallback(iapCallback)
|
|
534
|
+
iapService.checkNewPurchases()
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
fun launchIapPurchase(activity: Activity, sku: String) {
|
|
538
|
+
iapService.setCallback(iapCallback)
|
|
539
|
+
iapService.purchase(activity, sku)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
fun getIapProductDetails(skus: List<String>) {
|
|
543
|
+
iapService.setCallback(iapCallback)
|
|
544
|
+
iapService.getProducts(skus)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
fun acknowledgeIapReceipt(receipt: JSONObject) {
|
|
548
|
+
iapService.setCallback(iapCallback)
|
|
549
|
+
iapService.acknowledgeReceipt(receipt)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
|
553
|
+
// pause and stop events should be caught only when we are in player
|
|
554
|
+
// -> because we want to let user have option to pause/stop some background playback
|
|
555
|
+
if (isInPlayer) {
|
|
556
|
+
// we have to catch all media events, otherwise they will be unhandled and reported in onUnhandledKeyEvent
|
|
557
|
+
// -> and later sent to broadcast receivers of other media apps, which can resume stream/audio from background app
|
|
558
|
+
when (event.keyCode) {
|
|
559
|
+
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
|
|
560
|
+
KeyEvent.KEYCODE_MEDIA_STOP,
|
|
561
|
+
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
|
|
562
|
+
if (event.action == KeyEvent.ACTION_DOWN) {
|
|
563
|
+
jsBridge.bridgeAndroidToJavaScript_KeyEvent(event.keyCode, MediaEventType.KEY_DOWN)
|
|
564
|
+
} else if (event.action == KeyEvent.ACTION_UP) {
|
|
565
|
+
jsBridge.bridgeAndroidToJavaScript_KeyEvent(event.keyCode, MediaEventType.KEY_UP)
|
|
566
|
+
}
|
|
567
|
+
return true
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
when (event.keyCode) {
|
|
572
|
+
KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK,
|
|
573
|
+
KeyEvent.KEYCODE_MEDIA_CLOSE,
|
|
574
|
+
KeyEvent.KEYCODE_MEDIA_EJECT,
|
|
575
|
+
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
|
|
576
|
+
KeyEvent.KEYCODE_MEDIA_NEXT,
|
|
577
|
+
KeyEvent.KEYCODE_MEDIA_PREVIOUS,
|
|
578
|
+
KeyEvent.KEYCODE_MEDIA_RECORD,
|
|
579
|
+
KeyEvent.KEYCODE_MEDIA_REWIND,
|
|
580
|
+
KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD,
|
|
581
|
+
KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD,
|
|
582
|
+
KeyEvent.KEYCODE_MEDIA_STEP_BACKWARD,
|
|
583
|
+
KeyEvent.KEYCODE_MEDIA_STEP_FORWARD,
|
|
584
|
+
KeyEvent.KEYCODE_MEDIA_TOP_MENU,
|
|
585
|
+
KeyEvent.KEYCODE_MEDIA_PLAY -> {
|
|
586
|
+
if (event.action == KeyEvent.ACTION_DOWN) {
|
|
587
|
+
jsBridge.bridgeAndroidToJavaScript_KeyEvent(event.keyCode, MediaEventType.KEY_DOWN)
|
|
588
|
+
} else if (event.action == KeyEvent.ACTION_UP) {
|
|
589
|
+
jsBridge.bridgeAndroidToJavaScript_KeyEvent(event.keyCode, MediaEventType.KEY_UP)
|
|
590
|
+
}
|
|
591
|
+
return true
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return false
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
fun detectDeviceInfo(): JSONObject {
|
|
598
|
+
val deviceInfo = JSONObject()
|
|
599
|
+
try {
|
|
600
|
+
val engineVersion = PlayerFactoryVersion.from(playerFactory)
|
|
601
|
+
deviceInfo.put("deviceBrand", Build.MANUFACTURER)
|
|
602
|
+
deviceInfo.put("deviceModel", Build.MODEL)
|
|
603
|
+
deviceInfo.put("osVersion", Build.VERSION.RELEASE)
|
|
604
|
+
deviceInfo.put("appFlavor", BuildConfig.FLAVOR)
|
|
605
|
+
deviceInfo.put("appVersion", BuildConfig.VERSION_NAME)
|
|
606
|
+
deviceInfo.put("engineName", engineVersion.engineName)
|
|
607
|
+
deviceInfo.put("engineVersion", engineVersion.engineVersion)
|
|
608
|
+
|
|
609
|
+
val drmKeySystems = mutableSetOf<String>()
|
|
610
|
+
playerFactory.getSupportedFormats(context).manifest.forEach { manifest ->
|
|
611
|
+
manifest.drm.forEach { drm ->
|
|
612
|
+
drmKeySystems.addAll(drm.keySystems)
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
deviceInfo.put("drmKeySystems", JSONArray(drmKeySystems))
|
|
616
|
+
|
|
617
|
+
val ipAddresses = mutableSetOf<String>()
|
|
618
|
+
for (networkInterface in NetworkInterface.getNetworkInterfaces()) {
|
|
619
|
+
for (inetAddress in networkInterface.inetAddresses) {
|
|
620
|
+
if (inetAddress.isLoopbackAddress) continue
|
|
621
|
+
ipAddresses.add(inetAddress.hostAddress)
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
deviceInfo.put("ipLocal", JSONArray(ipAddresses))
|
|
625
|
+
|
|
626
|
+
} catch (e: JSONException) {
|
|
627
|
+
deviceInfo.put("errorType", e.javaClass.simpleName)
|
|
628
|
+
deviceInfo.put("errorMessage", e.message)
|
|
629
|
+
}
|
|
630
|
+
return deviceInfo
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
fun detectMediaInfo(): JSONObject? {
|
|
634
|
+
val player = this.player ?: return null
|
|
635
|
+
val mediaInfo = JSONObject()
|
|
636
|
+
try {
|
|
637
|
+
mediaInfo.put("resolution", "${player.videoWidth}x${player.videoHeight}")
|
|
638
|
+
player.selectedVideoTrack?.also { track ->
|
|
639
|
+
mediaInfo.put("videoCodec", track.codec)
|
|
640
|
+
mediaInfo.put("bitrate", track.bitrate)
|
|
641
|
+
mediaInfo.put("framerate", track.frameRate)
|
|
642
|
+
}
|
|
643
|
+
player.selectedAudioTrack?.also { track ->
|
|
644
|
+
mediaInfo.put("audioCodec", track.codec)
|
|
645
|
+
}
|
|
646
|
+
player.metrics.also { metrics ->
|
|
647
|
+
mediaInfo.put("estimatedBandwidth", metrics.bandwidthEstimate)
|
|
648
|
+
mediaInfo.put("totalDataLoaded", metrics.totalDownloadedBytes)
|
|
649
|
+
if (player.live) {
|
|
650
|
+
mediaInfo.put("liveLatency", metrics.liveLatency)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
} catch (e: JSONException) {
|
|
654
|
+
mediaInfo.put("errorType", e.javaClass.simpleName)
|
|
655
|
+
mediaInfo.put("errorMessage", e.message)
|
|
656
|
+
}
|
|
657
|
+
return mediaInfo
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
val adId get() = try {
|
|
661
|
+
// Let's try Google first
|
|
662
|
+
AdvertisingIdClient.getAdvertisingIdInfo(context).id
|
|
663
|
+
} catch (e: IOException) {
|
|
664
|
+
// Probably Amazon
|
|
665
|
+
try {
|
|
666
|
+
Settings.Secure.getString(context.contentResolver, "advertising_id")
|
|
667
|
+
} catch (snfe: Settings.SettingNotFoundException) {
|
|
668
|
+
null
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
val limitAdTracking get() = try {
|
|
673
|
+
// Let's try Google first
|
|
674
|
+
AdvertisingIdClient.getAdvertisingIdInfo(context).isLimitAdTrackingEnabled
|
|
675
|
+
} catch (e: IOException) {
|
|
676
|
+
// Probably Amazon
|
|
677
|
+
try {
|
|
678
|
+
Settings.Secure.getInt(context.contentResolver, "limit_ad_tracking") != 0
|
|
679
|
+
} catch (snfe: Settings.SettingNotFoundException) {
|
|
680
|
+
true
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
val language get() = Locale.getDefault().language
|
|
685
|
+
|
|
686
|
+
private inner class InnerIapCallback : IapCallback {
|
|
687
|
+
override fun checkNewPurchasesFinished(result: List<IapPurchase>) {
|
|
688
|
+
// not actually used at the moment
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
override fun checkNewPurchasesFinished(exception: Exception) {
|
|
692
|
+
// not actually used at the moment
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
override fun purchaseFinished(result: IapPurchase) {
|
|
696
|
+
jsBridge.bridgeAndroidToJavaScript_IAPValidationSuccess(result.userId, result.receipt, result.sku)
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
override fun purchaseFinished(exception: Exception) {
|
|
700
|
+
if (exception is IapException) {
|
|
701
|
+
jsBridge.bridgeAndroidToJavaScript_IAPValidationError(exception.status)
|
|
702
|
+
} else {
|
|
703
|
+
jsBridge.bridgeAndroidToJavaScript_IAPValidationError(exception.message
|
|
704
|
+
?: "Purchase failed")
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
override fun getProductsFinished(result: List<IapProduct>, missing: List<String>) {
|
|
709
|
+
val okSkus = JsonArray()
|
|
710
|
+
for (iapProduct in result) {
|
|
711
|
+
val jsonProduct = JsonObject()
|
|
712
|
+
jsonProduct.addProperty("title", iapProduct.title)
|
|
713
|
+
jsonProduct.addProperty("description", iapProduct.description)
|
|
714
|
+
jsonProduct.addProperty("price", iapProduct.price)
|
|
715
|
+
jsonProduct.addProperty("icon", iapProduct.icon)
|
|
716
|
+
jsonProduct.addProperty("type", iapProduct.type)
|
|
717
|
+
jsonProduct.addProperty("sku", iapProduct.sku)
|
|
718
|
+
okSkus.add(jsonProduct)
|
|
719
|
+
}
|
|
720
|
+
val badSkus = JsonArray()
|
|
721
|
+
for (sku in missing) {
|
|
722
|
+
badSkus.add(sku)
|
|
723
|
+
}
|
|
724
|
+
jsBridge.bridgeIAPGetProductDataSuccess(okSkus.toString(), badSkus.toString())
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
override fun getProductsFinished(exception: Exception) {
|
|
728
|
+
if (exception is IapException) {
|
|
729
|
+
jsBridge.bridgeIAPGetProductDataFail(exception.status)
|
|
730
|
+
} else {
|
|
731
|
+
jsBridge.bridgeIAPGetProductDataFail(exception.message ?: "Purchase failed")
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
override fun acknowledgeReceiptFinished(exception: Exception?) {
|
|
736
|
+
// JS does not care
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
companion object {
|
|
741
|
+
const val JAVA_SCRIPT_BRIDGE = "JavaScriptBridge"
|
|
742
|
+
const val LOW_END_DEVICE_MAX_BITRATE = 1500000
|
|
743
|
+
val DISABLED_TEXT_TRACK = TextTrack {
|
|
744
|
+
id = "text_off"
|
|
745
|
+
kind = TrackKind.EMPTY
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|