Skip to content

Commit caa49b3

Browse files
committed
Improve AuraCanvasOverlay: switch to TextureView, add error handling and fade-in animation
1 parent b280ca6 commit caa49b3

1 file changed

Lines changed: 55 additions & 12 deletions

File tree

app/src/main/kotlin/com/auramusic/app/ui/player/AuraCanvasOverlay.kt

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
package com.auramusic.app.ui.player
77

8+
import android.view.TextureView
89
import android.view.ViewGroup
10+
import androidx.compose.animation.core.animateFloatAsState
11+
import androidx.compose.animation.core.tween
912
import androidx.compose.foundation.layout.Box
1013
import androidx.compose.foundation.layout.fillMaxSize
1114
import androidx.compose.runtime.Composable
@@ -16,15 +19,17 @@ import androidx.compose.runtime.mutableStateOf
1619
import androidx.compose.runtime.remember
1720
import androidx.compose.runtime.setValue
1821
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.draw.alpha
1923
import androidx.compose.ui.platform.LocalContext
2024
import androidx.compose.ui.viewinterop.AndroidView
2125
import androidx.media3.common.MediaItem
26+
import androidx.media3.common.PlaybackException
2227
import androidx.media3.common.Player
2328
import androidx.media3.common.util.UnstableApi
2429
import androidx.media3.exoplayer.ExoPlayer
2530
import androidx.media3.ui.AspectRatioFrameLayout
26-
import androidx.media3.ui.PlayerView
2731
import com.auramusic.app.playback.AuraCanvasRepository
32+
import timber.log.Timber
2833

2934
/**
3035
* Looping, muted MP4 overlay that renders the matching AuraCanvas video
@@ -42,10 +47,12 @@ fun AuraCanvasOverlay(
4247
) {
4348
val context = LocalContext.current
4449
var canvasUrl by remember { mutableStateOf<String?>(null) }
50+
var isVideoReady by remember { mutableStateOf(false) }
4551

4652
// Resolve the URL for the current (title, artist). Cached in the repo.
4753
LaunchedEffect(title, artist) {
4854
canvasUrl = null
55+
isVideoReady = false
4956
canvasUrl = runCatching {
5057
AuraCanvasRepository.findCanvasUrl(title, artist)
5158
}.getOrNull()
@@ -61,34 +68,70 @@ fun AuraCanvasOverlay(
6168
}
6269
}
6370

71+
// Attach listener for errors and first frame
72+
DisposableEffect(exoPlayer) {
73+
val listener = object : Player.Listener {
74+
override fun onPlayerError(error: PlaybackException) {
75+
Timber.e("AuraCanvas playback error: ${error.errorCodeName} - ${error.message}")
76+
isVideoReady = false
77+
}
78+
79+
override fun onRenderedFirstFrame() {
80+
isVideoReady = true
81+
}
82+
}
83+
exoPlayer.addListener(listener)
84+
onDispose {
85+
exoPlayer.removeListener(listener)
86+
}
87+
}
88+
6489
// Swap the source whenever the resolved URL changes.
6590
LaunchedEffect(url) {
91+
isVideoReady = false
92+
exoPlayer.stop()
6693
exoPlayer.setMediaItem(MediaItem.fromUri(url))
6794
exoPlayer.prepare()
6895
exoPlayer.playWhenReady = true
6996
}
7097

71-
DisposableEffect(Unit) {
72-
onDispose { exoPlayer.release() }
98+
DisposableEffect(exoPlayer) {
99+
onDispose {
100+
exoPlayer.release()
101+
}
73102
}
74103

104+
val alpha by animateFloatAsState(
105+
targetValue = if (isVideoReady) 1f else 0f,
106+
animationSpec = tween(durationMillis = 250),
107+
label = "canvasFade"
108+
)
109+
75110
Box(modifier = modifier.fillMaxSize()) {
76111
AndroidView(
77112
factory = { ctx ->
78-
PlayerView(ctx).apply {
79-
player = exoPlayer
80-
useController = false
81-
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
82-
setBackgroundColor(android.graphics.Color.TRANSPARENT)
83-
setShutterBackgroundColor(android.graphics.Color.TRANSPARENT)
113+
AspectRatioFrameLayout(ctx).apply {
84114
layoutParams = ViewGroup.LayoutParams(
85115
ViewGroup.LayoutParams.MATCH_PARENT,
86-
ViewGroup.LayoutParams.MATCH_PARENT,
116+
ViewGroup.LayoutParams.MATCH_PARENT
87117
)
118+
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
119+
120+
val textureView = TextureView(ctx).apply {
121+
layoutParams = ViewGroup.LayoutParams(
122+
ViewGroup.LayoutParams.MATCH_PARENT,
123+
ViewGroup.LayoutParams.MATCH_PARENT
124+
)
125+
}
126+
addView(textureView)
127+
exoPlayer.setVideoTextureView(textureView)
128+
setBackgroundColor(android.graphics.Color.TRANSPARENT)
88129
}
89130
},
90-
modifier = Modifier.fillMaxSize(),
91-
update = { it.player = exoPlayer },
131+
modifier = Modifier
132+
.fillMaxSize()
133+
.alpha(alpha),
134+
update = { /* no-op */ }
92135
)
93136
}
94137
}

0 commit comments

Comments
 (0)