Open Android stream controls from own multiview tile

This commit is contained in:
evgeny committed 2026-10-20 20:11:49 +02:00
1 parent ca7e169811
commit 39bca58515
2 files changed
+35 -7

No files matched your search

@@ -50,7 +50,8 @@ private class GridStream(val source: StreamSource) {
* Пустой каталог оставляет экран открытым; временный сбой каталога сохраняет действующие readers. */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun StreamGridScreen(group: Long, title: String, sources: List<StreamSource>, unavailable: Boolean, onClose: () -> Unit) {
internal fun StreamGridScreen(group: Long, title: String, sources: List<StreamSource>, unavailable: Boolean,
ownPreviewVisible: Boolean, onOwnStream: () -> Unit, onClose: () -> Unit) {
val tiles = remember(group) { mutableStateListOf<GridStream>() }
var expanded by remember(group) { mutableStateOf<GridStream?>(null) }
val focused = expanded?.takeIf { it in tiles }
@@ -106,7 +107,7 @@ internal fun StreamGridScreen(group: Long, title: String, sources: List<StreamSo
delay(250)
}
}
Scaffold(topBar = {
Scaffold(modifier = if (ownPreviewVisible) Modifier else Modifier.clearAndSetSemantics {}, topBar = {
if (focused == null)
TopAppBar(title = { Text(stringResource(R.string.stream_all_title, title), maxLines = 1, overflow = TextOverflow.Ellipsis) },
navigationIcon = { IconButton(onClick = onClose) { Icon(Icons.Filled.Close, stringResource(R.string.stream_close_view)) } })
@@ -124,8 +125,10 @@ internal fun StreamGridScreen(group: Long, title: String, sources: List<StreamSo
}
Layout(content = {
for (tile in tiles) key(tile.source.id, tile.source.epoch) {
StreamGridTile(group, tile, visible, tile === focused, focused != null && tile !== focused) {
expanded = if (tile === focused) null else tile
StreamGridTile(group, tile, visible && (!tile.source.self || ownPreviewVisible),
tile === focused, focused != null && tile !== focused) {
if (tile.source.self) onOwnStream()
else expanded = if (tile === focused) null else tile
}
}
}, modifier = Modifier.fillMaxSize()) { children, constraints ->
@@ -179,7 +182,8 @@ private fun StreamGridTile(group: Long, tile: GridStream, visible: Boolean, expa
}
}
Box(Modifier.matchParentSize().clickable(interactionSource = remember { MutableInteractionSource() }, indication = null,
role = Role.Button, onClickLabel = stringResource(if (expanded) R.string.collapse else R.string.expand), onClick = onClick)
role = Role.Button, onClickLabel = stringResource(if (tile.source.self) R.string.stream_manage
else if (expanded) R.string.collapse else R.string.expand), onClick = onClick)
.semantics { contentDescription = tile.source.displayName })
}
}
@@ -11,6 +11,7 @@ import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.view.TextureView
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
@@ -27,6 +28,7 @@ import androidx.compose.material.icons.filled.ScreenRotation
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.geometry.CornerRadius
@@ -75,10 +77,24 @@ import kotlin.math.roundToInt
fun StreamScreen(channelId: String, channelName: String, sources: List<StreamSource>, selected: StreamSource?,
sourcesFailed: Boolean, onSelect: (StreamSource?) -> Unit, viewAll: Boolean = false,
onBack: () -> Unit) {
val context = LocalContext.current
val group = remember(channelId) { channelId.toULongOrNull()?.toLong() ?: 0L }
if (viewAll) {
StreamGridScreen(group, channelName, sources, sourcesFailed) { onSelect(null); onBack() }
var manageOwn by rememberSaveable(group) { mutableStateOf(false) }
LaunchedEffect(manageOwn) {
LogManager.addLog("INFO", "stream", "multiview controls group=$group open=$manageOwn " +
"publisher=${StreamController.state.value?.id} readersPreserved=true")
}
Box(Modifier.fillMaxSize()) {
StreamGridScreen(group, channelName, sources, sourcesFailed, ownPreviewVisible = !manageOwn, onOwnStream = {
val publisher = StreamController.state.value
if (publisher?.ongoing == true && publisher.group == group) manageOwn = true
else LogManager.addLog("WARN", "stream", "multiview controls stale group=$group publisher=${publisher?.id} phase=${publisher?.phase}")
}, onClose = { onSelect(null); onBack() })
if (manageOwn) {
BackHandler { manageOwn = false }
StreamPublishScreen(group, channelName) { manageOwn = false }
}
}
return
}
if (selected != null && !selected.self) {
@@ -87,6 +103,14 @@ fun StreamScreen(channelId: String, channelName: String, sources: List<StreamSou
}
return
}
StreamPublishScreen(group, channelName, onBack)
}
/** Управление публикацией используется отдельно и поверх сетки, не меняя выбор просмотра. */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun StreamPublishScreen(group: Long, channelName: String, onBack: () -> Unit) {
val context = LocalContext.current
val displayDegrees = StreamDisplayDegrees()
val state by StreamController.state.collectAsState()
val local = state?.takeIf { it.group == group }