Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rememberPlayer(THEOplayerView) #40

Merged
merged 6 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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