Add proper picture in picture support to compose CallScreen component.

This commit is contained in:
Alex Hart
2025-08-14 09:28:33 -03:00
committed by Jeffrey Starke
parent affe97a060
commit 515f3dd43f
4 changed files with 96 additions and 0 deletions

View File

@@ -80,6 +80,7 @@ fun CallScreen(
callRecipient: Recipient,
webRtcCallState: WebRtcViewModel.State,
isRemoteVideoOffer: Boolean,
isInPipMode: Boolean,
callScreenState: CallScreenState,
callControlsState: CallControlsState,
callScreenController: CallScreenController = CallScreenController.rememberCallScreenController(
@@ -110,6 +111,16 @@ fun CallScreen(
callStatus = callScreenState.callStatus,
callScreenControlsListener = callScreenControlsListener
)
return
}
if (isInPipMode) {
PictureInPictureCallScreen(
callParticipantsPagerState = callParticipantsPagerState,
callScreenController = callScreenController
)
return
}
@@ -532,6 +543,7 @@ private fun CallScreenPreview() {
callRecipient = Recipient(systemContactName = "Test User"),
webRtcCallState = WebRtcViewModel.State.CALL_CONNECTED,
isRemoteVideoOffer = false,
isInPipMode = false,
callScreenState = CallScreenState(),
callControlsState = CallControlsState(
displayMicToggle = true,

View File

@@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.signal.core.ui.compose.rememberIsInPipMode
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.components.webrtc.CallParticipantListUpdate
@@ -122,6 +123,7 @@ class ComposeCallScreenMediator(private val activity: WebRtcCallActivity, viewMo
callRecipient = recipient,
webRtcCallState = webRtcCallState,
isRemoteVideoOffer = viewModel.isAnswerWithVideoAvailable(),
isInPipMode = rememberIsInPipMode(),
callScreenState = callScreenState,
callControlsState = callControlsState,
callScreenController = callScreenController,

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.webrtc.v2
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import kotlinx.coroutines.launch
/**
* Displayed when the user minimizes the call screen while a call is ongoing.
*/
@Composable
fun PictureInPictureCallScreen(
callParticipantsPagerState: CallParticipantsPagerState,
callScreenController: CallScreenController
) {
val scope = rememberCoroutineScope()
CallParticipantsPager(
callParticipantsPagerState = callParticipantsPagerState,
modifier = Modifier
.fillMaxSize()
.clickable(
onClick = {
scope.launch {
callScreenController.handleEvent(CallScreenController.Event.TOGGLE_CONTROLS)
}
},
enabled = false
)
)
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.ui.compose
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.PictureInPictureModeChangedInfo
import androidx.core.util.Consumer
/**
* Returns whether the screen is currently in the system picture-in-picture mode.
*
* This requires an AppCompatActivity context, so it cannot be utilized in Composables
* that require a preview.
*/
@Composable
fun rememberIsInPipMode(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val activity = LocalContext.current as AppCompatActivity
var pipMode: Boolean by remember { mutableStateOf(activity.isInPictureInPictureMode) }
DisposableEffect(activity) {
val observer = Consumer<PictureInPictureModeChangedInfo> { info ->
pipMode = info.isInPictureInPictureMode
}
activity.addOnPictureInPictureModeChangedListener(
observer
)
onDispose { activity.removeOnPictureInPictureModeChangedListener(observer) }
}
return pipMode
} else {
return false
}
}