Skip to content

Commit

Permalink
Add rememberPlayer(THEOplayerView) (#40)
Browse files Browse the repository at this point in the history
* Add rememberPlayer(THEOplayerView)
* Create THEOplayerView manually in demo app
* Don't destroy on dispose when using rememberPlayer(THEOplayerView)
  • Loading branch information
MattiasBuelens authored Sep 10, 2024
1 parent 76f828c commit 89ab444
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* 💥 Updated to Jetpack Compose version 1.7.0 ([BOM](https://developer.android.com/jetpack/compose/bom) 2024.09.00).
* 💥 Changed `colors` parameter in `IconButton` and `LiveButton` to be an `IconButtonColors`.
* 🚀 Added support for Android Lollipop (API 21), to align with the THEOplayer Android SDK.
* 🚀 Added `rememberPlayer(THEOplayerView)` to create a `Player` wrapping an existing `THEOplayerView`.

## v1.8.0 (2024-09-06)

Expand Down
17 changes: 10 additions & 7 deletions app/src/main/java/com/theoplayer/android/ui/demo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.google.android.gms.cast.framework.CastContext
import com.theoplayer.android.api.THEOplayerConfig
import com.theoplayer.android.api.THEOplayerView
import com.theoplayer.android.api.ads.ima.GoogleImaIntegrationFactory
import com.theoplayer.android.api.cast.CastConfiguration
import com.theoplayer.android.api.cast.CastIntegrationFactory
Expand Down Expand Up @@ -55,22 +57,23 @@ fun MainContent() {
var stream by rememberSaveable(stateSaver = StreamSaver) { mutableStateOf(streams.first()) }
var streamMenuOpen by remember { mutableStateOf(false) }

val player = rememberPlayer()
LaunchedEffect(player) {
player.theoplayerView?.let { theoplayerView ->
val context = LocalContext.current
val theoplayerView = remember(context) {
THEOplayerView(context).apply {
// Add ads integration through Google IMA
theoplayerView.player.addIntegration(
GoogleImaIntegrationFactory.createGoogleImaIntegration(theoplayerView)
player.addIntegration(
GoogleImaIntegrationFactory.createGoogleImaIntegration(this)
)
// Add Chromecast integration
val castConfiguration = CastConfiguration.Builder().apply {
castStrategy(CastStrategy.AUTO)
}.build()
theoplayerView.player.addIntegration(
CastIntegrationFactory.createCastIntegration(theoplayerView, castConfiguration)
player.addIntegration(
CastIntegrationFactory.createCastIntegration(this, castConfiguration)
)
}
}
val player = rememberPlayer(theoplayerView)
LaunchedEffect(player, stream) {
player.source = stream.source
}
Expand Down
1 change: 1 addition & 0 deletions ui/src/main/java/com/theoplayer/android/ui/Player.kt
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ internal class PlayerImpl(override val theoplayerView: THEOplayerView?) : Player
override val player = theoplayerView?.player
override val ads = theoplayerView?.player?.ads
override var cast by mutableStateOf<Cast?>(null)
private set
override var currentTime by mutableStateOf(0.0)
private set
override var duration by mutableStateOf(Double.NaN)
Expand Down
66 changes: 53 additions & 13 deletions ui/src/main/java/com/theoplayer/android/ui/UIController.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.theoplayer.android.ui

import android.app.Activity
import android.view.ViewGroup
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
Expand Down Expand Up @@ -453,36 +454,75 @@ fun rememberPlayer(config: THEOplayerConfig? = null): Player {
val theoplayerView = if (LocalInspectionMode.current) {
null
} else {
rememberTHEOplayerView(config)
val context = LocalContext.current
remember { THEOplayerView(context, config) }
}

val player = remember(theoplayerView) { PlayerImpl(theoplayerView) }
DisposableEffect(player) {
DisposableEffect(theoplayerView) {
onDispose {
player.dispose()
theoplayerView?.onDestroy()
}
}

return player
return rememberPlayerInternal(theoplayerView)
}

/**
* Creates and remembers a THEOplayer view.
* Create a [Player] wrapping an existing [THEOplayerView].
*
* @param config the player configuration
* The [THEOplayerView] should be [remembered][remember] so it's not re-created on every
* recomposition.
*
* Example usage:
* ```kotlin
* val context = LocalContext.current
* val theoplayerView = remember(context) {
* val config = THEOplayerConfig.Builder().build()
* THEOplayerView(context, config)
* }
* val player = rememberPlayer(theoplayerView)
* ```
*
* This couples the lifecycle of the given [THEOplayerView] to the current activity.
* That is, it automatically calls [THEOplayerView.onPause] and [THEOplayerView.onResume]
* whenever the current activity is [paused][Activity.onPause] or [resumed][Activity.onResume].
*
* The [THEOplayerView] is **not** automatically destroyed when the composition is disposed.
* If you need this, use a [DisposableEffect]:
* ```kotlin
* val player = rememberPlayer(theoplayerView)
* DisposableEffect(theoplayerView) {
* onDispose {
* theoplayerView.onDestroy()
* }
* }
* ```
*
* @param theoplayerView the existing THEOplayer view
*/
@Composable
internal fun rememberTHEOplayerView(config: THEOplayerConfig? = null): THEOplayerView {
val context = LocalContext.current
val theoplayerView = remember { THEOplayerView(context, config) }
var wasPlayingAd by remember { mutableStateOf(false) }
fun rememberPlayer(theoplayerView: THEOplayerView): Player {
return rememberPlayerInternal(theoplayerView)
}

DisposableEffect(theoplayerView) {
@Composable
internal fun rememberPlayerInternal(theoplayerView: THEOplayerView?): Player {
theoplayerView?.let { setupTHEOplayerView(it) }

val player = remember(theoplayerView) { PlayerImpl(theoplayerView) }
DisposableEffect(player) {
onDispose {
theoplayerView.onDestroy()
player.dispose()
}
}

return player
}

@Composable
internal fun setupTHEOplayerView(theoplayerView: THEOplayerView): THEOplayerView {
var wasPlayingAd by remember { mutableStateOf(false) }

val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle, theoplayerView) {
val lifecycleObserver = LifecycleEventObserver { _, event ->
Expand Down

0 comments on commit 89ab444

Please sign in to comment.