Skip to content

Commit 33044ad

Browse files
Merge pull request #69 from THEOplayer/release/1.11.1
Release 1.11.1
2 parents 0fe7f0c + df8c068 commit 33044ad

5 files changed

Lines changed: 37 additions & 150 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
> - 🏠 Internal
1010
> - 💅 Polish
1111
12+
## v1.11.1 (2025-08-01)
13+
14+
* 🐛 Fixed clicking on overlays from OptiView Ads not working. ([#68](https://github.com/THEOplayer/android-ui/pull/68))
15+
1216
## v1.11.0 (2025-04-29)
1317

1418
* 💥 Bumped `compileSdk` to API 35 (Android 15).

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ org.gradle.configuration-cache=true
2727
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
2828
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
2929
# The version of the THEOplayer Open Video UI for Android.
30-
version=1.11.0
30+
version=1.11.1

ui/src/main/java/com/theoplayer/android/ui/Modifiers.kt

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
11
package com.theoplayer.android.ui
22

33
import androidx.annotation.FloatRange
4-
import androidx.compose.foundation.gestures.awaitFirstDown
54
import androidx.compose.foundation.gestures.detectTapGestures
6-
import androidx.compose.foundation.gestures.waitForUpOrCancellation
7-
import androidx.compose.foundation.interaction.MutableInteractionSource
8-
import androidx.compose.foundation.interaction.PressInteraction
9-
import androidx.compose.runtime.DisposableEffect
10-
import androidx.compose.runtime.LaunchedEffect
115
import androidx.compose.runtime.State
12-
import androidx.compose.runtime.getValue
13-
import androidx.compose.runtime.mutableStateOf
14-
import androidx.compose.runtime.remember
15-
import androidx.compose.runtime.rememberCoroutineScope
16-
import androidx.compose.runtime.setValue
176
import androidx.compose.ui.Modifier
18-
import androidx.compose.ui.composed
19-
import androidx.compose.ui.geometry.Offset
20-
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
217
import androidx.compose.ui.input.pointer.PointerEventPass
22-
import androidx.compose.ui.input.pointer.PointerInputChange
238
import androidx.compose.ui.input.pointer.PointerInputScope
24-
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
25-
import androidx.compose.ui.input.pointer.isOutOfBounds
269
import androidx.compose.ui.input.pointer.pointerInput
2710
import androidx.compose.ui.layout.IntrinsicMeasurable
2811
import androidx.compose.ui.layout.IntrinsicMeasureScope
@@ -42,94 +25,6 @@ import kotlinx.coroutines.isActive
4225
import kotlinx.coroutines.launch
4326
import kotlin.math.roundToInt
4427

45-
internal fun Modifier.pressable(
46-
interactionSource: MutableInteractionSource,
47-
enabled: Boolean = true,
48-
requireUnconsumed: Boolean = true
49-
): Modifier = composed(
50-
inspectorInfo = debugInspectorInfo {
51-
name = "pressable"
52-
properties["interactionSource"] = interactionSource
53-
properties["enabled"] = enabled
54-
properties["requireUnconsumed"] = requireUnconsumed
55-
}
56-
) {
57-
val scope = rememberCoroutineScope()
58-
var pressedInteraction by remember { mutableStateOf<PressInteraction.Press?>(null) }
59-
60-
suspend fun emitPress(pressPosition: Offset) {
61-
if (pressedInteraction == null) {
62-
val interaction = PressInteraction.Press(pressPosition)
63-
interactionSource.emit(interaction)
64-
pressedInteraction = interaction
65-
}
66-
}
67-
68-
suspend fun emitRelease() {
69-
pressedInteraction?.let { oldValue ->
70-
val interaction = PressInteraction.Release(oldValue)
71-
interactionSource.emit(interaction)
72-
pressedInteraction = null
73-
}
74-
}
75-
76-
fun tryEmitCancel() {
77-
pressedInteraction?.let { oldValue ->
78-
val interaction = PressInteraction.Cancel(oldValue)
79-
interactionSource.tryEmit(interaction)
80-
pressedInteraction = null
81-
}
82-
}
83-
84-
DisposableEffect(interactionSource) {
85-
onDispose { tryEmitCancel() }
86-
}
87-
LaunchedEffect(enabled) {
88-
if (!enabled) {
89-
emitRelease()
90-
}
91-
}
92-
93-
if (enabled) {
94-
Modifier
95-
.pointerInput(interactionSource) {
96-
val currentContext = currentCoroutineContext()
97-
awaitPointerEventScope {
98-
while (currentContext.isActive) {
99-
val down = awaitFirstDown(requireUnconsumed = requireUnconsumed)
100-
scope.launch { emitPress(down.position) }
101-
val up =
102-
if (requireUnconsumed) waitForUpOrCancellation() else waitForUpOrCancellationIgnoreConsumed()
103-
if (up == null) {
104-
tryEmitCancel()
105-
} else {
106-
scope.launch { emitRelease() }
107-
}
108-
}
109-
}
110-
}
111-
} else {
112-
Modifier
113-
}
114-
}
115-
116-
/**
117-
* Like [AwaitPointerEventScope.waitForUpOrCancellation],
118-
* but skips the [PointerInputChange.isConsumed] checks.
119-
*/
120-
private suspend fun AwaitPointerEventScope.waitForUpOrCancellationIgnoreConsumed(): PointerInputChange? {
121-
while (true) {
122-
val event = awaitPointerEvent(PointerEventPass.Main)
123-
if (event.changes.all { it.changedToUpIgnoreConsumed() }) {
124-
// All pointers are up
125-
return event.changes[0]
126-
}
127-
if (event.changes.any { it.isOutOfBounds(size, extendedTouchPadding) }) {
128-
return null // Canceled
129-
}
130-
}
131-
}
132-
13328
internal fun Modifier.toggleControlsOnTap(
13429
controlsVisible: State<Boolean>,
13530
showControlsTemporarily: () -> Unit,

ui/src/main/java/com/theoplayer/android/ui/Player.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import androidx.compose.runtime.MutableState
66
import androidx.compose.runtime.State
77
import androidx.compose.runtime.derivedStateOf
88
import androidx.compose.runtime.getValue
9+
import androidx.compose.runtime.mutableDoubleStateOf
10+
import androidx.compose.runtime.mutableIntStateOf
911
import androidx.compose.runtime.mutableStateListOf
1012
import androidx.compose.runtime.mutableStateOf
1113
import androidx.compose.runtime.setValue
@@ -296,9 +298,9 @@ enum class StreamType {
296298
internal class PlayerImpl(override val theoplayerView: THEOplayerView?) : Player {
297299
override val player = theoplayerView?.player
298300
override val ads = theoplayerView?.player?.ads
299-
override var currentTime by mutableStateOf(0.0)
301+
override var currentTime by mutableDoubleStateOf(0.0)
300302
private set
301-
override var duration by mutableStateOf(Double.NaN)
303+
override var duration by mutableDoubleStateOf(Double.NaN)
302304
private set
303305
override var seekable by mutableStateOf(TimeRanges.empty())
304306
private set
@@ -312,9 +314,9 @@ internal class PlayerImpl(override val theoplayerView: THEOplayerView?) : Player
312314
private set
313315
override var readyState by mutableStateOf(ReadyState.HAVE_NOTHING)
314316
private set
315-
override var videoWidth by mutableStateOf(0)
317+
override var videoWidth by mutableIntStateOf(0)
316318
private set
317-
override var videoHeight by mutableStateOf(0)
319+
override var videoHeight by mutableIntStateOf(0)
318320
private set
319321
override var firstPlay by mutableStateOf(false)
320322
private set
@@ -401,7 +403,7 @@ internal class PlayerImpl(override val theoplayerView: THEOplayerView?) : Player
401403
player?.source = value
402404
}
403405

404-
private var _volume by mutableStateOf(1.0)
406+
private var _volume by mutableDoubleStateOf(1.0)
405407
private var _muted by mutableStateOf(false)
406408
override var volume: Double
407409
get() = _volume
@@ -423,7 +425,7 @@ internal class PlayerImpl(override val theoplayerView: THEOplayerView?) : Player
423425

424426
private val volumeChangeListener = EventListener<VolumeChangeEvent> { updateVolumeAndMuted() }
425427

426-
private var _playbackRate by mutableStateOf(1.0)
428+
private var _playbackRate by mutableDoubleStateOf(1.0)
427429
override var playbackRate: Double
428430
get() = _playbackRate
429431
set(value) {

ui/src/main/java/com/theoplayer/android/ui/UIController.kt

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import androidx.compose.animation.slideOutHorizontally
1717
import androidx.compose.animation.slideOutVertically
1818
import androidx.compose.animation.togetherWith
1919
import androidx.compose.foundation.background
20-
import androidx.compose.foundation.interaction.Interaction
21-
import androidx.compose.foundation.interaction.MutableInteractionSource
22-
import androidx.compose.foundation.interaction.collectIsPressedAsState
2320
import androidx.compose.foundation.layout.Arrangement
2421
import androidx.compose.foundation.layout.Box
2522
import androidx.compose.foundation.layout.Column
@@ -32,6 +29,7 @@ import androidx.compose.runtime.DisposableEffect
3229
import androidx.compose.runtime.LaunchedEffect
3330
import androidx.compose.runtime.derivedStateOf
3431
import androidx.compose.runtime.getValue
32+
import androidx.compose.runtime.mutableIntStateOf
3533
import androidx.compose.runtime.mutableStateListOf
3634
import androidx.compose.runtime.mutableStateOf
3735
import androidx.compose.runtime.remember
@@ -68,9 +66,6 @@ import kotlin.time.DurationUnit
6866
* @param modifier the [Modifier] to be applied to this container
6967
* @param config the player configuration to be used when constructing the [THEOplayerView]
7068
* @param source the source description to load into the player
71-
* @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
72-
* for this container. You can create and pass in your own `remember`ed instance to observe
73-
* [Interaction]s and customize the behavior of this container.
7469
* @param color the background color for the overlay while showing the UI controls
7570
* @param centerOverlay content to show in the center of the player, typically a [LoadingSpinner].
7671
* @param errorOverlay content to show when the player encountered a fatal error,
@@ -85,7 +80,6 @@ fun UIController(
8580
modifier: Modifier = Modifier,
8681
config: THEOplayerConfig,
8782
source: SourceDescription? = null,
88-
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
8983
color: Color = Color.Black,
9084
centerOverlay: (@Composable UIControllerScope.() -> Unit)? = null,
9185
errorOverlay: (@Composable UIControllerScope.() -> Unit)? = null,
@@ -101,7 +95,6 @@ fun UIController(
10195
UIController(
10296
modifier = modifier,
10397
player = player,
104-
interactionSource = interactionSource,
10598
color = color,
10699
centerOverlay = centerOverlay,
107100
errorOverlay = errorOverlay,
@@ -120,9 +113,6 @@ fun UIController(
120113
*
121114
* @param modifier the [Modifier] to be applied to this container
122115
* @param player the player. This should always be created using [rememberPlayer].
123-
* @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
124-
* for this container. You can create and pass in your own `remember`ed instance to observe
125-
* [Interaction]s and customize the behavior of this container.
126116
* @param color the background color for the overlay while showing the UI controls
127117
* @param centerOverlay content to show in the center of the player, typically a [LoadingSpinner].
128118
* @param errorOverlay content to show when the player encountered a fatal error,
@@ -136,15 +126,14 @@ fun UIController(
136126
fun UIController(
137127
modifier: Modifier = Modifier,
138128
player: Player = rememberPlayer(),
139-
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
140129
color: Color = Color.Black,
141130
centerOverlay: (@Composable UIControllerScope.() -> Unit)? = null,
142131
errorOverlay: (@Composable UIControllerScope.() -> Unit)? = null,
143132
topChrome: (@Composable UIControllerScope.() -> Unit)? = null,
144133
centerChrome: (@Composable UIControllerScope.() -> Unit)? = null,
145134
bottomChrome: (@Composable UIControllerScope.() -> Unit)? = null
146135
) {
147-
var tapCount by remember { mutableStateOf(0) }
136+
var tapCount by remember { mutableIntStateOf(0) }
148137
var isRecentlyTapped by remember { mutableStateOf(false) }
149138
LaunchedEffect(tapCount) {
150139
if (tapCount > 0) {
@@ -153,7 +142,6 @@ fun UIController(
153142
isRecentlyTapped = false
154143
}
155144
}
156-
val isPressed by interactionSource.collectIsPressedAsState()
157145
var forceControlsHidden by remember { mutableStateOf(false) }
158146

159147
// Wait a little bit before showing the controls and enabling animations,
@@ -175,7 +163,7 @@ fun UIController(
175163
} else if (forceControlsHidden) {
176164
false
177165
} else {
178-
isRecentlyTapped || isPressed || player.paused
166+
isRecentlyTapped || player.paused
179167
}
180168
}
181169
}
@@ -216,27 +204,29 @@ fun UIController(
216204
)
217205
)
218206

219-
PlayerContainer(modifier = modifier, player = player) {
207+
PlayerContainer(
208+
player = player,
209+
modifier = Modifier
210+
.background(Color.Black)
211+
.then(modifier)
212+
.playerAspectRatio(player)
213+
.toggleControlsOnTap(
214+
controlsVisible = controlsVisible,
215+
showControlsTemporarily = {
216+
forceControlsHidden = false
217+
tapCount++
218+
},
219+
hideControls = {
220+
forceControlsHidden = true
221+
tapCount++
222+
}
223+
)
224+
) {
220225
CompositionLocalProvider(LocalPlayer provides player) {
221-
if (player.playingAd) {
222-
// Remove player UI entirely while playing an ad, to make clickthrough work
223-
return@CompositionLocalProvider
224-
}
225226
AnimatedContent(
226227
label = "ContentAnimation",
227228
modifier = Modifier
228-
.background(background)
229-
.pressable(interactionSource = interactionSource, requireUnconsumed = false)
230-
.toggleControlsOnTap(
231-
controlsVisible = controlsVisible,
232-
showControlsTemporarily = {
233-
forceControlsHidden = false
234-
tapCount++
235-
},
236-
hideControls = {
237-
forceControlsHidden = true
238-
tapCount++
239-
}),
229+
.background(background),
240230
targetState = uiState,
241231
transitionSpec = {
242232
if (targetState is UIState.Error) {
@@ -332,13 +322,9 @@ private fun PlayerContainer(
332322
ui: @Composable () -> Unit
333323
) {
334324
val theoplayerView = player.theoplayerView
335-
val containerModifier = Modifier
336-
.background(Color.Black)
337-
.then(modifier)
338-
.playerAspectRatio(player)
339325
if (theoplayerView == null) {
340326
Box(
341-
modifier = containerModifier
327+
modifier = modifier
342328
) {
343329
ui()
344330
}
@@ -348,7 +334,7 @@ private fun PlayerContainer(
348334
var composeView by remember { mutableStateOf<ComposeView?>(null) }
349335

350336
AndroidView(
351-
modifier = containerModifier,
337+
modifier = modifier,
352338
factory = { context ->
353339
uiContainer =
354340
theoplayerView.findViewById(com.theoplayer.android.R.id.theo_ui_container)

0 commit comments

Comments
 (0)