Rotate call video independently of Android call controls
This commit is contained in:
1 parent
78246e249e
commit
0c214dfca0
2 files changed
+25
-32
No files matched your search
@@ -159,9 +159,11 @@ fun CallScreen(
|
||||
if (videoActive) { view.keepScreenOn = previous; LogManager.addLog("INFO", "call", "video screen wake policy restored") }
|
||||
}
|
||||
}
|
||||
// Ориентация принадлежит экрану звонка; краткое переподключение Surface не должно поворачивать его обратно.
|
||||
DisposableEffect(video, phase == CallPhase.ACTIVE, selectedVideo) {
|
||||
// Геометрию видео меняет TextureView; Activity сохраняет ориентацию окна звонка.
|
||||
DisposableEffect(context) {
|
||||
val activity = context.findActivity(); val previous = activity?.requestedOrientation
|
||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
LogManager.addLog("INFO", "call", "call screen orientation locked; video rotates independently")
|
||||
onDispose {
|
||||
if (activity != null && previous != null && activity.requestedOrientation != previous) {
|
||||
activity.requestedOrientation = previous
|
||||
@@ -169,28 +171,6 @@ fun CallScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
val rotated = remote?.rotation?.rem(180) != 0
|
||||
val localFormat = local?.format
|
||||
val localRotated = (localFormat?.camera?.sensor ?: 0) % 180 != 0
|
||||
val videoWidth = if (expandedVideo == CallVideoSource.LOCAL) {
|
||||
if (localRotated) localFormat?.height else localFormat?.width
|
||||
}
|
||||
else if (rotated) remote?.height else remote?.width
|
||||
val videoHeight = if (expandedVideo == CallVideoSource.LOCAL) {
|
||||
if (localRotated) localFormat?.width else localFormat?.height
|
||||
}
|
||||
else if (rotated) remote?.width else remote?.height
|
||||
LaunchedEffect(video, phase, expandedVideo, videoWidth, videoHeight) {
|
||||
val activity = context.findActivity()
|
||||
if (viewingVideo && videoWidth != null && videoHeight != null && activity != null) {
|
||||
val orientation = if (videoWidth > videoHeight) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
else ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
|
||||
if (activity.requestedOrientation != orientation) {
|
||||
activity.requestedOrientation = orientation
|
||||
LogManager.addLog("INFO", "call", "video screen orientation=$orientation source=$expandedVideo display=${videoWidth}x$videoHeight")
|
||||
}
|
||||
}
|
||||
}
|
||||
val hasBtPermission = Build.VERSION.SDK_INT < Build.VERSION_CODES.S ||
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) ==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -94,12 +95,13 @@ internal fun CallVideoStage(video: CallVideoEngine, expanded: CallVideoSource?,
|
||||
val description = stringResource(if (source == CallVideoSource.LOCAL) R.string.call_video_local else R.string.call_video_remote)
|
||||
val click = { if (large) onTap() else onSelect(source) }
|
||||
if (source == CallVideoSource.REMOTE) {
|
||||
CallVideoPlayback(video, click, frameModifier, description)
|
||||
CallVideoPlayback(video, click, frameModifier, description, large)
|
||||
} else BoxWithConstraints(frameModifier, contentAlignment = Alignment.Center) {
|
||||
val aspect = local.format?.aspect(displayDegrees) ?: 1f
|
||||
val width = minOf(maxWidth, maxHeight * aspect)
|
||||
val turn = large && minOf(maxHeight.value, maxWidth.value * aspect) > minOf(maxWidth.value, maxHeight.value * aspect)
|
||||
val width = if (turn) minOf(maxHeight, maxWidth * aspect) else minOf(maxWidth, maxHeight * aspect)
|
||||
AndroidView(factory = { CameraPreviewView(it, video.preview, keepAwake = false) },
|
||||
modifier = Modifier.size(width, width / aspect), update = { view ->
|
||||
modifier = Modifier.requiredSize(width, width / aspect).graphicsLayer { rotationZ = if (turn) 90f else 0f }, update = { view ->
|
||||
view.update(local.format, displayDegrees); view.alpha = if (local.active) 1f else 0f
|
||||
view.contentDescription = description
|
||||
view.setOnClickListener { if (local.active) click() }; view.isClickable = local.active
|
||||
@@ -111,11 +113,11 @@ internal fun CallVideoStage(video: CallVideoEngine, expanded: CallVideoSource?,
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CallVideoPlayback(video: CallVideoEngine, onTap: () -> Unit, modifier: Modifier, description: String) {
|
||||
private fun CallVideoPlayback(video: CallVideoEngine, onTap: () -> Unit, modifier: Modifier, description: String, large: Boolean) {
|
||||
val format by video.display.collectAsState()
|
||||
key(video) {
|
||||
AndroidView(factory = { CallVideoTexture(it, video) }, modifier = modifier,
|
||||
update = { view -> view.format = format; view.alpha = if (format == null) 0f else 1f
|
||||
update = { view -> view.update(format, large); view.alpha = if (format == null) 0f else 1f
|
||||
view.contentDescription = description
|
||||
view.setOnClickListener { if (format != null) onTap() }; view.isClickable = format != null }, onRelease = { it.close() })
|
||||
}
|
||||
@@ -124,8 +126,12 @@ private fun CallVideoPlayback(video: CallVideoEngine, onTap: () -> Unit, modifie
|
||||
private class CallVideoTexture(context: Context, private val video: CallVideoEngine) : TextureView(context) {
|
||||
private var attached: Surface? = null
|
||||
private var closed = false
|
||||
var format: CallVideoFormat? = null
|
||||
set(value) { field = value; fit() }
|
||||
private var format: CallVideoFormat? = null
|
||||
private var large = false
|
||||
fun update(next: CallVideoFormat?, expanded: Boolean) {
|
||||
if (format == next && large == expanded) return
|
||||
format = next; large = expanded; fit()
|
||||
}
|
||||
init {
|
||||
isOpaque = false
|
||||
surfaceTextureListener = object : SurfaceTextureListener {
|
||||
@@ -146,7 +152,14 @@ private class CallVideoTexture(context: Context, private val video: CallVideoEng
|
||||
val rotated = f.rotation % 180 != 0
|
||||
val w = if (rotated) f.height else f.width; val h = if (rotated) f.width else f.height
|
||||
val scale = minOf(width.toFloat() / w, height.toFloat() / h)
|
||||
setTransform(Matrix().apply { setScale(w * scale / width, h * scale / height, width / 2f, height / 2f) })
|
||||
val turnedScale = minOf(width.toFloat() / h, height.toFloat() / w)
|
||||
val turn = large && turnedScale > scale
|
||||
val fitted = if (turn) turnedScale else scale
|
||||
setTransform(Matrix().apply {
|
||||
setScale(w * fitted / width, h * fitted / height, width / 2f, height / 2f)
|
||||
if (turn) postRotate(90f, width / 2f, height / 2f)
|
||||
})
|
||||
com.utun.chat.data.LogManager.addLog("DEBUG", "call", "video fit view=${width}x$height frame=${w}x$h expanded=$large turn=$turn")
|
||||
}
|
||||
private fun closeOutput() { attached?.let(video::detachSurface); attached = null }
|
||||
fun close() { closed = true; closeOutput() }
|
||||
|
||||
Reference in new issue
Block a user