Browse Source

Constrain live preview while keeping stream controls visible

master
evgeny 1 week ago
parent
commit
18661f0ef0
  1. 2
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamController.kt
  2. 23
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/StreamScreen.kt

2
tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamController.kt

@ -76,7 +76,7 @@ object StreamController {
fun ended(id: Long, owner: StreamService) {
if (service === owner) service = null
val st = mutable.value ?: return
if (st.id != id) return
if (st.id != id || !st.ongoing) return
capturing = false
mutable.value = st.copy(phase = if (st.error != null) StreamPhase.FAILED else StreamPhase.ENDED)
LogManager.addLog("INFO", "stream", "publisher released id=$id")

23
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/StreamScreen.kt

@ -10,11 +10,14 @@ import android.view.TextureView
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -111,11 +114,15 @@ fun StreamScreen(channelId: String, channelName: String, onBack: () -> Unit) {
}
local?.error?.let { Text(stringResource(it), color = MaterialTheme.colorScheme.error) }
if (sources.isEmpty() && local?.ongoing != true) Text(stringResource(R.string.stream_none))
sources.forEach { source ->
TextButton(onClick = { selected = source }) { Text(stringResource(R.string.stream_watch, source.name.ifEmpty { source.id.toULong().toString() })) }
Column(Modifier.fillMaxWidth().heightIn(max = 160.dp).verticalScroll(rememberScrollState())) {
sources.forEach { source ->
TextButton(onClick = { selected = source }) { Text(stringResource(R.string.stream_watch, source.name.ifEmpty { source.id.toULong().toString() })) }
}
}
selected?.let { source ->
key(group, source.id, source.epoch) { LiveStreamView(group, source.id, source.self) }
Box(Modifier.weight(1f).fillMaxWidth()) {
key(group, source.id, source.epoch) { LiveStreamView(group, source.id, source.self) }
}
TextButton(onClick = { selected = null }) { Text(stringResource(R.string.stream_close_view)) }
}
}
@ -129,8 +136,10 @@ private fun LiveStreamView(group: Long, source: Long, localPreview: Boolean) {
val status by engine.status.collectAsState()
var surface by remember { mutableStateOf<Surface?>(null) }
DisposableEffect(engine) { onDispose { engine.stop() } }
Text(stringResource(status))
AndroidView(factory = { context ->
Column(Modifier.fillMaxSize()) {
Text(stringResource(status))
Box(Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
AndroidView(factory = { context ->
TextureView(context).apply {
surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
@ -143,7 +152,9 @@ private fun LiveStreamView(group: Long, source: Long, localPreview: Boolean) {
override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {}
}
}
}, modifier = Modifier.fillMaxWidth().aspectRatio(aspect), onRelease = { view ->
}, modifier = Modifier.aspectRatio(aspect, matchHeightConstraintsFirst = true), onRelease = { view ->
engine.stop(); view.surfaceTextureListener = null; surface?.release(); surface = null
})
}
}
}

Loading…
Cancel
Save