Replace stream rotation menu with three marked toggle buttons
This commit is contained in:
1 parent
214a98efb1
commit
8bee8c10ff
1 file changed
+54
-31
@@ -9,25 +9,34 @@ import android.view.TextureView
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowUpward
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
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.draw.rotate
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.selected
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -166,10 +175,13 @@ private fun CaptureOptions(cameras: List<StreamCamera>, config: StreamCaptureCon
|
||||
}, label = { Text("${camera.lens.label} ${camera.lens.mm}".trim()) })
|
||||
}
|
||||
}
|
||||
val modes = cameras.firstOrNull { it.id == mode?.camera }?.modes.orEmpty()
|
||||
val camera = cameras.firstOrNull { it.id == mode?.camera }
|
||||
val modes = camera?.modes.orEmpty()
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
val display = remember(configuration) { ((context as? Activity)?.windowManager?.defaultDisplay?.rotation ?: 0) * 90 }
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var orientationsExpanded by remember { mutableStateOf(false) }
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Box {
|
||||
OutlinedButton(onClick = { expanded = true }, enabled = enabled && modes.isNotEmpty()) { Text(mode?.label.orEmpty()) }
|
||||
DropdownMenu(expanded = expanded && enabled, onDismissRequest = { expanded = false }) {
|
||||
@@ -178,19 +190,48 @@ private fun CaptureOptions(cameras: List<StreamCamera>, config: StreamCaptureCon
|
||||
}) }
|
||||
}
|
||||
}
|
||||
Box {
|
||||
OutlinedButton(onClick = { orientationsExpanded = true }, enabled = enabled && config != null) {
|
||||
Text(stringResource(R.string.stream_orientation, config?.orientation?.degrees ?: 0))
|
||||
}
|
||||
DropdownMenu(expanded = orientationsExpanded && enabled, onDismissRequest = { orientationsExpanded = false }) {
|
||||
StreamOrientation.values().forEach { option -> DropdownMenuItem(text = { Text("${option.degrees}°") }, onClick = {
|
||||
orientationsExpanded = false; config?.let { onChange(it.copy(orientation = option)) }
|
||||
}) }
|
||||
for (option in listOf(StreamOrientation.UPRIGHT, StreamOrientation.RIGHT, StreamOrientation.LEFT)) {
|
||||
val active = config?.orientation == option
|
||||
val label = stringResource(R.string.stream_orientation, option.degrees)
|
||||
val top = if (camera?.lens?.type == LensType.SELFIE) (display - option.degrees + 360) % 360
|
||||
else (option.degrees - display + 360) % 360
|
||||
OutlinedButton(onClick = { config?.let { onChange(it.copy(orientation = option)) } }, enabled = enabled && config != null,
|
||||
modifier = Modifier.size(44.dp).semantics { selected = active; contentDescription = label },
|
||||
shape = RoundedCornerShape(6.dp), contentPadding = PaddingValues(2.dp),
|
||||
border = BorderStroke(if (active) 2.dp else 1.dp,
|
||||
if (active) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline),
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
containerColor = if (active) MaterialTheme.colorScheme.primaryContainer else Color.Transparent)) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
StreamRotationMark(top)
|
||||
Text("${option.degrees}°", fontSize = 10.sp, lineHeight = 10.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StreamRotationMark(top: Int) {
|
||||
val frame = LocalContentColor.current
|
||||
val marker = MaterialTheme.colorScheme.error
|
||||
Box(Modifier.size(24.dp), contentAlignment = Alignment.Center) {
|
||||
Canvas(Modifier.matchParentSize()) {
|
||||
val left = size.width * 0.15f; val right = size.width * 0.85f
|
||||
val upper = size.height * 0.08f; val lower = size.height * 0.92f
|
||||
drawRoundRect(frame, Offset(left, upper), Size(right - left, lower - upper), CornerRadius(2.dp.toPx()), style = Stroke(1.dp.toPx()))
|
||||
val edge = when (top) {
|
||||
90 -> Offset(right, upper) to Offset(right, lower)
|
||||
180 -> Offset(left, lower) to Offset(right, lower)
|
||||
270 -> Offset(left, upper) to Offset(left, lower)
|
||||
else -> Offset(left, upper) to Offset(right, upper)
|
||||
}
|
||||
drawLine(marker, edge.first, edge.second, 3.dp.toPx(), StrokeCap.Round)
|
||||
}
|
||||
Text(stringResource(R.string.stream_top), fontSize = 7.sp, lineHeight = 7.sp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LiveStreamView(group: Long, source: Long, epoch: Long, localPreview: StreamPreview?) {
|
||||
val owner = LocalLifecycleOwner.current
|
||||
@@ -215,7 +256,6 @@ private fun LiveStreamView(group: Long, source: Long, epoch: Long, localPreview:
|
||||
private fun LiveStreamSurface(group: Long, source: Long, epoch: Long, localPreview: StreamPreview?, displayDegrees: Int) {
|
||||
val engine = remember(group, source, epoch, localPreview) { StreamPlayerEngine(group, source, epoch, localPreview, displayDegrees) }
|
||||
val aspect by engine.aspect.collectAsState()
|
||||
val top by engine.top.collectAsState()
|
||||
val status by engine.status.collectAsState()
|
||||
var surface by remember { mutableStateOf<Surface?>(null) }
|
||||
DisposableEffect(engine) { onDispose { engine.stop() } }
|
||||
@@ -240,25 +280,8 @@ private fun LiveStreamSurface(group: Long, source: Long, epoch: Long, localPrevi
|
||||
}, modifier = Modifier.matchParentSize(), onRelease = { view ->
|
||||
engine.stop(); view.surfaceTextureListener = null; surface?.release(); surface = null
|
||||
})
|
||||
top?.let { StreamTopMarker(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.StreamTopMarker(direction: Int) {
|
||||
val alignment = when (direction) {
|
||||
90 -> Alignment.CenterEnd
|
||||
180 -> Alignment.BottomCenter
|
||||
270 -> Alignment.CenterStart
|
||||
else -> Alignment.TopCenter
|
||||
}
|
||||
Surface(modifier = Modifier.align(alignment).padding(8.dp), shape = RoundedCornerShape(6.dp), color = Color.Black.copy(alpha = 0.65f)) {
|
||||
Row(Modifier.padding(horizontal = 8.dp, vertical = 4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(Icons.Filled.ArrowUpward, contentDescription = null, tint = Color.White, modifier = Modifier.size(16.dp).rotate(direction.toFloat()))
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(stringResource(R.string.stream_top), color = Color.White, style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user