From 3980c2844b1be9597cfc503c5832ac196f1578b3 Mon Sep 17 00:00:00 2001 From: rahullohra Date: Wed, 22 Oct 2025 10:38:15 +0530 Subject: [PATCH 1/4] pip ui configuration --- .../video/android/ui/call/CallScreen.kt | 28 ++++++- .../io/getstream/video/android/core/Call.kt | 1 - .../core/pip/PictureInPictureConfiguration.kt | 40 +++++++++ .../compose/lifecycle/MediaPiPLifecycle.kt | 27 +++++- .../android/compose/pip/PictureInPicture.kt | 13 ++- .../ui/components/audio/AudioRoomContent.kt | 81 ++++++++++++++++-- .../components/call/activecall/CallContent.kt | 84 +++++++++++++++++-- .../android/ui/common/AbstractCallActivity.kt | 12 ++- .../android/ui/common/StreamCallActivity.kt | 15 +++- .../common/StreamCallActivityConfiguration.kt | 4 + 10 files changed, 283 insertions(+), 22 deletions(-) create mode 100644 stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pip/PictureInPictureConfiguration.kt diff --git a/demo-app/src/main/kotlin/io/getstream/video/android/ui/call/CallScreen.kt b/demo-app/src/main/kotlin/io/getstream/video/android/ui/call/CallScreen.kt index d829f834eb4..a6289028654 100644 --- a/demo-app/src/main/kotlin/io/getstream/video/android/ui/call/CallScreen.kt +++ b/demo-app/src/main/kotlin/io/getstream/video/android/ui/call/CallScreen.kt @@ -117,6 +117,7 @@ import io.getstream.video.android.core.Call import io.getstream.video.android.core.RealtimeConnection import io.getstream.video.android.core.call.state.ChooseLayout import io.getstream.video.android.core.model.PreferredVideoResolution +import io.getstream.video.android.core.pip.PictureInPictureConfiguration import io.getstream.video.android.core.utils.isEnabled import io.getstream.video.android.filters.video.BlurredBackgroundVideoFilter import io.getstream.video.android.filters.video.VirtualBackgroundVideoFilter @@ -145,6 +146,7 @@ import kotlinx.coroutines.launch fun CallScreen( call: Call, showDebugOptions: Boolean = false, + pictureInPictureConfiguration: PictureInPictureConfiguration? = null, onCallDisconnected: () -> Unit = {}, onUserLeaveCall: () -> Unit = {}, ) { @@ -316,7 +318,8 @@ fun CallScreen( ), call = call, layout = layout, - enableInPictureInPicture = true, + pictureInPictureConfiguration = pictureInPictureConfiguration + ?: PictureInPictureConfiguration(true), enableDiagnostics = BuildConfig.DEBUG || StreamBuildFlavorUtil.isDevelopment, onCallAction = { when (it) { @@ -833,6 +836,29 @@ fun CallScreen( } } +@Deprecated( + "Use CallScreen with pictureInPictureConfiguration argument", + ReplaceWith( + "CallScreen(call, showDebugOptions, pictureInPictureConfiguration, onCallDisconnected, onUserLeaveCall)", + ), +) +@OptIn(FlowPreview::class) +@Composable +fun CallScreen( + call: Call, + showDebugOptions: Boolean = false, + onCallDisconnected: () -> Unit = {}, + onUserLeaveCall: () -> Unit = {}, +) { + CallScreen( + call, + showDebugOptions, + PictureInPictureConfiguration(true), + onCallDisconnected, + onUserLeaveCall, + ) +} + /** * Executes the transcription APIs based on the current transcription state and settings. * diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt index 0c29246f2b1..50beb184109 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt @@ -94,7 +94,6 @@ import io.getstream.webrtc.android.ui.VideoTextureViewRenderer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pip/PictureInPictureConfiguration.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pip/PictureInPictureConfiguration.kt new file mode 100644 index 00000000000..98298acb804 --- /dev/null +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pip/PictureInPictureConfiguration.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2024 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-video-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getstream.video.android.core.pip + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Configuration for controlling Picture-in-Picture (PiP) behavior within a call UI. + * + * @property enable + * Indicates whether Picture-in-Picture mode should be enabled for this call. + * When `true`, the SDK will handle entering PiP mode automatically when appropriate + * (for example, when the user navigates away from the app during an active call). + * + * @property autoEnterEnabled + * Determines whether to call [android.app.PictureInPictureParams.Builder.setAutoEnterEnabled] + * when configuring the PiP parameters. When `true`, the system automatically enters PiP mode + * when the user presses the home button or performs an equivalent action. Set this to `false` + * if you prefer to manually control when PiP mode should be entered. + */ +@Parcelize +public data class PictureInPictureConfiguration( + val enable: Boolean, + val autoEnterEnabled: Boolean = true, +) : Parcelable diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt index 80128ae4160..12c4faa72b6 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt @@ -31,6 +31,7 @@ import io.getstream.video.android.compose.pip.enterPictureInPicture import io.getstream.video.android.compose.pip.findActivity import io.getstream.video.android.compose.pip.isInPictureInPictureMode import io.getstream.video.android.core.Call +import io.getstream.video.android.core.pip.PictureInPictureConfiguration /** * Register a call media lifecycle that controls camera and microphone depending on lifecycles. @@ -44,7 +45,8 @@ import io.getstream.video.android.core.Call @Composable public fun MediaPiPLifecycle( call: Call, - enableInPictureInPicture: Boolean = false, + pictureInPictureConfiguration: PictureInPictureConfiguration = + PictureInPictureConfiguration(true), ) { val context = LocalContext.current val lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current @@ -64,7 +66,7 @@ public fun MediaPiPLifecycle( override fun onActivityResumed(activity: Activity) { if (activity == currentActivity) { val isInPictureInPicture = context.isInPictureInPictureMode - if (!isInPictureInPicture && !enableInPictureInPicture) { + if (!isInPictureInPicture && !pictureInPictureConfiguration.enable) { call.camera.resume(fromUser = false) call.microphone.resume(fromUser = false) } @@ -74,13 +76,17 @@ public fun MediaPiPLifecycle( override fun onActivityPaused(activity: Activity) { if (activity == currentActivity) { val isInPictureInPicture = context.isInPictureInPictureMode - if (!isInPictureInPicture && !enableInPictureInPicture) { + if (!isInPictureInPicture && !pictureInPictureConfiguration.enable) { call.camera.pause(fromUser = false) call.microphone.pause(fromUser = false) } else if (!isInPictureInPicture) { Handler(Looper.getMainLooper()).post { try { - enterPictureInPicture(context = context, call = call) + enterPictureInPicture( + context = context, + call = call, + pictureInPictureConfiguration, + ) } catch (e: Exception) { StreamLog.d("MediaPiPLifecycle") { e.stackTraceToString() } } @@ -103,3 +109,16 @@ public fun MediaPiPLifecycle( } } } + +@Deprecated( + "Use MediaPiPLifecycle with pictureInPictureConfiguration", + ReplaceWith("MediaPiPLifecycle(call, pictureInPictureConfiguration"), + DeprecationLevel.ERROR, +) +@Composable +public fun MediaPiPLifecycle( + call: Call, + enableInPictureInPicture: Boolean = false, +) { + MediaPiPLifecycle(call, PictureInPictureConfiguration(enableInPictureInPicture)) +} diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/pip/PictureInPicture.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/pip/PictureInPicture.kt index c6146dfa514..09f46d5785b 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/pip/PictureInPicture.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/pip/PictureInPicture.kt @@ -35,9 +35,14 @@ import androidx.compose.ui.platform.LocalContext import androidx.core.app.PictureInPictureModeChangedInfo import androidx.core.util.Consumer import io.getstream.video.android.core.Call +import io.getstream.video.android.core.pip.PictureInPictureConfiguration @Suppress("DEPRECATION") -internal fun enterPictureInPicture(context: Context, call: Call) { +internal fun enterPictureInPicture( + context: Context, + call: Call, + pictureInPictureConfiguration: PictureInPictureConfiguration, +) { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val currentOrientation = context.resources.configuration.orientation @@ -52,8 +57,12 @@ internal fun enterPictureInPicture(context: Context, call: Call) { val params = PictureInPictureParams.Builder() params.setAspectRatio(aspect).apply { + var defaultAutoEnterEnabled = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + defaultAutoEnterEnabled = pictureInPictureConfiguration.autoEnterEnabled + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - setAutoEnterEnabled(true) + setAutoEnterEnabled(defaultAutoEnterEnabled) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { setTitle("Video Player") diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt index 9ce3728fbe4..2f49e1d5eea 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt @@ -48,6 +48,7 @@ import io.getstream.video.android.compose.pip.rememberIsInPipMode import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.core.Call import io.getstream.video.android.core.ParticipantState +import io.getstream.video.android.core.pip.PictureInPictureConfiguration import io.getstream.video.android.mock.StreamPreviewDataUtils import io.getstream.video.android.mock.previewCall @@ -105,7 +106,10 @@ public fun AudioRoomContent( }, onLeaveRoom: (() -> Unit)? = null, onBackPressed: () -> Unit = {}, - enableInPictureInPicture: Boolean = true, + pictureInPictureConfiguration: PictureInPictureConfiguration = PictureInPictureConfiguration( + true, + true, + ), pictureInPictureContent: @Composable ( call: Call, orientation: Int, @@ -128,13 +132,13 @@ public fun AudioRoomContent( MediaPiPLifecycle( call = call, - enableInPictureInPicture = enableInPictureInPicture, + pictureInPictureConfiguration, ) BackHandler { - if (enableInPictureInPicture) { + if (pictureInPictureConfiguration.enable) { try { - enterPictureInPicture(context = context, call = call) + enterPictureInPicture(context = context, call = call, pictureInPictureConfiguration) } catch (e: Exception) { StreamLog.e(tag = "AudioRoomContent") { e.stackTraceToString() } call.leave() @@ -144,7 +148,7 @@ public fun AudioRoomContent( } } - if (isInPictureInPicture && enableInPictureInPicture) { + if (isInPictureInPicture && pictureInPictureConfiguration.enable) { pictureInPictureContent(call, orientation) } else { Scaffold( @@ -171,6 +175,73 @@ public fun AudioRoomContent( } } +@Deprecated( + "Use AudioRoomContent with pictureInPictureConfiguration", + level = DeprecationLevel.ERROR, +) +@Composable +public fun AudioRoomContent( + modifier: Modifier = Modifier, + call: Call, + isShowingAppBar: Boolean = true, + permissions: VideoPermissionsState = rememberMicrophonePermissionState(call = call), + title: String = + stringResource( + id = io.getstream.video.android.ui.common.R.string.stream_video_audio_room_title, + ), + appBarContent: @Composable (call: Call) -> Unit = { + AudioAppBar( + modifier = Modifier.fillMaxWidth(), + title = title, + ) + }, + style: AudioRendererStyle = RegularAudioRendererStyle(), + audioRenderer: @Composable ( + participant: ParticipantState, + style: AudioRendererStyle, + ) -> Unit = { audioParticipant, audioStyle -> + ParticipantAudio( + participant = audioParticipant, + style = audioStyle, + ) + }, + audioContent: @Composable BoxScope.(call: Call) -> Unit = { + val participants by call.state.participants.collectAsStateWithLifecycle() + AudioParticipantsGrid( + modifier = Modifier + .testTag("audio_content") + .fillMaxSize(), + participants = participants, + style = style, + audioRenderer = audioRenderer, + ) + }, + onLeaveRoom: (() -> Unit)? = null, + onBackPressed: () -> Unit = {}, + enableInPictureInPicture: Boolean, + pictureInPictureContent: @Composable ( + call: Call, + orientation: Int, + ) -> Unit = { call, _ -> DefaultPictureInPictureContent(call, audioContent) }, + controlsContent: @Composable (call: Call) -> Unit = { + AudioControlActions( + modifier = Modifier + .testTag("audio_controls_content") + .fillMaxWidth(), + call = call, + onLeaveRoom = onLeaveRoom, + ) + }, +) { + AudioRoomContent( + modifier, call, isShowingAppBar, permissions, title, appBarContent, style, audioRenderer, audioContent, onLeaveRoom, onBackPressed, + PictureInPictureConfiguration( + enableInPictureInPicture, + ), + pictureInPictureContent, controlsContent, + ) +} + @Composable internal fun DefaultPictureInPictureContent( call: Call, diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt index 4021dd8c0d2..40bba2ede76 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt @@ -73,6 +73,7 @@ import io.getstream.video.android.compose.ui.components.video.VideoRenderer import io.getstream.video.android.core.Call import io.getstream.video.android.core.ParticipantState import io.getstream.video.android.core.call.state.CallAction +import io.getstream.video.android.core.pip.PictureInPictureConfiguration import io.getstream.video.android.mock.StreamPreviewDataUtils import io.getstream.video.android.mock.previewCall @@ -93,7 +94,7 @@ import io.getstream.video.android.mock.previewCall * @param videoContent Content is shown that renders all participants' videos. * @param videoOverlayContent Content is shown that will be drawn over the [videoContent], excludes [controlsContent]. * @param controlsContent Content is shown that allows users to trigger different actions to control a joined call. - * @param enableInPictureInPicture If the user has engaged in Picture-In-Picture mode. + * @param pictureInPictureConfiguration User can provide Picture-In-Picture configuration. * @param pictureInPictureContent Content shown when the user enters Picture in Picture mode, if it's been enabled in the app. * @param closedCaptionUi You can pass your composable lambda here to render Closed Captions */ @@ -148,7 +149,10 @@ public fun CallContent( onCallAction = onCallAction, ) }, - enableInPictureInPicture: Boolean = true, + pictureInPictureConfiguration: PictureInPictureConfiguration = PictureInPictureConfiguration( + true, + true, + ), pictureInPictureContent: @Composable (Call) -> Unit = { DefaultPictureInPictureContent(it) }, enableDiagnostics: Boolean = false, closedCaptionUi: @Composable (Call) -> Unit = {}, @@ -161,13 +165,13 @@ public fun CallContent( MediaPiPLifecycle( call = call, - enableInPictureInPicture = enableInPictureInPicture, + enableInPictureInPicture = pictureInPictureConfiguration.enable, ) BackHandler { - if (enableInPictureInPicture) { + if (pictureInPictureConfiguration.enable) { try { - enterPictureInPicture(context = context, call = call) + enterPictureInPicture(context = context, call = call, pictureInPictureConfiguration) } catch (e: Exception) { StreamLog.e(tag = "CallContent") { e.stackTraceToString() } call.leave() @@ -177,7 +181,7 @@ public fun CallContent( } } - if (isInPictureInPicture && enableInPictureInPicture) { + if (isInPictureInPicture && pictureInPictureConfiguration.enable) { pictureInPictureContent(call) } else { Scaffold( @@ -240,6 +244,74 @@ public fun CallContent( } } +@Deprecated("Use CallContentV2 instead") +@Composable +public fun CallContent( + call: Call, + modifier: Modifier = Modifier, + layout: LayoutType = LayoutType.DYNAMIC, + permissions: VideoPermissionsState = rememberCallPermissionsState(call = call), + onBackPressed: () -> Unit = {}, + onCallAction: (CallAction) -> Unit = { DefaultOnCallActionHandler.onCallAction(call, it) }, + appBarContent: @Composable (call: Call) -> Unit = { + CallAppBar( + call = call, + onBackPressed = onBackPressed, + onCallAction = onCallAction, + ) + }, + style: VideoRendererStyle = RegularVideoRendererStyle(), + videoRenderer: @Composable ( + modifier: Modifier, + call: Call, + participant: ParticipantState, + style: VideoRendererStyle, + ) -> Unit = { videoModifier, videoCall, videoParticipant, videoStyle -> + ParticipantVideo( + modifier = videoModifier, + call = videoCall, + participant = videoParticipant, + style = videoStyle, + ) + }, + floatingVideoRenderer: @Composable (BoxScope.(call: Call, IntSize) -> Unit)? = null, + videoContent: @Composable RowScope.(call: Call) -> Unit = { + ParticipantsLayout( + layoutType = layout, + call = call, + modifier = Modifier + .fillMaxSize() + .weight(1f) + .padding(bottom = VideoTheme.dimens.spacingXXs), + style = style, + videoRenderer = videoRenderer, + floatingVideoRenderer = floatingVideoRenderer, + ) + }, + videoOverlayContent: @Composable (call: Call) -> Unit = {}, + controlsContent: @Composable (call: Call) -> Unit = { + ControlActions( + modifier = Modifier.wrapContentWidth(), + call = call, + onCallAction = onCallAction, + ) + }, + enableInPictureInPicture: Boolean, + pictureInPictureContent: @Composable (Call) -> Unit = { DefaultPictureInPictureContent(it) }, + enableDiagnostics: Boolean = false, + closedCaptionUi: @Composable (Call) -> Unit = {}, +) { + CallContent( + call, modifier, layout, permissions, onBackPressed, onCallAction, appBarContent, style, + videoRenderer, floatingVideoRenderer, videoContent, videoOverlayContent, controlsContent, + PictureInPictureConfiguration( + enableInPictureInPicture, + true, + ), + pictureInPictureContent, enableDiagnostics, closedCaptionUi, + ) +} + /** * Renders the default PiP content, using the call state that's provided. * diff --git a/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/AbstractCallActivity.kt b/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/AbstractCallActivity.kt index a201a73d11f..d17e9106dc8 100644 --- a/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/AbstractCallActivity.kt +++ b/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/AbstractCallActivity.kt @@ -31,6 +31,7 @@ import android.view.WindowManager import androidx.activity.ComponentActivity import io.getstream.video.android.core.Call import io.getstream.video.android.core.call.state.ToggleScreenConfiguration +import io.getstream.video.android.core.pip.PictureInPictureConfiguration import io.getstream.video.android.model.StreamCallId /** @@ -128,6 +129,10 @@ public abstract class AbstractCallActivity : ComponentActivity() { } } + protected open fun getPictureInPictureConfiguration(): PictureInPictureConfiguration { + return PictureInPictureConfiguration(true) + } + public fun enterPictureInPicture() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val currentOrientation = resources.configuration.orientation @@ -142,8 +147,13 @@ public abstract class AbstractCallActivity : ComponentActivity() { enterPictureInPictureMode( PictureInPictureParams.Builder().setAspectRatio(aspect).apply { + var defaultAutoEnterEnabled = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + defaultAutoEnterEnabled = + getPictureInPictureConfiguration().autoEnterEnabled + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - this.setAutoEnterEnabled(true) + this.setAutoEnterEnabled(defaultAutoEnterEnabled) } }.build(), ) diff --git a/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt b/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt index 3cefdde79c9..8a55fa92539 100644 --- a/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt +++ b/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt @@ -157,7 +157,6 @@ public abstract class StreamCallActivity : ComponentActivity(), ActivityCallOper extraData?.also { putAll(it) } - logger.d { "Created Bundle. -> $this" } } } @@ -527,6 +526,7 @@ public abstract class StreamCallActivity : ComponentActivity(), ActivityCallOper } else -> { + // Join Video Room logger.w { "[onIntentAction] #ringing; No action provided to the intent will try to join call by default [action: $action], [cid: ${call.cid}]" } @@ -710,8 +710,19 @@ public abstract class StreamCallActivity : ComponentActivity(), ActivityCallOper enterPictureInPictureMode( PictureInPictureParams.Builder().setAspectRatio(aspect).apply { + var defaultAutoEnterEnabled = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + var configuration = StreamCallActivityConfiguration() + if (!::cachedCall.isInitialized && configurationMap.containsKey(cachedCall.id)) { + configuration = + configurationMap[cachedCall.id] ?: StreamCallActivityConfiguration() + } + + defaultAutoEnterEnabled = + configuration.pictureInPictureConfiguration.autoEnterEnabled + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - this.setAutoEnterEnabled(true) + this.setAutoEnterEnabled(defaultAutoEnterEnabled) } }.build(), ) diff --git a/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivityConfiguration.kt b/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivityConfiguration.kt index 95c9a946df3..21733fa6e75 100644 --- a/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivityConfiguration.kt +++ b/stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivityConfiguration.kt @@ -17,6 +17,7 @@ package io.getstream.video.android.ui.common import android.os.Bundle +import io.getstream.video.android.core.pip.PictureInPictureConfiguration internal object StreamCallActivityConfigStrings { const val EXTRA_STREAM_CONFIG = "stream-activity-config" @@ -50,6 +51,9 @@ public data class StreamCallActivityConfiguration( * Can be used same as normal extras. */ val custom: Bundle? = null, + + val pictureInPictureConfiguration: PictureInPictureConfiguration = + PictureInPictureConfiguration(true), ) /** From f95f200e23d83b052af0356f78bce4d246497999 Mon Sep 17 00:00:00 2001 From: rahullohra Date: Wed, 22 Oct 2025 11:31:46 +0530 Subject: [PATCH 2/4] pip ui configuration --- .../api/stream-video-android-core.api | 25 +++++++++++++++++++ .../api/stream-video-android-ui-compose.api | 25 ++++++++++++++----- .../components/call/activecall/CallContent.kt | 5 +--- .../ui/components/call/lobby/CallLobby.kt | 3 ++- .../api/stream-video-android-ui-core.api | 11 +++++--- 5 files changed, 54 insertions(+), 15 deletions(-) diff --git a/stream-video-android-core/api/stream-video-android-core.api b/stream-video-android-core/api/stream-video-android-core.api index 380db0432dc..38a72f99e33 100644 --- a/stream-video-android-core/api/stream-video-android-core.api +++ b/stream-video-android-core/api/stream-video-android-core.api @@ -12225,6 +12225,31 @@ public abstract interface class io/getstream/video/android/core/permission/andro public abstract fun checkAndroidPermissionsGroup (Landroid/content/Context;Lio/getstream/video/android/core/Call;)Lkotlin/Pair; } +public final class io/getstream/video/android/core/pip/PictureInPictureConfiguration : android/os/Parcelable { + public static final field CREATOR Landroid/os/Parcelable$Creator; + public fun (ZZ)V + public synthetic fun (ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Z + public final fun copy (ZZ)Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; + public static synthetic fun copy$default (Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;ZZILjava/lang/Object;)Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; + public fun describeContents ()I + public fun equals (Ljava/lang/Object;)Z + public final fun getAutoEnterEnabled ()Z + public final fun getEnable ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class io/getstream/video/android/core/pip/PictureInPictureConfiguration$Creator : android/os/Parcelable$Creator { + public fun ()V + public final fun createFromParcel (Landroid/os/Parcel;)Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; + public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; + public final fun newArray (I)[Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; + public synthetic fun newArray (I)[Ljava/lang/Object; +} + public final class io/getstream/video/android/core/socket/ErrorResponse : java/lang/Throwable { public static final field Companion Lio/getstream/video/android/core/socket/ErrorResponse$Companion; public fun ()V diff --git a/stream-video-android-ui-compose/api/stream-video-android-ui-compose.api b/stream-video-android-ui-compose/api/stream-video-android-ui-compose.api index 1e476ffe463..a213bf82dd7 100644 --- a/stream-video-android-ui-compose/api/stream-video-android-ui-compose.api +++ b/stream-video-android-ui-compose/api/stream-video-android-ui-compose.api @@ -1,4 +1,5 @@ public final class io/getstream/video/android/compose/lifecycle/MediaPiPLifecycleKt { + public static final fun MediaPiPLifecycle (Lio/getstream/video/android/core/Call;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;Landroidx/compose/runtime/Composer;II)V public static final fun MediaPiPLifecycle (Lio/getstream/video/android/core/Call;ZLandroidx/compose/runtime/Composer;II)V } @@ -447,6 +448,7 @@ public final class io/getstream/video/android/compose/ui/components/audio/AudioR } public final class io/getstream/video/android/compose/ui/components/audio/AudioRoomContentKt { + public static final fun AudioRoomContent (Landroidx/compose/ui/Modifier;Lio/getstream/video/android/core/Call;ZLio/getstream/video/android/compose/permission/VideoPermissionsState;Ljava/lang/String;Lkotlin/jvm/functions/Function3;Lio/getstream/video/android/compose/ui/components/audio/AudioRendererStyle;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V public static final fun AudioRoomContent (Landroidx/compose/ui/Modifier;Lio/getstream/video/android/core/Call;ZLio/getstream/video/android/compose/permission/VideoPermissionsState;Ljava/lang/String;Lkotlin/jvm/functions/Function3;Lio/getstream/video/android/compose/ui/components/audio/AudioRendererStyle;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V } @@ -476,10 +478,12 @@ public final class io/getstream/video/android/compose/ui/components/audio/Compos public final class io/getstream/video/android/compose/ui/components/audio/ComposableSingletons$AudioRoomContentKt { public static final field INSTANCE Lio/getstream/video/android/compose/ui/components/audio/ComposableSingletons$AudioRoomContentKt; public static field lambda-1 Lkotlin/jvm/functions/Function4; - public static field lambda-2 Lkotlin/jvm/functions/Function2; + public static field lambda-2 Lkotlin/jvm/functions/Function4; + public static field lambda-3 Lkotlin/jvm/functions/Function2; public fun ()V public final fun getLambda-1$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function4; - public final fun getLambda-2$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda-2$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function4; + public final fun getLambda-3$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/video/android/compose/ui/components/audio/ComposableSingletons$ParticipantAudioKt { @@ -1053,6 +1057,7 @@ public final class io/getstream/video/android/compose/ui/components/call/activec } public final class io/getstream/video/android/compose/ui/components/call/activecall/CallContentKt { + public static final fun CallContent (Lio/getstream/video/android/core/Call;Landroidx/compose/ui/Modifier;Lio/getstream/video/android/compose/ui/components/call/renderer/LayoutType;Lio/getstream/video/android/compose/permission/VideoPermissionsState;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lio/getstream/video/android/compose/ui/components/call/renderer/VideoRendererStyle;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;Lkotlin/jvm/functions/Function3;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V public static final fun CallContent (Lio/getstream/video/android/core/Call;Landroidx/compose/ui/Modifier;Lio/getstream/video/android/compose/ui/components/call/renderer/LayoutType;Lio/getstream/video/android/compose/permission/VideoPermissionsState;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lio/getstream/video/android/compose/ui/components/call/renderer/VideoRendererStyle;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;ZLkotlin/jvm/functions/Function3;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V } @@ -1068,18 +1073,26 @@ public final class io/getstream/video/android/compose/ui/components/call/activec public final class io/getstream/video/android/compose/ui/components/call/activecall/ComposableSingletons$CallContentKt { public static final field INSTANCE Lio/getstream/video/android/compose/ui/components/call/activecall/ComposableSingletons$CallContentKt; public static field lambda-1 Lkotlin/jvm/functions/Function6; + public static field lambda-10 Lkotlin/jvm/functions/Function2; public static field lambda-2 Lkotlin/jvm/functions/Function3; public static field lambda-3 Lkotlin/jvm/functions/Function3; public static field lambda-4 Lkotlin/jvm/functions/Function3; - public static field lambda-5 Lkotlin/jvm/functions/Function2; - public static field lambda-6 Lkotlin/jvm/functions/Function2; + public static field lambda-5 Lkotlin/jvm/functions/Function6; + public static field lambda-6 Lkotlin/jvm/functions/Function3; + public static field lambda-7 Lkotlin/jvm/functions/Function3; + public static field lambda-8 Lkotlin/jvm/functions/Function3; + public static field lambda-9 Lkotlin/jvm/functions/Function2; public fun ()V public final fun getLambda-1$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function6; + public final fun getLambda-10$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function2; public final fun getLambda-2$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3; public final fun getLambda-3$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3; public final fun getLambda-4$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3; - public final fun getLambda-5$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function2; - public final fun getLambda-6$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function2; + public final fun getLambda-5$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function6; + public final fun getLambda-6$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-7$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-8$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-9$stream_video_android_ui_compose_release ()Lkotlin/jvm/functions/Function2; } public final class io/getstream/video/android/compose/ui/components/call/activecall/internal/ComposableSingletons$InviteUsersDialogKt { diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt index 40bba2ede76..ef82976c33b 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt @@ -163,10 +163,7 @@ public fun CallContent( DefaultPermissionHandler(videoPermission = permissions) - MediaPiPLifecycle( - call = call, - enableInPictureInPicture = pictureInPictureConfiguration.enable, - ) + MediaPiPLifecycle(call = call, pictureInPictureConfiguration) BackHandler { if (pictureInPictureConfiguration.enable) { diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/lobby/CallLobby.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/lobby/CallLobby.kt index 3fdbbffbef5..c8ef7502d1f 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/lobby/CallLobby.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/lobby/CallLobby.kt @@ -55,6 +55,7 @@ import io.getstream.video.android.core.ParticipantState import io.getstream.video.android.core.StreamVideo import io.getstream.video.android.core.call.state.CallAction import io.getstream.video.android.core.model.VideoTrack +import io.getstream.video.android.core.pip.PictureInPictureConfiguration import io.getstream.video.android.mock.StreamPreviewDataUtils import io.getstream.video.android.mock.previewCall import io.getstream.video.android.model.User @@ -131,7 +132,7 @@ public fun CallLobby( ) { DefaultPermissionHandler(videoPermission = permissions) - MediaPiPLifecycle(call = call) + MediaPiPLifecycle(call = call, PictureInPictureConfiguration(false, false)) val configuration = LocalConfiguration.current val isPortrait = configuration.orientation == Configuration.ORIENTATION_PORTRAIT val screenHeightDp = configuration.screenHeightDp diff --git a/stream-video-android-ui-core/api/stream-video-android-ui-core.api b/stream-video-android-ui-core/api/stream-video-android-ui-core.api index 039615dfb04..fb32a36d161 100644 --- a/stream-video-android-ui-core/api/stream-video-android-ui-core.api +++ b/stream-video-android-ui-core/api/stream-video-android-ui-core.api @@ -5,6 +5,7 @@ public abstract class io/getstream/video/android/ui/common/AbstractCallActivity public final fun enterPictureInPicture ()V public final fun exitFullscreen ()V public final fun getCall ()Lio/getstream/video/android/core/Call; + protected fun getPictureInPictureConfiguration ()Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; protected fun handleBackPressed ()V public fun onConfigurationChanged (Landroid/content/res/Configuration;)V protected fun onDestroy ()V @@ -144,16 +145,17 @@ public final class io/getstream/video/android/ui/common/StreamCallActivity$Compa public final class io/getstream/video/android/ui/common/StreamCallActivityConfiguration { public fun ()V - public fun (ZZZZZLandroid/os/Bundle;)V - public synthetic fun (ZZZZZLandroid/os/Bundle;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (ZZZZZLandroid/os/Bundle;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;)V + public synthetic fun (ZZZZZLandroid/os/Bundle;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Z public final fun component2 ()Z public final fun component3 ()Z public final fun component4 ()Z public final fun component5 ()Z public final fun component6 ()Landroid/os/Bundle; - public final fun copy (ZZZZZLandroid/os/Bundle;)Lio/getstream/video/android/ui/common/StreamCallActivityConfiguration; - public static synthetic fun copy$default (Lio/getstream/video/android/ui/common/StreamCallActivityConfiguration;ZZZZZLandroid/os/Bundle;ILjava/lang/Object;)Lio/getstream/video/android/ui/common/StreamCallActivityConfiguration; + public final fun component7 ()Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; + public final fun copy (ZZZZZLandroid/os/Bundle;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;)Lio/getstream/video/android/ui/common/StreamCallActivityConfiguration; + public static synthetic fun copy$default (Lio/getstream/video/android/ui/common/StreamCallActivityConfiguration;ZZZZZLandroid/os/Bundle;Lio/getstream/video/android/core/pip/PictureInPictureConfiguration;ILjava/lang/Object;)Lio/getstream/video/android/ui/common/StreamCallActivityConfiguration; public fun equals (Ljava/lang/Object;)Z public final fun getCanKeepScreenOn ()Z public final fun getCanSkiPermissionRationale ()Z @@ -161,6 +163,7 @@ public final class io/getstream/video/android/ui/common/StreamCallActivityConfig public final fun getCloseScreenOnCallEnded ()Z public final fun getCloseScreenOnError ()Z public final fun getCustom ()Landroid/os/Bundle; + public final fun getPictureInPictureConfiguration ()Lio/getstream/video/android/core/pip/PictureInPictureConfiguration; public fun hashCode ()I public fun toString ()Ljava/lang/String; } From 8f38fa6e5509323c16983267520f497e42b38610 Mon Sep 17 00:00:00 2001 From: rahullohra Date: Wed, 22 Oct 2025 12:14:04 +0530 Subject: [PATCH 3/4] remove DeprecationLevel.ERROR --- .../video/android/compose/lifecycle/MediaPiPLifecycle.kt | 1 - .../android/compose/ui/components/audio/AudioRoomContent.kt | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt index 12c4faa72b6..7b095851af9 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/lifecycle/MediaPiPLifecycle.kt @@ -113,7 +113,6 @@ public fun MediaPiPLifecycle( @Deprecated( "Use MediaPiPLifecycle with pictureInPictureConfiguration", ReplaceWith("MediaPiPLifecycle(call, pictureInPictureConfiguration"), - DeprecationLevel.ERROR, ) @Composable public fun MediaPiPLifecycle( diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt index 2f49e1d5eea..18c910d9e04 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/audio/AudioRoomContent.kt @@ -63,7 +63,7 @@ import io.getstream.video.android.mock.previewCall * @param controlsContent Content is shown that allows users to trigger different actions to control a joined call. * @param audioRenderer A single audio renderer renders each individual participant. * @param onLeaveRoom A lambda that will be invoked when the leave quietly button was clicked. - * @param enableInPictureInPicture If the user has engaged in Picture-In-Picture mode. + * @param pictureInPictureConfiguration User can provide Picture-In-Picture configuration. * @param pictureInPictureContent Content shown when the user enters Picture in Picture mode, if it's been enabled in the app. * @param audioContent Content is shown by rendering audio when we're connected to a call successfully. */ @@ -177,7 +177,6 @@ public fun AudioRoomContent( @Deprecated( "Use AudioRoomContent with pictureInPictureConfiguration", - level = DeprecationLevel.ERROR, ) @Composable public fun AudioRoomContent( From e01545985dd41502f7012bfd99113cf9dc0a1570 Mon Sep 17 00:00:00 2001 From: rahullohra Date: Wed, 22 Oct 2025 12:50:53 +0530 Subject: [PATCH 4/4] refactor --- .../compose/ui/components/call/activecall/CallContent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt index ef82976c33b..067fb2ccf03 100644 --- a/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt +++ b/stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/CallContent.kt @@ -241,7 +241,7 @@ public fun CallContent( } } -@Deprecated("Use CallContentV2 instead") +@Deprecated("Use CallContent with pictureInPictureConfiguration argument") @Composable public fun CallContent( call: Call,