Skip to content

Commit 382417b

Browse files
Merge pull request #653 from THEOplayer/release/v10.1.0
Release/v10.1.0
2 parents 33a7661 + aec2792 commit 382417b

46 files changed

Lines changed: 3501 additions & 2004 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [10.1.0] - 25-10-06
9+
10+
### Added
11+
12+
- Added an explicit clearing of the NowPlayingInfo on iOS, when the app receives a `willTerminateNotification`, to make sure all NowPlayingInfo is removed from the lock screen when an app is closed.
13+
- Added `videoWidth` and `videoHeight` properties to `THEOplayer` containing the active video's resolution in pixels.
14+
- Added `videoresize` player event enabling monitoring of video rendition resizes.
15+
- Added `dimensionchange` player event enabling monitoring of player resizes. This event is set to replace the now deprecated `resize` player event.
16+
- Added `fontPath` to `TextTrackStyle` to allow loading custom fonts from the Android assets folder.
17+
18+
### Fixed
19+
20+
- Fixed an issue on Android where play-out of Millicast sources in the example app would fail. The Android NDK version needs to be at least v28.
21+
- Fixed an issue on Web where on older devices the `display: contents` style property is sometimes not supported.
22+
- Fixed an issue on Android where the `MediaPlaybackService` could be restarted after the app was closed, in some cases causing a `ForegroundServiceDidNotStartInTimeException`. A conditional receiver makes sure the service is never restarted.
23+
- Fixed an issue on iOS where a setup with multiple players could cause a view index NSInternalInconsistencyException after moving players in and out of fullscreen.
24+
- Fixed an issue on Android where some event listeners were not properly disposed of after destroying the player.
25+
- Fixed an issue on Web where the `width` and `height` properties on `THEOplayer` would show the active video rendition's resolution width and height instead of the width and height of the video element.
26+
827
## [10.0.0] - 25-09-15
928

1029
### Added

android/build.gradle

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ def enabledMediaSession = safeExtGet("THEOplayer_extensionMediaSession", 'true')
4343
def enabledMillicast = safeExtGet("THEOplayer_extensionMillicast", 'false').toBoolean()
4444

4545
android {
46-
compileSdk safeExtGet('THEOplayer_compileSdkVersion', 35)
46+
compileSdk safeExtGet('THEOplayer_compileSdkVersion', 36)
4747
namespace "com.theoplayer"
4848

4949
defaultConfig {
5050
minSdkVersion safeExtGet('THEOplayer_minSdkVersion', 23)
51-
targetSdkVersion safeExtGet('THEOplayer_targetSdkVersion', 35)
51+
targetSdkVersion safeExtGet('THEOplayer_targetSdkVersion', 36)
5252
versionCode 1
5353
versionName "1.0"
5454

@@ -124,16 +124,15 @@ repositories {
124124
mavenLocal()
125125
}
126126

127-
// The minimum supported THEOplayer version is 9.9.0
128-
def theoVersion = safeExtGet('THEOplayer_sdk', '[9.9.0, 11.0.0)')
127+
// The minimum supported THEOplayer version is 10.0.1
128+
def theoVersion = safeExtGet('THEOplayer_sdk', '[10.0.1, 11.0.0)')
129129
def theoMediaSessionVersion = safeExtGet('THEOplayer_mediasession', '[8.0.0, 11.0.0)')
130130
def theoAdsWrapperVersion = "10.0.0"
131131
def coroutinesVersion = safeExtGet('coroutinesVersion', '1.10.2')
132132
def appcompatVersion = safeExtGet('appcompatVersion', '1.7.1')
133133
def corektxVersion = safeExtGet('corektxVersion', '1.16.0')
134134
def gsonVersion = safeExtGet('gsonVersion', '2.13.1')
135135
def activityktxVersion = safeExtGet('activityktxVersion', '1.10.1')
136-
def millicastVersion = safeExtGet('millicastVersion', '2.5.0')
137136

138137
dependencies {
139138
def addOptiViewIntegration = { enabled, notation, additional = null ->
@@ -169,5 +168,5 @@ dependencies {
169168
addOptiViewIntegration(enabledGoogleDAI || enabledTHEOads, 'integration-ads-dai')
170169
addOptiViewIntegration(enabledTHEOads, 'integration-ads-theoads')
171170
addOptiViewIntegration(enabledCast, 'integration-cast')
172-
addOptiViewIntegration(enabledMillicast, 'integration-millicast', "com.millicast:millicast-sdk-android:$millicastVersion")
171+
addOptiViewIntegration(enabledMillicast, 'integration-millicast')
173172
}

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
</intent-filter>
4242
</service>
4343

44-
<receiver android:name="androidx.media.session.MediaButtonReceiver"
44+
<receiver android:name=".media.ConditionalMediaButtonReceiver"
4545
android:exported="false">
4646
<intent-filter>
4747
<action android:name="android.intent.action.MEDIA_BUTTON" />

android/src/main/java/com/theoplayer/PlayerEventEmitter.kt

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ private const val EVENT_THEOLIVE_EVENT = "onNativeTHEOliveEvent"
8282
private const val EVENT_THEOADS_EVENT = "onNativeTHEOadsEvent"
8383
private const val EVENT_PRESENTATIONMODECHANGE = "onNativePresentationModeChange"
8484
private const val EVENT_VOLUMECHANGE = "onNativeVolumeChange"
85-
private const val EVENT_RESIZE = "onNativeResize"
85+
private const val EVENT_DIMENSIONCHANGE = "onNativeDimensionChange"
86+
private const val EVENT_VIDEORESIZE = "onNativeVideoResize"
8687

8788
private const val EVENT_PROP_TYPE = "type"
8889
private const val EVENT_PROP_STATE = "state"
@@ -126,7 +127,8 @@ class PlayerEventEmitter internal constructor(
126127
EVENT_THEOADS_EVENT,
127128
EVENT_PRESENTATIONMODECHANGE,
128129
EVENT_VOLUMECHANGE,
129-
EVENT_RESIZE
130+
EVENT_DIMENSIONCHANGE,
131+
EVENT_VIDEORESIZE
130132
)
131133
annotation class VideoEvents
132134

@@ -162,7 +164,8 @@ class PlayerEventEmitter internal constructor(
162164
EVENT_THEOADS_EVENT,
163165
EVENT_PRESENTATIONMODECHANGE,
164166
EVENT_VOLUMECHANGE,
165-
EVENT_RESIZE
167+
EVENT_DIMENSIONCHANGE,
168+
EVENT_VIDEORESIZE
166169
)
167170
}
168171

@@ -177,9 +180,9 @@ class PlayerEventEmitter internal constructor(
177180
private var theoAdsEventAdapter: THEOadsEventAdapter? = null
178181
private var lastTimeUpdate: Long = 0
179182
private var lastCurrentTime = 0.0
180-
private var resizeListener = View.OnLayoutChangeListener { v, _, _, _, _, oldLeft, oldTop, oldRight, oldBottom ->
183+
private var dimensionChangeListener = View.OnLayoutChangeListener { v, _, _, _, _, oldLeft, oldTop, oldRight, oldBottom ->
181184
if (v.width != oldRight - oldLeft || v.height != oldBottom - oldTop) {
182-
onResize(v.width, v.height)
185+
onDimensionChange(v.width, v.height)
183186
}
184187
}
185188

@@ -233,6 +236,8 @@ class PlayerEventEmitter internal constructor(
233236
EventListener { event: PresentationModeChange -> onPresentationModeChange(event) }
234237
playerListeners[PlayerEventTypes.VOLUMECHANGE] =
235238
EventListener { event: VolumeChangeEvent -> onVolumeChange(event) }
239+
playerListeners[PlayerEventTypes.RESIZE] =
240+
EventListener { event: ResizeEvent -> onResize(event) }
236241
textTrackListeners[TextTrackListEventTypes.ADDTRACK] =
237242
EventListener { event: AddTrackEvent -> onTextTrackAdd(event) }
238243
textTrackListeners[TextTrackListEventTypes.REMOVETRACK] =
@@ -438,13 +443,20 @@ class PlayerEventEmitter internal constructor(
438443
)
439444
}
440445

441-
private fun onResize(width: Int, height: Int) {
446+
private fun onDimensionChange(width: Int, height: Int) {
442447
receiveEvent(
443-
EVENT_RESIZE,
448+
EVENT_DIMENSIONCHANGE,
444449
PayloadBuilder().size(width, height).build()
445450
)
446451
}
447452

453+
private fun onResize(event: ResizeEvent) {
454+
receiveEvent(
455+
EVENT_VIDEORESIZE,
456+
PayloadBuilder().videoSize(event.width, event.height).build()
457+
)
458+
}
459+
448460
private val onTextTrackAddCue = EventListener<AddCueEvent> { event ->
449461
val payload = PayloadBuilder().textTrackCue(event.cue, event.track).build().apply {
450462
putInt(EVENT_PROP_TYPE, TextTrackCueEventType.ADD_CUE.type)
@@ -700,7 +712,7 @@ class PlayerEventEmitter internal constructor(
700712
}
701713

702714
// Attach view size listener
703-
playerView.addOnLayoutChangeListener(resizeListener)
715+
playerView.addOnLayoutChangeListener(dimensionChangeListener)
704716
}
705717

706718
fun removeListeners(player: Player?) {
@@ -737,7 +749,7 @@ class PlayerEventEmitter internal constructor(
737749
}
738750

739751
// Remove view size listener
740-
playerView.removeOnLayoutChangeListener(resizeListener)
752+
playerView.removeOnLayoutChangeListener(dimensionChangeListener)
741753

742754
castEventAdapter?.destroy()
743755
adEventAdapter?.destroy()

android/src/main/java/com/theoplayer/cast/CastEventAdapter.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.theoplayer.android.api.cast.Cast
66
import com.theoplayer.android.api.cast.chromecast.CastError
77
import com.theoplayer.android.api.cast.chromecast.ErrorCode
88
import com.theoplayer.android.api.cast.chromecast.PlayerCastState
9+
import com.theoplayer.android.api.event.EventListener
910
import com.theoplayer.android.api.event.chromecast.CastErrorEvent
1011
import com.theoplayer.android.api.event.chromecast.CastStateChangeEvent
1112
import com.theoplayer.android.api.event.chromecast.ChromecastEventTypes
@@ -22,24 +23,27 @@ class CastEventAdapter(private val castApi: Cast, private val emitter: Emitter)
2223
fun emit(payload: WritableMap?)
2324
}
2425

26+
private val onCastError = EventListener<CastErrorEvent> { handleCastError(it) }
27+
private val onStateChange = EventListener<CastStateChangeEvent> { handleStateChange(it) }
28+
2529
init {
26-
castApi.chromecast.addEventListener(ChromecastEventTypes.ERROR, this::onCastError)
27-
castApi.chromecast.addEventListener(ChromecastEventTypes.STATECHANGE, this::onStateChange)
30+
castApi.chromecast.addEventListener(ChromecastEventTypes.ERROR, onCastError)
31+
castApi.chromecast.addEventListener(ChromecastEventTypes.STATECHANGE, onStateChange)
2832
}
2933

3034
fun destroy() {
31-
castApi.chromecast.removeEventListener(ChromecastEventTypes.ERROR, this::onCastError)
32-
castApi.chromecast.removeEventListener(ChromecastEventTypes.STATECHANGE, this::onStateChange)
35+
castApi.chromecast.removeEventListener(ChromecastEventTypes.ERROR, onCastError)
36+
castApi.chromecast.removeEventListener(ChromecastEventTypes.STATECHANGE, onStateChange)
3337
}
3438

35-
private fun onCastError(event: CastErrorEvent) {
39+
private fun handleCastError(event: CastErrorEvent) {
3640
val payload = Arguments.createMap()
3741
payload.putString(EVENT_PROP_TYPE, "chromecasterror")
3842
payload.putMap(EVENT_PROP_ERROR, serializeError(event.error))
3943
emitter.emit(payload)
4044
}
4145

42-
private fun serializeError(error: CastError): WritableMap {
46+
private fun serializeError(error: CastError): WritableMap {
4347
val errorPayload = Arguments.createMap()
4448
@Suppress("SENSELESS_NULL_IN_WHEN")
4549
errorPayload.putString(
@@ -62,7 +66,7 @@ class CastEventAdapter(private val castApi: Cast, private val emitter: Emitter)
6266
return errorPayload
6367
}
6468

65-
private fun onStateChange(event: CastStateChangeEvent) {
69+
private fun handleStateChange(event: CastStateChangeEvent) {
6670
val payload = Arguments.createMap()
6771
payload.putString(EVENT_PROP_TYPE, "chromecaststatechange")
6872
if (event.state != null) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.theoplayer.media
2+
3+
import android.app.ActivityManager
4+
import android.content.BroadcastReceiver
5+
import android.content.Context
6+
import android.content.Intent
7+
8+
/**
9+
* A MediaButtonReceiver that only forwards MediaButton events if the MediaPlaybackService is
10+
* running, otherwise ignore the event.
11+
* This avoid the service is being restarted when the parent app was closed.
12+
* It also avoids the ForegroundServiceDidNotStartInTimeException, which is sent when the
13+
* service doesn't start in time (within 5 seconds) due to heavy load.
14+
*/
15+
class ConditionalMediaButtonReceiver : BroadcastReceiver() {
16+
17+
override fun onReceive(context: Context, intent: Intent) {
18+
if (Intent.ACTION_MEDIA_BUTTON == intent.action) {
19+
// Only send the intent if service is already running.
20+
if (isServiceRunning(context, MediaPlaybackService::class.java)) {
21+
intent.setClass(context, MediaPlaybackService::class.java)
22+
context.startService(intent)
23+
}
24+
}
25+
}
26+
27+
private fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
28+
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
29+
@Suppress("DEPRECATION") // Only alternative is usage stats, heavier
30+
val runningServices = am.getRunningServices(Int.MAX_VALUE)
31+
for (service in runningServices) {
32+
if (serviceClass.name == service.service.className) {
33+
return true
34+
}
35+
}
36+
return false
37+
}
38+
}

android/src/main/java/com/theoplayer/player/PlayerModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ class PlayerModule(context: ReactApplicationContext) : ReactContextBaseJavaModul
253253
fun setTextTrackStyle(tag: Int, style: ReadableMap?) {
254254
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
255255
view?.player?.let { player ->
256-
TextTrackStyleAdapter.applyTextTrackStyle(player.textTrackStyle, style)
256+
TextTrackStyleAdapter.applyTextTrackStyle(player.textTrackStyle, reactApplicationContext, style)
257257
}
258258
}
259259
}

android/src/main/java/com/theoplayer/source/SourceAdapter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ class SourceAdapter {
427427
private fun parseCmcdTransmissionMode(cmcdConfiguration : JSONObject) : CMCDTransmissionMode {
428428
try {
429429
val transmissionMode = cmcdConfiguration.optInt(CMCD_TRANSMISSION_MODE)
430-
if (transmissionMode === CmcdTransmissionMode.QUERY_ARGUMENT.ordinal) {
430+
if (transmissionMode == CmcdTransmissionMode.QUERY_ARGUMENT.ordinal) {
431431
return CMCDTransmissionMode.QUERY_ARGUMENT
432432
}
433433
return CMCDTransmissionMode.HTTP_HEADER

android/src/main/java/com/theoplayer/theoads/THEOadsEventAdapter.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.theoplayer.android.api.ads.theoads.event.InterstitialEvent
88
import com.theoplayer.android.api.ads.theoads.event.TheoAdsErrorEvent
99
import com.theoplayer.android.api.ads.theoads.event.TheoAdsEvent
1010
import com.theoplayer.android.api.ads.theoads.event.TheoAdsEventTypes
11+
import com.theoplayer.android.api.event.EventListener
1112

1213
private const val EVENT_PROP_TYPE = "type"
1314
private const val EVENT_PROP_INTERSTITIAL = "interstitial"
@@ -27,15 +28,17 @@ class THEOadsEventAdapter(private val api: TheoAdsIntegration, private val emitt
2728
fun emit(payload: WritableMap?)
2829
}
2930

31+
private val onEvent = EventListener<TheoAdsEvent<*>> { handleEvent(it) }
32+
3033
init {
31-
FORWARDED_EVENTS.forEach { api.addEventListener(it, this::onEvent) }
34+
FORWARDED_EVENTS.forEach { api.addEventListener(it, onEvent) }
3235
}
3336

3437
fun destroy() {
35-
FORWARDED_EVENTS.forEach { api.removeEventListener(it, this::onEvent) }
38+
FORWARDED_EVENTS.forEach { api.removeEventListener(it, onEvent) }
3639
}
3740

38-
private fun onEvent(event: TheoAdsEvent<*>) {
41+
private fun handleEvent(event: TheoAdsEvent<*>) {
3942
emitter.emit(Arguments.createMap().apply {
4043
putString(EVENT_PROP_TYPE, event.type.name)
4144
(event as? InterstitialEvent)?.let {

android/src/main/java/com/theoplayer/theolive/THEOliveEventAdapter.kt

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.theoplayer.theolive
22

33
import com.facebook.react.bridge.Arguments
44
import com.facebook.react.bridge.WritableMap
5+
import com.theoplayer.android.api.event.EventListener
56
import com.theoplayer.android.api.event.player.theolive.DistributionLoadStartEvent
67
import com.theoplayer.android.api.event.player.theolive.DistributionOfflineEvent
78
import com.theoplayer.android.api.event.player.theolive.EndpointLoadedEvent
@@ -21,18 +22,30 @@ class THEOliveEventAdapter(private val theoLiveApi: TheoLive, private val emitte
2122
fun emit(payload: WritableMap?)
2223
}
2324

25+
private val onDistributionLoadStart =
26+
EventListener<DistributionLoadStartEvent> { onDistributionLoadStart(it) }
27+
private val onDistributionOffline =
28+
EventListener<DistributionOfflineEvent> { onDistributionOffline(it) }
29+
private val onEndPointLoaded =
30+
EventListener<EndpointLoadedEvent> { onEndPointLoaded(it) }
31+
private val onIntentOfFallback =
32+
EventListener<IntentToFallbackEvent> { onIntentOfFallback(it) }
33+
2434
init {
25-
theoLiveApi.addEventListener(TheoLiveEventTypes.DISTRIBUTIONLOADSTART, this::onDistributionLoadStart)
26-
theoLiveApi.addEventListener(TheoLiveEventTypes.DISTRIBUTIONOFFLINE, this::onDistributionOffline)
27-
theoLiveApi.addEventListener(TheoLiveEventTypes.ENDPOINTLOADED, this::onEndPointLoaded)
28-
theoLiveApi.addEventListener(TheoLiveEventTypes.INTENTTOFALLBACK, this::onIntentOfFallback)
35+
theoLiveApi.addEventListener(TheoLiveEventTypes.DISTRIBUTIONLOADSTART, onDistributionLoadStart)
36+
theoLiveApi.addEventListener(TheoLiveEventTypes.DISTRIBUTIONOFFLINE, onDistributionOffline)
37+
theoLiveApi.addEventListener(TheoLiveEventTypes.ENDPOINTLOADED, onEndPointLoaded)
38+
theoLiveApi.addEventListener(TheoLiveEventTypes.INTENTTOFALLBACK, onIntentOfFallback)
2939
}
3040

3141
fun destroy() {
32-
theoLiveApi.removeEventListener(TheoLiveEventTypes.DISTRIBUTIONLOADSTART, this::onDistributionLoadStart)
33-
theoLiveApi.removeEventListener(TheoLiveEventTypes.DISTRIBUTIONOFFLINE, this::onDistributionOffline)
34-
theoLiveApi.removeEventListener(TheoLiveEventTypes.ENDPOINTLOADED, this::onEndPointLoaded)
35-
theoLiveApi.removeEventListener(TheoLiveEventTypes.INTENTTOFALLBACK, this::onIntentOfFallback)
42+
theoLiveApi.removeEventListener(
43+
TheoLiveEventTypes.DISTRIBUTIONLOADSTART,
44+
onDistributionLoadStart
45+
)
46+
theoLiveApi.removeEventListener(TheoLiveEventTypes.DISTRIBUTIONOFFLINE, onDistributionOffline)
47+
theoLiveApi.removeEventListener(TheoLiveEventTypes.ENDPOINTLOADED, onEndPointLoaded)
48+
theoLiveApi.removeEventListener(TheoLiveEventTypes.INTENTTOFALLBACK, onIntentOfFallback)
3649
}
3750

3851
private fun onDistributionLoadStart(event: DistributionLoadStartEvent) {

0 commit comments

Comments
 (0)